Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,6 @@ class ApiClient(

companion object {
const val PROD_SERVER_URL = "https://api.simplesplitapp.link"
const val DEBUG_SERVER_URL = "http://10.0.2.2:8080"
const val DEBUG_SERVER_URL = "https://10.0.2.2:8443"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class SettingsImpl(

override var httpLogLevel: LogLevel
get() {
return prefs.pull<String>(HTTP_LOG_LEVEL.key, StringUtils.EMPTY).let { name ->
return prefs.pull(HTTP_LOG_LEVEL.key, StringUtils.EMPTY).let { name ->
LogLevel.entries.find { level -> level.name == name }
?: LogLevel.INFO
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ApiClient(
type ApiResponse = ZIO[Scope, Throwable, Response]

private val DefaultPassword = "abc123"
private val baseUrl = "http://127.0.0.1:8080"
private val baseUrl = "https://127.0.0.1:8443"

def getGroup(
uid: String = Groups.TripToDisneyLand,
Expand Down
41 changes: 32 additions & 9 deletions backend/app/src/main/scala/com/github/ai/split/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import com.github.ai.split.data.currency.CurrencyParser
import com.github.ai.split.domain.CliArgumentParser
import com.github.ai.split.domain.usecases.{FillTestDataUseCase, StartUpServerUseCase}
import com.github.ai.split.entity.CliArguments
import com.github.ai.split.entity.HttpProtocol.{HTTP, HTTPS}
import com.github.ai.split.presentation.routes.{CurrencyRoutes, ExpenseRoutes, ExportRoutes, GroupRoutes, MemberRoutes}
import io.getquill.SnakeCase
import io.getquill.jdbczio.Quill
import zio.*
import zio.http.*
import zio.logging.LogFormat
import zio.logging.backend.SLF4J
import zio.direct.*

object Main extends ZIOAppDefault {

Expand All @@ -24,19 +26,39 @@ object Main extends ZIOAppDefault {
Runtime.removeDefaultLoggers >>> SLF4J.slf4j(LogFormat.colored)
}

private def application() = {
for {
startupUseCase <- ZIO.service[StartUpServerUseCase]
_ <- startupUseCase.startUpServer()
_ <- Server.serve(routes)
} yield ()
private def application() = defer {
val startUpUseCase = ZIO.service[StartUpServerUseCase].run
startUpUseCase.startUpServer().run

Server.serve(routes).run

()
}

private def createServerConfig(
arguments: CliArguments
) = defer {
arguments.protocol match {
case HTTP =>
Server.Config.default
.port(arguments.getPort())

case HTTPS =>
Server.Config.default
.port(arguments.getPort())
.ssl(SSLConfig.fromFile("dev-data/server.crt", "dev-data/server.key"))
}
}

override def run: ZIO[ZIOAppArgs, Throwable, Unit] = {
for {
arguments <- CliArgumentParser().parse()
_ <- ZIO.logInfo(s"Starting application with arguments:")
_ <- ZIO.logInfo(arguments.toReadableString())
_ <- ZIO.logInfo(s"Starting server on port ${arguments.getPort()}")
_ <- ZIO.logInfo(s" isUseInMemoryDatabase=${arguments.isUseInMemoryDatabase}")
_ <- ZIO.logInfo(s" isPopulateTestData=${arguments.isPopulateTestData}")
_ <- ZIO.logInfo(s" protocol=${arguments.protocol}")

serverConfig <- createServerConfig(arguments)

_ <- application().provide(
// Application arguments
Expand Down Expand Up @@ -96,7 +118,8 @@ object Main extends ZIOAppDefault {

// Others
Layers.currencyParser,
Server.defaultWithPort(8080),
Server.live,
ZLayer.succeed(serverConfig),
Quill.H2.fromNamingStrategy(SnakeCase),
if (arguments.isUseInMemoryDatabase) {
Quill.DataSource.fromPrefix("test-h2db")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,72 @@
package com.github.ai.split.domain

import com.github.ai.split.utils.some
import com.github.ai.split.entity.CliArguments
import com.github.ai.split.entity.exception.DomainError
import com.github.ai.split.entity.{CliArguments, HttpProtocol}
import com.github.ai.split.entity.exception.{DomainError, ParsingError}
import zio.*
import zio.direct.*

import scala.collection.mutable

class CliArgumentParser {

def parse(): ZIO[ZIOAppArgs, DomainError, CliArguments] = {
for {
appArgs <- ZIO.service[ZIOAppArgs]
parsedArgs <- parseArguments(appArgs.getArgs.toArray)
} yield parsedArgs
defer {
val args = ZIO.service[ZIOAppArgs].run
parseArguments(args.getArgs.toList).run
}
}

private def parseArguments(args: Array[String]): IO[DomainError, CliArguments] = {
ZIO.foldLeft(args)(CliArguments()) { (acc, arg) =>
arg match {
case "--in-memory-db" => ZIO.succeed(acc.copy(isUseInMemoryDatabase = true))
case "--populate-data" => ZIO.succeed(acc.copy(isPopulateTestData = true))
case arg =>
ZIO.fail(DomainError(message = s"Unexpected argument: $arg".some))
private def parseArguments(args: List[String]): IO[DomainError, CliArguments] = {
ZIO
.attempt {
var useInMemoryDb = false
var populateData = false
var protocol: Option[HttpProtocol] = None

val queue = mutable.Queue[String]()
queue.addAll(args)

while (queue.nonEmpty) {
val optionName = queue.removeHead()
optionName match {
case CliOptions.InMemoryDb.cliName => useInMemoryDb = true
case CliOptions.PopulateData.cliName => populateData = true
case CliOptions.Protocol.cliName => {
val protocolValue = queue
.removeHeadOption()
.flatMap(value => HttpProtocol.fromString(value))

protocolValue match {
case Some(p) => protocol = Some(p)
case None =>
throw new ParsingError(
s"Invalid option: ${CliOptions.Protocol.cliName}. Expected 'http' or 'https'"
)
}
}

case _ =>
throw new ParsingError(s"Invalid option specified: '$optionName'")
}
}

if (protocol.isEmpty) {
throw new ParsingError(s"Option '${CliOptions.Protocol.cliName}' is required ")
}

CliArguments(
isUseInMemoryDatabase = useInMemoryDb,
isPopulateTestData = populateData,
protocol = protocol.get
)
}
}
.mapError(error => DomainError(cause = error.some))
}

private enum CliOptions(val cliName: String) {
case InMemoryDb extends CliOptions("--in-memory-db")
case PopulateData extends CliOptions("--populate-data")
case Protocol extends CliOptions("--protocol")
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.github.ai.split.entity

import com.github.ai.split.entity.HttpProtocol.{HTTP, HTTPS}

case class CliArguments(
isUseInMemoryDatabase: Boolean = false,
isPopulateTestData: Boolean = false
isUseInMemoryDatabase: Boolean,
isPopulateTestData: Boolean,
protocol: HttpProtocol
) {

def toReadableString(): String =
s"${classOf[CliArguments]}(IN_MEMORY_DB=${isUseInMemoryDatabase}, POPULATE_DATA=${isPopulateTestData})"
def getPort(): Int = {
protocol match
case HTTP => 8080
case HTTPS => 8443
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.github.ai.split.entity

enum HttpProtocol {
case HTTP, HTTPS
}

object HttpProtocol {
def fromString(name: String): Option[HttpProtocol] = {
HttpProtocol.values.find(protocol => protocol.toString.equalsIgnoreCase(name))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,7 @@ class DomainError(
message.orNull,
cause.orNull
)

class ParsingError(
message: String
) extends DomainError(message = Some(message), cause = None)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package com.github.ai.split.utils
import com.github.ai.split.entity.exception.DomainError

import java.io.{PrintWriter, StringWriter}
import java.sql.SQLException

extension (exception: Throwable)
def stackTraceToString(): String = {
Expand Down