diff --git a/api/app/db/InternalApplicationsDao.scala b/api/app/db/InternalApplicationsDao.scala index 272c7d958..5e3228527 100644 --- a/api/app/db/InternalApplicationsDao.scala +++ b/api/app/db/InternalApplicationsDao.scala @@ -1,105 +1,44 @@ package db -import anorm._ +import anorm.* import cats.data.ValidatedNec -import cats.implicits._ +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.task.v0.models.{EmailDataApplicationCreated, TaskType} -import io.flow.postgresql.Query +import io.flow.postgresql.{OrderBy, Query} import lib.{UrlKey, Validation} import org.joda.time.DateTime -import play.api.db._ +import play.api.db.* import processor.EmailProcessorQueue import util.OptionalQueryFilter +import java.sql.Connection import java.util.UUID import javax.inject.{Inject, Singleton} -case class InternalApplication( - guid: UUID, - name: String, - key: String, - description: Option[String], - visibility: Visibility, - organizationGuid: UUID, - lastUpdatedAt: DateTime, - audit: Audit - ) +case class InternalApplication(db: generated.Application) { + val guid: UUID = db.guid + val organizationGuid: UUID = db.organizationGuid + val name: String = db.name + val key: String = db.key + val description: Option[String] = db.description + val visibility: Visibility = Visibility(db.visibility) +} class InternalApplicationsDao @Inject()( - @NamedDatabase("default") db: Database, + dao: ApplicationsDao, emailQueue: EmailProcessorQueue, organizationsDao: InternalOrganizationsDao, tasksDao: InternalTasksDao, + movesDao: ApplicationMovesDao, ) { - private val dbHelpers = DbHelpers(db, "applications") - - private val BaseQuery = Query( - s""" - select guid, name, key, description, visibility, organization_guid, - ${AuditsDao.query("applications")}, - coalesce( - (select versions.created_at - from versions - where versions.application_guid = applications.guid - and versions.deleted_at is null - order by versions.version_sort_key desc, versions.created_at desc - limit 1), - applications.updated_at - ) as last_updated_at - from applications - """ - ) - - private val InsertQuery = - """ - insert into applications - (guid, organization_guid, name, description, key, visibility, created_by_guid, updated_by_guid) - values - ({guid}::uuid, {organization_guid}::uuid, {name}, {description}, {key}, {visibility}, {created_by_guid}::uuid, {created_by_guid}::uuid) - """ - - private val UpdateQuery = - """ - update applications - set name = {name}, - visibility = {visibility}, - description = {description}, - updated_by_guid = {updated_by_guid}::uuid - where guid = {guid}::uuid - """ - - private val InsertMoveQuery = - """ - insert into application_moves - (guid, application_guid, from_organization_guid, to_organization_guid, created_by_guid) - values - ({guid}::uuid, {application_guid}::uuid, {from_organization_guid}::uuid, {to_organization_guid}::uuid, {created_by_guid}::uuid) - """ - - private val UpdateOrganizationQuery = - """ - update applications - set organization_guid = {org_guid}::uuid, - updated_by_guid = {updated_by_guid}::uuid - where guid = {guid}::uuid - """ - - private val UpdateVisibilityQuery = - """ - update applications - set visibility = {visibility}, - updated_by_guid = {updated_by_guid}::uuid - where guid = {guid}::uuid - """ - private def validateOrganizationByKey(auth: Authorization, key: String): ValidatedNec[Error, InternalOrganization] = { organizationsDao.findByKey(auth, key).toValidNec(Validation.singleError(s"Organization[$key] not found")) } - private def validateMove( authorization: Authorization, app: InternalApplication, @@ -176,13 +115,11 @@ class InternalApplicationsDao @Inject()( assert(errors.isEmpty, errors.map(_.message).mkString(" ")) withTasks(app.guid, { implicit c => - SQL(UpdateQuery).on( - "guid" -> app.guid, - "name" -> form.name.trim, - "visibility" -> form.visibility.toString, - "description" -> form.description.map(_.trim), - "updated_by_guid" -> updatedBy.guid - ).execute() + dao.update(c, updatedBy.guid, app.db, app.db.form.copy( + name = form.name.trim, + visibility = form.visibility.toString, + description = form.description.map(_.trim).filterNot(_.isEmpty) + )) }) findByGuid(Authorization.All, app.guid).getOrElse { @@ -202,19 +139,15 @@ class InternalApplicationsDao @Inject()( } else { withTasks(app.guid, { implicit c => - SQL(InsertMoveQuery).on( - "guid" -> UUID.randomUUID, - "application_guid" -> app.guid, - "from_organization_guid" -> app.organizationGuid, - "to_organization_guid" -> newOrg.guid, - "created_by_guid" -> updatedBy.guid - ).execute() - - SQL(UpdateOrganizationQuery).on( - "guid" -> app.guid, - "org_guid" -> newOrg.guid, - "updated_by_guid" -> updatedBy.guid - ).execute() + movesDao.insert(c, updatedBy.guid, ApplicationMoveForm( + applicationGuid = app.guid, + fromOrganizationGuid = app.organizationGuid, + toOrganizationGuid = newOrg.guid + )) + + dao.update(updatedBy.guid, app.db, app.db.form.copy( + organizationGuid = newOrg.guid + )) }) findByGuid(Authorization.All, app.guid).getOrElse { @@ -231,15 +164,13 @@ class InternalApplicationsDao @Inject()( ): InternalApplication = { assert( canUserUpdate(updatedBy, app), - "User[${user.guid}] not authorized to update app[${app.key}]" + s"User[${updatedBy.guid}] not authorized to update app[${app.key}]" ) withTasks(app.guid, { implicit c => - SQL(UpdateVisibilityQuery).on( - "guid" -> app.guid, - "visibility" -> visibility.toString, - "updated_by_guid" -> updatedBy.guid - ).execute() + dao.update(c, updatedBy.guid, app.db, app.db.form.copy( + visibility = visibility.toString + )) }) findByGuid(Authorization.All, app.guid).getOrElse { @@ -255,31 +186,29 @@ class InternalApplicationsDao @Inject()( val errors = validate(org, form) assert(errors.isEmpty, errors.map(_.message).mkString(" ")) - val guid = UUID.randomUUID val key = form.key.getOrElse(UrlKey.generate(form.name)) - withTasks(guid, { implicit c => - SQL(InsertQuery).on( - "guid" -> guid, - "organization_guid" -> org.guid, - "name" -> form.name.trim, - "description" -> form.description.map(_.trim), - "key" -> key, - "visibility" -> form.visibility.toString, - "created_by_guid" -> createdBy.guid, - "updated_by_guid" -> createdBy.guid - ).execute() + val guid = dao.db.withTransaction { c => + val guid = dao.insert(c, createdBy.guid, db.generated.ApplicationForm( + organizationGuid = org.guid, + name = form.name.trim, + key = key, + visibility = form.visibility.toString, + description = form.description.map(_.trim).filterNot(_.isEmpty) + )) emailQueue.queueWithConnection(c, EmailDataApplicationCreated(guid)) - }) + queueTasks(c, guid) + guid + } - findAll(Authorization.All, orgKey = Some(org.key), key = Some(key), limit = Some(1)).headOption.getOrElse { + findByGuid(Authorization.All, guid).getOrElse { sys.error("Failed to create application") } } - def softDelete(deletedBy: User, application: InternalApplication): Unit = { - withTasks(application.guid, { c => - dbHelpers.delete(c, deletedBy.guid, application.guid) + def softDelete(deletedBy: User, app: InternalApplication): Unit = { + withTasks(app.guid, { c => + dao.delete(c, deletedBy.guid, app.db) }) } @@ -319,98 +248,85 @@ class InternalApplicationsDao @Inject()( override def filter(q: Query, value: String): Query = { q.in("organization_guid", Query("select guid from organizations").equals("key", orgKey)) } - } - ) - - db.withConnection { implicit c => - val appQuery = authorization.applicationFilter( - filters.foldLeft(BaseQuery) { case (q, f) => f.filter(q) }, - "guid" - ). - equals("guid", guid). - optionalIn("guid", guids). - and( - name.map { _ => - "lower(trim(name)) = lower(trim({name}))" - } - ).bind("name", name). - and( - key.map { _ => - "key = lower(trim({application_key}))" - } - ).bind("application_key", key). - and( - version.map { _ => - "guid = (select application_guid from versions where deleted_at is null and versions.guid = {version_guid}::uuid)" + }, + new OptionalQueryFilter(isDeleted) { + override def filter(q: Query, value: Boolean): Query = { + if (value) { + q.isNotNull("deleted_at") + } else { + q.isNull("deleted_at") } - ).bind("version_guid", version.map(_.guid.toString)). - and( - hasVersion.map { v => - val clause = "select 1 from versions where versions.deleted_at is null and versions.application_guid = applications.guid" - if (v) { - s"exists ($clause)" - } else { - s"not exists ($clause)" - } + } + }, + new OptionalQueryFilter(version) { + override def filter(q: Query, v: Version): Query = { + q.in( + "guid", + Query("select application_guid from versions") + .isNull("deleted_at") + .equals("guid", v.guid) + ) + } + }, + new OptionalQueryFilter(hasVersion) { + override def filter(q: Query, value: Boolean): Query = { + val clause = "select 1 from versions where versions.deleted_at is null and versions.application_guid = applications.guid" + if (value) { + q.and(s"exists ($clause)") + } else { + q.and(s"not exists ($clause)") } - ). - and(isDeleted.map(Filters.isDeleted("applications", _))). - optionalLimit(limit). - offset(offset) - sorting.fold(appQuery) { sorting => - val sort = sorting match { - case AppSortBy.Visibility => "visibility" - case AppSortBy.CreatedAt => "created_at" - case AppSortBy.UpdatedAt => "last_updated_at" - case _ => "name" } - val ord = ordering.getOrElse(SortOrder.Asc).toString - appQuery.orderBy(s"$sort $ord") - }.as(parser.*) - } + } + ) + + dao.findAll( + guid = guid, + guids = guids, + limit = limit, + offset = offset, + orderBy = Some(toOrderBy(sorting, ordering)), + ) { q => + 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(_)) } - private val parser: RowParser[InternalApplication] = { - import org.joda.time.DateTime - - SqlParser.get[UUID]("guid") ~ - SqlParser.get[UUID]("organization_guid") ~ - SqlParser.str("name") ~ - SqlParser.str("key") ~ - SqlParser.str("visibility") ~ - SqlParser.str("description").? ~ - SqlParser.get[DateTime]("last_updated_at") ~ - SqlParser.get[DateTime]("created_at") ~ - SqlParser.get[UUID]("created_by_guid") ~ - SqlParser.get[DateTime]("updated_at") ~ - SqlParser.get[UUID]("updated_by_guid") map { case guid ~ organizationGuid ~ name ~ key ~ visibility ~ description ~ lastUpdatedAt ~ createdAt ~ createdByGuid ~ updatedAt ~ updatedByGuid => { - InternalApplication( - guid = guid, - organizationGuid = organizationGuid, - name = name, - key = key, - visibility = Visibility(visibility), - description = description, - lastUpdatedAt = lastUpdatedAt, - audit = Audit( - createdAt = createdAt, - createdBy = ReferenceGuid(createdByGuid), - updatedAt = updatedAt, - updatedBy = ReferenceGuid(updatedByGuid), - ) - ) - } + private def toOrderBy( + sorting: Option[AppSortBy] = None, + ordering: Option[SortOrder] = None + ): OrderBy = { + val sort = sorting.getOrElse(AppSortBy.Name) match { + case AppSortBy.Visibility => "visibility" + case AppSortBy.CreatedAt => "created_at" + case AppSortBy.UpdatedAt => "updated_at" + case AppSortBy.Name | AppSortBy.UNDEFINED(_) => "name" + } + + val ord = ordering.getOrElse(SortOrder.Asc) match { + case SortOrder.Desc => "-" + case SortOrder.Asc | SortOrder.UNDEFINED(_) => "" } + + OrderBy(s"$ord$sort") } private def withTasks( guid: UUID, f: java.sql.Connection => Unit ): Unit = { - db.withTransaction { implicit c => + dao.db.withTransaction { implicit c => f(c) - tasksDao.queueWithConnection(c, TaskType.IndexApplication, guid.toString) + queueTasks(c, guid) } } + private def queueTasks(c: Connection, guid: UUID): Unit = { + tasksDao.queueWithConnection(c, TaskType.IndexApplication, guid.toString) + } + } \ No newline at end of file diff --git a/api/app/models/ApplicationsModel.scala b/api/app/models/ApplicationsModel.scala index 29f750da7..653afa22a 100644 --- a/api/app/models/ApplicationsModel.scala +++ b/api/app/models/ApplicationsModel.scala @@ -2,13 +2,18 @@ package models import db.{Authorization, InternalApplication, InternalOrganizationsDao} import io.apibuilder.api.v0.models.Application -import io.apibuilder.common.v0.models.Reference +import io.apibuilder.common.v0.models.{Audit, Reference, ReferenceGuid} +import io.flow.postgresql.Query +import org.joda.time.DateTime +import play.api.db.Database +import java.util.UUID import javax.inject.Inject class ApplicationsModel @Inject()( - organizationsDao: InternalOrganizationsDao, - ) { + db: Database, + organizationsDao: InternalOrganizationsDao, +) { def toModel(application: InternalApplication): Option[Application] = { toModels(Seq(application)).headOption } @@ -16,9 +21,12 @@ class ApplicationsModel @Inject()( def toModels(applications: Seq[InternalApplication]): Seq[Application] = { val organizations = organizationsDao.findAll( Authorization.All, - guids = Some(applications.map(_.organizationGuid).toSeq.distinct), + guids = Some(applications.map(_.organizationGuid).distinct), limit = None ).map { o => o.guid -> o }.toMap + val lastUpdated = lookupLastVersionCreatedAt(applications.map(_.guid)).map { d => + d.applicationGuid -> d.timestamp + }.toMap applications.flatMap { app => organizations.get(app.organizationGuid).map { org => @@ -29,9 +37,49 @@ class ApplicationsModel @Inject()( key = app.key, visibility = app.visibility, description = app.description, - lastUpdatedAt = app.lastUpdatedAt, - audit = app.audit + lastUpdatedAt = lastUpdated.getOrElse(app.guid, app.db.updatedAt), + audit = Audit( + createdAt = org.db.createdAt, + createdBy = ReferenceGuid(org.db.createdByGuid), + updatedAt = org.db.updatedAt, + updatedBy = ReferenceGuid(org.db.updatedByGuid), + ) ) } } - } } \ No newline at end of file + } + + private case class LastVersionCreated(applicationGuid: UUID, timestamp: DateTime) + private val LastVersionCreatedQuery = Query( + """ + |select application_guid::text, + | max(coalesce(deleted_at, created_at)) as timestamp + | from versions + |""".stripMargin + ) + + private def lookupLastVersionCreatedAt(guids: Seq[UUID]): Seq[LastVersionCreated] = { + if (guids.isEmpty) { + Nil + } else { + db.withConnection { c => + LastVersionCreatedQuery + .in("application_guid", guids) + .groupBy("1") + .as(parser.*)(c) + } + } + } + + private val parser: anorm.RowParser[LastVersionCreated] = { + import anorm.* + + SqlParser.str("application_guid") ~ + SqlParser.get[org.joda.time.DateTime]("timestamp") map { case applicationGuid ~ timestamp => + LastVersionCreated( + applicationGuid = java.util.UUID.fromString(applicationGuid), + timestamp = timestamp + ) + } + } +} \ No newline at end of file diff --git a/dao/spec/psql-apibuilder.json b/dao/spec/psql-apibuilder.json index 5181ae18c..ffbf71d37 100644 --- a/dao/spec/psql-apibuilder.json +++ b/dao/spec/psql-apibuilder.json @@ -32,6 +32,80 @@ ], "models": { + "application": { + "fields": [ + { "name": "guid", "type": "uuid" }, + { "name": "organization_guid", "type": "uuid" }, + { "name": "name", "type": "string" }, + { "name": "key", "type": "string" }, + { "name": "visibility", "type": "string" }, + { "name": "description", "type": "string", "required": false } + ], + "attributes": [ + { + "name": "scala", + "value": { + "pkey_generator": { + "method": "java.util.UUID.randomUUID", + "returns": "uuid" + } + } + }, + { + "name": "psql", + "value": { + "indexes": [ + { "fields": ["organization_guid"] }, + { "fields": ["organization_guid", "key"], "where": "deleted_at is null" } + ] + } + } + ] + }, + + "application_move": { + "fields": [ + { "name": "guid", "type": "uuid" }, + { "name": "application_guid", "type": "uuid" }, + { "name": "from_organization_guid", "type": "uuid" }, + { "name": "to_organization_guid", "type": "uuid" } + ], + "attributes": [ + { + "name": "scala", + "value": { + "pkey_generator": { + "method": "java.util.UUID.randomUUID", + "returns": "uuid" + } + } + }, + { + "name": "psql", + "value": { + "indexes": [ + { "fields": ["application_guid"] }, + { "fields": ["from_organization_guid"] }, + { "fields": ["to_organization_guid"] } + ], + "audit": { + "created": { + "at": { "type": "date-time-iso8601" }, + "by": { "name": "created_by_guid", "type": "uuid" } + }, + "updated": { + "at": { "type": "date-time-iso8601" } + }, + "deleted": { + "at": { "type": "date-time-iso8601", "required": false }, + "by": { "name": "deleted_by_guid", "type": "uuid", "required": false } + } + } + } + } + ] + }, + "organization": { "fields": [ { "name": "guid", "type": "uuid" }, diff --git a/generated/app/db/ApplicationMovesDao.scala b/generated/app/db/ApplicationMovesDao.scala new file mode 100644 index 000000000..2f55a0ee1 --- /dev/null +++ b/generated/app/db/ApplicationMovesDao.scala @@ -0,0 +1,639 @@ +package db.generated + +case class ApplicationMove( + guid: java.util.UUID, + applicationGuid: java.util.UUID, + fromOrganizationGuid: java.util.UUID, + toOrganizationGuid: java.util.UUID, + createdAt: org.joda.time.DateTime, + createdByGuid: java.util.UUID, + updatedAt: org.joda.time.DateTime, + deletedAt: Option[org.joda.time.DateTime], + deletedByGuid: Option[java.util.UUID] +) { + def form: ApplicationMoveForm = { + ApplicationMoveForm( + applicationGuid = applicationGuid, + fromOrganizationGuid = fromOrganizationGuid, + toOrganizationGuid = toOrganizationGuid, + ) + } +} + +case class ApplicationMoveForm( + applicationGuid: java.util.UUID, + fromOrganizationGuid: java.util.UUID, + toOrganizationGuid: java.util.UUID +) + +case object ApplicationMovesTable { + val SchemaName: String = "public" + + val TableName: String = "application_moves" + + val QualifiedName: String = "public.application_moves" + + sealed trait Column { + def name: String + } + + object Columns { + case object Guid extends Column { + override val name: String = "guid" + } + + case object ApplicationGuid extends Column { + override val name: String = "application_guid" + } + + case object FromOrganizationGuid extends Column { + override val name: String = "from_organization_guid" + } + + case object ToOrganizationGuid extends Column { + override val name: String = "to_organization_guid" + } + + case object CreatedAt extends Column { + override val name: String = "created_at" + } + + case object CreatedByGuid extends Column { + override val name: String = "created_by_guid" + } + + case object UpdatedAt extends Column { + override val name: String = "updated_at" + } + + case object DeletedAt extends Column { + override val name: String = "deleted_at" + } + + case object DeletedByGuid extends Column { + override val name: String = "deleted_by_guid" + } + + val all: List[Column] = List(Guid, ApplicationGuid, FromOrganizationGuid, ToOrganizationGuid, CreatedAt, CreatedByGuid, UpdatedAt, DeletedAt, DeletedByGuid) + } +} + +trait BaseApplicationMovesDao { + import anorm.* + + import anorm.JodaParameterMetaData.* + + import anorm.postgresql.* + + def db: play.api.db.Database + + private val BaseQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | select guid::text, + | application_guid::text, + | from_organization_guid::text, + | to_organization_guid::text, + | created_at, + | created_by_guid::text, + | updated_at, + | deleted_at, + | deleted_by_guid::text + | from public.application_moves + |""".stripMargin.stripTrailing + ) + } + + def findAll( + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + applicationGuid: Option[java.util.UUID] = None, + applicationGuids: Option[Seq[java.util.UUID]] = None, + fromOrganizationGuid: Option[java.util.UUID] = None, + fromOrganizationGuids: Option[Seq[java.util.UUID]] = None, + toOrganizationGuid: Option[java.util.UUID] = None, + toOrganizationGuids: Option[Seq[java.util.UUID]] = None, + limit: Option[Long], + offset: Long = 0, + orderBy: Option[io.flow.postgresql.OrderBy] = None + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Seq[ApplicationMove] = { + db.withConnection { c => + findAllWithConnection(c, guid, guids, applicationGuid, applicationGuids, fromOrganizationGuid, fromOrganizationGuids, toOrganizationGuid, toOrganizationGuids, limit, offset, orderBy) + } + } + + def findAllWithConnection( + c: java.sql.Connection, + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + applicationGuid: Option[java.util.UUID] = None, + applicationGuids: Option[Seq[java.util.UUID]] = None, + fromOrganizationGuid: Option[java.util.UUID] = None, + fromOrganizationGuids: Option[Seq[java.util.UUID]] = None, + toOrganizationGuid: Option[java.util.UUID] = None, + toOrganizationGuids: Option[Seq[java.util.UUID]] = None, + limit: Option[Long], + offset: Long = 0, + orderBy: Option[io.flow.postgresql.OrderBy] = None + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Seq[ApplicationMove] = { + customQueryModifier(BaseQuery) + .equals("application_moves.guid", guid) + .optionalIn("application_moves.guid", guids) + .equals("application_moves.application_guid", applicationGuid) + .optionalIn("application_moves.application_guid", applicationGuids) + .equals("application_moves.from_organization_guid", fromOrganizationGuid) + .optionalIn("application_moves.from_organization_guid", fromOrganizationGuids) + .equals("application_moves.to_organization_guid", toOrganizationGuid) + .optionalIn("application_moves.to_organization_guid", toOrganizationGuids) + .optionalLimit(limit) + .offset(offset) + .orderBy(orderBy.flatMap(_.sql)) + .as(parser.*)(c) + } + + def iterateAll( + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + applicationGuid: Option[java.util.UUID] = None, + applicationGuids: Option[Seq[java.util.UUID]] = None, + fromOrganizationGuid: Option[java.util.UUID] = None, + fromOrganizationGuids: Option[Seq[java.util.UUID]] = None, + toOrganizationGuid: Option[java.util.UUID] = None, + toOrganizationGuids: Option[Seq[java.util.UUID]] = None, + pageSize: Long = 1000 + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Iterator[ApplicationMove] = { + assert(pageSize > 0, "pageSize must be > 0") + + def iterate(lastValue: Option[ApplicationMove]): Iterator[ApplicationMove] = { + val page: Seq[ApplicationMove] = db.withConnection { c => + customQueryModifier(BaseQuery) + .equals("application_moves.guid", guid) + .optionalIn("application_moves.guid", guids) + .equals("application_moves.application_guid", applicationGuid) + .optionalIn("application_moves.application_guid", applicationGuids) + .equals("application_moves.from_organization_guid", fromOrganizationGuid) + .optionalIn("application_moves.from_organization_guid", fromOrganizationGuids) + .equals("application_moves.to_organization_guid", toOrganizationGuid) + .optionalIn("application_moves.to_organization_guid", toOrganizationGuids) + .greaterThan("application_moves.guid", lastValue.map(_.guid)) + .orderBy("application_moves.guid") + .limit(pageSize) + .as(parser.*)(c) + } + if (page.length >= pageSize) { + page.iterator ++ iterate(page.lastOption) + } else { + page.iterator + } + } + + iterate(None) + } + + def findByGuid(guid: java.util.UUID): Option[ApplicationMove] = { + db.withConnection { c => + findByGuidWithConnection(c, guid) + } + } + + def findByGuidWithConnection( + c: java.sql.Connection, + guid: java.util.UUID + ): Option[ApplicationMove] = { + findAllWithConnection( + c = c, + guid = Some(guid), + limit = Some(1) + ).headOption + } + + def findAllByApplicationGuid(applicationGuid: java.util.UUID): Seq[ApplicationMove] = { + db.withConnection { c => + findAllByApplicationGuidWithConnection(c, applicationGuid) + } + } + + def findAllByApplicationGuidWithConnection( + c: java.sql.Connection, + applicationGuid: java.util.UUID + ): Seq[ApplicationMove] = { + findAllWithConnection( + c = c, + applicationGuid = Some(applicationGuid), + limit = None + ) + } + + def findAllByFromOrganizationGuid(fromOrganizationGuid: java.util.UUID): Seq[ApplicationMove] = { + db.withConnection { c => + findAllByFromOrganizationGuidWithConnection(c, fromOrganizationGuid) + } + } + + def findAllByFromOrganizationGuidWithConnection( + c: java.sql.Connection, + fromOrganizationGuid: java.util.UUID + ): Seq[ApplicationMove] = { + findAllWithConnection( + c = c, + fromOrganizationGuid = Some(fromOrganizationGuid), + limit = None + ) + } + + def findAllByToOrganizationGuid(toOrganizationGuid: java.util.UUID): Seq[ApplicationMove] = { + db.withConnection { c => + findAllByToOrganizationGuidWithConnection(c, toOrganizationGuid) + } + } + + def findAllByToOrganizationGuidWithConnection( + c: java.sql.Connection, + toOrganizationGuid: java.util.UUID + ): Seq[ApplicationMove] = { + findAllWithConnection( + c = c, + toOrganizationGuid = Some(toOrganizationGuid), + limit = None + ) + } + + private val parser: anorm.RowParser[ApplicationMove] = { + anorm.SqlParser.str("guid") ~ + anorm.SqlParser.str("application_guid") ~ + anorm.SqlParser.str("from_organization_guid") ~ + anorm.SqlParser.str("to_organization_guid") ~ + anorm.SqlParser.get[org.joda.time.DateTime]("created_at") ~ + anorm.SqlParser.str("created_by_guid") ~ + anorm.SqlParser.get[org.joda.time.DateTime]("updated_at") ~ + anorm.SqlParser.get[org.joda.time.DateTime]("deleted_at").? ~ + anorm.SqlParser.str("deleted_by_guid").? map { case guid ~ applicationGuid ~ fromOrganizationGuid ~ toOrganizationGuid ~ createdAt ~ createdByGuid ~ updatedAt ~ deletedAt ~ deletedByGuid => + ApplicationMove( + guid = java.util.UUID.fromString(guid), + applicationGuid = java.util.UUID.fromString(applicationGuid), + fromOrganizationGuid = java.util.UUID.fromString(fromOrganizationGuid), + toOrganizationGuid = java.util.UUID.fromString(toOrganizationGuid), + createdAt = createdAt, + createdByGuid = java.util.UUID.fromString(createdByGuid), + updatedAt = updatedAt, + deletedAt = deletedAt, + deletedByGuid = deletedByGuid.map { v => java.util.UUID.fromString(v) } + ) + } + } +} + +class ApplicationMovesDao @javax.inject.Inject() (override val db: play.api.db.Database) extends BaseApplicationMovesDao { + import anorm.JodaParameterMetaData.* + + import anorm.postgresql.* + + def randomPkey: java.util.UUID = { + java.util.UUID.randomUUID + } + + private val InsertQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | insert into public.application_moves + | (guid, application_guid, from_organization_guid, to_organization_guid, created_at, created_by_guid, updated_at) + | values + | ({guid}::uuid, {application_guid}::uuid, {from_organization_guid}::uuid, {to_organization_guid}::uuid, {created_at}::timestamptz, {created_by_guid}::uuid, {updated_at}::timestamptz) + """.stripMargin) + } + + private val UpdateQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | update public.application_moves + | set application_guid = {application_guid}::uuid, + | from_organization_guid = {from_organization_guid}::uuid, + | to_organization_guid = {to_organization_guid}::uuid, + | updated_at = {updated_at}::timestamptz + | where guid = {guid}::uuid + """.stripMargin) + } + + private val DeleteQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query("update public.application_moves set deleted_at = {deleted_at}::timestamptz, deleted_by_guid = {deleted_by_guid}::uuid") + } + + def insert( + user: java.util.UUID, + form: ApplicationMoveForm + ): java.util.UUID = { + db.withConnection { c => + insert(c, user, form) + } + } + + def insert( + c: java.sql.Connection, + user: java.util.UUID, + form: ApplicationMoveForm + ): java.util.UUID = { + val id = randomPkey + bindQuery(InsertQuery, user, form) + .bind("created_at", org.joda.time.DateTime.now) + .bind("created_by_guid", user) + .bind("guid", id) + .execute(c) + id + } + + def insertBatch( + user: java.util.UUID, + forms: Seq[ApplicationMoveForm] + ): Seq[java.util.UUID] = { + db.withConnection { c => + insertBatch(c, user, forms) + } + } + + def insertBatch( + c: java.sql.Connection, + user: java.util.UUID, + forms: Seq[ApplicationMoveForm] + ): Seq[java.util.UUID] = { + forms.map { f => + val guid = randomPkey + (guid, Seq(anorm.NamedParameter("created_at", org.joda.time.DateTime.now)) ++ toNamedParameter(user, guid, f)) + }.toList match { + case Nil => Nil + case one :: rest => { + anorm.BatchSql(InsertQuery.sql(), one._2, rest.map(_._2)*).execute()(c) + Seq(one._1) ++ rest.map(_._1) + } + } + } + + def update( + user: java.util.UUID, + applicationMove: ApplicationMove, + form: ApplicationMoveForm + ): Unit = { + db.withConnection { c => + update(c, user, applicationMove, form) + } + } + + def update( + c: java.sql.Connection, + user: java.util.UUID, + applicationMove: ApplicationMove, + form: ApplicationMoveForm + ): Unit = { + updateByGuid( + c = c, + user = user, + guid = applicationMove.guid, + form = form + ) + } + + def updateByGuid( + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationMoveForm + ): Unit = { + db.withConnection { c => + updateByGuid(c, user, guid, form) + } + } + + def updateByGuid( + c: java.sql.Connection, + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationMoveForm + ): Unit = { + bindQuery(UpdateQuery, user, form) + .bind("guid", guid) + .execute(c) + () + } + + def updateBatch( + user: java.util.UUID, + forms: Seq[(java.util.UUID, ApplicationMoveForm)] + ): Unit = { + db.withConnection { c => + updateBatch(c, user, forms) + } + } + + def updateBatch( + c: java.sql.Connection, + user: java.util.UUID, + forms: Seq[(java.util.UUID, ApplicationMoveForm)] + ): Unit = { + forms.map { case (guid, f) => toNamedParameter(user, guid, f) }.toList match { + case Nil => // no-op + case first :: rest => anorm.BatchSql(UpdateQuery.sql(), first, rest*).execute()(c) + } + } + + def delete( + user: java.util.UUID, + applicationMove: ApplicationMove + ): Unit = { + db.withConnection { c => + delete(c, user, applicationMove) + } + } + + def delete( + c: java.sql.Connection, + user: java.util.UUID, + applicationMove: ApplicationMove + ): Unit = { + deleteByGuid( + c = c, + user = user, + guid = applicationMove.guid + ) + } + + def deleteByGuid( + user: java.util.UUID, + guid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteByGuid(c, user, guid) + } + } + + def deleteByGuid( + c: java.sql.Connection, + user: java.util.UUID, + guid: java.util.UUID + ): Unit = { + DeleteQuery.equals("guid", guid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByGuids( + user: java.util.UUID, + guids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByGuids(c, user, guids) + } + } + + def deleteAllByGuids( + c: java.sql.Connection, + user: java.util.UUID, + guids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("guid", guids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByApplicationGuid( + user: java.util.UUID, + applicationGuid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteAllByApplicationGuid(c, user, applicationGuid) + } + } + + def deleteAllByApplicationGuid( + c: java.sql.Connection, + user: java.util.UUID, + applicationGuid: java.util.UUID + ): Unit = { + DeleteQuery.equals("application_guid", applicationGuid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByApplicationGuids( + user: java.util.UUID, + applicationGuids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByApplicationGuids(c, user, applicationGuids) + } + } + + def deleteAllByApplicationGuids( + c: java.sql.Connection, + user: java.util.UUID, + applicationGuids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("application_guid", applicationGuids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByFromOrganizationGuid( + user: java.util.UUID, + fromOrganizationGuid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteAllByFromOrganizationGuid(c, user, fromOrganizationGuid) + } + } + + def deleteAllByFromOrganizationGuid( + c: java.sql.Connection, + user: java.util.UUID, + fromOrganizationGuid: java.util.UUID + ): Unit = { + DeleteQuery.equals("from_organization_guid", fromOrganizationGuid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByFromOrganizationGuids( + user: java.util.UUID, + fromOrganizationGuids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByFromOrganizationGuids(c, user, fromOrganizationGuids) + } + } + + def deleteAllByFromOrganizationGuids( + c: java.sql.Connection, + user: java.util.UUID, + fromOrganizationGuids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("from_organization_guid", fromOrganizationGuids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByToOrganizationGuid( + user: java.util.UUID, + toOrganizationGuid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteAllByToOrganizationGuid(c, user, toOrganizationGuid) + } + } + + def deleteAllByToOrganizationGuid( + c: java.sql.Connection, + user: java.util.UUID, + toOrganizationGuid: java.util.UUID + ): Unit = { + DeleteQuery.equals("to_organization_guid", toOrganizationGuid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByToOrganizationGuids( + user: java.util.UUID, + toOrganizationGuids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByToOrganizationGuids(c, user, toOrganizationGuids) + } + } + + def deleteAllByToOrganizationGuids( + c: java.sql.Connection, + user: java.util.UUID, + toOrganizationGuids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("to_organization_guid", toOrganizationGuids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + private def bindQuery( + query: io.flow.postgresql.Query, + user: java.util.UUID, + form: ApplicationMoveForm + ): io.flow.postgresql.Query = { + query + .bind("application_guid", form.applicationGuid.toString) + .bind("from_organization_guid", form.fromOrganizationGuid.toString) + .bind("to_organization_guid", form.toOrganizationGuid.toString) + .bind("updated_at", org.joda.time.DateTime.now) + } + + private def toNamedParameter( + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationMoveForm + ): Seq[anorm.NamedParameter] = { + Seq( + anorm.NamedParameter("guid", guid.toString), + anorm.NamedParameter("application_guid", form.applicationGuid.toString), + anorm.NamedParameter("from_organization_guid", form.fromOrganizationGuid.toString), + anorm.NamedParameter("to_organization_guid", form.toOrganizationGuid.toString), + anorm.NamedParameter("updated_at", org.joda.time.DateTime.now) + ) + } +} \ No newline at end of file diff --git a/generated/app/db/ApplicationsDao.scala b/generated/app/db/ApplicationsDao.scala new file mode 100644 index 000000000..2e2bcf12b --- /dev/null +++ b/generated/app/db/ApplicationsDao.scala @@ -0,0 +1,543 @@ +package db.generated + +case class Application( + guid: java.util.UUID, + organizationGuid: java.util.UUID, + name: String, + key: String, + visibility: String, + description: Option[String], + createdAt: org.joda.time.DateTime, + createdByGuid: java.util.UUID, + updatedAt: org.joda.time.DateTime, + updatedByGuid: java.util.UUID, + deletedAt: Option[org.joda.time.DateTime], + deletedByGuid: Option[java.util.UUID] +) { + def form: ApplicationForm = { + ApplicationForm( + organizationGuid = organizationGuid, + name = name, + key = key, + visibility = visibility, + description = description, + ) + } +} + +case class ApplicationForm( + organizationGuid: java.util.UUID, + name: String, + key: String, + visibility: String, + description: Option[String] +) + +case object ApplicationsTable { + val SchemaName: String = "public" + + val TableName: String = "applications" + + val QualifiedName: String = "public.applications" + + sealed trait Column { + def name: String + } + + object Columns { + case object Guid extends Column { + override val name: String = "guid" + } + + case object OrganizationGuid extends Column { + override val name: String = "organization_guid" + } + + case object Name extends Column { + override val name: String = "name" + } + + case object Key extends Column { + override val name: String = "key" + } + + case object Visibility extends Column { + override val name: String = "visibility" + } + + case object Description extends Column { + override val name: String = "description" + } + + case object CreatedAt extends Column { + override val name: String = "created_at" + } + + case object CreatedByGuid extends Column { + override val name: String = "created_by_guid" + } + + case object UpdatedAt extends Column { + override val name: String = "updated_at" + } + + case object UpdatedByGuid extends Column { + override val name: String = "updated_by_guid" + } + + case object DeletedAt extends Column { + override val name: String = "deleted_at" + } + + case object DeletedByGuid extends Column { + override val name: String = "deleted_by_guid" + } + + val all: List[Column] = List(Guid, OrganizationGuid, Name, Key, Visibility, Description, CreatedAt, CreatedByGuid, UpdatedAt, UpdatedByGuid, DeletedAt, DeletedByGuid) + } +} + +trait BaseApplicationsDao { + import anorm.* + + import anorm.JodaParameterMetaData.* + + import anorm.postgresql.* + + def db: play.api.db.Database + + private val BaseQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | select guid::text, + | organization_guid::text, + | name, + | key, + | visibility, + | description, + | created_at, + | created_by_guid::text, + | updated_at, + | updated_by_guid::text, + | deleted_at, + | deleted_by_guid::text + | from public.applications + |""".stripMargin.stripTrailing + ) + } + + def findAll( + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + organizationGuid: Option[java.util.UUID] = None, + organizationGuids: Option[Seq[java.util.UUID]] = None, + limit: Option[Long], + offset: Long = 0, + orderBy: Option[io.flow.postgresql.OrderBy] = None + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Seq[Application] = { + db.withConnection { c => + findAllWithConnection(c, guid, guids, organizationGuid, organizationGuids, limit, offset, orderBy) + } + } + + def findAllWithConnection( + c: java.sql.Connection, + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + organizationGuid: Option[java.util.UUID] = None, + organizationGuids: Option[Seq[java.util.UUID]] = None, + limit: Option[Long], + offset: Long = 0, + orderBy: Option[io.flow.postgresql.OrderBy] = None + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Seq[Application] = { + customQueryModifier(BaseQuery) + .equals("applications.guid", guid) + .optionalIn("applications.guid", guids) + .equals("applications.organization_guid", organizationGuid) + .optionalIn("applications.organization_guid", organizationGuids) + .optionalLimit(limit) + .offset(offset) + .orderBy(orderBy.flatMap(_.sql)) + .as(parser.*)(c) + } + + def iterateAll( + guid: Option[java.util.UUID] = None, + guids: Option[Seq[java.util.UUID]] = None, + organizationGuid: Option[java.util.UUID] = None, + organizationGuids: Option[Seq[java.util.UUID]] = None, + pageSize: Long = 1000 + )(implicit customQueryModifier: io.flow.postgresql.Query => io.flow.postgresql.Query = identity): Iterator[Application] = { + assert(pageSize > 0, "pageSize must be > 0") + + def iterate(lastValue: Option[Application]): Iterator[Application] = { + val page: Seq[Application] = db.withConnection { c => + customQueryModifier(BaseQuery) + .equals("applications.guid", guid) + .optionalIn("applications.guid", guids) + .equals("applications.organization_guid", organizationGuid) + .optionalIn("applications.organization_guid", organizationGuids) + .greaterThan("applications.guid", lastValue.map(_.guid)) + .orderBy("applications.guid") + .limit(pageSize) + .as(parser.*)(c) + } + if (page.length >= pageSize) { + page.iterator ++ iterate(page.lastOption) + } else { + page.iterator + } + } + + iterate(None) + } + + def findByGuid(guid: java.util.UUID): Option[Application] = { + db.withConnection { c => + findByGuidWithConnection(c, guid) + } + } + + def findByGuidWithConnection( + c: java.sql.Connection, + guid: java.util.UUID + ): Option[Application] = { + findAllWithConnection( + c = c, + guid = Some(guid), + limit = Some(1) + ).headOption + } + + def findAllByOrganizationGuid(organizationGuid: java.util.UUID): Seq[Application] = { + db.withConnection { c => + findAllByOrganizationGuidWithConnection(c, organizationGuid) + } + } + + def findAllByOrganizationGuidWithConnection( + c: java.sql.Connection, + organizationGuid: java.util.UUID + ): Seq[Application] = { + findAllWithConnection( + c = c, + organizationGuid = Some(organizationGuid), + limit = None + ) + } + + private val parser: anorm.RowParser[Application] = { + anorm.SqlParser.str("guid") ~ + anorm.SqlParser.str("organization_guid") ~ + anorm.SqlParser.str("name") ~ + anorm.SqlParser.str("key") ~ + anorm.SqlParser.str("visibility") ~ + anorm.SqlParser.str("description").? ~ + anorm.SqlParser.get[org.joda.time.DateTime]("created_at") ~ + anorm.SqlParser.str("created_by_guid") ~ + anorm.SqlParser.get[org.joda.time.DateTime]("updated_at") ~ + anorm.SqlParser.str("updated_by_guid") ~ + anorm.SqlParser.get[org.joda.time.DateTime]("deleted_at").? ~ + anorm.SqlParser.str("deleted_by_guid").? map { case guid ~ organizationGuid ~ name ~ key ~ visibility ~ description ~ createdAt ~ createdByGuid ~ updatedAt ~ updatedByGuid ~ deletedAt ~ deletedByGuid => + Application( + guid = java.util.UUID.fromString(guid), + organizationGuid = java.util.UUID.fromString(organizationGuid), + name = name, + key = key, + visibility = visibility, + description = description, + createdAt = createdAt, + createdByGuid = java.util.UUID.fromString(createdByGuid), + updatedAt = updatedAt, + updatedByGuid = java.util.UUID.fromString(updatedByGuid), + deletedAt = deletedAt, + deletedByGuid = deletedByGuid.map { v => java.util.UUID.fromString(v) } + ) + } + } +} + +class ApplicationsDao @javax.inject.Inject() (override val db: play.api.db.Database) extends BaseApplicationsDao { + import anorm.JodaParameterMetaData.* + + import anorm.postgresql.* + + def randomPkey: java.util.UUID = { + java.util.UUID.randomUUID + } + + private val InsertQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | insert into public.applications + | (guid, organization_guid, name, key, visibility, description, created_at, created_by_guid, updated_at, updated_by_guid) + | values + | ({guid}::uuid, {organization_guid}::uuid, {name}, {key}, {visibility}, {description}, {created_at}::timestamptz, {created_by_guid}::uuid, {updated_at}::timestamptz, {updated_by_guid}::uuid) + """.stripMargin) + } + + private val UpdateQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query(""" + | update public.applications + | set organization_guid = {organization_guid}::uuid, + | name = {name}, + | key = {key}, + | visibility = {visibility}, + | description = {description}, + | updated_at = {updated_at}::timestamptz, + | updated_by_guid = {updated_by_guid}::uuid + | where guid = {guid}::uuid + """.stripMargin) + } + + private val DeleteQuery: io.flow.postgresql.Query = { + io.flow.postgresql.Query("update public.applications set deleted_at = {deleted_at}::timestamptz, deleted_by_guid = {deleted_by_guid}::uuid") + } + + def insert( + user: java.util.UUID, + form: ApplicationForm + ): java.util.UUID = { + db.withConnection { c => + insert(c, user, form) + } + } + + def insert( + c: java.sql.Connection, + user: java.util.UUID, + form: ApplicationForm + ): java.util.UUID = { + val id = randomPkey + bindQuery(InsertQuery, user, form) + .bind("created_at", org.joda.time.DateTime.now) + .bind("created_by_guid", user) + .bind("guid", id) + .execute(c) + id + } + + def insertBatch( + user: java.util.UUID, + forms: Seq[ApplicationForm] + ): Seq[java.util.UUID] = { + db.withConnection { c => + insertBatch(c, user, forms) + } + } + + def insertBatch( + c: java.sql.Connection, + user: java.util.UUID, + forms: Seq[ApplicationForm] + ): Seq[java.util.UUID] = { + forms.map { f => + val guid = randomPkey + (guid, Seq(anorm.NamedParameter("created_at", org.joda.time.DateTime.now)) ++ toNamedParameter(user, guid, f)) + }.toList match { + case Nil => Nil + case one :: rest => { + anorm.BatchSql(InsertQuery.sql(), one._2, rest.map(_._2)*).execute()(c) + Seq(one._1) ++ rest.map(_._1) + } + } + } + + def update( + user: java.util.UUID, + application: Application, + form: ApplicationForm + ): Unit = { + db.withConnection { c => + update(c, user, application, form) + } + } + + def update( + c: java.sql.Connection, + user: java.util.UUID, + application: Application, + form: ApplicationForm + ): Unit = { + updateByGuid( + c = c, + user = user, + guid = application.guid, + form = form + ) + } + + def updateByGuid( + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationForm + ): Unit = { + db.withConnection { c => + updateByGuid(c, user, guid, form) + } + } + + def updateByGuid( + c: java.sql.Connection, + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationForm + ): Unit = { + bindQuery(UpdateQuery, user, form) + .bind("guid", guid) + .bind("updated_by_guid", user) + .execute(c) + () + } + + def updateBatch( + user: java.util.UUID, + forms: Seq[(java.util.UUID, ApplicationForm)] + ): Unit = { + db.withConnection { c => + updateBatch(c, user, forms) + } + } + + def updateBatch( + c: java.sql.Connection, + user: java.util.UUID, + forms: Seq[(java.util.UUID, ApplicationForm)] + ): Unit = { + forms.map { case (guid, f) => toNamedParameter(user, guid, f) }.toList match { + case Nil => // no-op + case first :: rest => anorm.BatchSql(UpdateQuery.sql(), first, rest*).execute()(c) + } + } + + def delete( + user: java.util.UUID, + application: Application + ): Unit = { + db.withConnection { c => + delete(c, user, application) + } + } + + def delete( + c: java.sql.Connection, + user: java.util.UUID, + application: Application + ): Unit = { + deleteByGuid( + c = c, + user = user, + guid = application.guid + ) + } + + def deleteByGuid( + user: java.util.UUID, + guid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteByGuid(c, user, guid) + } + } + + def deleteByGuid( + c: java.sql.Connection, + user: java.util.UUID, + guid: java.util.UUID + ): Unit = { + DeleteQuery.equals("guid", guid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByGuids( + user: java.util.UUID, + guids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByGuids(c, user, guids) + } + } + + def deleteAllByGuids( + c: java.sql.Connection, + user: java.util.UUID, + guids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("guid", guids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByOrganizationGuid( + user: java.util.UUID, + organizationGuid: java.util.UUID + ): Unit = { + db.withConnection { c => + deleteAllByOrganizationGuid(c, user, organizationGuid) + } + } + + def deleteAllByOrganizationGuid( + c: java.sql.Connection, + user: java.util.UUID, + organizationGuid: java.util.UUID + ): Unit = { + DeleteQuery.equals("organization_guid", organizationGuid) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + def deleteAllByOrganizationGuids( + user: java.util.UUID, + organizationGuids: Seq[java.util.UUID] + ): Unit = { + db.withConnection { c => + deleteAllByOrganizationGuids(c, user, organizationGuids) + } + } + + def deleteAllByOrganizationGuids( + c: java.sql.Connection, + user: java.util.UUID, + organizationGuids: Seq[java.util.UUID] + ): Unit = { + DeleteQuery.in("organization_guid", organizationGuids) + .bind("deleted_at", org.joda.time.DateTime.now) + .bind("deleted_by_guid", user) + .execute(c) + } + + private def bindQuery( + query: io.flow.postgresql.Query, + user: java.util.UUID, + form: ApplicationForm + ): io.flow.postgresql.Query = { + query + .bind("organization_guid", form.organizationGuid.toString) + .bind("name", form.name) + .bind("key", form.key) + .bind("visibility", form.visibility) + .bind("description", form.description) + .bind("updated_at", org.joda.time.DateTime.now) + .bind("updated_by_guid", user) + } + + private def toNamedParameter( + user: java.util.UUID, + guid: java.util.UUID, + form: ApplicationForm + ): Seq[anorm.NamedParameter] = { + Seq( + anorm.NamedParameter("guid", guid.toString), + anorm.NamedParameter("organization_guid", form.organizationGuid.toString), + anorm.NamedParameter("name", form.name), + anorm.NamedParameter("key", form.key), + anorm.NamedParameter("visibility", form.visibility), + anorm.NamedParameter("description", form.description), + anorm.NamedParameter("updated_at", org.joda.time.DateTime.now), + anorm.NamedParameter("updated_by_guid", user.toString) + ) + } +} \ No newline at end of file