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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,6 @@ out/
# Development and application files
backend/app-data
backend/dev-data

# Env files
.env
4 changes: 4 additions & 0 deletions backend/.env.debug
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
POSTGRES_HOST=127.0.0.1
POSTGRES_DB=simplesplit
POSTGRES_USER=changeme
POSTGRES_PASSWORD=changeme
2 changes: 1 addition & 1 deletion backend/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ FROM openjdk:17
EXPOSE 8080 8443
RUN mkdir /app /data /app-data
COPY --from=build /home/sbtuser/src/app/target/scala-*/simple-split-backend.jar /app/simplesplit-backend.jar
ENTRYPOINT ["java", "-jar", "/app/simplesplit-backend.jar"]
ENTRYPOINT ["java", "-jar", "/app/simplesplit-backend.jar", "--protocol", "http"]
17 changes: 0 additions & 17 deletions backend/app/src/main/resources/application.conf

This file was deleted.

51 changes: 0 additions & 51 deletions backend/app/src/main/resources/init.sql

This file was deleted.

2 changes: 1 addition & 1 deletion backend/app/src/main/resources/logback.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@

<logger name="io.netty" level="INFO" />
<logger name="com.zaxxer" level="INFO" />
<logger name="io.getquill" level="INFO" />
<logger name="slick" level="INFO" />
</configuration>
12 changes: 10 additions & 2 deletions backend/app/src/main/scala/com/github/ai/split/Layers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.github.ai.split

