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
17 changes: 8 additions & 9 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,27 @@ jobs:
test:
# runs-on: self-hosted
runs-on: ubuntu-latest
env:
# define Java options for both official sbt and sbt-extras
JAVA_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8
JVM_OPTS: -Xms2048M -Xmx2048M -Xss6M -XX:ReservedCodeCacheSize=256M -Dfile.encoding=UTF-8
steps:
- name: Env
run: |
echo "JFROG_USER=${{ secrets.JFROG_USER }}" >> $GITHUB_ENV
echo "JFROG_PASSWORD=${{ secrets.JFROG_PASSWORD }}" >> $GITHUB_ENV
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
echo "CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 8
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '8'
java-version: '17'
distribution: 'temurin'
# cache: 'sbt'
- name: Setup sbt launcher
uses: sbt/setup-sbt@v1
- name: Cross Compile
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt + compile
Copy link

Copilot AI Apr 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The cross-compile step uses sbt + compile, which sbt may interpret as an invalid standalone + command. For cross-building, use sbt +compile or quote the command as sbt '+ compile' (as in release.yml) to ensure sbt parses it correctly.

Suggested change
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt + compile
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt +compile

Copilot uses AI. Check for mistakes.
- name: Run tests & Coverage Report
run: sbt coverage test coverageReport
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt coverage test coverageReport
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
Expand All @@ -60,10 +59,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 8
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '8'
java-version: '17'
distribution: 'temurin'
# cache: 'sbt'
- name: Setup sbt launcher
Expand Down
13 changes: 8 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@ jobs:
echo "JFROG_USER=${{ secrets.JFROG_USER }}" >> $GITHUB_ENV
echo "JFROG_PASSWORD=${{ secrets.JFROG_PASSWORD }}" >> $GITHUB_ENV
echo "GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}" >> $GITHUB_ENV
echo "CODECOV_TOKEN=${{ secrets.CODECOV_TOKEN }}" >> $GITHUB_ENV
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 8
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '8'
java-version: '17'
distribution: 'temurin'
# cache: 'sbt'
- name: Setup sbt launcher
uses: sbt/setup-sbt@v1
- name: Cross Compile
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt '+ compile'
- name: Run tests & Coverage Report
run: sbt coverage test coverageReport coverageAggregate
run: SBT_OPTS="-Xss4M -Xms1g -Xmx4g -Dfile.encoding=UTF-8" sbt coverage test coverageReport coverageAggregate
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v3
with:
Expand All @@ -59,10 +62,10 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up JDK 8
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: '8'
java-version: '17'
distribution: 'temurin'
# cache: 'sbt'
- name: Setup sbt launcher
Expand Down
27 changes: 22 additions & 5 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,24 @@ import app.softnetwork.*

lazy val scala212 = "2.12.20"
lazy val scala213 = "2.13.16"
lazy val javacCompilerVersion = "1.8"
lazy val javacCompilerVersion = "17"
lazy val scalacCompilerOptions = Seq(
"-deprecation",
"-feature",
s"-target:jvm-$javacCompilerVersion"
"-feature"
)

ThisBuild / organization := "app.softnetwork"

name := "generic-persistence-api"

ThisBuild / version := "0.8.0"
ThisBuild / version := "0.8-SNAPSHOT"

lazy val moduleSettings = Seq(
crossScalaVersions := Seq(scala212, scala213),
scalacOptions ++= {
CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 12)) => scalacCompilerOptions :+ "-Ypartial-unification"
case Some((2, 13)) => scalacCompilerOptions
case Some((2, 13)) => scalacCompilerOptions :+ s"-release:$javacCompilerVersion"
case _ => Seq.empty
}
}
Expand Down Expand Up @@ -55,6 +54,24 @@ ThisBuild / dependencyOverrides ++= Seq(
"org.lmdbjava" % "lmdbjava" % "0.9.1" exclude("org.slf4j", "slf4j-api"),
)

ThisBuild / javaOptions ++= Seq(
"--add-opens=java.base/java.util=ALL-UNNAMED",
"--add-opens=java.base/java.util.concurrent=ALL-UNNAMED",
"--add-opens=java.base/java.lang=ALL-UNNAMED",
"--add-opens=java.base/java.lang.invoke=ALL-UNNAMED",
"--add-opens=java.base/java.math=ALL-UNNAMED",
"--add-opens=java.base/java.io=ALL-UNNAMED",
"--add-opens=java.base/java.net=ALL-UNNAMED",
"--add-opens=java.base/java.nio=ALL-UNNAMED",
"--add-opens=java.base/java.text=ALL-UNNAMED",
"--add-opens=java.base/java.time=ALL-UNNAMED",
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"
)

