From b4674e2cefa4222e746353f09ca006b62619dd98 Mon Sep 17 00:00:00 2001 From: Kristaps Fabians Geikins Date: Tue, 10 Sep 2024 14:11:33 +0300 Subject: [PATCH] chore(server): emails IoC 5 - requestEmailVerificationFactory --- .../modules/emails/graph/resolvers/index.ts | 18 ++++- .../emails/services/verification/request.ts | 80 +++++++++++-------- .../emails/tests/verifications.spec.ts | 13 ++- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/packages/server/modules/emails/graph/resolvers/index.ts b/packages/server/modules/emails/graph/resolvers/index.ts index b5349d10ec..77e13f0b9a 100644 --- a/packages/server/modules/emails/graph/resolvers/index.ts +++ b/packages/server/modules/emails/graph/resolvers/index.ts @@ -1,8 +1,20 @@ import { db } from '@/db/knex' import { Resolvers } from '@/modules/core/graph/generated/graphql' -import { getUserByEmail } from '@/modules/core/repositories/users' -import { getPendingTokenFactory } from '@/modules/emails/repositories' -import { requestEmailVerification } from '@/modules/emails/services/verification/request' +import { findPrimaryEmailForUserFactory } from '@/modules/core/repositories/userEmails' +import { getUser, getUserByEmail } from '@/modules/core/repositories/users' +import { getServerInfo } from '@/modules/core/services/generic' +import { + deleteOldAndInsertNewVerificationFactory, + getPendingTokenFactory +} from '@/modules/emails/repositories' +import { requestEmailVerificationFactory } from '@/modules/emails/services/verification/request' + +const requestEmailVerification = requestEmailVerificationFactory({ + getUser, + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ db }), + findPrimaryEmailForUser: findPrimaryEmailForUserFactory({ db }) +}) export = { User: { diff --git a/packages/server/modules/emails/services/verification/request.ts b/packages/server/modules/emails/services/verification/request.ts index 694e87e5a8..79ef53e136 100644 --- a/packages/server/modules/emails/services/verification/request.ts +++ b/packages/server/modules/emails/services/verification/request.ts @@ -1,4 +1,7 @@ -import { FindEmail } from '@/modules/core/domain/userEmails/operations' +import { + FindEmail, + FindPrimaryEmailForUser +} from '@/modules/core/domain/userEmails/operations' import { UserEmail } from '@/modules/core/domain/userEmails/types' import { UsersEmitter, UsersEvents } from '@/modules/core/events/usersEmitter' import { getEmailVerificationFinalizationRoute } from '@/modules/core/helpers/routeHelper' @@ -14,47 +17,50 @@ import { } from '@/modules/emails/services/emailRendering' import { sendEmail } from '@/modules/emails/services/sending' import { getServerOrigin } from '@/modules/shared/helpers/envHelper' -import { db } from '@/db/knex' import { DeleteOldAndInsertNewVerification, RequestNewEmailVerification } from '@/modules/emails/domain/operations' +import { db } from '@/db/knex' const EMAIL_SUBJECT = 'Speckle Account E-mail Verification' -const findPrimaryEmailForUser = findPrimaryEmailForUserFactory({ db }) +type CreateNewVerificationDeps = { + getUser: typeof getUser + findPrimaryEmailForUser: FindPrimaryEmailForUser + getServerInfo: typeof getServerInfo + deleteOldAndInsertNewVerification: DeleteOldAndInsertNewVerification +} -async function createNewVerification( - userId: string -): Promise { - if (!userId) - throw new EmailVerificationRequestError('User for verification not specified') +const createNewVerificationFactory = + (deps: CreateNewVerificationDeps) => + async (userId: string): Promise => { + if (!userId) + throw new EmailVerificationRequestError('User for verification not specified') - const [user, email, serverInfo] = await Promise.all([ - getUser(userId), - findPrimaryEmailForUser({ userId }), - getServerInfo() - ]) + const [user, email, serverInfo] = await Promise.all([ + deps.getUser(userId), + deps.findPrimaryEmailForUser({ userId }), + deps.getServerInfo() + ]) - if (!user || !email) - throw new EmailVerificationRequestError( - 'Unable to resolve verification target user' - ) + if (!user || !email) + throw new EmailVerificationRequestError( + 'Unable to resolve verification target user' + ) - if (user.verified) - throw new EmailVerificationRequestError("User's email is already verified") + if (user.verified) + throw new EmailVerificationRequestError("User's email is already verified") - const verificationId = await deleteOldAndInsertNewVerificationFactory({ db })( - user.email - ) + const verificationId = await deps.deleteOldAndInsertNewVerification(user.email) - return { - user, - email, - verificationId, - serverInfo + return { + user, + email, + verificationId, + serverInfo + } } -} type VerificationRequestContext = { user: UserRecord @@ -165,10 +171,11 @@ async function sendVerificationEmail(state: VerificationRequestContext) { /** * Request email verification (send out verification message) for user with specified ID */ -export async function requestEmailVerification(userId: string) { - const newVerificationState = await createNewVerification(userId) - await sendVerificationEmail(newVerificationState) -} +export const requestEmailVerificationFactory = + (deps: CreateNewVerificationDeps) => async (userId: string) => { + const newVerificationState = await createNewVerificationFactory(deps)(userId) + await sendVerificationEmail(newVerificationState) + } /** * Listen for user:created events and trigger email verification initialization @@ -178,6 +185,15 @@ export function initializeVerificationOnRegistration() { // user might already be verified because of registration through an external identity provider if (user.verified) return + const requestEmailVerification = requestEmailVerificationFactory({ + getUser, + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ + db + }), + findPrimaryEmailForUser: findPrimaryEmailForUserFactory({ db }) + }) + await requestEmailVerification(user.id) }) } diff --git a/packages/server/modules/emails/tests/verifications.spec.ts b/packages/server/modules/emails/tests/verifications.spec.ts index 942e8a9737..605141f753 100644 --- a/packages/server/modules/emails/tests/verifications.spec.ts +++ b/packages/server/modules/emails/tests/verifications.spec.ts @@ -4,6 +4,7 @@ import { buildApp, truncateTables } from '@/test/hooks' import request from 'supertest' import { expect } from 'chai' import { + deleteOldAndInsertNewVerificationFactory, deleteVerificationsFactory, getPendingTokenFactory } from '@/modules/emails/repositories' @@ -11,7 +12,6 @@ import { getPendingEmailVerificationStatus, requestVerification } from '@/test/graphql/users' -import { requestEmailVerification } from '@/modules/emails/services/verification/request' import { getEmailVerificationFinalizationRoute } from '@/modules/core/helpers/routeHelper' import { Express } from 'express' import { getUser } from '@/modules/core/repositories/users' @@ -24,10 +24,21 @@ import { } from '@/test/graphqlHelper' import { buildApolloServer } from '@/app' import { db } from '@/db/knex' +import { requestEmailVerificationFactory } from '@/modules/emails/services/verification/request' +import { getServerInfo } from '@/modules/core/services/generic' +import { findPrimaryEmailForUserFactory } from '@/modules/core/repositories/userEmails' const mailerMock = EmailSendingServiceMock const getPendingToken = getPendingTokenFactory({ db }) const deleteVerifications = deleteVerificationsFactory({ db }) +const requestEmailVerification = requestEmailVerificationFactory({ + getUser, + getServerInfo, + deleteOldAndInsertNewVerification: deleteOldAndInsertNewVerificationFactory({ + db + }), + findPrimaryEmailForUser: findPrimaryEmailForUserFactory({ db }) +}) const cleanup = async () => { await truncateTables([Users.name, EmailVerifications.name, UserEmails.name])