Skip to content

Commit b1273a1

Browse files
Add notification setup event presenter (#1758)
* Add notification setup even presenter https://eaflood.atlassian.net/browse/WATER-4904 As part of the work to handle notifications in the system code. We need to follow some patterns established by the legacy code. This change adds the event presenter to structure the data to insert into 'wabs.public.events'. This presenter will need to use existing legacy concepts such as setting a 'subtype' in order for the legacy reporting pages to continue to work.
1 parent f225cab commit b1273a1

File tree

3 files changed

+304
-1
lines changed

3 files changed

+304
-1
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
'use strict'
2+
3+
/**
4+
* Formats a notification `SessionModel` instance into the data needed for a 'EventModel' record
5+
* @module CreateEventPresenter
6+
*/
7+
8+
const { transformStringOfLicencesToArray } = require('../../../lib/general.lib.js')
9+
10+
/**
11+
* Formats a notification `SessionModel` instance into the data needed for a 'EventModel' record
12+
*
13+
* @param {SessionModel} session
14+
*
15+
* @returns {object} - The data formatted for the view template
16+
*/
17+
function go(session) {
18+
const { referenceCode, recipients, determinedReturnsPeriod, journey, removeLicences = [] } = session
19+
20+
return {
21+
licences: _licences(recipients),
22+
metadata: {
23+
name: _name(journey),
24+
options: {
25+
excludeLicences: removeLicences
26+
},
27+
recipients: recipients.length,
28+
returnCycle: _returnCycle(determinedReturnsPeriod)
29+
},
30+
referenceCode,
31+
status: 'started',
32+
subtype: _subType(journey)
33+
}
34+
}
35+
36+
/**
37+
* All the licences associated with an event (licences that will receive notifications) are stored in
38+
* `water.events.licences`. It is not clear where theses are used. But to be consistent we follow the established
39+
* pattern.
40+
*
41+
* @private
42+
*/
43+
function _licences(recipients) {
44+
return recipients.flatMap((recipient) => {
45+
return transformStringOfLicencesToArray(recipient.licence_refs)
46+
})
47+
}
48+
49+
/**
50+
* This name is used in the legacy UI to render the notification type on '/notifications/report'.
51+
* This has been done to allow all types of notifications to be rendered in the UI. The data is taken straight from the
52+
* metadata and rendered in the view.
53+
*
54+
* We need to be consistent with this pattern.
55+
*
56+
* @private
57+
*/
58+
function _name(journey) {
59+
if (journey === 'invitations') {
60+
return 'Returns: invitation'
61+
} else if (journey === 'reminders') {
62+
return 'Returns: reminder'
63+
} else {
64+
return ''
65+
}
66+
}
67+
68+
function _returnCycle(returnsPeriod) {
69+
return {
70+
dueDate: returnsPeriod.dueDate,
71+
endDate: returnsPeriod.endDate,
72+
isSummer: returnsPeriod.summer,
73+
startDate: returnsPeriod.startDate
74+
}
75+
}
76+
77+
/**
78+
*
79+
* The legacy code has the concept of 'subType' this is used when querying to get notifications.
80+
*
81+
* Below is an example of a query used in 'water-abstraction-service'.
82+
*
83+
* ```sql
84+
* SELECT * FROM water.scheduled_notification
85+
* WHERE event_id = (SELECT event_id FROM water.events
86+
* WHERE subtype = 'returnInvitation'
87+
* AND status = 'completed'
88+
* ORDER BY created DESC LIMIT 1)
89+
* AND licences \\? :licenceRef
90+
* ```
91+
*
92+
* @private
93+
*/
94+
function _subType(journey) {
95+
if (journey === 'invitations') {
96+
return 'returnInvitation'
97+
} else if (journey === 'reminders') {
98+
return 'returnReminder'
99+
} else {
100+
return ''
101+
}
102+
}
103+
104+
module.exports = {
105+
go
106+
}

test/fixtures/recipients.fixtures.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ function _addReturnTo() {
113113

114114
function _addLicenceHolderWithMultipleLicences() {
115115
return {
116-
licence_refs: `${generateLicenceRef()}, ${generateLicenceRef()}`,
116+
licence_refs: `${generateLicenceRef()},${generateLicenceRef()}`,
117117
contact_type: 'Licence holder',
118118
contact: _contact('3', 'Licence holder with multiple licences', 'Licence holder'),
119119
contact_hash_id: '22f6457b6be9fd63d8a9a8dd2ed09878075'
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
'use strict'
2+
3+
// Test framework dependencies
4+
const Lab = require('@hapi/lab')
5+
const Code = require('@hapi/code')
6+
7+
const { describe, it, beforeEach } = (exports.lab = Lab.script())
8+
const { expect } = Code
9+
10+
// Test helpers
11+
const RecipientsFixture = require('../../../fixtures/recipients.fixtures.js')
12+
13+
// Thing under test
14+
const CreateEventPresenter = require('../../../../app/presenters/notifications/setup/create-event.presenter.js')
15+
16+
describe('Notifications Setup - Event presenter', () => {
17+
let session
18+
let recipients
19+
20+
beforeEach(async () => {
21+
recipients = RecipientsFixture.recipients()
22+
23+
session = {
24+
returnsPeriod: 'quarterFour',
25+
removeLicences: [],
26+
journey: 'invitations',
27+
referenceCode: 'RINV-123',
28+
recipients: [...Object.values(recipients)],
29+
determinedReturnsPeriod: {
30+
dueDate: new Date(`2025-07-28`),
31+
endDate: new Date(`2025-06-30`),
32+
startDate: new Date(`2025-04-01`),
33+
summer: 'true'
34+
}
35+
}
36+
})
37+
38+
it('correctly presents the data', () => {
39+
const result = CreateEventPresenter.go(session)
40+
41+
expect(result).to.equal({
42+
licences: [
43+
recipients.primaryUser.licence_refs,
44+
recipients.returnsAgent.licence_refs,
45+
recipients.licenceHolder.licence_refs,
46+
recipients.returnsTo.licence_refs,
47+
...recipients.licenceHolderWithMultipleLicences.licence_refs.split(',')
48+
],
49+
metadata: {
50+
name: 'Returns: invitation',
51+
options: {
52+
excludeLicences: []
53+
},
54+
recipients: 5,
55+
returnCycle: {
56+
dueDate: session.determinedReturnsPeriod.dueDate,
57+
endDate: session.determinedReturnsPeriod.endDate,
58+
isSummer: 'true',
59+
startDate: session.determinedReturnsPeriod.startDate
60+
}
61+
},
62+
referenceCode: 'RINV-123',
63+
status: 'started',
64+
subtype: 'returnInvitation'
65+
})
66+
})
67+
68+
describe('the "licences" property', () => {
69+
it('correctly return an array of all licence from all recipients', () => {
70+
const result = CreateEventPresenter.go(session)
71+
72+
expect(result.licences).to.equal([
73+
recipients.primaryUser.licence_refs,
74+
recipients.returnsAgent.licence_refs,
75+
recipients.licenceHolder.licence_refs,
76+
recipients.returnsTo.licence_refs,
77+
...recipients.licenceHolderWithMultipleLicences.licence_refs.split(',')
78+
])
79+
})
80+
})
81+
82+
describe('the "metadata" property', () => {
83+
describe('the "options.excludeLicences" property', () => {
84+
describe('when there licences excluded from the recipients list', () => {
85+
beforeEach(() => {
86+
session.removeLicences = ['123', '456']
87+
})
88+
89+
it('correctly returns the exclude licences', () => {
90+
const result = CreateEventPresenter.go(session)
91+
92+
expect(result.metadata.options.excludeLicences).to.equal(['123', '456'])
93+
})
94+
})
95+
96+
describe('when there are no licences excluded from the recipients list', () => {
97+
beforeEach(() => {
98+
session.removeLicences = []
99+
})
100+
101+
it('correctly returns the exclude licences', () => {
102+
const result = CreateEventPresenter.go(session)
103+
104+
expect(result.metadata.options.excludeLicences).to.equal([])
105+
})
106+
})
107+
})
108+
109+
describe('the "recipients" property', () => {
110+
beforeEach(() => {
111+
session.recipients = [...Object.values(recipients)]
112+
})
113+
114+
it('correctly returns the length of recipients', () => {
115+
const result = CreateEventPresenter.go(session)
116+
117+
expect(result.metadata.recipients).to.equal(5)
118+
})
119+
})
120+
121+
describe('the "returnCycle" property', () => {
122+
beforeEach(() => {
123+
session.determinedReturnsPeriod = {
124+
dueDate: new Date(`2025-07-28`),
125+
endDate: new Date(`2025-06-30`),
126+
startDate: new Date(`2025-04-01`),
127+
summer: 'true'
128+
}
129+
})
130+
131+
it('correctly returns the return cycle', () => {
132+
const result = CreateEventPresenter.go(session)
133+
134+
expect(result.metadata.returnCycle).to.equal({
135+
dueDate: session.determinedReturnsPeriod.dueDate,
136+
endDate: session.determinedReturnsPeriod.endDate,
137+
isSummer: 'true',
138+
startDate: session.determinedReturnsPeriod.startDate
139+
})
140+
})
141+
})
142+
})
143+
144+
describe('when the journey is for "invitations"', () => {
145+
beforeEach(() => {
146+
session.journey = 'invitations'
147+
})
148+
149+
it('correctly sets the "metadata.name"', () => {
150+
const result = CreateEventPresenter.go(session)
151+
152+
expect(result.metadata.name).to.equal('Returns: invitation')
153+
})
154+
155+
it('correctly sets the "subtype"', () => {
156+
const result = CreateEventPresenter.go(session)
157+
158+
expect(result.subtype).to.equal('returnInvitation')
159+
})
160+
})
161+
162+
describe('when the journey is for "reminders"', () => {
163+
beforeEach(() => {
164+
session.journey = 'reminders'
165+
})
166+
167+
it('correctly sets the "metadata.name"', () => {
168+
const result = CreateEventPresenter.go(session)
169+
170+
expect(result.metadata.name).to.equal('Returns: reminder')
171+
})
172+
173+
it('correctly sets the "subtype"', () => {
174+
const result = CreateEventPresenter.go(session)
175+
176+
expect(result.subtype).to.equal('returnReminder')
177+
})
178+
})
179+
180+
describe('when the journey is not recognised', () => {
181+
beforeEach(() => {
182+
session.journey = ''
183+
})
184+
185+
it('correctly sets the "metadata.name"', () => {
186+
const result = CreateEventPresenter.go(session)
187+
188+
expect(result.metadata.name).to.equal('')
189+
})
190+
191+
it('correctly sets the "subtype"', () => {
192+
const result = CreateEventPresenter.go(session)
193+
194+
expect(result.subtype).to.equal('')
195+
})
196+
})
197+
})

0 commit comments

Comments
 (0)