ThisBuild / Test / fork := true

Test / javaOptions ++= javaOptions.value

Test / parallelExecution := false

lazy val common = project.in(file("common"))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ import scala.util.{Failure, Success, Try}
import scala.language.reflectiveCalls

/** Created by smanciot on 12/04/2021.
*/
*/
trait CompletionTestKit extends Completion with Assertions {
_: {def log: Logger} =>
_: { def log: Logger } =>

implicit class AwaitAssertion[T](future: Future[T])(implicit atMost: Duration = defaultTimeout) {
def assert(fun: T => Assertion): Assertion =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ package object model {
acc + char
}
}
def $: String = toSnakeCase
def $ : String = toSnakeCase
}

case class StateWrapper[T <: State](
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/scala/app/softnetwork/persistence/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import java.time.Instant
import scala.language.implicitConversions

/** Created by smanciot on 13/04/2020.
*/
*/
package object persistence {

trait ManifestWrapper[T] {
Expand All @@ -20,7 +20,7 @@ package object persistence {
def generateUUID(key: Option[String] = None): String =
key match {
case Some(clearText) => sha256(clearText)
case _ => UUID.randomUUID().toString
case _ => UUID.randomUUID().toString
}

def now(): Date = Date.from(Instant.now())
Expand All @@ -30,8 +30,8 @@ package object persistence {
}

/** Used for akka and elastic persistence ids, one per targeted environment (development,
* production, ...)
*/
* production, ...)
*/
val version: String = sys.env.getOrElse("VERSION", PersistenceCoreBuildInfo.version)

val environment: String = sys.env.getOrElse(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,16 @@ trait ExternalPersistenceProvider[T <: Timestamped] { _: ManifestWrapper[T] =>
*/
def deleteDocument(uuid: String): Boolean = false

/** Hard-deletes the underlying document referenced by its uuid from the external system. Use for
* GDPR compliance or permanent data removal.
*
* @param uuid
* - the uuid of the document to destroy
* @return
* whether the operation is successful or not
*/
def destroy(uuid: String): Boolean = false

/** Load the document referenced by its uuid
*
* @param uuid
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package app.softnetwork.persistence.query

import app.softnetwork.persistence.ManifestWrapper
import app.softnetwork.persistence.model.{CamelCaseString, StateWrapper, StateWrappertReader, Timestamped}
import app.softnetwork.persistence.model.{
CamelCaseString,
StateWrapper,
StateWrappertReader,
Timestamped
}
import app.softnetwork.serialization.{commonFormats, serialization, updateCaseClass}
import com.typesafe.config.{Config, ConfigFactory}
import org.json4s.Formats
Expand Down Expand Up @@ -167,10 +172,9 @@ trait JsonProvider[T <: Timestamped] extends ExternalPersistenceProvider[T] {
} match {
case Success(value) if value.uuid != uuid => // do nothing
case Success(value) if value.uuid == uuid && value.state.isDefined || value.deleted =>
if(value.deleted){
if (value.deleted) {
lastMatchingLine = None
}
else{
} else {
lastMatchingLine = value.state // return the state
}
case _ =>
Expand All @@ -184,19 +188,18 @@ trait JsonProvider[T <: Timestamped] extends ExternalPersistenceProvider[T] {
case Some(d) => d.toString.toBoolean
case _ => false
}
if(deleted){
if (deleted) {
lastMatchingLine = None
}
else{
} else {
parsed.get("state") match {
case Some(updated: Map[String, Any]) =>
lastMatchingLine match {
case Some(l)
if updated
.get("lastUpdated")
.map(lu => Instant.parse(lu.toString))
.getOrElse(Instant.MIN)
.isAfter(l.lastUpdated) => // update the state
if updated
.get("lastUpdated")
.map(lu => Instant.parse(lu.toString))
.getOrElse(Instant.MIN)
.isAfter(l.lastUpdated) => // update the state
Try(updateCaseClass(l, updated)) match {
case Success(updated: T) =>
lastMatchingLine = Some(updated)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package app.softnetwork.persistence.person.query

import app.softnetwork.persistence.person.model.Person
import app.softnetwork.persistence.query.{InMemoryJournalProvider, InMemoryOffsetProvider, JsonProvider}
import app.softnetwork.persistence.query.{
InMemoryJournalProvider,
InMemoryOffsetProvider,
JsonProvider
}

import java.nio.file.{Files, Paths}

trait PersonToJsonProcessorStream
extends PersonToExternalProcessorStream
extends PersonToExternalProcessorStream
with InMemoryJournalProvider
with InMemoryOffsetProvider
with JsonProvider[Person] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import scala.language.implicitConversions
import scala.reflect.ClassTag

/** Created by smanciot on 04/01/2020.
*/
*/
trait PersistenceTestKit
extends PersistenceGuardian
extends PersistenceGuardian
with BeforeAndAfterAll
with Eventually
with CompletionTestKit
Expand Down Expand Up @@ -61,8 +61,8 @@ trait PersistenceTestKit
}

/** @return
* roles associated with this node
*/
* roles associated with this node
*/
def roles: Seq[String] = Seq.empty

final lazy val akka: String =
Expand Down Expand Up @@ -145,8 +145,8 @@ trait PersistenceTestKit
|""".stripMargin + additionalConfig

/** @return
* additional configuration
*/
* additional configuration
*/
def additionalConfig: String = ""

lazy val akkaConfig: Config = ConfigFactory.parseString(akka)
Expand All @@ -160,7 +160,7 @@ trait PersistenceTestKit
def typedSystem(): ActorSystem[Nothing] = system

/** `PatienceConfig` from [[_root_.akka.actor.testkit.typed.TestKitSettings#DefaultTimeout]]
*/
*/
implicit val patience: PatienceConfig =
PatienceConfig(Settings.DefaultTimeout, Span(100, org.scalatest.time.Millis))

Expand All @@ -175,7 +175,7 @@ trait PersistenceTestKit
}

/** init and join cluster
*/
*/
final def initAndJoinCluster(): Unit = {
testKit.spawn(setup(), "guardian")
// let the nodes join and become Up
Expand Down
7 changes: 6 additions & 1 deletion jdbc/build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@ val akkaPersistenceJdbc = Seq(
"com.typesafe.slick" %% "slick" % Versions.slick,
"com.typesafe.slick" %% "slick-hikaricp" % Versions.slick,
"org.postgresql" % "postgresql" % Versions.postgresql,
"com.mysql" % "mysql-connector-j" % Versions.mysql
"com.mysql" % "mysql-connector-j" % Versions.mysql,
"org.flywaydb" % "flyway-core" % Versions.flyway,
"org.flywaydb" % "flyway-database-postgresql" % Versions.flyway,
// H2 uses the HSQLDB Flyway plugin — test-scoped because H2 is only used in tests.
// If H2 is needed outside tests, move this to Compile scope or add it in the consuming project.
"org.flywaydb" % "flyway-database-hsqldb" % Versions.flyway % Test
)

libraryDependencies ++= akkaPersistenceJdbc
28 changes: 28 additions & 0 deletions jdbc/src/main/resources/db/migration/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Flyway Migrations

## Convention

Each entity type that uses `ColumnMappedJdbcStateProvider` has its own
migration folder: `db/migration/{tablename}/`

## Naming

`V{version}__{description}.sql` (double underscore)

Examples:

- `V1__create_api_keys.sql`
- `V2__add_revoked_at_column.sql`

## Database Compatibility

- Production: PostgreSQL 16+
- Tests: H2 (use PostgreSQL-compatible subset)
- Avoid: PostgreSQL-specific syntax not supported by H2
(e.g., `GENERATED ALWAYS AS IDENTITY`, `jsonb`)

## Flyway Metadata Tables

Each migration folder gets its own Flyway history table:
`flyway_schema_history_{tablename}`. This prevents conflicts when
multiple entities use Flyway in the same database.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ trait SlickDatabase extends ClasspathResources {

def config: Config

/** Provides a DataSource for use by Flyway migrations. Extracted from the underlying HikariCP
* connection pool. Override this method if using a non-HikariCP connection pool.
*/
lazy val dataSource: javax.sql.DataSource = {
db.source match {
case hikari: slick.jdbc.hikaricp.HikariCPJdbcDataSource => hikari.ds
case other =>
throw new IllegalStateException(
s"Expected HikariCP data source for Flyway, got ${other.getClass.getName}. " +
"Configure slick-hikaricp or override the dataSource method."
)
}
}

lazy val slickProfile: String = config.getString("slick.profile")

lazy val db: Database = {
Expand Down
Loading
Loading