From 8b1a04582426cc007156426c356a8d2e6350b892 Mon Sep 17 00:00:00 2001 From: JR40159 <126243293+JR40159@users.noreply.github.com> Date: Wed, 14 Feb 2024 12:24:34 +0000 Subject: [PATCH 1/2] Add urls to emails --- backend/src/services/v2/smtp/smtp.ts | 29 ++++++---- .../smtp/__snapshots__/smtp.spec.ts.snap | 24 ++++++++- backend/test/services/smtp/smtp.spec.ts | 54 ++++++++++++++++++- 3 files changed, 94 insertions(+), 13 deletions(-) diff --git a/backend/src/services/v2/smtp/smtp.ts b/backend/src/services/v2/smtp/smtp.ts index 3c9bc11f2..c6e9678f0 100644 --- a/backend/src/services/v2/smtp/smtp.ts +++ b/backend/src/services/v2/smtp/smtp.ts @@ -28,7 +28,7 @@ async function dispatchEmail(entity: string, emailContent: EmailContent) { await Promise.all(sendEmailResponses) } -//const appBaseUrl = `${config.app.protocol}://${config.app.host}:${config.app.port}` +const appBaseUrl = `${config.app.protocol}://${config.app.host}:${config.app.port}` export async function requestReviewForRelease(entity: string, review: ReviewDoc, release: ReleaseDoc) { if (!config.smtp.enabled) { log.info('Not sending email due to SMTP disabled') @@ -44,8 +44,8 @@ export async function requestReviewForRelease(entity: string, review: ReviewDoc, { title: 'Created By', data: release.createdBy }, ], [ - { name: 'Open Release', url: 'TODO' }, - { name: 'See Reviews', url: 'TODO' }, + { name: 'Open Release', url: getReleaseUrl(release) }, + { name: 'See Reviews', url: `${appBaseUrl}/review` }, ], ) @@ -71,8 +71,11 @@ export async function requestReviewForAccessRequest( { title: 'Created By', data: accessRequest.createdBy }, ], [ - { name: 'Open Access Request', url: 'TODO' }, - { name: 'See Reviews', url: 'TODO' }, + { + name: 'Open Access Request', + url: getAccessRequestUrl(accessRequest), + }, + { name: 'See Reviews', url: `${appBaseUrl}/review` }, ], ) @@ -98,8 +101,8 @@ export async function notifyReviewResponseForRelease(review: ReviewDoc, release: { title: 'Decision', data: reviewResponse.decision.replace(/_/g, ' ') }, ], [ - { name: 'Open Release', url: 'TODO' }, - { name: 'See Reviews', url: 'TODO' }, + { name: 'Open Release', url: getReleaseUrl(release) }, + { name: 'See Reviews', url: `${appBaseUrl}/review` }, ], ) await dispatchEmail(toEntity('user', release.createdBy), emailContent) @@ -124,8 +127,8 @@ export async function notifyReviewResponseForAccess(review: ReviewDoc, accessReq { title: 'Decision', data: reviewResponse.decision.replace(/_/g, ' ') }, ], [ - { name: 'Open Release', url: 'TODO' }, - { name: 'See Reviews', url: 'TODO' }, + { name: 'Open Access Request', url: getAccessRequestUrl(accessRequest) }, + { name: 'See Reviews', url: `${appBaseUrl}/review` }, ], ) await dispatchEmail(toEntity('user', accessRequest.createdBy), emailContent) @@ -154,3 +157,11 @@ async function sendEmail(email: Mail.Options) { return Promise.reject(`Unable to send email: ${JSON.stringify(content)}`) } } + +function getReleaseUrl(release: ReleaseDoc) { + return `${appBaseUrl}/model/${release.modelId}/release/${release.semver}` +} + +function getAccessRequestUrl(accessRequest: AccessRequestDoc) { + return `${appBaseUrl}/model/${accessRequest.modelId}/access-request/${accessRequest.id}` +} diff --git a/backend/test/services/smtp/__snapshots__/smtp.spec.ts.snap b/backend/test/services/smtp/__snapshots__/smtp.spec.ts.snap index ca615a355..f4e41f058 100644 --- a/backend/test/services/smtp/__snapshots__/smtp.spec.ts.snap +++ b/backend/test/services/smtp/__snapshots__/smtp.spec.ts.snap @@ -1,8 +1,28 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`services > smtp > smtp > that an email is sent after a response for a an access request review 1`] = `undefined`; +exports[`services > smtp > smtp > that an email is sent after a response for a an access request review 1`] = ` +[ + { + "from": "\\"Bailo 📝\\" ", + "html": "html", + "subject": "subject", + "text": "text", + "to": "email@email.com", + }, +] +`; -exports[`services > smtp > smtp > that an email is sent after a response for a release review 1`] = `undefined`; +exports[`services > smtp > smtp > that an email is sent after a response for a release review 1`] = ` +[ + { + "from": "\\"Bailo 📝\\" ", + "html": "html", + "subject": "subject", + "text": "text", + "to": "email@email.com", + }, +] +`; exports[`services > smtp > smtp > that an email is sent for Access Request Reviews 1`] = ` [ diff --git a/backend/test/services/smtp/smtp.spec.ts b/backend/test/services/smtp/smtp.spec.ts index 93aaf769a..af41d8cae 100644 --- a/backend/test/services/smtp/smtp.spec.ts +++ b/backend/test/services/smtp/smtp.spec.ts @@ -71,7 +71,7 @@ const emailBuilderMock = vi.hoisted(() => ({ vi.mock('../../../src/services/v2/smtp/emailBuilder.js', async () => emailBuilderMock) describe('services > smtp > smtp', () => { - const review = new Review({ role: 'owner' }) + const review = new Review({ role: 'owner', responses: [{ decision: 'approve' }] }) const release = new Release({ modelId: 'testmodel-123', semver: '1.2.3', createdBy: 'user:user' }) const access = new AccessRequest({ metadata: { overview: { entities: ['user:user'] } } }) @@ -115,6 +115,44 @@ describe('services > smtp > smtp', () => { expect(transporterMock.sendMail).not.toBeCalled() }) + test('that an email is not sent after a response for a release review if disabled in config', async () => { + vi.spyOn(config, 'smtp', 'get').mockReturnValue({ + enabled: false, + connection: { + host: 'localhost', + port: 1025, + secure: false, + auth: { user: '', pass: '' }, + tls: { + rejectUnauthorized: false, + }, + }, + from: '"Bailo 📝" ', + }) + await notifyReviewResponseForRelease(review, release) + + expect(transporterMock.sendMail).not.toBeCalled() + }) + + test('that an email is not sent after a response for a an access request review if disabled in config', async () => { + vi.spyOn(config, 'smtp', 'get').mockReturnValue({ + enabled: false, + connection: { + host: 'localhost', + port: 1025, + secure: false, + auth: { user: '', pass: '' }, + tls: { + rejectUnauthorized: false, + }, + }, + from: '"Bailo 📝" ', + }) + await notifyReviewResponseForAccess(review, access) + + expect(transporterMock.sendMail).not.toBeCalled() + }) + test('that an email is sent for Release Reviews', async () => { await requestReviewForRelease('user:user', review, release) @@ -133,12 +171,24 @@ describe('services > smtp > smtp', () => { expect(transporterMock.sendMail.mock.calls.at(0)).toMatchSnapshot() }) + test('that an email is not sent if a response for a release review cannot be found', async () => { + await notifyReviewResponseForRelease(new Review({ role: 'owner', responses: [] }), release) + + expect(transporterMock.sendMail).not.toBeCalled() + }) + test('that an email is sent after a response for a an access request review', async () => { await notifyReviewResponseForAccess(review, access) expect(transporterMock.sendMail.mock.calls.at(0)).toMatchSnapshot() }) + test('that an email is not sent if a response for an access request review cannot be found', async () => { + await notifyReviewResponseForAccess(new Review({ role: 'owner', responses: [] }), access) + + expect(transporterMock.sendMail).not.toBeCalled() + }) + test('that sendEmail is called for each member of a group entity', async () => { authenticationMock.getUserInformationList.mockReturnValueOnce([ Promise.resolve({ email: 'member1@email.com' }), @@ -152,7 +202,7 @@ describe('services > smtp > smtp', () => { test('that sendEmail is called a maximum of 20 times', async () => { const users: Promise<{ email: string }>[] = [] - for (let i = 0; i < 20; i += 1) { + for (let i = 0; i <= 20; i += 1) { users[i] = Promise.resolve({ email: `member${i}@email.com` }) } authenticationMock.getUserInformationList.mockReturnValueOnce(users) From 789f131ed1060126644a2a140b11dfc4d4200c39 Mon Sep 17 00:00:00 2001 From: JR40159 <126243293+JR40159@users.noreply.github.com> Date: Wed, 14 Feb 2024 13:20:10 +0000 Subject: [PATCH 2/2] Update default mock config --- backend/src/utils/v2/__mocks__/config.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/backend/src/utils/v2/__mocks__/config.ts b/backend/src/utils/v2/__mocks__/config.ts index 05caf8dc3..a8a4f28b5 100644 --- a/backend/src/utils/v2/__mocks__/config.ts +++ b/backend/src/utils/v2/__mocks__/config.ts @@ -1,4 +1,9 @@ const config = { + app: { + protocol: '', + host: '', + port: 3000, + }, connectors: { authentication: { kind: 'silly',