From 42b9f8c6a8ee10c9da5bc5b4190b02ca5da57b1b Mon Sep 17 00:00:00 2001 From: Jonathan Goulding <58443816+jonathangoulding@users.noreply.github.com> Date: Tue, 17 Dec 2024 14:19:13 +0000 Subject: [PATCH] Feature notifications setup session (#1574) https://eaflood.atlassian.net/browse/WATER-4716 As part of the ongoing work to migrate the legacy UI we are replacing the notification journey from the UI and rebuilding in system. This change introduces session initialization to allow the journey to store user answers. This new route notifications/setup is now the target for the water abstraction UI redirect --- .../notifications-setup.controller.js | 8 ++++ app/routes/notifications-setup.routes.js | 18 +++++++-- .../setup/initiate-session.service.js | 31 +++++++++++++++ ...=> notifications-setup.controller.test.js} | 38 +++++++++++++++++-- .../setup/initiate-session.service.test.js | 26 +++++++++++++ 5 files changed, 115 insertions(+), 6 deletions(-) create mode 100644 app/services/notifications/setup/initiate-session.service.js rename test/controllers/{notifications.controller.test.js => notifications-setup.controller.test.js} (71%) create mode 100644 test/services/notifications/setup/initiate-session.service.test.js diff --git a/app/controllers/notifications-setup.controller.js b/app/controllers/notifications-setup.controller.js index ad9580d1c2..795fd3eddb 100644 --- a/app/controllers/notifications-setup.controller.js +++ b/app/controllers/notifications-setup.controller.js @@ -2,6 +2,7 @@ const ReturnsPeriodService = require('../services/notifications/setup/returns-period.service.js') const SubmitReturnsPeriodService = require('../services/notifications/setup/submit-returns-period.service.js') +const InitiateSessionService = require('../services/notifications/setup/initiate-session.service') /** * Controller for /notifications/setup endpoints @@ -18,6 +19,12 @@ async function viewReturnsPeriod(_request, h) { }) } +async function setup(_request, h) { + const session = await InitiateSessionService.go() + + return h.redirect(`/system/${basePath}/${session.id}/returns-period`) +} + async function submitReturnsPeriod(request, h) { const { payload } = request @@ -34,5 +41,6 @@ async function submitReturnsPeriod(request, h) { module.exports = { viewReturnsPeriod, + setup, submitReturnsPeriod } diff --git a/app/routes/notifications-setup.routes.js b/app/routes/notifications-setup.routes.js index 857cb3616d..536898673f 100644 --- a/app/routes/notifications-setup.routes.js +++ b/app/routes/notifications-setup.routes.js @@ -2,12 +2,24 @@ const NotificationsSetupController = require('../controllers/notifications-setup.controller.js') -const basePath = '/notifications/setup/' +const basePath = '/notifications/setup' const routes = [ { method: 'GET', - path: basePath + 'returns-period', + path: basePath, + options: { + handler: NotificationsSetupController.setup, + auth: { + access: { + scope: ['returns'] + } + } + } + }, + { + method: 'GET', + path: basePath + '/{sessionId}/returns-period', options: { handler: NotificationsSetupController.viewReturnsPeriod, auth: { @@ -19,7 +31,7 @@ const routes = [ }, { method: 'POST', - path: basePath + 'returns-period', + path: basePath + '/{sessionId}/returns-period', options: { handler: NotificationsSetupController.submitReturnsPeriod, auth: { diff --git a/app/services/notifications/setup/initiate-session.service.js b/app/services/notifications/setup/initiate-session.service.js new file mode 100644 index 0000000000..e404a647b2 --- /dev/null +++ b/app/services/notifications/setup/initiate-session.service.js @@ -0,0 +1,31 @@ +'use strict' + +/** + * Initiates the session record used for setting up a new ad-hoc returns notification + * @module InitiateSessionService + */ + +const SessionModel = require('../../../models/session.model.js') + +/** + * Initiates the session record used for setting up a new ad-hoc returns notification + * + * During the setup journey for a new ad-hoc returns notification we temporarily store the data in a `SessionModel` + * instance. It is expected that on each page of the journey the GET will fetch the session record and use it to + * populate the view. + * When the page is submitted the session record will be updated with the next piece of data. + * + * At the end when the journey is complete the data from the session will be used to create the ad-hoc returns + * notification and the session record itself deleted. + * + * @returns {Promise} the newly created session record + */ +async function go() { + // NOTE: data defaults to {} when a new record is created. But Objection.js throws a 'The query is empty' if we pass + // nothing into `insert()`. + return SessionModel.query().insert({ data: {} }).returning('id') +} + +module.exports = { + go +} diff --git a/test/controllers/notifications.controller.test.js b/test/controllers/notifications-setup.controller.test.js similarity index 71% rename from test/controllers/notifications.controller.test.js rename to test/controllers/notifications-setup.controller.test.js index 9603fdba53..d01aeb5e56 100644 --- a/test/controllers/notifications.controller.test.js +++ b/test/controllers/notifications-setup.controller.test.js @@ -11,6 +11,7 @@ const { expect } = Code const { postRequestOptions } = require('../support/general.js') // Things we need to stub +const InitiateSessionService = require('../../app/services/notifications/setup/initiate-session.service.js') const ReturnsPeriodService = require('../../app/services/notifications/setup/returns-period.service.js') const SubmitReturnsPeriodService = require('../../app/services/notifications/setup/submit-returns-period.service.js') @@ -19,6 +20,7 @@ const { init } = require('../../app/server.js') describe('Notifications Setup controller', () => { const basePath = '/notifications/setup' + const session = { id: 'e0c77b74-7326-493d-be5e-0d1ad41594b5', data: {} } let getOptions let postOptions @@ -42,12 +44,40 @@ describe('Notifications Setup controller', () => { Sinon.restore() }) + describe('notifications/setup', () => { + describe('GET', () => { + beforeEach(async () => { + getOptions = { + method: 'GET', + url: '/notifications/setup', + auth: { + strategy: 'session', + credentials: { scope: ['returns'] } + } + } + }) + + describe('when a request is valid', () => { + beforeEach(async () => { + Sinon.stub(InitiateSessionService, 'go').resolves(session) + }) + + it('redirects successfully', async () => { + const response = await server.inject(getOptions) + + expect(response.statusCode).to.equal(302) + expect(response.headers.location).to.equal(`/system/notifications/setup/${session.id}/returns-period`) + }) + }) + }) + }) + describe('notifications/setup/returns-period', () => { describe('GET', () => { beforeEach(async () => { getOptions = { method: 'GET', - url: basePath + '/returns-period', + url: basePath + `/${session.id}/returns-period`, auth: { strategy: 'session', credentials: { scope: ['returns'] } @@ -56,6 +86,7 @@ describe('Notifications Setup controller', () => { }) describe('when a request is valid', () => { beforeEach(async () => { + Sinon.stub(InitiateSessionService, 'go').resolves(session) Sinon.stub(ReturnsPeriodService, 'go').returns(_viewReturnsPeriod()) }) @@ -75,11 +106,12 @@ describe('Notifications Setup controller', () => { describe('when the request succeeds', () => { describe('and the validation fails', () => { beforeEach(async () => { + Sinon.stub(InitiateSessionService, 'go').resolves(session) Sinon.stub(SubmitReturnsPeriodService, 'go').returns({ ..._viewReturnsPeriod(), error: 'Something went wrong' }) - postOptions = postRequestOptions(basePath + '/returns-period', {}) + postOptions = postRequestOptions(basePath + `/${session.id}/returns-period`, {}) }) it('returns the page successfully with the error summary banner', async () => { @@ -93,7 +125,7 @@ describe('Notifications Setup controller', () => { describe('and the validation succeeds', () => { beforeEach(async () => { Sinon.stub(SubmitReturnsPeriodService, 'go').returns({ redirect: 'send-notice' }) - postOptions = postRequestOptions(basePath + '/returns-period', {}) + postOptions = postRequestOptions(basePath + `/${session.id}/returns-period`, {}) }) it('redirects the to the next page', async () => { diff --git a/test/services/notifications/setup/initiate-session.service.test.js b/test/services/notifications/setup/initiate-session.service.test.js new file mode 100644 index 0000000000..dce38fb4bf --- /dev/null +++ b/test/services/notifications/setup/initiate-session.service.test.js @@ -0,0 +1,26 @@ +'use strict' + +// Test framework dependencies +const Lab = require('@hapi/lab') +const Code = require('@hapi/code') + +const { describe, it } = (exports.lab = Lab.script()) +const { expect } = Code + +// Test helpers +const SessionModel = require('../../../../app/models/session.model.js') + +// Thing under test +const InitiateSessionService = require('../../../../app/services/notifications/setup/initiate-session.service.js') + +describe('Notifications Setup - Initiate Session service', () => { + describe('when called', () => { + it('creates a new session record with an empty data property', async () => { + const result = await InitiateSessionService.go() + + const matchingSession = await SessionModel.query().findById(result.id) + + expect(matchingSession.data).to.equal({}) + }) + }) +})