diff --git a/integration-tests/aws-sdk/setup.ts b/integration-tests/aws-sdk/setup.ts index 305c9e66..18f563ce 100644 --- a/integration-tests/aws-sdk/setup.ts +++ b/integration-tests/aws-sdk/setup.ts @@ -5,6 +5,7 @@ import type { Logger } from "pino"; import { promisify } from "util"; import { createServer } from "../../src"; import { MockLogger } from "../../src/__tests__/mockLogger"; +import { newMockMessageDelivery } from "../../src/__tests__/mockMessageDelivery"; import { DefaultConfig } from "../../src/server/config"; import { Clock, @@ -59,10 +60,7 @@ export const withCognitoSdk = clock, cognito: cognitoClient, config: DefaultConfig, - messageDelivery: { - deliver: jest.fn(), - }, - messages: new MessagesService(triggers), + messages: new MessagesService(triggers, newMockMessageDelivery()), otp, triggers, tokenGenerator: new JwtTokenGenerator( diff --git a/src/__tests__/mockMessageDelivery.ts b/src/__tests__/mockMessageDelivery.ts index 529c4972..ca051d64 100644 --- a/src/__tests__/mockMessageDelivery.ts +++ b/src/__tests__/mockMessageDelivery.ts @@ -1,4 +1,4 @@ -import { MessageDelivery } from "../services"; +import { MessageDelivery } from "../services/messageDelivery/messageDelivery"; export const newMockMessageDelivery = (): jest.Mocked => ({ deliver: jest.fn(), diff --git a/src/__tests__/mockMessages.ts b/src/__tests__/mockMessages.ts index 745ffc9a..913b13bd 100644 --- a/src/__tests__/mockMessages.ts +++ b/src/__tests__/mockMessages.ts @@ -1,5 +1,5 @@ import { Messages } from "../services"; export const newMockMessages = (): jest.Mocked => ({ - create: jest.fn(), + deliver: jest.fn(), }); diff --git a/src/server/defaults.ts b/src/server/defaults.ts index 653d6f09..04307311 100644 --- a/src/server/defaults.ts +++ b/src/server/defaults.ts @@ -66,8 +66,10 @@ export const createDefaultServer = async ( clock, cognito: cognitoClient, config, - messageDelivery: new MessageDeliveryService(new ConsoleMessageSender()), - messages: new MessagesService(triggers), + messages: new MessagesService( + triggers, + new MessageDeliveryService(new ConsoleMessageSender()) + ), otp, tokenGenerator: new JwtTokenGenerator( clock, diff --git a/src/services/index.ts b/src/services/index.ts index c03770c8..ad91a70a 100644 --- a/src/services/index.ts +++ b/src/services/index.ts @@ -3,7 +3,6 @@ import { Clock } from "./clock"; import { Messages } from "./messages"; import { TokenGenerator } from "./tokenGenerator"; import { Triggers } from "./triggers"; -import { MessageDelivery } from "./messageDelivery/messageDelivery"; import { CognitoService } from "./cognitoService"; export { Clock, DateClock } from "./clock"; @@ -11,14 +10,12 @@ export { CognitoService, CognitoServiceImpl } from "./cognitoService"; export { UserPoolService, UserPoolServiceImpl } from "./userPoolService"; export { Triggers, TriggersService } from "./triggers"; export { Lambda, LambdaService } from "./lambda"; -export { MessageDelivery } from "./messageDelivery/messageDelivery"; export { Messages, MessagesService } from "./messages"; export interface Services { clock: Clock; cognito: CognitoService; config: Config; - messageDelivery: MessageDelivery; messages: Messages; otp: () => string; tokenGenerator: TokenGenerator; diff --git a/src/services/messages.test.ts b/src/services/messages.test.ts index 19d6a9bc..235db92c 100644 --- a/src/services/messages.test.ts +++ b/src/services/messages.test.ts @@ -1,22 +1,31 @@ +import { newMockMessageDelivery } from "../__tests__/mockMessageDelivery"; import { newMockTriggers } from "../__tests__/mockTriggers"; import { TestContext } from "../__tests__/testContext"; +import { MessageDelivery } from "./messageDelivery/messageDelivery"; import { MessagesService } from "./messages"; import * as TDB from "../__tests__/testDataBuilder"; import { Triggers } from "./triggers"; describe("messages service", () => { let mockTriggers: jest.Mocked; + let mockMessageDelivery: jest.Mocked; const user = TDB.user(); + const deliveryDetails = { + AttributeName: "email", + DeliveryMedium: "EMAIL", + Destination: "example@example.com", + } as const; beforeEach(() => { mockTriggers = newMockTriggers(); + mockMessageDelivery = newMockMessageDelivery(); }); describe("authentication", () => { describe("CustomMessage lambda is configured", () => { describe("lambda returns a custom message", () => { - it("returns the custom message and code", async () => { + it("delivers the customised message", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); @@ -26,8 +35,11 @@ describe("messages service", () => { emailMessage: "email", }); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "Authentication", "clientId", @@ -36,16 +48,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - smsMessage: "sms", - emailSubject: "email subject", - emailMessage: "email", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -57,18 +63,33 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + emailMessage: "email", + emailSubject: "email subject", + smsMessage: "sms", + } + ); }); }); describe("lambda does not return a custom message", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); mockTriggers.customMessage.mockResolvedValue(null); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "Authentication", "clientId", @@ -77,13 +98,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -95,16 +113,25 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); describe("CustomMessage lambda is not configured", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockReturnValue(false); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService(mockTriggers, mockMessageDelivery); + await messages.deliver( TestContext, "Authentication", "clientId", @@ -113,14 +140,19 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).not.toHaveBeenCalled(); + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); @@ -128,7 +160,7 @@ describe("messages service", () => { describe("forgotPassword", () => { describe("CustomMessage lambda is configured", () => { describe("lambda returns a custom message", () => { - it("returns the custom message and code", async () => { + it("delivers the customised message", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); @@ -138,8 +170,11 @@ describe("messages service", () => { emailMessage: "email", }); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "ForgotPassword", "clientId", @@ -148,16 +183,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - smsMessage: "sms", - emailSubject: "email subject", - emailMessage: "email", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -169,18 +198,32 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + emailMessage: "email", + emailSubject: "email subject", + smsMessage: "sms", + } + ); }); }); describe("lambda does not return a custom message", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); mockTriggers.customMessage.mockResolvedValue(null); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "ForgotPassword", "clientId", @@ -189,13 +232,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -207,16 +247,25 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); describe("CustomMessage lambda is not configured", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockReturnValue(false); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService(mockTriggers, mockMessageDelivery); + const message = await messages.deliver( TestContext, "ForgotPassword", "clientId", @@ -225,14 +274,19 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).not.toHaveBeenCalled(); + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); @@ -240,7 +294,7 @@ describe("messages service", () => { describe("signUp", () => { describe("CustomMessage lambda is configured", () => { describe("lambda returns a custom message", () => { - it("returns the custom message and code", async () => { + it("delivers the customised message", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); @@ -250,8 +304,11 @@ describe("messages service", () => { emailMessage: "email", }); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "SignUp", "clientId", @@ -260,16 +317,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - smsMessage: "sms", - emailSubject: "email subject", - emailMessage: "email", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -281,18 +332,33 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + emailMessage: "email", + emailSubject: "email subject", + smsMessage: "sms", + } + ); }); }); describe("lambda does not return a custom message", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockImplementation((name) => { return name === "CustomMessage"; }); mockTriggers.customMessage.mockResolvedValue(null); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService( + mockTriggers, + mockMessageDelivery + ); + await messages.deliver( TestContext, "SignUp", "clientId", @@ -301,13 +367,10 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).toHaveBeenCalledWith(TestContext, { clientId: "clientId", clientMetadata: { @@ -319,16 +382,25 @@ describe("messages service", () => { userPoolId: "userPoolId", username: user.Username, }); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); describe("CustomMessage lambda is not configured", () => { - it("returns just the code", async () => { + it("delivers just the code", async () => { mockTriggers.enabled.mockReturnValue(false); - const messages = new MessagesService(mockTriggers); - const message = await messages.create( + const messages = new MessagesService(mockTriggers, mockMessageDelivery); + await messages.deliver( TestContext, "SignUp", "clientId", @@ -337,14 +409,20 @@ describe("messages service", () => { "1234", { client: "metadata", - } + }, + deliveryDetails ); - expect(message).toMatchObject({ - __code: "1234", - }); - expect(mockTriggers.customMessage).not.toHaveBeenCalled(); + + expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + TestContext, + user, + deliveryDetails, + { + __code: "1234", + } + ); }); }); }); diff --git a/src/services/messages.ts b/src/services/messages.ts index 25bf11a3..00f592ec 100644 --- a/src/services/messages.ts +++ b/src/services/messages.ts @@ -1,4 +1,8 @@ import { Context } from "./context"; +import { + DeliveryDetails, + MessageDelivery, +} from "./messageDelivery/messageDelivery"; import { Triggers } from "./triggers"; import { User } from "./userPoolService"; @@ -21,25 +25,51 @@ type MessageSource = | "VerifyUserAttribute"; export interface Messages { - create( + deliver( ctx: Context, source: MessageSource, clientId: string | null, userPoolId: string, user: User, code: string, - clientMetadata: Record | undefined - ): Promise; + clientMetadata: Record | undefined, + deliveryDetails: DeliveryDetails + ): Promise; } export class MessagesService implements Messages { private readonly triggers: Triggers; + private readonly messageDelivery: MessageDelivery; - public constructor(triggers: Triggers) { + public constructor(triggers: Triggers, messageDelivery: MessageDelivery) { this.triggers = triggers; + this.messageDelivery = messageDelivery; + } + + public async deliver( + ctx: Context, + source: MessageSource, + clientId: string | null, + userPoolId: string, + user: User, + code: string, + clientMetadata: Record | undefined, + deliveryDetails: DeliveryDetails + ): Promise { + const message = await this.create( + ctx, + source, + clientId, + userPoolId, + user, + code, + clientMetadata + ); + + await this.messageDelivery.deliver(ctx, user, deliveryDetails, message); } - public async create( + private async create( ctx: Context, source: MessageSource, clientId: string | null, diff --git a/src/targets/adminCreateUser.test.ts b/src/targets/adminCreateUser.test.ts index fa176495..7579e4b6 100644 --- a/src/targets/adminCreateUser.test.ts +++ b/src/targets/adminCreateUser.test.ts @@ -7,7 +7,7 @@ import { UUID } from "../__tests__/patterns"; import { TestContext } from "../__tests__/testContext"; import * as TDB from "../__tests__/testDataBuilder"; import { InvalidParameterError, UsernameExistsError } from "../errors"; -import { MessageDelivery, Messages, UserPoolService } from "../services"; +import { Messages, UserPoolService } from "../services"; import { AdminCreateUser, AdminCreateUserTarget } from "./adminCreateUser"; const originalDate = new Date(); @@ -15,17 +15,14 @@ const originalDate = new Date(); describe("AdminCreateUser target", () => { let adminCreateUser: AdminCreateUserTarget; let mockUserPoolService: jest.Mocked; - let mockMessageDelivery: jest.Mocked; let mockMessages: jest.Mocked; beforeEach(() => { mockUserPoolService = newMockUserPoolService(); - mockMessageDelivery = newMockMessageDelivery(); mockMessages = newMockMessages(); adminCreateUser = AdminCreateUser({ cognito: newMockCognitoService(mockUserPoolService), clock: new ClockFake(originalDate), - messageDelivery: mockMessageDelivery, messages: mockMessages, }); }); @@ -92,11 +89,6 @@ describe("AdminCreateUser target", () => { describe("messages", () => { describe("DesiredDeliveryMediums=EMAIL", () => { it("sends a welcome email to the user", async () => { - mockMessages.create.mockResolvedValue({ - emailMessage: "email message", - emailSubject: "email subject", - }); - const response = await adminCreateUser(TestContext, { ClientMetadata: { client: "metadata", @@ -108,7 +100,7 @@ describe("AdminCreateUser target", () => { UserPoolId: "test", }); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "AdminCreateUser", null, @@ -117,21 +109,12 @@ describe("AdminCreateUser target", () => { "pwd", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - { - ...response.User, - Password: "pwd", - RefreshTokens: [], }, { AttributeName: "email", DeliveryMedium: "EMAIL", Destination: "example@example.com", - }, - { emailMessage: "email message", emailSubject: "email subject" } + } ); }); @@ -150,17 +133,12 @@ describe("AdminCreateUser target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); describe("DesiredDeliveryMediums=SMS", () => { it("sends a welcome sms to the user", async () => { - mockMessages.create.mockResolvedValue({ - smsMessage: "sms message", - }); - const response = await adminCreateUser(TestContext, { ClientMetadata: { client: "metadata", @@ -172,7 +150,7 @@ describe("AdminCreateUser target", () => { UserPoolId: "test", }); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "AdminCreateUser", null, @@ -181,21 +159,12 @@ describe("AdminCreateUser target", () => { "pwd", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - { - ...response.User, - Password: "pwd", - RefreshTokens: [], }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0400000000", - }, - { smsMessage: "sms message" } + } ); }); @@ -214,17 +183,12 @@ describe("AdminCreateUser target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); describe("DesiredDeliveryMediums=default", () => { it("sends a welcome sms to the user", async () => { - mockMessages.create.mockResolvedValue({ - smsMessage: "sms message", - }); - const response = await adminCreateUser(TestContext, { ClientMetadata: { client: "metadata", @@ -235,7 +199,7 @@ describe("AdminCreateUser target", () => { UserPoolId: "test", }); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "AdminCreateUser", null, @@ -244,21 +208,12 @@ describe("AdminCreateUser target", () => { "pwd", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - { - ...response.User, - Password: "pwd", - RefreshTokens: [], }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0400000000", - }, - { smsMessage: "sms message" } + } ); }); @@ -279,17 +234,12 @@ describe("AdminCreateUser target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); describe("DesiredDeliveryMediums=EMAIL and SMS", () => { it("sends a welcome sms to a user with a phone_number and an email", async () => { - mockMessages.create.mockResolvedValue({ - smsMessage: "sms message", - }); - const response = await adminCreateUser(TestContext, { ClientMetadata: { client: "metadata", @@ -304,7 +254,7 @@ describe("AdminCreateUser target", () => { UserPoolId: "test", }); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "AdminCreateUser", null, @@ -313,30 +263,16 @@ describe("AdminCreateUser target", () => { "pwd", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - { - ...response.User, - Password: "pwd", - RefreshTokens: [], }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0400000000", - }, - { smsMessage: "sms message" } + } ); }); it("sends a welcome email to a user without a phone_number but with an email", async () => { - mockMessages.create.mockResolvedValue({ - emailMessage: "email message", - emailSubject: "email subject", - }); - const response = await adminCreateUser(TestContext, { ClientMetadata: { client: "metadata", @@ -348,7 +284,7 @@ describe("AdminCreateUser target", () => { UserPoolId: "test", }); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "AdminCreateUser", null, @@ -357,21 +293,12 @@ describe("AdminCreateUser target", () => { "pwd", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - { - ...response.User, - Password: "pwd", - RefreshTokens: [], }, { AttributeName: "email", DeliveryMedium: "EMAIL", Destination: "example@example.com", - }, - { emailMessage: "email message", emailSubject: "email subject" } + } ); }); @@ -390,8 +317,7 @@ describe("AdminCreateUser target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); }); diff --git a/src/targets/adminCreateUser.ts b/src/targets/adminCreateUser.ts index bb261f73..2b7d2da4 100644 --- a/src/targets/adminCreateUser.ts +++ b/src/targets/adminCreateUser.ts @@ -10,12 +10,7 @@ import { UnsupportedError, UsernameExistsError, } from "../errors"; -import { - MessageDelivery, - Messages, - Services, - UserPoolService, -} from "../services"; +import { Messages, Services, UserPoolService } from "../services"; import { DeliveryDetails } from "../services/messageDelivery/messageDelivery"; import { attributesInclude, @@ -33,10 +28,7 @@ export type AdminCreateUserTarget = Target< AdminCreateUserResponse >; -type AdminCreateUserServices = Pick< - Services, - "clock" | "cognito" | "messageDelivery" | "messages" ->; +type AdminCreateUserServices = Pick; const selectAppropriateDeliveryMethod = ( desiredDeliveryMediums: DeliveryMediumListType, @@ -73,8 +65,7 @@ const deliverWelcomeMessage = async ( temporaryPassword: string, user: User, messages: Messages, - userPool: UserPoolService, - messageDelivery: MessageDelivery + userPool: UserPoolService ) => { const deliveryDetails = selectAppropriateDeliveryMethod( req.DesiredDeliveryMediums ?? ["SMS"], @@ -87,23 +78,22 @@ const deliverWelcomeMessage = async ( ); } - const message = await messages.create( + await messages.deliver( ctx, "AdminCreateUser", null, userPool.config.Id, user, temporaryPassword, - req.ClientMetadata + req.ClientMetadata, + deliveryDetails ); - await messageDelivery.deliver(ctx, user, deliveryDetails, message); }; export const AdminCreateUser = ({ clock, cognito, - messageDelivery, messages, }: AdminCreateUserServices): AdminCreateUserTarget => async (ctx, req) => { @@ -150,8 +140,7 @@ export const AdminCreateUser = temporaryPassword, user, messages, - userPool, - messageDelivery + userPool ); return { diff --git a/src/targets/forgotPassword.test.ts b/src/targets/forgotPassword.test.ts index e817b7c4..cb3a2ac1 100644 --- a/src/targets/forgotPassword.test.ts +++ b/src/targets/forgotPassword.test.ts @@ -5,7 +5,7 @@ import { newMockMessages } from "../__tests__/mockMessages"; import { newMockUserPoolService } from "../__tests__/mockUserPoolService"; import { TestContext } from "../__tests__/testContext"; import { UserNotFoundError } from "../errors"; -import { MessageDelivery, Messages, UserPoolService } from "../services"; +import { Messages, UserPoolService } from "../services"; import { attributeValue } from "../services/userPoolService"; import { ForgotPassword, ForgotPasswordTarget } from "./forgotPassword"; import * as TDB from "../__tests__/testDataBuilder"; @@ -15,22 +15,16 @@ const currentDate = new Date(); describe("ForgotPassword target", () => { let forgotPassword: ForgotPasswordTarget; let mockUserPoolService: jest.Mocked; - let mockMessageDelivery: jest.Mocked; let mockMessages: jest.Mocked; let mockOtp: jest.MockedFunction<() => string>; beforeEach(() => { mockUserPoolService = newMockUserPoolService(); - mockMessageDelivery = newMockMessageDelivery(); mockMessages = newMockMessages(); - mockMessages.create.mockResolvedValue({ - emailSubject: "Mock message", - }); mockOtp = jest.fn().mockReturnValue("1234"); forgotPassword = ForgotPassword({ cognito: newMockCognitoService(mockUserPoolService), clock: new ClockFake(currentDate), - messageDelivery: mockMessageDelivery, messages: mockMessages, otp: mockOtp, }); @@ -55,17 +49,22 @@ describe("ForgotPassword target", () => { const result = await forgotPassword(TestContext, { ClientId: "clientId", Username: user.Username, + ClientMetadata: { client: "metadata" }, }); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, + "ForgotPassword", + "clientId", + "test", user, + "1234", + { client: "metadata" }, { AttributeName: "email", DeliveryMedium: "EMAIL", Destination: attributeValue("email", user.Attributes), - }, - { emailSubject: "Mock message" } + } ); expect(result).toEqual({ diff --git a/src/targets/forgotPassword.ts b/src/targets/forgotPassword.ts index ce70c3ec..897c8d02 100644 --- a/src/targets/forgotPassword.ts +++ b/src/targets/forgotPassword.ts @@ -15,14 +15,13 @@ export type ForgotPasswordTarget = Target< type ForgotPasswordServices = Pick< Services, - "cognito" | "clock" | "messageDelivery" | "messages" | "otp" + "cognito" | "clock" | "messages" | "otp" >; export const ForgotPassword = ({ cognito, clock, - messageDelivery, messages, otp, }: ForgotPasswordServices): ForgotPasswordTarget => @@ -47,16 +46,16 @@ export const ForgotPassword = }; const code = otp(); - const message = await messages.create( + await messages.deliver( ctx, "ForgotPassword", req.ClientId, userPool.config.Id, user, code, - req.ClientMetadata + req.ClientMetadata, + deliveryDetails ); - await messageDelivery.deliver(ctx, user, deliveryDetails, message); await userPool.saveUser(ctx, { ...user, diff --git a/src/targets/initiateAuth.test.ts b/src/targets/initiateAuth.test.ts index 592451ad..a18eeb43 100644 --- a/src/targets/initiateAuth.test.ts +++ b/src/targets/initiateAuth.test.ts @@ -1,5 +1,4 @@ import { newMockCognitoService } from "../__tests__/mockCognitoService"; -import { newMockMessageDelivery } from "../__tests__/mockMessageDelivery"; import { newMockMessages } from "../__tests__/mockMessages"; import { newMockTokenGenerator } from "../__tests__/mockTokenGenerator"; import { newMockTriggers } from "../__tests__/mockTriggers"; @@ -13,12 +12,7 @@ import { NotAuthorizedError, PasswordResetRequiredError, } from "../errors"; -import { - MessageDelivery, - Messages, - Triggers, - UserPoolService, -} from "../services"; +import { Messages, Triggers, UserPoolService } from "../services"; import { TokenGenerator } from "../services/tokenGenerator"; import { attributesToRecord, User } from "../services/userPoolService"; import { InitiateAuth, InitiateAuthTarget } from "./initiateAuth"; @@ -26,7 +20,6 @@ import { InitiateAuth, InitiateAuthTarget } from "./initiateAuth"; describe("InitiateAuth target", () => { let initiateAuth: InitiateAuthTarget; let mockUserPoolService: jest.Mocked; - let mockMessageDelivery: jest.Mocked; let mockMessages: jest.Mocked; let mockOtp: jest.MockedFunction<() => string>; let mockTriggers: jest.Mocked; @@ -34,17 +27,12 @@ describe("InitiateAuth target", () => { beforeEach(() => { mockUserPoolService = newMockUserPoolService(); - mockMessageDelivery = newMockMessageDelivery(); mockMessages = newMockMessages(); - mockMessages.create.mockResolvedValue({ - emailSubject: "Mock message", - }); mockOtp = jest.fn().mockReturnValue("1234"); mockTriggers = newMockTriggers(); mockTokenGenerator = newMockTokenGenerator(); initiateAuth = InitiateAuth({ cognito: newMockCognitoService(mockUserPoolService), - messageDelivery: mockMessageDelivery, messages: mockMessages, otp: mockOtp, triggers: mockTriggers, @@ -200,15 +188,19 @@ describe("InitiateAuth target", () => { expect(output).toBeDefined(); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, + "Authentication", + "clientId", + "test", user, + "1234", + undefined, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0411000111", - }, - { emailSubject: "Mock message" } + } ); // also saves the code on the user for comparison later @@ -304,7 +296,7 @@ describe("InitiateAuth target", () => { expect(output).toBeDefined(); - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "Authentication", "clientId", @@ -313,17 +305,12 @@ describe("InitiateAuth target", () => { "1234", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - user, + }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0411000111", - }, - { emailSubject: "Mock message" } + } ); // also saves the code on the user for comparison later diff --git a/src/targets/initiateAuth.ts b/src/targets/initiateAuth.ts index e2d8a9bc..b3257263 100644 --- a/src/targets/initiateAuth.ts +++ b/src/targets/initiateAuth.ts @@ -27,12 +27,7 @@ export type InitiateAuthTarget = Target< type InitiateAuthServices = Pick< Services, - | "cognito" - | "messageDelivery" - | "messages" - | "otp" - | "tokenGenerator" - | "triggers" + "cognito" | "messages" | "otp" | "tokenGenerator" | "triggers" >; const verifyMfaChallenge = async ( @@ -62,24 +57,19 @@ const verifyMfaChallenge = async ( } const code = services.otp(); - const message = await services.messages.create( + await services.messages.deliver( ctx, "Authentication", req.ClientId, userPool.config.Id, user, code, - req.ClientMetadata - ); - await services.messageDelivery.deliver( - ctx, - user, + req.ClientMetadata, { DeliveryMedium: smsMfaOption.DeliveryMedium, AttributeName: smsMfaOption.AttributeName, Destination: deliveryDestination, - }, - message + } ); await userPool.saveUser(ctx, { diff --git a/src/targets/signUp.test.ts b/src/targets/signUp.test.ts index 955845da..7ce63abe 100644 --- a/src/targets/signUp.test.ts +++ b/src/targets/signUp.test.ts @@ -1,6 +1,5 @@ import { ClockFake } from "../__tests__/clockFake"; import { newMockCognitoService } from "../__tests__/mockCognitoService"; -import { newMockMessageDelivery } from "../__tests__/mockMessageDelivery"; import { newMockMessages } from "../__tests__/mockMessages"; import { newMockTriggers } from "../__tests__/mockTriggers"; import { newMockUserPoolService } from "../__tests__/mockUserPoolService"; @@ -12,18 +11,12 @@ import { UserLambdaValidationError, UsernameExistsError, } from "../errors"; -import { - MessageDelivery, - Messages, - Triggers, - UserPoolService, -} from "../services"; +import { Messages, Triggers, UserPoolService } from "../services"; import { SignUp, SignUpTarget } from "./signUp"; describe("SignUp target", () => { let signUp: SignUpTarget; let mockUserPoolService: jest.Mocked; - let mockMessageDelivery: jest.Mocked; let mockMessages: jest.Mocked; let mockOtp: jest.MockedFunction<() => string>; let mockTriggers: jest.Mocked; @@ -33,17 +26,12 @@ describe("SignUp target", () => { now = new Date(2020, 1, 2, 3, 4, 5); mockUserPoolService = newMockUserPoolService(); - mockMessageDelivery = newMockMessageDelivery(); mockMessages = newMockMessages(); - mockMessages.create.mockResolvedValue({ - emailSubject: "Mock message", - }); mockOtp = jest.fn(); mockTriggers = newMockTriggers(); signUp = SignUp({ cognito: newMockCognitoService(mockUserPoolService), clock: new ClockFake(now), - messageDelivery: mockMessageDelivery, messages: mockMessages, otp: mockOtp, triggers: mockTriggers, @@ -441,7 +429,7 @@ describe("SignUp target", () => { UserAttributes: [{ Name: "email", Value: "example@example.com" }], }); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); @@ -478,7 +466,7 @@ describe("SignUp target", () => { RefreshTokens: [], }; - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "SignUp", "clientId", @@ -487,17 +475,12 @@ describe("SignUp target", () => { "1234", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - createdUser, + }, { AttributeName: "email", DeliveryMedium: "EMAIL", Destination: "example@example.com", - }, - { emailSubject: "Mock message" } + } ); }); @@ -515,8 +498,7 @@ describe("SignUp target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); @@ -553,7 +535,7 @@ describe("SignUp target", () => { RefreshTokens: [], }; - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "SignUp", "clientId", @@ -562,17 +544,12 @@ describe("SignUp target", () => { "1234", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - createdUser, + }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0400000000", - }, - { emailSubject: "Mock message" } + } ); }); @@ -590,8 +567,7 @@ describe("SignUp target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); @@ -635,7 +611,7 @@ describe("SignUp target", () => { RefreshTokens: [], }; - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "SignUp", "clientId", @@ -644,17 +620,12 @@ describe("SignUp target", () => { "1234", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - createdUser, + }, { AttributeName: "phone_number", DeliveryMedium: "SMS", Destination: "0400000000", - }, - { emailSubject: "Mock message" } + } ); }); @@ -686,7 +657,7 @@ describe("SignUp target", () => { RefreshTokens: [], }; - expect(mockMessages.create).toHaveBeenCalledWith( + expect(mockMessages.deliver).toHaveBeenCalledWith( TestContext, "SignUp", "clientId", @@ -695,17 +666,12 @@ describe("SignUp target", () => { "1234", { client: "metadata", - } - ); - expect(mockMessageDelivery.deliver).toHaveBeenCalledWith( - TestContext, - createdUser, + }, { AttributeName: "email", DeliveryMedium: "EMAIL", Destination: "example@example.com", - }, - { emailSubject: "Mock message" } + } ); }); @@ -723,8 +689,7 @@ describe("SignUp target", () => { ) ); - expect(mockMessages.create).not.toHaveBeenCalled(); - expect(mockMessageDelivery.deliver).not.toHaveBeenCalled(); + expect(mockMessages.deliver).not.toHaveBeenCalled(); }); }); }); diff --git a/src/targets/signUp.ts b/src/targets/signUp.ts index fcb9ef22..41f78207 100644 --- a/src/targets/signUp.ts +++ b/src/targets/signUp.ts @@ -6,12 +6,7 @@ import { } from "aws-sdk/clients/cognitoidentityserviceprovider"; import * as uuid from "uuid"; import { InvalidParameterError, UsernameExistsError } from "../errors"; -import { - MessageDelivery, - Messages, - Services, - UserPoolService, -} from "../services"; +import { Messages, Services, UserPoolService } from "../services"; import { DeliveryDetails } from "../services/messageDelivery/messageDelivery"; import { attribute, @@ -26,7 +21,7 @@ export type SignUpTarget = Target; type SignUpServices = Pick< Services, - "clock" | "cognito" | "messages" | "messageDelivery" | "otp" | "triggers" + "clock" | "cognito" | "messages" | "otp" | "triggers" >; const selectAppropriateDeliveryMethod = ( @@ -65,7 +60,6 @@ const deliverWelcomeMessage = async ( user: User, userPool: UserPoolService, messages: Messages, - messageDelivery: MessageDelivery, clientMetadata: Record | undefined ): Promise => { const deliveryDetails = selectAppropriateDeliveryMethod( @@ -83,29 +77,22 @@ const deliverWelcomeMessage = async ( ); } - const message = await messages.create( + await messages.deliver( ctx, "SignUp", clientId, userPool.config.Id, user, code, - clientMetadata + clientMetadata, + deliveryDetails ); - await messageDelivery.deliver(ctx, user, deliveryDetails, message); return deliveryDetails; }; export const SignUp = - ({ - clock, - cognito, - messageDelivery, - messages, - otp, - triggers, - }: SignUpServices): SignUpTarget => + ({ clock, cognito, messages, otp, triggers }: SignUpServices): SignUpTarget => async (ctx, req) => { // TODO: This should behave differently depending on if PreventUserExistenceErrors // is enabled on the updatedUser pool. This will be the default after Feb 2020. @@ -166,7 +153,6 @@ export const SignUp = updatedUser, userPool, messages, - messageDelivery, req.ClientMetadata );