import com.github.ai.split.data.JsonSerializer
import com.github.ai.split.data.currency.CurrencyParser
import com.github.ai.split.data.db.{AppDatabase, DatabaseConnectionFactory}
import com.github.ai.split.data.db.dao.{
CurrencyEntityDao,
ExpenseEntityDao,
Expand Down Expand Up @@ -49,8 +50,15 @@ import zio.{ZIO, ZLayer}

object Layers {

// Database
val appDatabase = ZLayer.fromZIO {
for {
db <- DatabaseConnectionFactory().create()
} yield AppDatabase(db)
}

// Dao's
val userDao = ZLayer.fromFunction(UserEntityDao(_))
val userDao = ZLayer.fromFunction(UserEntityDao(_, _))
val groupDao = ZLayer.fromFunction(GroupEntityDao(_))
val groupMemberDao = ZLayer.fromFunction(GroupMemberEntityDao(_))
val expenseDao = ZLayer.fromFunction(ExpenseEntityDao(_))
Expand Down Expand Up @@ -86,7 +94,7 @@ object Layers {
val removeExpenseUseCase = ZLayer.fromFunction(RemoveExpenseUseCase(_))
val exportGroupDataUseCase = ZLayer.fromFunction(ExportGroupDataUseCase(_, _))
val updateMemberUseCase = ZLayer.fromFunction(UpdateMemberUseCase(_, _, _, _))
val startUpServerUseCase = ZLayer.fromFunction(StartUpServerUseCase(_, _, _))
val startUpServerUseCase = ZLayer.fromFunction(StartUpServerUseCase(_, _, _, _))
val fillCurrencyDataUseCase = ZLayer.fromFunction(FillCurrencyDataUseCase(_, _))
val validateCurrencyUseCase = ZLayer.fromFunction(ValidateCurrencyUseCase(_))

Expand Down
14 changes: 5 additions & 9 deletions backend/app/src/main/scala/com/github/ai/split/Main.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.github.ai.split

import com.github.ai.split.data.currency.CurrencyParser
import com.github.ai.split.data.db.AppDatabase
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
Expand Down Expand Up @@ -102,6 +101,9 @@ object Main extends ZIOAppDefault {
Layers.passwordService,
Layers.accessResolverService,

// Database
Layers.appDatabase,

// Repositories
Layers.expenseRepository,
Layers.groupRepository,
Expand All @@ -120,13 +122,7 @@ object Main extends ZIOAppDefault {
Layers.currencyParser,
Layers.jsonSerialized,
Server.live,
ZLayer.succeed(serverConfig),
Quill.H2.fromNamingStrategy(SnakeCase),
if (arguments.isUseInMemoryDatabase) {
Quill.DataSource.fromPrefix("test-h2db")
} else {
Quill.DataSource.fromPrefix("h2db")
}
ZLayer.succeed(serverConfig)
)
} yield ()
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
package com.github.ai.split.data.db

import com.github.ai.split.data.db.model.DatabaseConnection
import com.github.ai.split.entity.db.{
CurrencyEntity,
ExpenseEntity,
ExpenseUid,
GroupEntity,
GroupMemberEntity,
GroupUid,
MemberUid,
PaidByEntity,
SplitBetweenEntity,
Timestamp,
UserEntity,
UserUid
}
import com.github.ai.split.entity.exception.DomainError
import com.github.ai.split.utils.{toDomainError, toProperties}
import slick.jdbc.PostgresProfile.api.*
import slick.lifted.ProvenShape
import zio.{IO, ZIO}
import zio.direct.*

import java.util.{Properties, UUID}

class AppDatabase(
private val connection: DatabaseConnection
) {

val CurrencyTable = TableQuery[CurrencyEntityTable]
val UserTable = TableQuery[UserEntityTable]
val GroupMemberTable = TableQuery[GroupMemberEntityTable]
val PaidByTable = TableQuery[PaidByEntityTable]
val SplitBetweenTable = TableQuery[SplitBetweenEntityTable]
val ExpenseTable = TableQuery[ExpenseEntityTable]
val GroupTable = TableQuery[GroupEntityTable]

val context = Database.forURL(
url = connection.url,
user = connection.user,
password = connection.password,
driver = "org.postgresql.Driver",
keepAliveConnection = true,
prop = Map(("connectionPool", "HikariCP")).toProperties()
)

def initialize(): IO[DomainError, Unit] =
defer {
ZIO
.fromFuture { _ =>
context.run(
DBIO.seq(
CurrencyTable.schema.createIfNotExists,
UserTable.schema.createIfNotExists,
GroupMemberTable.schema.createIfNotExists,
PaidByTable.schema.createIfNotExists,
SplitBetweenTable.schema.createIfNotExists,
ExpenseTable.schema.createIfNotExists,
GroupTable.schema.createIfNotExists
)
)
}
.mapError(_.toDomainError())
.run

()
}
}

class CurrencyEntityTable(tag: Tag) extends Table[CurrencyEntity](tag, None, "CurrencyEntity") {
val isoCode = column[String]("iso_code", O.PrimaryKey)
val name = column[String]("name")
val symbol = column[String]("symbol")

override def * = (isoCode, name, symbol).mapTo[CurrencyEntity]
}

class UserEntityTable(tag: Tag) extends Table[UserEntity](tag, None, "UserEntity") {
val uid = column[UserUid]("uid", O.PrimaryKey)
val name = column[String]("name")

override def * = (uid, name).mapTo[UserEntity]
}

class GroupMemberEntityTable(tag: Tag) extends Table[GroupMemberEntity](tag, None, "GroupMemberEntity") {
val uid = column[MemberUid]("uid", O.PrimaryKey)
val groupUid = column[GroupUid]("group_uid")
val userUid = column[UserUid]("user_uid")

override def * = (uid, groupUid, userUid).mapTo[GroupMemberEntity]
}

class PaidByEntityTable(tag: Tag) extends Table[PaidByEntity](tag, None, "PaidByEntity") {
val groupUid = column[GroupUid]("group_uid")
val expenseUid = column[ExpenseUid]("expense_uid")
val memberUid = column[MemberUid]("member_uid")

override def * = (groupUid, expenseUid, memberUid).mapTo[PaidByEntity]
}

class SplitBetweenEntityTable(tag: Tag) extends Table[SplitBetweenEntity](tag, None, "SplitBetweenEntity") {
val groupUid = column[GroupUid]("group_uid")
val expenseUid = column[ExpenseUid]("expense_uid")
val memberUid = column[MemberUid]("member_uid")

override def * = (groupUid, expenseUid, memberUid).mapTo[SplitBetweenEntity]
}

class ExpenseEntityTable(tag: Tag) extends Table[ExpenseEntity](tag, None, "ExpenseEntity") {
val uid = column[ExpenseUid]("uid", O.PrimaryKey)
val groupUid = column[GroupUid]("group_uid")
val title = column[String]("title")
val description = column[String]("description")
val amount = column[Double]("amount")
val isSplitBetweenAll = column[Boolean]("is_split_between_all")
val created = column[Timestamp]("created")
val modified = column[Timestamp]("modified")

override def * =
(uid, groupUid, title, description, amount, isSplitBetweenAll, created, modified).mapTo[ExpenseEntity]
}

class GroupEntityTable(tag: Tag) extends Table[GroupEntity](tag, None, "GroupEntity") {
val uid = column[GroupUid]("uid", O.PrimaryKey)
val title = column[String]("title")
val description = column[String]("description")
val passwordHash = column[String]("password_hash")
val currencyIsoCode = column[String]("currency_iso_code")
val created = column[Timestamp]("created")
val modified = column[Timestamp]("modified")

override def * =
(uid, title, description, passwordHash, currencyIsoCode, created, modified).mapTo[GroupEntity]
}

given userUidColumnType: BaseColumnType[UserUid] = MappedColumnType.base[UserUid, UUID](
uid => uid.value, // UserUid to UUID
uuid => UserUid(uuid) // UUID to UserUid
)

given groupUidColumnType: BaseColumnType[GroupUid] = MappedColumnType.base[GroupUid, UUID](
uid => uid.value, // GroupUid to UUID
uuid => GroupUid(uuid) // UUID to GroupUid
)

given memberUidColumnType: BaseColumnType[MemberUid] = MappedColumnType.base[MemberUid, UUID](
uid => uid.value, // MemberUid to UUID
uuid => MemberUid(uuid) // UUID to MemberUid
)

given expenseUidColumnType: BaseColumnType[ExpenseUid] = MappedColumnType.base[ExpenseUid, UUID](
uid => uid.value, // ExpenseUid to UUID
uuid => ExpenseUid(uuid) // UUID to ExpenseUid
)

given timestampColumnType: BaseColumnType[Timestamp] = MappedColumnType.base[Timestamp, Long](
timestamp => timestamp.seconds, // Timestamp to Long
seconds => Timestamp(seconds) // Long to Timestamp
)
Loading