diff --git a/src/controllers/deepLinkController.js b/src/controllers/deepLinkController.js index cb2bf139..1d9e4db9 100644 --- a/src/controllers/deepLinkController.js +++ b/src/controllers/deepLinkController.js @@ -11,6 +11,28 @@ const QueryParams = v.object({ orgName: NonEmptyString }) +/** + * Potentially updates sessionData with 'referrer' + * + * @param req + * @param sessionData + */ +function maybeSetReferrer (req, sessionData) { + if (req.headers.referer) { + try { + /* eslint-disable-next-line no-new */ + new URL(req.headers.referer) + sessionData.referrer = req.headers.referer + } catch (err) { + logger.info('DeepLinkController.get(): invalid referrer URL, skipping', { + type: types.App, + referrer: req.headers.referer, + errorMessage: err.message + }) + } + } +} + /** * Handles deep links in the Check Tool. * @@ -33,8 +55,9 @@ class DeepLinkController extends PageController { req.sessionModel.set('dataset', dataset) const datasetInfo = datasets.get(dataset) ?? { dataSubject: '', requiresGeometryTypeSelection: false } req.sessionModel.set('data-subject', datasetInfo.dataSubject) - req.sessionModel.set(this.checkToolDeepLinkSessionKey, - { 'data-subject': datasetInfo.dataSubject, orgName, dataset, datasetName: datasetInfo.text }) + const sessionData = { 'data-subject': datasetInfo.dataSubject, orgName, dataset, datasetName: datasetInfo.text } + maybeSetReferrer(req, sessionData) + req.sessionModel.set(this.checkToolDeepLinkSessionKey, sessionData) this.#addHistoryStep(req, '/check/dataset') diff --git a/src/controllers/pageController.js b/src/controllers/pageController.js index 6f314d4d..2b0aaf0f 100644 --- a/src/controllers/pageController.js +++ b/src/controllers/pageController.js @@ -1,7 +1,29 @@ import hmpoFormWizard from 'hmpo-form-wizard' -import { logPageView } from '../utils/logging.js' +import { logPageView, types } from '../utils/logging.js' +import logger from '../utils/logger.js' const { Controller } = hmpoFormWizard +/** + * If we arrived at the page via deep from another page, we'll use that page as the back link. + * + * @param {string} url current page URL + * @param {{ referrer?: string, dataset: string }} deepLinkInfo deep link info from the session + * @returns {string|undefined} back link URL + */ +function wizardBackLink (currentUrl, deepLinkInfo) { + if (deepLinkInfo && 'referrer' in deepLinkInfo) { + const { referrer, dataset } = deepLinkInfo + if (dataset === 'tree' && currentUrl === '/check/geometry-type') { + return referrer + } + if (dataset !== 'tree' && currentUrl === '/check/upload-method') { + return referrer + } + } + + return undefined +} + class PageController extends Controller { checkToolDeepLinkSessionKey = 'check-tool-deep-link' @@ -11,12 +33,25 @@ class PageController extends Controller { } locals (req, res, next) { - if (this.options.backLink) { - req.form.options.lastPage = this.options.backLink - } - if (req.sessionModel) { - req.form.options.deepLink = req.sessionModel.get(this.checkToolDeepLinkSessionKey) + try { + let backLink + const deepLinkInfo = req?.sessionModel?.get(this.checkToolDeepLinkSessionKey) + if (deepLinkInfo) { + req.form.options.deepLink = deepLinkInfo + backLink = wizardBackLink(req.originalUrl, deepLinkInfo) + } + + backLink = backLink ?? this.options.backLink + if (backLink) { + req.form.options.lastPage = backLink + } + } catch (e) { + logger.warn('PageController.locals(): error setting back link', { + type: types.App, + errorMessage: e.message + }) } + super.locals(req, res, next) } } diff --git a/test/unit/PageController.test.js b/test/unit/PageController.test.js index dd94bac1..c9a42fd2 100644 --- a/test/unit/PageController.test.js +++ b/test/unit/PageController.test.js @@ -39,3 +39,48 @@ describe('PageController', () => { expect(callArgs.service).toEqual('lpa-data-validation-frontend') }) }) + +describe('Correctly detects the wizard back link', () => { + const referrer = 'https://example.com/this-is-where-we-came-from' + const makeReq = () => { + return ({ + originalUrl: '/check/upload-method', + sessionID: '123', + sessionModel: { + get: vi.fn().mockReturnValue({ referrer }) + }, + form: { + options: {} + } + }) + } + + it('arriving at the deep link entry point', () => { + const pageController = new PageController({ route: '/upload-method' }) + const req = makeReq() + pageController.locals(req, {}, vi.fn()) + expect(req.form.options.lastPage).toEqual(referrer) + }) + + it('arriving at some other step', () => { + const pageController = new PageController({ route: '/upload-method' }) + const req = { ...makeReq(), originalUrl: '/check/another-step' } + pageController.locals(req, {}, vi.fn()) + expect(req.form.options.lastPage).toBe(undefined) + }) + + it('don\'t touch the back link if there\'s no deep link session info (lastPage not set)', () => { + const pageController = new PageController({ route: '/upload-method' }) + const req = { ...makeReq(), sessionModel: new Map() } + pageController.locals(req, {}, vi.fn()) + expect(req.form.options.lastPage).toEqual(undefined) + }) + + it('don\'t touch the back link if there\'s no deep link session info (lastPage set)', () => { + const pageController = new PageController({ route: '/upload-method' }) + pageController.options.backLink = '/go-back' + const req = { ...makeReq(), sessionModel: new Map() } + pageController.locals(req, {}, vi.fn()) + expect(req.form.options.lastPage).toEqual('/go-back') + }) +}) diff --git a/test/unit/deepLinkController.test.js b/test/unit/deepLinkController.test.js index 5fafe574..48e74389 100644 --- a/test/unit/deepLinkController.test.js +++ b/test/unit/deepLinkController.test.js @@ -4,7 +4,7 @@ import DeepLinkController from '../../src/controllers/deepLinkController.js' function mockRequestObject () { const sessionModel = new Map() const journeyModel = new Map() - return { sessionModel, journeyModel, query: {} } + return { sessionModel, journeyModel, query: {}, headers: {} } } function mockMiddlewareArgs (reqOpts) {