From 9b09e729028efa84eb88b5a1f43c3dc418098c68 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 11:46:34 -0400 Subject: [PATCH 01/13] wip --- api/test/db/DbUtils.scala | 2 +- api/test/processor/PurgeDeletedProcessorSpec.scala | 4 ++-- api/test/util/LockUtilSpec.scala | 2 +- build.sbt | 6 +++--- project/build.properties | 2 +- project/plugins.sbt | 6 +++--- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/test/db/DbUtils.scala b/api/test/db/DbUtils.scala index c7db812f1..233edaeb5 100644 --- a/api/test/db/DbUtils.scala +++ b/api/test/db/DbUtils.scala @@ -11,7 +11,7 @@ trait DbUtils { def execute(queries: Query*): Unit = { database.withConnection { c => - queries.foreach(_.anormSql().executeUpdate()(c)) + queries.foreach(_.anormSql().executeUpdate()(using c)) } } diff --git a/api/test/processor/PurgeDeletedProcessorSpec.scala b/api/test/processor/PurgeDeletedProcessorSpec.scala index e8b95a6c1..4df6d03f2 100644 --- a/api/test/processor/PurgeDeletedProcessorSpec.scala +++ b/api/test/processor/PurgeDeletedProcessorSpec.scala @@ -20,7 +20,7 @@ class PurgeDeletedProcessorSpec extends PlaySpec with GuiceOneAppPerSuite with H database.withConnection { c => Query(s"select count(*) from $table") .equals("guid", guid) - .as(SqlParser.int(1).*)(c) + .as(SqlParser.int(1).*)(using c) }.head == 0 } @@ -31,7 +31,7 @@ class PurgeDeletedProcessorSpec extends PlaySpec with GuiceOneAppPerSuite with H .bind("deleted_at", deletedAt) .bind("deleted_by_guid", testUser.guid) .anormSql() - .executeUpdate()(c) + .executeUpdate()(using c) } () } diff --git a/api/test/util/LockUtilSpec.scala b/api/test/util/LockUtilSpec.scala index 90141692a..89b8c0629 100644 --- a/api/test/util/LockUtilSpec.scala +++ b/api/test/util/LockUtilSpec.scala @@ -24,7 +24,7 @@ class LockUtilSpec extends PlaySpec with GuiceOneAppPerSuite with BeforeAndAfter private def exec(query: Query): Unit = { db.withConnection { c => - query.anormSql().executeUpdate()(c) + query.anormSql().executeUpdate()(using c) } } diff --git a/build.sbt b/build.sbt index cb18ff291..b09daec38 100644 --- a/build.sbt +++ b/build.sbt @@ -1,7 +1,7 @@ name := "apibuilder" organization := "io.apibuilder" -ThisBuild / scalaVersion := "3.4.2" +ThisBuild / scalaVersion := "3.7.3" val playJsonVersion = "2.10.6" @@ -90,7 +90,7 @@ lazy val generated = project "com.github.mbryzek" % "lib-util" % "0.0.7", "joda-time" % "joda-time" % "2.12.7", "org.playframework.anorm" %% "anorm-postgres" % "2.7.0", - "org.postgresql" % "postgresql" % "42.7.3", + "org.postgresql" % "postgresql" % "42.7.7", "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.1" % Test, ) ) @@ -122,7 +122,7 @@ lazy val api = project "org.projectlombok" % "lombok" % "1.18.32" % "provided", ("com.github.mbryzek" % "lib-cipher" % "0.0.7").cross(CrossVersion.for3Use2_13), "com.github.mbryzek" % "lib-util" % "0.0.7", - "com.sendgrid" % "sendgrid-java" % "4.10.2", + "com.sendgrid" % "sendgrid-java" % "4.10.3", "com.github.mbryzek" % "lib-query" % "0.0.2", "com.rollbar" % "rollbar-java" % "1.10.0", "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.1" % Test, diff --git a/project/build.properties b/project/build.properties index ee4c672cd..5e6884d37 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.10.1 +sbt.version=1.11.6 diff --git a/project/plugins.sbt b/project/plugins.sbt index ab6517077..1b3b449b5 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -5,12 +5,12 @@ logLevel := Level.Warn resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/" // Use the Play sbt plugin for Play projects -addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.9.5") +addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.9.9") -addSbtPlugin("com.typesafe.play" % "sbt-twirl" % "1.6.8") +addSbtPlugin("com.typesafe.play" % "sbt-twirl" % "1.6.10") addSbtPlugin("com.jsuereth" % "sbt-pgp" % "1.1.1") addSbtPlugin("com.github.sbt" % "sbt-javaagent" % "0.1.8") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.5") From 87cb73d6714e8b20bd6aec6749a522e54a5d07cb Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 11:47:07 -0400 Subject: [PATCH 02/13] wip --- api/test/controllers/OrganizationAttributesSpec.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/test/controllers/OrganizationAttributesSpec.scala b/api/test/controllers/OrganizationAttributesSpec.scala index cf5ffd3a8..1e0cdc575 100644 --- a/api/test/controllers/OrganizationAttributesSpec.scala +++ b/api/test/controllers/OrganizationAttributesSpec.scala @@ -50,7 +50,7 @@ class OrganizationAttributesSpec extends PlaySpec with MockClient with GuiceOneS val updated = await(client.organizations.putAttributesByKeyAndName(org.key, attribute.name, form2)) updated.value must equal("test2") } - + "GET /organizations/:key/attributes" in { val org = createOrganization() val attribute1 = createAttribute() @@ -59,7 +59,7 @@ class OrganizationAttributesSpec extends PlaySpec with MockClient with GuiceOneS await(client.organizations.putAttributesByKeyAndName(org.key, attribute1.name, AttributeValueForm("a"))) await(client.organizations.putAttributesByKeyAndName(org.key, attribute2.name, AttributeValueForm("b"))) - await(client.organizations.getAttributesByKey(org.key)).map(_.value) must equal(Seq("a", "b")) + await(client.organizations.getAttributesByKey(org.key)).map(_.value).sorted must equal(Seq("a", "b")) } "GET /organizations/:key/attributes w/ name filter" in { From 755d78247824c3c96ada0f2e54cac5a3554b9e0a Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 11:49:08 -0400 Subject: [PATCH 03/13] wip --- build.sbt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build.sbt b/build.sbt index b09daec38..11cfdc115 100644 --- a/build.sbt +++ b/build.sbt @@ -24,7 +24,7 @@ lazy val lib = project .settings( libraryDependencies ++= Seq( "com.typesafe.play" %% "play-json" % playJsonVersion, - "joda-time" % "joda-time" % "2.12.7", + "joda-time" % "joda-time" % "2.14.0", ) ) @@ -88,7 +88,7 @@ lazy val generated = project jdbc, "com.github.mbryzek" % "lib-query" % "0.0.5", "com.github.mbryzek" % "lib-util" % "0.0.7", - "joda-time" % "joda-time" % "2.12.7", + "joda-time" % "joda-time" % "2.14.0", "org.playframework.anorm" %% "anorm-postgres" % "2.7.0", "org.postgresql" % "postgresql" % "42.7.7", "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.1" % Test, @@ -107,7 +107,7 @@ lazy val api = project PlayKeys.fileWatchService := play.dev.filewatch.FileWatchService.jdk7(play.sbt.run.toLoggerProxy(sLog.value)), PlayKeys.playDefaultPort := 9001, testOptions += Tests.Argument("-oF"), - javaAgents += "com.datadoghq" % "dd-java-agent" % "1.8.0", + javaAgents += "com.datadoghq" % "dd-java-agent" % "1.53.0", routesImport += "io.apibuilder.api.v0.Bindables.Core._", routesImport += "io.apibuilder.api.v0.Bindables.Models._", routesImport += "io.apibuilder.common.v0.Bindables.Models._", @@ -119,12 +119,12 @@ lazy val api = project "com.typesafe.play" %% "play-guice" % "2.9.4", "com.google.inject" % "guice" % "5.1.0", "com.google.inject.extensions" % "guice-assistedinject" % "5.1.0", - "org.projectlombok" % "lombok" % "1.18.32" % "provided", + "org.projectlombok" % "lombok" % "1.18.42" % "provided", ("com.github.mbryzek" % "lib-cipher" % "0.0.7").cross(CrossVersion.for3Use2_13), "com.github.mbryzek" % "lib-util" % "0.0.7", "com.sendgrid" % "sendgrid-java" % "4.10.3", "com.github.mbryzek" % "lib-query" % "0.0.2", - "com.rollbar" % "rollbar-java" % "1.10.0", + "com.rollbar" % "rollbar-java" % "2.0.0", "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.1" % Test, "com.github.tomakehurst" % "wiremock-standalone" % "3.0.1" % Test ), @@ -146,7 +146,7 @@ lazy val app = project scalacOptions ++= allScalacOptions, PlayKeys.fileWatchService := play.dev.filewatch.FileWatchService.jdk7(play.sbt.run.toLoggerProxy(sLog.value)), PlayKeys.playDefaultPort := 9000, - javaAgents += "com.datadoghq" % "dd-java-agent" % "1.8.0", + javaAgents += "com.datadoghq" % "dd-java-agent" % "1.53.0", routesImport += "io.apibuilder.api.v0.Bindables.Core._", routesImport += "io.apibuilder.api.v0.Bindables.Models._", routesImport += "io.apibuilder.common.v0.Bindables.Models._", @@ -155,8 +155,8 @@ lazy val app = project guice, "com.google.inject" % "guice" % "5.1.0", "com.google.inject.extensions" % "guice-assistedinject" % "5.1.0", - "org.projectlombok" % "lombok" % "1.18.28" % "provided", - "org.apache.commons" % "commons-compress" % "1.26.2", + "org.projectlombok" % "lombok" % "1.18.42" % "provided", + "org.apache.commons" % "commons-compress" % "1.28.0", "com.github.tototoshi" %% "scala-csv" % "1.4.0", "com.vladsch.flexmark" % "flexmark-all" % "0.64.8", "org.webjars" %% "webjars-play" % "3.0.1", From e79c012c155da7a81bda070f500659df3a59688a Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 11:54:43 -0400 Subject: [PATCH 04/13] wip --- api/app/db/InternalUserPasswordsDao.scala | 5 +++-- api/app/util/LockUtil.scala | 4 ++-- build.sbt | 8 ++++---- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/api/app/db/InternalUserPasswordsDao.scala b/api/app/db/InternalUserPasswordsDao.scala index 14894a40f..86ea17ea4 100644 --- a/api/app/db/InternalUserPasswordsDao.scala +++ b/api/app/db/InternalUserPasswordsDao.scala @@ -5,6 +5,7 @@ import cats.implicits.* import com.mbryzek.cipher.Ciphers import db.generated.UserPasswordsDao import io.apibuilder.api.v0.models.Error +import io.flow.postgresql.Query import lib.Validation import java.sql.Connection @@ -97,8 +98,8 @@ class InternalUserPasswordsDao @Inject()( guid = guid, userGuid = userGuid, limit = limit - ) { q => + )(using (q: Query) => { q.and(isDeleted.map(Filters.isDeleted("user_passwords", _))) - }.map(InternalUserPassword(_)) + }).map(InternalUserPassword(_)) } } diff --git a/api/app/util/LockUtil.scala b/api/app/util/LockUtil.scala index 47dc350c6..98c64edf5 100644 --- a/api/app/util/LockUtil.scala +++ b/api/app/util/LockUtil.scala @@ -30,7 +30,7 @@ class LockUtil @Inject() ( Query("SELECT pg_try_advisory_xact_lock({key1}::int, {key2}::int)") .bind("key1", key1) .bind("key2", key2) - .as(SqlParser.bool(1).single)(c) + .as(SqlParser.bool(1).single)(using c) } private def toHashInts(id: String): (Int, Int) = { @@ -39,4 +39,4 @@ class LockUtil @Inject() ( val key2 = buffer.getInt(4) (key1, key2) } -} \ No newline at end of file +} diff --git a/build.sbt b/build.sbt index 11cfdc115..94e1b5936 100644 --- a/build.sbt +++ b/build.sbt @@ -87,7 +87,7 @@ lazy val generated = project ws, jdbc, "com.github.mbryzek" % "lib-query" % "0.0.5", - "com.github.mbryzek" % "lib-util" % "0.0.7", + "com.github.mbryzek" % "lib-util" % "0.0.16", "joda-time" % "joda-time" % "2.14.0", "org.playframework.anorm" %% "anorm-postgres" % "2.7.0", "org.postgresql" % "postgresql" % "42.7.7", @@ -120,10 +120,10 @@ lazy val api = project "com.google.inject" % "guice" % "5.1.0", "com.google.inject.extensions" % "guice-assistedinject" % "5.1.0", "org.projectlombok" % "lombok" % "1.18.42" % "provided", - ("com.github.mbryzek" % "lib-cipher" % "0.0.7").cross(CrossVersion.for3Use2_13), - "com.github.mbryzek" % "lib-util" % "0.0.7", + ("com.github.mbryzek" % "lib-cipher" % "0.0.8").cross(CrossVersion.for3Use2_13), + "com.github.mbryzek" % "lib-util" % "0.0.16", "com.sendgrid" % "sendgrid-java" % "4.10.3", - "com.github.mbryzek" % "lib-query" % "0.0.2", + "com.github.mbryzek" % "lib-query" % "0.0.5", "com.rollbar" % "rollbar-java" % "2.0.0", "org.scalatestplus.play" %% "scalatestplus-play" % "7.0.1" % Test, "com.github.tomakehurst" % "wiremock-standalone" % "3.0.1" % Test From 9cc5209bbe8ee4bc45de7bd6c81d95a78c2dfbdf Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:08:15 -0400 Subject: [PATCH 05/13] wip --- core/app/builder/ServiceSpecValidator.scala | 35 +++++++++++++++---- .../api_json/ApiJsonServiceValidator.scala | 18 ++-------- 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/core/app/builder/ServiceSpecValidator.scala b/core/app/builder/ServiceSpecValidator.scala index 8d90cda03..5f1f751c8 100644 --- a/core/app/builder/ServiceSpecValidator.scala +++ b/core/app/builder/ServiceSpecValidator.scala @@ -130,12 +130,29 @@ case class ServiceSpecValidator( sequenceUnique( Seq( validateName(s"$p name", model.name), - validateFields(p, model.fields) + validateFields(p, model.fields), + validateTypeInterfaces(s"Model[${model.name}]", model.interfaces) ) ++ model.interfaces.map { n => validateInterfaceFields(p, n, model.fields) } ) } - def validateAnnotation(prefix: String, anno: String): ValidatedNec[String, Unit] = { + + private def validateTypeInterfaces(name: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { + interfaces.map { iName => + val exists = typeResolver.parse(iName)(using { + case Kind.Interface => true + case _ => false + }).isDefined + + if (exists) { + ().validNec + } else { + s"$name Interface[$iName] not found".invalidNec + } + }.sequence.map { _ => () } + } + + private def validateAnnotation(prefix: String, anno: String): ValidatedNec[String, Unit] = { if (service.annotations.map(_.name).contains(anno)){ ().validNec } else { @@ -163,10 +180,7 @@ case class ServiceSpecValidator( } private def validateInterfaceFields(prefix: String, interfaceName: String, fields: Seq[Field]): ValidatedNec[String, Unit] = { - typeResolver.parse(interfaceName) { - case _: Kind.Interface => true - case _ => false - } match { + typeResolver.parse(interfaceName) match { case Some(_: Kind.Interface) => { service.interfaces.find(_.name == interfaceName) match { case Some(i) => validateInterfaceFields(prefix, i, fields) @@ -280,10 +294,11 @@ case class ServiceSpecValidator( sequenceUnique( Seq( validateUnionNames(), + validateUnionInterfaces(), validateUnionTypesNonEmpty(), validateUnionTypes(), validateUnionDiscriminator(), - validateUnionCyclicReferences() + validateUnionCyclicReferences(), ) ++ Seq( DuplicateErrorMessage.validate("Union", service.unions.map(_.name)) ) @@ -298,6 +313,12 @@ case class ServiceSpecValidator( ) } + private def validateUnionInterfaces(): ValidatedNec[String, Unit] = { + service.unions.map { union => + validateTypeInterfaces(s"Union[${union.name}]", union.interfaces) + }.sequence.map { _ => () } + } + private def validateUnionTypesNonEmpty(): ValidatedNec[String, Unit] = { sequenceUnique( service.unions.map { union => diff --git a/core/app/builder/api_json/ApiJsonServiceValidator.scala b/core/app/builder/api_json/ApiJsonServiceValidator.scala index 0fb174d6c..ca84cdd72 100644 --- a/core/app/builder/api_json/ApiJsonServiceValidator.scala +++ b/core/app/builder/api_json/ApiJsonServiceValidator.scala @@ -129,9 +129,7 @@ case class ApiJsonServiceValidator( private def validateUnions(form: InternalApiJsonForm): ValidatedNec[String, Unit] = { sequenceUnique( form.unions.map(_.warnings) ++ form.unions.map { union => - (validateAttributes(s"Union[${union.name}]", union.attributes), - validateTypeInterfaces(form, s"Union[${union.name}]", union.interfaces) - ).mapN { case (_, _) => () } + validateAttributes(s"Union[${union.name}]", union.attributes).map { _ => () } } ++ form.unions.map(validateUnionTypes) ) } @@ -185,23 +183,11 @@ case class ApiJsonServiceValidator( private def validateModels(form: InternalApiJsonForm): ValidatedNec[String, Unit] = { sequenceUnique( form.models.map(_.warnings) ++ form.models.map { model => - (validateAttributes(s"Model[${model.name}]", model.attributes), - validateTypeInterfaces(form, s"Model[${model.name}]", model.interfaces) - ).mapN { case (_, _) => () } + validateAttributes(s"Model[${model.name}]", model.attributes).map { _ => () } } ++ Seq(validateFields(form.models)) ) } - private def validateTypeInterfaces(form: InternalApiJsonForm, name: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { - interfaces.map { iName => - if (form.interfaces.exists(_.name == iName)) { - ().validNec - } else { - s"$name cannot find interface named '$iName'".invalidNec - } - }.sequence.map { _ => () } - } - private def validateHeaders(headers: Seq[InternalHeaderForm]): ValidatedNec[String, Unit] = { sequenceUnique( headers.map(_.warnings) ++ headers.filter(_.name.isDefined).map { header => From d869ad1178b3285f2e966ac79bd048786cf2c648 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:16:55 -0400 Subject: [PATCH 06/13] wip --- core/app/builder/ServiceSpecValidator.scala | 22 ++++++++------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/core/app/builder/ServiceSpecValidator.scala b/core/app/builder/ServiceSpecValidator.scala index 5f1f751c8..c6b88c40e 100644 --- a/core/app/builder/ServiceSpecValidator.scala +++ b/core/app/builder/ServiceSpecValidator.scala @@ -137,17 +137,14 @@ case class ServiceSpecValidator( } - private def validateTypeInterfaces(name: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { - interfaces.map { iName => - val exists = typeResolver.parse(iName)(using { - case Kind.Interface => true + private def validateTypeInterfaces(prefix: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { + interfaces.map { interfaceName => + typeResolver.parse(interfaceName) { + case _: Kind.Interface => true case _ => false - }).isDefined - - if (exists) { - ().validNec - } else { - s"$name Interface[$iName] not found".invalidNec + } match { + case Some(_) => ().validNec + case _ => s"$prefix Interface[$interfaceName] not found".invalidNec } }.sequence.map { _ => () } } @@ -184,10 +181,7 @@ case class ServiceSpecValidator( case Some(_: Kind.Interface) => { service.interfaces.find(_.name == interfaceName) match { case Some(i) => validateInterfaceFields(prefix, i, fields) - case None => { - // TODO: should we validate for imported interfaces? - ().validNec - } + case None => ().validNec // Other methods validate the interface itself } } case _ => s"$prefix Interface[$interfaceName] not found".invalidNec From 43f0c2345482e914dc3e4fa4e8a4b8e47f274d23 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:26:26 -0400 Subject: [PATCH 07/13] Update --- core/app/builder/ServiceSpecValidator.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/app/builder/ServiceSpecValidator.scala b/core/app/builder/ServiceSpecValidator.scala index c6b88c40e..9f79ec522 100644 --- a/core/app/builder/ServiceSpecValidator.scala +++ b/core/app/builder/ServiceSpecValidator.scala @@ -138,13 +138,17 @@ case class ServiceSpecValidator( private def validateTypeInterfaces(prefix: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { + println(s"validateTypeInterfaces[$prefix] $interfaces") interfaces.map { interfaceName => typeResolver.parse(interfaceName) { case _: Kind.Interface => true case _ => false } match { case Some(_) => ().validNec - case _ => s"$prefix Interface[$interfaceName] not found".invalidNec + case o => { + println(s" -- interface[${interfaceName}] => $o") + s"$prefix Interface[$interfaceName] not found".invalidNec + } } }.sequence.map { _ => () } } From 1944baf14478b2d037ea1482430bbb612a3a2d5a Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:29:59 -0400 Subject: [PATCH 08/13] wip --- core/app/builder/ServiceSpecValidator.scala | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/core/app/builder/ServiceSpecValidator.scala b/core/app/builder/ServiceSpecValidator.scala index 9f79ec522..e7e72fd62 100644 --- a/core/app/builder/ServiceSpecValidator.scala +++ b/core/app/builder/ServiceSpecValidator.scala @@ -136,14 +136,17 @@ case class ServiceSpecValidator( ) } + private def findInterface(interfaceName: String): Option[Kind.Interface] = { + typeResolver.parse(interfaceName) { + case _: Kind.Interface => true + case _ => false + }.map(_.asInstanceOf[Kind.Interface]) + } + private def validateTypeInterfaces(prefix: String, interfaces: Seq[String]): ValidatedNec[String, Unit] = { - println(s"validateTypeInterfaces[$prefix] $interfaces") interfaces.map { interfaceName => - typeResolver.parse(interfaceName) { - case _: Kind.Interface => true - case _ => false - } match { + findInterface(interfaceName) match { case Some(_) => ().validNec case o => { println(s" -- interface[${interfaceName}] => $o") @@ -181,14 +184,14 @@ case class ServiceSpecValidator( } private def validateInterfaceFields(prefix: String, interfaceName: String, fields: Seq[Field]): ValidatedNec[String, Unit] = { - typeResolver.parse(interfaceName) match { - case Some(_: Kind.Interface) => { + findInterface(interfaceName) match { + case Some(_) => { service.interfaces.find(_.name == interfaceName) match { case Some(i) => validateInterfaceFields(prefix, i, fields) - case None => ().validNec // Other methods validate the interface itself + case None => ().validNec // TODO: Future work to validate the fields from the imported interface } } - case _ => s"$prefix Interface[$interfaceName] not found".invalidNec + case _ => ().validNec // Other methods will validate the presence of the interface itself } } From 9d8f5a35327ba26025e796908e466047418420c6 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:31:11 -0400 Subject: [PATCH 09/13] wip --- core/app/builder/ServiceSpecValidator.scala | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/core/app/builder/ServiceSpecValidator.scala b/core/app/builder/ServiceSpecValidator.scala index e7e72fd62..02f8a7bab 100644 --- a/core/app/builder/ServiceSpecValidator.scala +++ b/core/app/builder/ServiceSpecValidator.scala @@ -137,10 +137,10 @@ case class ServiceSpecValidator( } private def findInterface(interfaceName: String): Option[Kind.Interface] = { - typeResolver.parse(interfaceName) { + typeResolver.parse(interfaceName)(using { case _: Kind.Interface => true case _ => false - }.map(_.asInstanceOf[Kind.Interface]) + }).map(_.asInstanceOf[Kind.Interface]) } @@ -148,10 +148,7 @@ case class ServiceSpecValidator( interfaces.map { interfaceName => findInterface(interfaceName) match { case Some(_) => ().validNec - case o => { - println(s" -- interface[${interfaceName}] => $o") - s"$prefix Interface[$interfaceName] not found".invalidNec - } + case o => s"$prefix Interface[$interfaceName] not found".invalidNec } }.sequence.map { _ => () } } @@ -1083,7 +1080,7 @@ case class ServiceSpecValidator( } private def validateConcreteType(prefix: String, typeName: String): ValidatedNec[String, Kind] = { - validateType(prefix, typeName) { + validateType(prefix, typeName)(using { case _: Kind.Interface => s"$prefix type[$typeName] is an interface and cannot be used as a field type. Specify the specific model you need or use a union type".invalidNec case _: Kind.Primitive => ().validNec case _: Kind.Enum => ().validNec @@ -1091,6 +1088,6 @@ case class ServiceSpecValidator( case _: Kind.Union => ().validNec case _: Kind.List => ().validNec case _: Kind.Map => ().validNec - } + }) } } From 45c6b7a9f8595c6f005298b616e5305a9779ce5a Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:40:15 -0400 Subject: [PATCH 10/13] wip --- api/app/actors/ScheduleTasksActor.scala | 4 ++-- api/app/db/InternalApplicationsDao.scala | 15 ++++++--------- api/app/db/InternalAttributesDao.scala | 5 +++-- api/app/db/InternalChangesDao.scala | 8 +++----- api/app/db/InternalEmailVerificationsDao.scala | 8 +++----- app/app/lib/MemberDownload.scala | 2 +- core/app/Util.scala | 8 ++++---- 7 files changed, 22 insertions(+), 28 deletions(-) diff --git a/api/app/actors/ScheduleTasksActor.scala b/api/app/actors/ScheduleTasksActor.scala index a0a459d22..308e07078 100644 --- a/api/app/actors/ScheduleTasksActor.scala +++ b/api/app/actors/ScheduleTasksActor.scala @@ -33,7 +33,7 @@ class ScheduleTasksActor @Inject()( private def scheduleOnce(taskType: TaskType): Unit = { context.system.scheduler.scheduleOnce(FiniteDuration(10, SECONDS)) { - UpsertTask(taskType) + self ! UpsertTask(taskType) } } @@ -44,7 +44,7 @@ class ScheduleTasksActor @Inject()( Seq( schedule(ScheduleSyncGeneratorServices, FiniteDuration(1, HOURS)), schedule(CheckInvariants, FiniteDuration(12, HOURS)), - schedule(PurgeDeleted, FiniteDuration(1, HOURS))(FiniteDuration(5, SECONDS)), + schedule(PurgeDeleted, FiniteDuration(1, HOURS)), ) } diff --git a/api/app/db/InternalApplicationsDao.scala b/api/app/db/InternalApplicationsDao.scala index 8faaa89af..40c81c59c 100644 --- a/api/app/db/InternalApplicationsDao.scala +++ b/api/app/db/InternalApplicationsDao.scala @@ -3,19 +3,16 @@ package db import cats.data.ValidatedNec import cats.implicits.* import db.generated.{ApplicationMoveForm, ApplicationMovesDao, ApplicationsDao} -import io.apibuilder.api.v0.models.{AppSortBy, ApplicationForm, Error, MoveForm, SortOrder, User, Version, Visibility} -import io.apibuilder.common.v0.models.{Audit, ReferenceGuid} +import io.apibuilder.api.v0.models.{AppSortBy, ApplicationForm, Error, MoveForm, SortOrder, Version, Visibility} import io.apibuilder.task.v0.models.{EmailDataApplicationCreated, TaskType} import io.flow.postgresql.{OrderBy, Query} import lib.{UrlKey, Validation} -import org.joda.time.DateTime -import play.api.db.* import processor.EmailProcessorQueue import util.OptionalQueryFilter import java.sql.Connection import java.util.UUID -import javax.inject.{Inject, Singleton} +import javax.inject.Inject case class InternalApplication(db: generated.Application) { val guid: UUID = db.guid @@ -230,7 +227,7 @@ class InternalApplicationsDao @Inject()( def findAllByGuids(authorization: Authorization, guids: Seq[UUID]): Seq[InternalApplication] = { findAll(authorization, guids = Some(guids), limit = None) } - + def findAll( authorization: Authorization, orgKey: Option[String] = None, @@ -289,14 +286,14 @@ class InternalApplicationsDao @Inject()( limit = limit, offset = offset, orderBy = Some(toOrderBy(sorting, ordering)), - ) { q => + )( using (q: Query) => { authorization.applicationFilter( filters.foldLeft(q) { case (q, f) => f.filter(q) }, "guid" ) .equals("lower(name)", name.map(_.toLowerCase().trim)) .equals("lower(key)", key.map(_.toLowerCase().trim)) - }.map(InternalApplication(_)) + }).map(InternalApplication(_)) } private def toOrderBy( @@ -332,4 +329,4 @@ class InternalApplicationsDao @Inject()( tasksDao.queueWithConnection(c, TaskType.IndexApplication, guid.toString) } -} \ No newline at end of file +} diff --git a/api/app/db/InternalAttributesDao.scala b/api/app/db/InternalAttributesDao.scala index bac24d452..48fc5e94e 100644 --- a/api/app/db/InternalAttributesDao.scala +++ b/api/app/db/InternalAttributesDao.scala @@ -5,6 +5,7 @@ import cats.data.ValidatedNec import cats.implicits.* import db.generated.AttributesDao import io.apibuilder.api.v0.models.{AttributeForm, User} +import io.flow.postgresql.Query import lib.{UrlKey, Validation} import java.util.UUID @@ -95,12 +96,12 @@ class InternalAttributesDao @Inject()( guids = guids, limit = limit, offset = offset - ) { q => + )(using (q: Query) => { q.and(name.map { _ => "lower(trim(attributes.name)) = lower(trim({name}))" }).bind("name", name) .and(isDeleted.map(Filters.isDeleted("attributes", _))) - }.map(InternalAttribute(_)) + }).map(InternalAttribute(_)) } } diff --git a/api/app/db/InternalChangesDao.scala b/api/app/db/InternalChangesDao.scala index 96a1e3b4e..22d1f49ce 100644 --- a/api/app/db/InternalChangesDao.scala +++ b/api/app/db/InternalChangesDao.scala @@ -1,7 +1,5 @@ package db -import anorm.JodaParameterMetaData.* -import anorm.* import db.generated.{ChangeForm, ChangesDao} import io.apibuilder.api.v0.models.* import io.flow.postgresql.{OrderBy, Query} @@ -157,7 +155,7 @@ class InternalChangesDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("-changed_at, type, -description")), - ) { q => + )(using (q: Query) => { authorization.applicationFilter( filters.foldLeft(q) { case (q, f) => f.filter(q) }, "application_guid" @@ -170,6 +168,6 @@ class InternalChangesDao @Inject()( "lower(description) = lower(trim({description}))" } ).bind("description", description) - }.map(InternalChange(_)) + }).map(InternalChange(_)) } -} \ No newline at end of file +} diff --git a/api/app/db/InternalEmailVerificationsDao.scala b/api/app/db/InternalEmailVerificationsDao.scala index 7dd7ba38a..ee9a4b755 100644 --- a/api/app/db/InternalEmailVerificationsDao.scala +++ b/api/app/db/InternalEmailVerificationsDao.scala @@ -1,16 +1,14 @@ package db import db.generated.EmailVerificationsDao -import io.apibuilder.api.v0.models.User import io.apibuilder.task.v0.models.EmailDataEmailVerificationCreated import io.flow.postgresql.{OrderBy, Query} import lib.TokenGenerator import org.joda.time.DateTime -import play.api.db.* import processor.EmailProcessorQueue import java.util.UUID -import javax.inject.{Inject, Singleton} +import javax.inject.Inject case class InternalEmailVerification(db: generated.EmailVerification) { val guid: UUID = db.guid @@ -80,11 +78,11 @@ class InternalEmailVerificationsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")) - ) { q => + )( using (q: Query) => { q.equals("lower(email)", email.map(_.toLowerCase)) .and(isExpired.map(Filters.isExpired("email_verifications", _))) .and(isDeleted.map(Filters.isDeleted("email_verifications", _))) - }.map(InternalEmailVerification(_)) + }).map(InternalEmailVerification(_)) } } diff --git a/app/app/lib/MemberDownload.scala b/app/app/lib/MemberDownload.scala index e7c6fb0b4..5ff4ddba1 100644 --- a/app/app/lib/MemberDownload.scala +++ b/app/app/lib/MemberDownload.scala @@ -17,7 +17,7 @@ case class MemberDownload( def csv()(implicit ec: ExecutionContext): Future[File] = Future { val file = File.createTempFile(s"member-download-$orgKey", "csv") - val writer = CSVWriter.open(file)(defaultCSVFormat) + val writer = CSVWriter.open(file)(using defaultCSVFormat) writer.writeRow(Seq("guid", "role", "user_guid", "user_email", "user_nickname", "user_name")) Pager.eachPage[Membership] { offset => diff --git a/core/app/Util.scala b/core/app/Util.scala index 18e8d6692..90e45a373 100644 --- a/core/app/Util.scala +++ b/core/app/Util.scala @@ -1,11 +1,11 @@ package core +import cats.data.Validated.{Invalid, Valid} import cats.data.ValidatedNec import cats.implicits.* -import cats.data.Validated.{Invalid, Valid} -import io.apibuilder.api.v0.models.Error import io.apibuilder.spec.v0.models.Enum -import java.net.{MalformedURLException, URL} + +import java.net.{MalformedURLException, URI} import scala.util.{Failure, Success, Try} object Util { @@ -55,7 +55,7 @@ object Util { } else if (formatted.endsWith("/")) { s"URI[$formatted] cannot end with a '/'".invalidNec } else { - Try(new URL(formatted)) match { + Try(URI.create(formatted)) match { case Success(_) => formatted.validNec case Failure(e) => e match { case e: MalformedURLException => s"URL is not valid: ${e.getMessage}".invalidNec From 408a3e94cc809fb8df5324778420ae8a8e05cfd6 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:43:25 -0400 Subject: [PATCH 11/13] wip --- .../db/InternalEmailVerificationConfirmationsDao.scala | 5 ++--- api/app/db/InternalMembershipRequestsDao.scala | 4 ++-- api/app/db/InternalMembershipsDao.scala | 4 ++-- .../db/InternalOrganizationAttributeValuesDao.scala | 6 +++--- api/app/db/InternalOrganizationDomainsDao.scala | 6 +++--- api/app/db/InternalOrganizationsDao.scala | 7 ++++--- api/app/db/InternalOriginalsDao.scala | 3 ++- api/app/db/InternalPasswordResetsDao.scala | 4 ++-- api/app/db/InternalSubscriptionsDao.scala | 10 +++++----- api/app/db/InternalTokensDao.scala | 4 ++-- api/app/db/InternalUsersDao.scala | 4 ++-- api/app/db/InternalVersionsDao.scala | 8 ++++---- api/app/db/InternalWatchesDao.scala | 4 ++-- 13 files changed, 35 insertions(+), 34 deletions(-) diff --git a/api/app/db/InternalEmailVerificationConfirmationsDao.scala b/api/app/db/InternalEmailVerificationConfirmationsDao.scala index 9b7725b5d..6b9f09e27 100644 --- a/api/app/db/InternalEmailVerificationConfirmationsDao.scala +++ b/api/app/db/InternalEmailVerificationConfirmationsDao.scala @@ -47,10 +47,9 @@ class InternalEmailVerificationConfirmationsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")) - ) { q => + )( using (q: Query) => { q.and(isDeleted.map(Filters.isDeleted("email_verification_confirmations", _))) - }.map(InternalEmailVerificationConfirmation(_)) + }).map(InternalEmailVerificationConfirmation(_)) } } - diff --git a/api/app/db/InternalMembershipRequestsDao.scala b/api/app/db/InternalMembershipRequestsDao.scala index a8c98ddb7..04ea1807f 100644 --- a/api/app/db/InternalMembershipRequestsDao.scala +++ b/api/app/db/InternalMembershipRequestsDao.scala @@ -147,10 +147,10 @@ class InternalMembershipRequestsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")), - ) { q => + )( using (q: Query) => { filters.foldLeft(q) { case (q, f) => f.filter(q) } .and(isDeleted.map(Filters.isDeleted("membership_requests", _))) .equals("role", role.map(_.toString)) - }.map(InternalMembershipRequest(_)) + }).map(InternalMembershipRequest(_)) } } diff --git a/api/app/db/InternalMembershipsDao.scala b/api/app/db/InternalMembershipsDao.scala index 35b8cda90..c8f10414e 100644 --- a/api/app/db/InternalMembershipsDao.scala +++ b/api/app/db/InternalMembershipsDao.scala @@ -179,11 +179,11 @@ class InternalMembershipsDao @Inject()( userGuid = userGuid, limit = limit, offset = offset, - ) { q => + )( using (q: Query) => { filters.foldLeft(q) { case (q, f) => f.filter(q) } .equals("memberships.role", role.map(_.toString)) .optionalIn("memberships.role", roles.map(_.map(_.toString))) .and(isDeleted.map(Filters.isDeleted("memberships", _))) - }.map(InternalMembership(_)) + }).map(InternalMembership(_)) } } diff --git a/api/app/db/InternalOrganizationAttributeValuesDao.scala b/api/app/db/InternalOrganizationAttributeValuesDao.scala index 896821035..df5f13ae2 100644 --- a/api/app/db/InternalOrganizationAttributeValuesDao.scala +++ b/api/app/db/InternalOrganizationAttributeValuesDao.scala @@ -103,7 +103,7 @@ class InternalOrganizationAttributeValuesDao @Inject()( private def findByOrganizationGuidAndAttributeGuid(organizationGuid: UUID, attributeGuid: UUID): Option[InternalOrganizationAttributeValue] = { findAll(organizationGuid = Some(organizationGuid), attributeGuid = Some(attributeGuid), limit = Some(1)).headOption } - + def findAll( guid: Option[UUID] = None, organizationGuid: Option[UUID] = None, @@ -133,9 +133,9 @@ class InternalOrganizationAttributeValuesDao @Inject()( attributeGuid = attributeGuid, limit = limit, offset = offset, - ) { q => + )( using (q: Query) => { filters.foldLeft(q) { case (q, f) => f.filter(q) } .and(isDeleted.map(Filters.isDeleted("organization_attribute_values", _))) - }.map(InternalOrganizationAttributeValue(_)) + }).map(InternalOrganizationAttributeValue(_)) } } diff --git a/api/app/db/InternalOrganizationDomainsDao.scala b/api/app/db/InternalOrganizationDomainsDao.scala index f36cf483e..1d23c81de 100644 --- a/api/app/db/InternalOrganizationDomainsDao.scala +++ b/api/app/db/InternalOrganizationDomainsDao.scala @@ -57,9 +57,9 @@ class InternalOrganizationDomainsDao @Inject()( domain = domain.map(_.trim.toLowerCase()), limit = limit, orderBy = orderBy, - ) { q => + )( using (q: Query) => { q.and(isDeleted.map(Filters.isDeleted("organization_domains", _))) - }.map(InternalOrganizationDomain(_)) + }).map(InternalOrganizationDomain(_)) } - + } diff --git a/api/app/db/InternalOrganizationsDao.scala b/api/app/db/InternalOrganizationsDao.scala index 78ba59aa2..94ce55c01 100644 --- a/api/app/db/InternalOrganizationsDao.scala +++ b/api/app/db/InternalOrganizationsDao.scala @@ -1,7 +1,8 @@ package db -import io.apibuilder.api.v0.models._ +import io.apibuilder.api.v0.models.* import io.apibuilder.common.v0.models.MembershipRole +import io.flow.postgresql.Query import lib.{Misc, UrlKey, Validation} import org.joda.time.DateTime import play.api.inject.Injector @@ -213,7 +214,7 @@ class InternalOrganizationsDao @Inject()( guids = guids, limit = limit, offset = offset, - ) { q => + )( using (q: Query) => { authorization.organizationFilter(q, "organizations.guid") .equals("key", key) .equals("lower(name)", name.map(_.toLowerCase().trim)) @@ -237,6 +238,6 @@ class InternalOrganizationsDao @Inject()( }).bind("deleted_at_before", deletedAtBefore) .and(isDeleted.map(Filters.isDeleted("organizations", _))) .orderBy("lower(name), created_at") - }.map(InternalOrganization(_)) + }).map(InternalOrganization(_)) } } diff --git a/api/app/db/InternalOriginalsDao.scala b/api/app/db/InternalOriginalsDao.scala index 4d6e5824c..985622dd2 100644 --- a/api/app/db/InternalOriginalsDao.scala +++ b/api/app/db/InternalOriginalsDao.scala @@ -2,6 +2,7 @@ package db import db.generated.OriginalsDao import io.apibuilder.api.v0.models.{Original, OriginalType} +import io.flow.postgresql.Query import java.util.UUID import javax.inject.Inject @@ -40,7 +41,7 @@ class InternalOriginalsDao @Inject()(dao: OriginalsDao) { dao.findAll( versionGuids = Some(versionGuids), limit = None, - ) { q => q.isNull("deleted_at") }.map(InternalOriginal(_)) + )( using (q: Query) => { q.isNull("deleted_at") }).map(InternalOriginal(_)) } def findByVersionGuid(guid: UUID): Option[InternalOriginal] = { diff --git a/api/app/db/InternalPasswordResetsDao.scala b/api/app/db/InternalPasswordResetsDao.scala index 3582f8057..374b822c0 100644 --- a/api/app/db/InternalPasswordResetsDao.scala +++ b/api/app/db/InternalPasswordResetsDao.scala @@ -95,10 +95,10 @@ class InternalPasswordResetsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")) - ) { q => + )( using (q: Query) => { q.and(isDeleted.map(Filters.isDeleted("password_resets", _))) .and(isExpired.map(Filters.isExpired("password_resets", _))) - }.map(InternalPasswordReset(_)) + }).map(InternalPasswordReset(_)) } } diff --git a/api/app/db/InternalSubscriptionsDao.scala b/api/app/db/InternalSubscriptionsDao.scala index 976e16b5e..9c4722824 100644 --- a/api/app/db/InternalSubscriptionsDao.scala +++ b/api/app/db/InternalSubscriptionsDao.scala @@ -41,10 +41,10 @@ class InternalSubscriptionsDao @Inject()( private def validateOrg(key: String): ValidatedNec[Error, Organization] = { organizationsDao.findAll( limit = Some(1), - ) { q => + )( using (q: Query) => { q.equals("key", key) .isNull("deleted_at") - } + }) .headOption .toValidNec(Validation.singleError("Organization not found")) } @@ -68,7 +68,7 @@ class InternalSubscriptionsDao @Inject()( case Some(_) => Validation.singleError("User is already subscribed to this publication for this organization").invalidNec } } - + private def validate(form: SubscriptionForm): ValidatedNec[Error, ValidatedSubscriptionForm] = { ( validateOrg(form.organizationKey).andThen { org => @@ -142,14 +142,14 @@ class InternalSubscriptionsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")) - ) { q => + )( using (q: Query) => { authorization.subscriptionFilter( filters.foldLeft(q) { case (q, f) => f.filter(q) }, "subscriptions" ) .and(isDeleted.map(Filters.isDeleted("subscriptions", _))) .equals("publication", publication.map(_.toString)) - }.map(InternalSubscription(_)) + }).map(InternalSubscription(_)) } } diff --git a/api/app/db/InternalTokensDao.scala b/api/app/db/InternalTokensDao.scala index 8c1e7bdcc..be6b145bc 100644 --- a/api/app/db/InternalTokensDao.scala +++ b/api/app/db/InternalTokensDao.scala @@ -85,9 +85,9 @@ class InternalTokensDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("created_at")) - ) { q => + )( using (q: Query) => { authorization.tokenFilter(q) .and(isDeleted.map(Filters.isDeleted("tokens", _))) - }.map(InternalToken(_)) + }).map(InternalToken(_)) } } diff --git a/api/app/db/InternalUsersDao.scala b/api/app/db/InternalUsersDao.scala index 5a2f33d3a..6fcfbc55e 100644 --- a/api/app/db/InternalUsersDao.scala +++ b/api/app/db/InternalUsersDao.scala @@ -233,7 +233,7 @@ class InternalUsersDao @Inject()( guid = guid, guids = guids, limit = limit, - ) { q => + )( using (q: Query) => { q.and( email.map { _ => "email = trim(lower({email}))" } ).bind("email", email) @@ -247,7 +247,7 @@ class InternalUsersDao @Inject()( token.map { _ => "guid = (select user_guid from tokens where token = {token} and deleted_at is null)" } ).bind("token", token) .and(isDeleted.map(Filters.isDeleted("users", _))) - }.map(InternalUser(_)) + }).map(InternalUser(_)) } @tailrec diff --git a/api/app/db/InternalVersionsDao.scala b/api/app/db/InternalVersionsDao.scala index 5371abc39..348eec8fd 100644 --- a/api/app/db/InternalVersionsDao.scala +++ b/api/app/db/InternalVersionsDao.scala @@ -200,13 +200,13 @@ class InternalVersionsDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("-version_sort_key, -created_at")) - ) { q => + )( using (q: Query) => { authorization.applicationFilter(q, "application_guid") .and(HasServiceJsonClause) .and(versionConstraint.map(vc => s"version like '${vc}%'")) .and(isDeleted.map(Filters.isDeleted("versions", _))) .equals("version", version) - }.map(InternalVersion(_)) + }).map(InternalVersion(_)) } // Efficient query to fetch all versions of a given application @@ -296,10 +296,10 @@ class InternalVersionsDao @Inject()( servicesDao.findAll( versionGuid = Some(versionGuid), limit = None - ) { q => + )( using (q: Query) => { q.equals("version", MigrateVersion.ServiceVersionNumber) .isNull("deleted_at") - }.foreach { s => + }).foreach { s => servicesDao.delete(user.guid, s) } } diff --git a/api/app/db/InternalWatchesDao.scala b/api/app/db/InternalWatchesDao.scala index e5ed334e8..200e054ee 100644 --- a/api/app/db/InternalWatchesDao.scala +++ b/api/app/db/InternalWatchesDao.scala @@ -111,13 +111,13 @@ class InternalWatchesDao @Inject()( applicationGuid = applicationGuid, limit = limit, offset = offset, - ) { q => + )( using (q: Query) => { authorization.applicationFilter( filters.foldLeft(q) { case (q, f) => f.filter(q) }, "application_guid" ) .and(isDeleted.map(Filters.isDeleted("watches", _))) - }.map(InternalWatch(_)) + }).map(InternalWatch(_)) } } From 1288aab71e3f3f415a0e26c87fc6ffa617f0d93b Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:49:44 -0400 Subject: [PATCH 12/13] wip --- api/app/db/InternalMembershipsDao.scala | 2 +- api/app/db/InternalOrganizationsDao.scala | 2 +- api/app/db/InternalOriginalsDao.scala | 4 ++-- api/app/db/InternalVersionsDao.scala | 8 ++++---- api/app/db/ItemsDao.scala | 6 +++--- api/app/db/OrganizationLogsDao.scala | 4 ++-- .../generators/InternalGeneratorServicesDao.scala | 4 ++-- api/app/db/generators/InternalGeneratorsDao.scala | 14 +++++++------- api/app/models/ApplicationsModel.scala | 4 ++-- api/app/models/VersionsModel.scala | 10 +++++----- api/app/processor/CheckInvariantsProcessor.scala | 4 ++-- api/app/processor/DiffVersionProcessor.scala | 4 ++-- api/app/processor/PurgeDeletedProcessor.scala | 6 +++--- .../processor/SyncGeneratorServiceProcessor.scala | 4 ++-- api/app/processor/TaskDispatchActorCompanion.scala | 2 +- api/app/processor/TaskProcessor.scala | 8 ++++---- 16 files changed, 43 insertions(+), 43 deletions(-) diff --git a/api/app/db/InternalMembershipsDao.scala b/api/app/db/InternalMembershipsDao.scala index c8f10414e..6ebc69356 100644 --- a/api/app/db/InternalMembershipsDao.scala +++ b/api/app/db/InternalMembershipsDao.scala @@ -49,7 +49,7 @@ class InternalMembershipsDao @Inject()( } } - private[db] def create(implicit c: java.sql.Connection, createdBy: UUID, org: OrganizationReference, user: UserReference, role: MembershipRole): InternalMembership = { + private[db] def create(c: java.sql.Connection, createdBy: UUID, org: OrganizationReference, user: UserReference, role: MembershipRole): InternalMembership = { val guid = dao.insert(c, createdBy, generated.MembershipForm( userGuid = user.guid, organizationGuid = org.guid, diff --git a/api/app/db/InternalOrganizationsDao.scala b/api/app/db/InternalOrganizationsDao.scala index 94ce55c01..497750c42 100644 --- a/api/app/db/InternalOrganizationsDao.scala +++ b/api/app/db/InternalOrganizationsDao.scala @@ -159,7 +159,7 @@ class InternalOrganizationsDao @Inject()( } } - private def create(implicit c: java.sql.Connection, user: InternalUser, form: OrganizationForm): InternalOrganization = { + private def create(c: java.sql.Connection, user: InternalUser, form: OrganizationForm): InternalOrganization = { val errors = validate(form) assert(errors.isEmpty, errors.map(_.message).mkString("\n")) diff --git a/api/app/db/InternalOriginalsDao.scala b/api/app/db/InternalOriginalsDao.scala index 985622dd2..9478ac767 100644 --- a/api/app/db/InternalOriginalsDao.scala +++ b/api/app/db/InternalOriginalsDao.scala @@ -17,7 +17,7 @@ case class InternalOriginal(db: generated.Original) { class InternalOriginalsDao @Inject()(dao: OriginalsDao) { def create( - implicit c: java.sql.Connection, + c: java.sql.Connection, user: InternalUser, versionGuid: UUID, original: Original @@ -30,7 +30,7 @@ class InternalOriginalsDao @Inject()(dao: OriginalsDao) { } def softDeleteByVersionGuid( - implicit c: java.sql.Connection, + c: java.sql.Connection, user: InternalUser, versionGuid: UUID ): Unit = { diff --git a/api/app/db/InternalVersionsDao.scala b/api/app/db/InternalVersionsDao.scala index 348eec8fd..835d2807b 100644 --- a/api/app/db/InternalVersionsDao.scala +++ b/api/app/db/InternalVersionsDao.scala @@ -78,7 +78,7 @@ class InternalVersionsDao @Inject()( } private def doCreate( - implicit c: java.sql.Connection, + c: java.sql.Connection, user: InternalUser, application: InternalApplication, version: String, @@ -226,7 +226,7 @@ class InternalVersionsDao @Inject()( .optionalLimit(limit) .offset(offset) .orderBy("version_sort_key desc, created_at desc") - .as(SqlParser.str(1).*)(c) + .as(SqlParser.str(1).*)(using c) .map { version => ApplicationMetadataVersion(version = version) } @@ -289,7 +289,7 @@ class InternalVersionsDao @Inject()( } private def softDeleteService( - implicit c: java.sql.Connection, + c: java.sql.Connection, user: InternalUser, versionGuid: UUID ): Unit = { @@ -305,7 +305,7 @@ class InternalVersionsDao @Inject()( } private def insertService( - implicit c: java.sql.Connection, + c: java.sql.Connection, user: InternalUser, versionGuid: UUID, service: Service diff --git a/api/app/db/ItemsDao.scala b/api/app/db/ItemsDao.scala index ec3f6e737..423c274eb 100644 --- a/api/app/db/ItemsDao.scala +++ b/api/app/db/ItemsDao.scala @@ -77,9 +77,9 @@ class ItemsDao @Inject() ( delete(c, guid) } } - - private def delete(implicit c: java.sql.Connection, guid: UUID): Unit = { - SQL(DeleteQuery).on("guid" -> guid).execute() + + private def delete(c: java.sql.Connection, guid: UUID): Unit = { + SQL(DeleteQuery).on("guid" -> guid).execute()(using c) } def findByGuid( diff --git a/api/app/db/OrganizationLogsDao.scala b/api/app/db/OrganizationLogsDao.scala index e37ebd739..ed6696ba8 100644 --- a/api/app/db/OrganizationLogsDao.scala +++ b/api/app/db/OrganizationLogsDao.scala @@ -35,7 +35,7 @@ class OrganizationLogsDao @Inject() ( } } - private[db] def create(implicit c: java.sql.Connection, createdBy: UUID, org: OrganizationReference, message: String): OrganizationLog = { + private[db] def create(c: java.sql.Connection, createdBy: UUID, org: OrganizationReference, message: String): OrganizationLog = { val log = OrganizationLog( guid = UUID.randomUUID, organizationGuid = org.guid, @@ -47,7 +47,7 @@ class OrganizationLogsDao @Inject() ( "organization_guid" -> log.organizationGuid, "message" -> log.message, "created_by_guid" -> createdBy - ).execute() + ).execute()(using c) log } diff --git a/api/app/db/generators/InternalGeneratorServicesDao.scala b/api/app/db/generators/InternalGeneratorServicesDao.scala index 055425e57..57dfeabba 100644 --- a/api/app/db/generators/InternalGeneratorServicesDao.scala +++ b/api/app/db/generators/InternalGeneratorServicesDao.scala @@ -97,7 +97,7 @@ class InternalGeneratorServicesDao @Inject()( limit = limit, offset = offset, orderBy = Some(OrderBy("lower(uri)")) - ) { q => + )( using (q: Query) => { q.and( uri.map { _ => "lower(uri) = lower(trim({uri}))" @@ -109,7 +109,7 @@ class InternalGeneratorServicesDao @Inject()( } ).bind("generator_key", generatorKey) .and(isDeleted.map(Filters.isDeleted("services", _))) - }.map(InternalGeneratorService(_)) + }).map(InternalGeneratorService(_)) } } diff --git a/api/app/db/generators/InternalGeneratorsDao.scala b/api/app/db/generators/InternalGeneratorsDao.scala index a8b31a871..94b8e30ec 100644 --- a/api/app/db/generators/InternalGeneratorsDao.scala +++ b/api/app/db/generators/InternalGeneratorsDao.scala @@ -33,7 +33,7 @@ class InternalGeneratorsDao @Inject()( findByKey(form.generator.key) match { case None => { dao.db.withConnection { c => - create(c, user, form).validNec + create(user, form)(using c).validNec } } @@ -42,7 +42,7 @@ class InternalGeneratorsDao @Inject()( // Update to catch any updates to properties dao.db.withTransaction { implicit c => dao.delete(c, user.guid, existing.db) - create(c, user, form).validNec + create(user, form)(using c).validNec } } else { existing.validNec @@ -79,15 +79,15 @@ class InternalGeneratorsDao @Inject()( val all = dao.findAllWithConnection( c, limit = None - ) { q => + )( using (q: Query) => { q.equals("service_guid", serviceGuid) - } + }) if (all.nonEmpty) { dao.deleteAllByGuids(deletedBy.guid, all.map(_.guid)) } } - private def create(implicit c: java.sql.Connection, user: InternalUser, form: GeneratorForm): InternalGenerator = { + private def create(user: InternalUser, form: GeneratorForm)(implicit c: java.sql.Connection): InternalGenerator = { val guid = dao.insert(c, user.guid, _root_.db.generated.generators.GeneratorForm( serviceGuid = form.serviceGuid, key = form.generator.key.trim, @@ -129,7 +129,7 @@ class InternalGeneratorsDao @Inject()( guid = guid, limit = limit, offset = offset - ) { q => + )( using (q: Query) => { q.equals("service_guid", serviceGuid) .and( serviceUri.map { _ => @@ -149,6 +149,6 @@ class InternalGeneratorsDao @Inject()( ).bind("attribute_name", attributeName) .and(isDeleted.map(Filters.isDeleted("generators", _))) .orderBy("lower(name), lower(key), created_at desc") - }.map(InternalGenerator(_)) + }).map(InternalGenerator(_)) } } diff --git a/api/app/models/ApplicationsModel.scala b/api/app/models/ApplicationsModel.scala index 653afa22a..0e8eb41a5 100644 --- a/api/app/models/ApplicationsModel.scala +++ b/api/app/models/ApplicationsModel.scala @@ -66,7 +66,7 @@ class ApplicationsModel @Inject()( LastVersionCreatedQuery .in("application_guid", guids) .groupBy("1") - .as(parser.*)(c) + .as(parser.*)(using c) } } } @@ -82,4 +82,4 @@ class ApplicationsModel @Inject()( ) } } -} \ No newline at end of file +} diff --git a/api/app/models/VersionsModel.scala b/api/app/models/VersionsModel.scala index 8c23fdfb7..ec74a5cae 100644 --- a/api/app/models/VersionsModel.scala +++ b/api/app/models/VersionsModel.scala @@ -10,7 +10,7 @@ import io.apibuilder.api.v0.models.Version import io.apibuilder.common.v0.models.{Audit, Reference, ReferenceGuid} import io.apibuilder.spec.v0.models.{Apidoc, Service} import io.apibuilder.spec.v0.models.json.* -import io.flow.postgresql.OrderBy +import io.flow.postgresql.{OrderBy, Query} import javax.inject.Inject @@ -50,7 +50,7 @@ class VersionsModel @Inject()( ).map { o => o.guid -> o }.toMap val originals = originalsDao.findAllByVersionGuids(versions.map(_.guid)).map { o => o.versionGuid -> o }.toMap - + versions.map { v => ( applications.get(v.applicationGuid).toValidNec("Cannot find application").andThen { app => @@ -83,9 +83,9 @@ class VersionsModel @Inject()( versionGuid = Some(v.guid), orderBy = Some(OrderBy("-created_at")), limit = Some(1), - ) { q => + )( using (q: Query) => q.isNull("deleted_at") - }.headOption + ).headOption .toValidNec("Version does not have service json") .map(_.json.as[Service]) .map { service => @@ -96,4 +96,4 @@ class VersionsModel @Inject()( case None => service.copy(apidoc = Some(Apidoc(version = service.version))) } } -} \ No newline at end of file +} diff --git a/api/app/processor/CheckInvariantsProcessor.scala b/api/app/processor/CheckInvariantsProcessor.scala index b3e8ee61f..2028e1868 100644 --- a/api/app/processor/CheckInvariantsProcessor.scala +++ b/api/app/processor/CheckInvariantsProcessor.scala @@ -23,7 +23,7 @@ class CheckInvariantsProcessor @Inject()( override def processRecord(id: String): ValidatedNec[String, Unit] = { val results = invariants.all.map { i => val count = database.withConnection { c => - i.query.as(SqlParser.long(1).*)(c).headOption.getOrElse(0L) + i.query.as(SqlParser.long(1).*)(using c).headOption.getOrElse(0L) } InvariantResult(i, count) } @@ -50,4 +50,4 @@ class CheckInvariantsProcessor @Inject()( } } } -} \ No newline at end of file +} diff --git a/api/app/processor/DiffVersionProcessor.scala b/api/app/processor/DiffVersionProcessor.scala index ac68ce12c..662e6e742 100644 --- a/api/app/processor/DiffVersionProcessor.scala +++ b/api/app/processor/DiffVersionProcessor.scala @@ -124,14 +124,14 @@ class DiffVersionProcessor @Inject()( publication = publication, subject = s"${org.name}/${application.name}:${version.version} Updated", body = generateBody(org, application, breakingDiffs, nonBreakingDiffs).toString - ) { subscription => + )( using { subscription => watchesDao.findAll( Authorization.All, applicationGuid = Some(application.guid), userGuid = Some(subscription.user.guid), limit = Some(1) ).nonEmpty - } + }) } } } diff --git a/api/app/processor/PurgeDeletedProcessor.scala b/api/app/processor/PurgeDeletedProcessor.scala index 8496553fe..c16c46f70 100644 --- a/api/app/processor/PurgeDeletedProcessor.scala +++ b/api/app/processor/PurgeDeletedProcessor.scala @@ -81,7 +81,7 @@ class PurgeDeletedProcessor @Inject()( s"select ${table.pkey.name}::text as pkey, deleted_at from ${table.qualified}" ).isNotNull("deleted_at") .limit(Limit) - .as(parser(table.pkey).*)(c) + .as(parser(table.pkey).*)(using c) } } @@ -114,7 +114,7 @@ class PurgeDeletedProcessor @Inject()( DeletedAtQuery .bind("table_schema", table.schema) .bind("table_name", table.name) - .as(SqlParser.int(1).single)(c) + .as(SqlParser.int(1).single)(using c) } > 0 }) } @@ -140,7 +140,7 @@ class PurgeDeletedProcessor @Inject()( private def exec(q: Query): Unit = { db.withConnection { c => - q.anormSql().executeUpdate()(c) + q.anormSql().executeUpdate()(using c) } () } diff --git a/api/app/processor/SyncGeneratorServiceProcessor.scala b/api/app/processor/SyncGeneratorServiceProcessor.scala index 3f5a931db..5e8f4ee7f 100644 --- a/api/app/processor/SyncGeneratorServiceProcessor.scala +++ b/api/app/processor/SyncGeneratorServiceProcessor.scala @@ -35,7 +35,7 @@ class SyncGeneratorServiceProcessor @Inject()( private val ec: ExecutionContext = system.dispatchers.lookup("generator-service-sync-context") override def processRecord(guid: UUID): ValidatedNec[String, Unit] = { - syncAll()(ec).validNec + syncAll()(using ec).validNec } private def syncAll(pageSize: Long = 200)(implicit ec: scala.concurrent.ExecutionContext): Unit = { @@ -112,4 +112,4 @@ class SyncGeneratorServiceProcessor @Inject()( case Valid(_) => // no-op } } -} \ No newline at end of file +} diff --git a/api/app/processor/TaskDispatchActorCompanion.scala b/api/app/processor/TaskDispatchActorCompanion.scala index f7d4ca30d..6dfb504ca 100644 --- a/api/app/processor/TaskDispatchActorCompanion.scala +++ b/api/app/processor/TaskDispatchActorCompanion.scala @@ -17,7 +17,7 @@ class TaskDispatchActorCompanion @Inject() ( def typesWithWork: Seq[TaskType] = { database .withConnection { c => - TypesQuery.as(SqlParser.str(1).*)(c) + TypesQuery.as(SqlParser.str(1).*)(using c) } .flatMap(TaskType.fromString) } diff --git a/api/app/processor/TaskProcessor.scala b/api/app/processor/TaskProcessor.scala index 3c52af136..83120be42 100644 --- a/api/app/processor/TaskProcessor.scala +++ b/api/app/processor/TaskProcessor.scala @@ -2,10 +2,10 @@ package processor import cats.data.Validated.{Invalid, Valid} import cats.data.ValidatedNec -import cats.implicits._ +import cats.implicits.* import db.generated.{Task, TaskForm, TasksDao} import io.apibuilder.task.v0.models.TaskType -import io.flow.postgresql.OrderBy +import io.flow.postgresql.{OrderBy, Query} import lib.Constants import org.joda.time.DateTime import play.api.libs.json.{JsObject, JsValue, Reads, Writes} @@ -91,10 +91,10 @@ abstract class BaseTaskProcessor( .findAll( limit = Some(Limit), orderBy = Some(OrderBy("num_attempts, next_attempt_at")) - ) { q => + )( using (q: Query) => { q.equals("type", typ.toString) .and("next_attempt_at <= now()") - } + }) .foreach(processRecordSafe) } } From ca3892a1022e9308a55061fe113a2d710fd0bde8 Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Sat, 20 Sep 2025 12:50:54 -0400 Subject: [PATCH 13/13] wip --- api/app/actors/TaskDispatchActor.scala | 2 +- api/app/db/InternalOrganizationDomainsDao.scala | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/api/app/actors/TaskDispatchActor.scala b/api/app/actors/TaskDispatchActor.scala index c1a1a1b47..9bf3be970 100644 --- a/api/app/actors/TaskDispatchActor.scala +++ b/api/app/actors/TaskDispatchActor.scala @@ -20,7 +20,7 @@ class TaskDispatchActor @Inject() ( private val actors = scala.collection.mutable.Map[TaskType, ActorRef]() private def schedule(message: Any, interval: FiniteDuration): Cancellable = { - context.system.scheduler.scheduleWithFixedDelay(FiniteDuration(1, SECONDS), interval, self, message)(ec) + context.system.scheduler.scheduleWithFixedDelay(FiniteDuration(1, SECONDS), interval, self, message)(using ec) } private val cancellables: Seq[Cancellable] = { diff --git a/api/app/db/InternalOrganizationDomainsDao.scala b/api/app/db/InternalOrganizationDomainsDao.scala index 1d23c81de..820df75c4 100644 --- a/api/app/db/InternalOrganizationDomainsDao.scala +++ b/api/app/db/InternalOrganizationDomainsDao.scala @@ -1,9 +1,7 @@ package db import db.generated.OrganizationDomainsDao -import io.apibuilder.api.v0.models.Domain import io.flow.postgresql.{OrderBy, Query} -import play.api.db.* import java.util.UUID import javax.inject.Inject @@ -18,12 +16,12 @@ class InternalOrganizationDomainsDao @Inject()( ) { def create(createdBy: InternalUser, org: InternalOrganization, domainName: String): InternalOrganizationDomain = { - dao.db.withConnection { implicit c => + dao.db.withConnection { c => create(c, createdBy, org.guid, domainName) } } - private[db] def create(implicit c: java.sql.Connection, createdBy: InternalUser, orgGuid: UUID, domainName: String): InternalOrganizationDomain = { + private[db] def create(c: java.sql.Connection, createdBy: InternalUser, orgGuid: UUID, domainName: String): InternalOrganizationDomain = { val guid = dao.insert(c, createdBy.guid, generated.OrganizationDomainForm( organizationGuid = orgGuid, domain = domainName.trim