From c80be6d8bc9e8de22ce52816e3ed24ab01d5c2fa Mon Sep 17 00:00:00 2001 From: Michael Bryzek Date: Wed, 5 Jun 2024 23:42:13 -0400 Subject: [PATCH] wip --- api/app/db/ApplicationsDao.scala | 15 +++++-------- api/app/db/EmailVerificationsDao.scala | 18 ++++++++------- api/app/db/MembershipRequestsDao.scala | 26 +++++++++++----------- api/app/db/MembershipsDao.scala | 15 ++++++++----- api/app/db/PasswordResetRequestsDao.scala | 23 ++++++++++--------- api/app/processor/EmailProcessor.scala | 12 +++++++++- api/app/processor/TaskActorCompanion.scala | 4 +++- 7 files changed, 64 insertions(+), 49 deletions(-) diff --git a/api/app/db/ApplicationsDao.scala b/api/app/db/ApplicationsDao.scala index 82b6eca26..f7c6217db 100644 --- a/api/app/db/ApplicationsDao.scala +++ b/api/app/db/ApplicationsDao.scala @@ -2,18 +2,19 @@ package db import anorm._ import io.apibuilder.api.v0.models.{AppSortBy, Application, ApplicationForm, Error, MoveForm, Organization, SortOrder, User, Version, Visibility} -import io.apibuilder.task.v0.models.TaskType +import io.apibuilder.task.v0.models.{EmailDataApplicationCreated, TaskType} import io.flow.postgresql.Query import lib.{UrlKey, Validation} import play.api.db._ +import processor.EmailProcessorQueue import java.util.UUID -import javax.inject.{Inject, Named, Singleton} +import javax.inject.{Inject, Singleton} @Singleton class ApplicationsDao @Inject() ( - @Named("main-actor") mainActor: akka.actor.ActorRef, @NamedDatabase("default") db: Database, + emailQueue: EmailProcessorQueue, organizationsDao: OrganizationsDao, tasksDao: InternalTasksDao, ) { @@ -263,10 +264,9 @@ class ApplicationsDao @Inject() ( "created_by_guid" -> createdBy.guid, "updated_by_guid" -> createdBy.guid ).execute() + emailQueue.queueWithConnection(c, EmailDataApplicationCreated(guid)) }) - mainActor ! actors.MainActor.Messages.ApplicationCreated(guid) - findAll(Authorization.All, orgKey = Some(org.key), key = Some(key)).headOption.getOrElse { sys.error("Failed to create application") } @@ -279,10 +279,7 @@ class ApplicationsDao @Inject() ( } def canUserUpdate(user: User, app: Application): Boolean = { - findAll(Authorization.User(user.guid), key = Some(app.key)).headOption match { - case None => false - case Some(a) => true - } + findAll(Authorization.User(user.guid), key = Some(app.key)).nonEmpty } private[db] def findByOrganizationAndName(authorization: Authorization, org: Organization, name: String): Option[Application] = { diff --git a/api/app/db/EmailVerificationsDao.scala b/api/app/db/EmailVerificationsDao.scala index 13828f9a3..9cdcbef70 100644 --- a/api/app/db/EmailVerificationsDao.scala +++ b/api/app/db/EmailVerificationsDao.scala @@ -1,14 +1,17 @@ package db +import anorm.JodaParameterMetaData._ +import anorm._ import io.apibuilder.api.v0.models.User +import io.apibuilder.task.v0.models.EmailDataEmailVerificationCreated import io.flow.postgresql.Query import lib.{Role, TokenGenerator} -import anorm._ -import anorm.JodaParameterMetaData._ -import javax.inject.{Inject, Named, Singleton} +import org.joda.time.DateTime import play.api.db._ +import processor.EmailProcessorQueue + import java.util.UUID -import org.joda.time.DateTime +import javax.inject.{Inject, Singleton} case class EmailVerification( guid: UUID, @@ -20,8 +23,8 @@ case class EmailVerification( @Singleton class EmailVerificationsDao @Inject() ( - @Named("main-actor") mainActor: akka.actor.ActorRef, @NamedDatabase("default") db: Database, + emailQueue: EmailProcessorQueue, emailVerificationConfirmationsDao: EmailVerificationConfirmationsDao, membershipRequestsDao: MembershipRequestsDao, organizationsDao: OrganizationsDao @@ -56,7 +59,7 @@ class EmailVerificationsDao @Inject() ( def create(createdBy: User, user: User, email: String): EmailVerification = { val guid = UUID.randomUUID - db.withConnection { implicit c => + db.withTransaction { implicit c => SQL(InsertQuery).on( "guid" -> guid, "user_guid" -> user.guid, @@ -65,10 +68,9 @@ class EmailVerificationsDao @Inject() ( "expires_at" -> DateTime.now.plusHours(HoursUntilTokenExpires), "created_by_guid" -> createdBy.guid ).execute() + emailQueue.queueWithConnection(c, EmailDataEmailVerificationCreated(guid)) } - mainActor ! actors.MainActor.Messages.EmailVerificationCreated(guid) - findByGuid(guid).getOrElse { sys.error("Failed to create email verification") } diff --git a/api/app/db/MembershipRequestsDao.scala b/api/app/db/MembershipRequestsDao.scala index bc36b70c4..e949510c9 100644 --- a/api/app/db/MembershipRequestsDao.scala +++ b/api/app/db/MembershipRequestsDao.scala @@ -1,17 +1,20 @@ package db +import anorm._ import io.apibuilder.api.v0.models.{MembershipRequest, Organization, User} +import io.apibuilder.task.v0.models.{EmailDataMembershipRequestAccepted, EmailDataMembershipRequestCreated, EmailDataMembershipRequestDeclined} import io.flow.postgresql.Query import lib.Role -import anorm._ -import javax.inject.{Inject, Named, Singleton} import play.api.db._ +import processor.EmailProcessorQueue + import java.util.UUID +import javax.inject.{Inject, Singleton} @Singleton class MembershipRequestsDao @Inject() ( - @Named("main-actor") mainActor: akka.actor.ActorRef, @NamedDatabase("default") db: Database, + emailQueue: EmailProcessorQueue, membershipsDao: MembershipsDao, organizationLogsDao: OrganizationLogsDao ) { @@ -59,7 +62,7 @@ class MembershipRequestsDao @Inject() ( private[db] def create(createdBy: User, organization: Organization, user: User, role: Role): MembershipRequest = { val guid = UUID.randomUUID - db.withConnection { implicit c => + db.withTransaction { implicit c => SQL(InsertQuery).on( "guid" -> guid, "organization_guid" -> organization.guid, @@ -67,10 +70,9 @@ class MembershipRequestsDao @Inject() ( "role" -> role.key, "created_by_guid" -> createdBy.guid ).execute() + emailQueue.queueWithConnection(c, EmailDataMembershipRequestCreated(guid)) } - mainActor ! actors.MainActor.Messages.MembershipRequestCreated(guid) - findByGuid(Authorization.All, guid).getOrElse { sys.error("Failed to create membership_request") } @@ -101,13 +103,12 @@ class MembershipRequestsDao @Inject() ( sys.error(s"Invalid role[${request.role}]") } - db.withTransaction { implicit conn => + db.withTransaction { implicit c => organizationLogsDao.create(createdBy, request.organization, message) - dbHelpers.delete(conn, createdBy, request.guid) + dbHelpers.delete(c, createdBy, request.guid) membershipsDao.upsert(createdBy, request.organization, request.user, r) + emailQueue.queueWithConnection(c, EmailDataMembershipRequestAccepted(request.organization.guid, request.user.guid, r.toString)) } - - mainActor ! actors.MainActor.Messages.MembershipRequestAccepted(request.organization.guid, request.user.guid, r) } /** @@ -121,12 +122,11 @@ class MembershipRequestsDao @Inject() ( } val message = s"Declined membership request for ${request.user.email} to join as ${r.name}" - db.withTransaction { implicit conn => + db.withTransaction { implicit c => organizationLogsDao.create(createdBy.guid, request.organization, message) softDelete(createdBy, request) + emailQueue.queueWithConnection(c, EmailDataMembershipRequestDeclined(request.organization.guid, request.user.guid, r.toString)) } - - mainActor ! actors.MainActor.Messages.MembershipRequestDeclined(request.organization.guid, request.user.guid, r) } private[this] def assertUserCanReview(user: User, request: MembershipRequest): Unit = { diff --git a/api/app/db/MembershipsDao.scala b/api/app/db/MembershipsDao.scala index f8f640f66..91df2fa90 100644 --- a/api/app/db/MembershipsDao.scala +++ b/api/app/db/MembershipsDao.scala @@ -1,19 +1,22 @@ package db +import anorm._ import io.apibuilder.api.v0.models.{Membership, Organization, User} import io.apibuilder.common.v0.models.{Audit, ReferenceGuid} +import io.apibuilder.task.v0.models.EmailDataMembershipCreated import io.flow.postgresql.Query import lib.Role -import anorm._ -import javax.inject.{Inject, Named, Singleton} +import org.joda.time.DateTime import play.api.db._ +import processor.EmailProcessorQueue + import java.util.UUID -import org.joda.time.DateTime +import javax.inject.{Inject, Singleton} @Singleton class MembershipsDao @Inject() ( - @Named("main-actor") mainActor: akka.actor.ActorRef, @NamedDatabase("default") db: Database, + emailQueue: EmailProcessorQueue, subscriptionsDao: SubscriptionsDao ) { @@ -67,7 +70,7 @@ class MembershipsDao @Inject() ( } private[db] def create(createdBy: UUID, organization: Organization, user: User, role: Role): Membership = { - db.withConnection { implicit c => + db.withTransaction { implicit c => create(c, createdBy, organization, user, role) } } @@ -94,7 +97,7 @@ class MembershipsDao @Inject() ( "created_by_guid" -> createdBy ).execute() - mainActor ! actors.MainActor.Messages.MembershipCreated(membership.guid) + emailQueue.queueWithConnection(c, EmailDataMembershipCreated(membership.guid)) membership } diff --git a/api/app/db/PasswordResetRequestsDao.scala b/api/app/db/PasswordResetRequestsDao.scala index 4f37c0be6..00bad985d 100644 --- a/api/app/db/PasswordResetRequestsDao.scala +++ b/api/app/db/PasswordResetRequestsDao.scala @@ -1,15 +1,17 @@ package db -import io.apibuilder.api.v0.models.User -import lib.TokenGenerator -import anorm._ import anorm.JodaParameterMetaData._ -import java.util.UUID -import javax.inject.{Inject, Named, Singleton} - -import play.api.db._ +import anorm._ +import io.apibuilder.api.v0.models.User +import io.apibuilder.task.v0.models.EmailDataPasswordResetRequestCreated import io.flow.postgresql.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} case class PasswordReset( guid: UUID, @@ -20,8 +22,8 @@ case class PasswordReset( @Singleton class PasswordResetRequestsDao @Inject() ( - @Named("main-actor") mainActor: akka.actor.ActorRef, @NamedDatabase("default") db: Database, + emailQueue: EmailProcessorQueue, userPasswordsDao: UserPasswordsDao, usersDao: UsersDao ) { @@ -48,7 +50,7 @@ class PasswordResetRequestsDao @Inject() ( def create(createdBy: Option[User], user: User): PasswordReset = { val guid = UUID.randomUUID - db.withConnection { implicit c => + db.withTransaction { implicit c => SQL(InsertQuery).on( "guid" -> guid, "user_guid" -> user.guid, @@ -56,10 +58,9 @@ class PasswordResetRequestsDao @Inject() ( "expires_at" -> DateTime.now.plusHours(HoursUntilTokenExpires), "created_by_guid" -> createdBy.getOrElse(user).guid ).execute() + emailQueue.queueWithConnection(c, EmailDataPasswordResetRequestCreated(guid)) } - mainActor ! actors.MainActor.Messages.PasswordResetRequestCreated(guid) - findByGuid(guid).getOrElse { sys.error("Failed to create password reset") } diff --git a/api/app/processor/EmailProcessor.scala b/api/app/processor/EmailProcessor.scala index 0601eb8d9..59ed14fe0 100644 --- a/api/app/processor/EmailProcessor.scala +++ b/api/app/processor/EmailProcessor.scala @@ -3,15 +3,25 @@ package processor import actors.Emails import cats.data.ValidatedNec import cats.implicits._ -import db.{Authorization, OrganizationsDao, UsersDao} +import db.{Authorization, InternalTasksDao, OrganizationsDao, UsersDao} import io.apibuilder.api.v0.models.Publication import io.apibuilder.task.v0.models._ import io.apibuilder.task.v0.models.json._ import lib.{AppConfig, EmailUtil, Person, Role} +import play.api.libs.json.Json +import java.sql.Connection import java.util.UUID import javax.inject.Inject +class EmailProcessorQueue @Inject() ( + internalTasksDao: InternalTasksDao + ) { + def queueWithConnection(c: Connection, data: EmailData): Unit = { + val dataJson = Json.toJson(data) + internalTasksDao.queue(TaskType.Email, id = Json.asciiStringify(dataJson), data = dataJson) + } +} class EmailProcessor @Inject()( args: TaskProcessorArgs, diff --git a/api/app/processor/TaskActorCompanion.scala b/api/app/processor/TaskActorCompanion.scala index 6438a4ae3..702597948 100644 --- a/api/app/processor/TaskActorCompanion.scala +++ b/api/app/processor/TaskActorCompanion.scala @@ -12,7 +12,8 @@ class TaskActorCompanion @Inject() ( migrateVersion: MigrateVersionProcessor, userCreated: UserCreatedProcessor, scheduleSyncGeneratorServices: ScheduleSyncGeneratorServicesProcessor, - syncGeneratorService: SyncGeneratorServiceProcessor + syncGeneratorService: SyncGeneratorServiceProcessor, + email: EmailProcessor ) { def process(typ: TaskType): Unit = { @@ -30,6 +31,7 @@ class TaskActorCompanion @Inject() ( case UserCreated => userCreated case ScheduleSyncGeneratorServices => scheduleSyncGeneratorServices case SyncGeneratorService => syncGeneratorService + case Email => email case UNDEFINED(_) => sys.error(s"Undefined task type '$typ") } }