Skip to content

Commit

Permalink
Merge pull request #1091 from gchq/feature/BAI-1174-emails-sent-inclu…
Browse files Browse the repository at this point in the history
…de-todo-in-the-url-fields-for-the-buttons-e.g-reviews

Add urls to emails
  • Loading branch information
JR40159 authored Feb 14, 2024
2 parents 9fe0cca + 789f131 commit 5b2f2a6
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 13 deletions.
29 changes: 20 additions & 9 deletions backend/src/services/v2/smtp/smtp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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')
Expand All @@ -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` },
],
)

Expand All @@ -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` },
],
)

Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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}`
}
5 changes: 5 additions & 0 deletions backend/src/utils/v2/__mocks__/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
const config = {
app: {
protocol: '',
host: '',
port: 3000,
},
connectors: {
authentication: {
kind: 'silly',
Expand Down
24 changes: 22 additions & 2 deletions backend/test/services/smtp/__snapshots__/smtp.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -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 📝\\" <[email protected]>",
"html": "html",
"subject": "subject",
"text": "text",
"to": "[email protected]",
},
]
`;

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 📝\\" <[email protected]>",
"html": "html",
"subject": "subject",
"text": "text",
"to": "[email protected]",
},
]
`;

exports[`services > smtp > smtp > that an email is sent for Access Request Reviews 1`] = `
[
Expand Down
54 changes: 52 additions & 2 deletions backend/test/services/smtp/smtp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'] } } })

Expand Down Expand Up @@ -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 📝" <[email protected]>',
})
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 📝" <[email protected]>',
})
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)

Expand All @@ -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: '[email protected]' }),
Expand All @@ -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)
Expand Down

0 comments on commit 5b2f2a6

Please sign in to comment.