@dwp/govuk-casa
Version:
A framework for building GOVUK Collect-And-Submit-Applications
155 lines (135 loc) • 5.23 kB
JavaScript
import JourneyContext from "../lib/JourneyContext.js";
import { validateUrlPath } from "../lib/utils.js";
import waypointUrl from "../lib/waypoint-url.js";
/**
* @typedef {import("express").RequestHandler} RequestHandler
* @access private
*/
/**
* @typedef {import("../casa.js").Plan} Plan
* @access private
*/
/**
* @typedef {import("../casa.js").ContextEventHandler} ContextEventHandler
* @access private
*/
/**
* @typedef {import("../casa.js").ContextIdGenerator} ContextIdGenerator
* @access private
*/
const editOrigin = (req) => {
if (Object.hasOwn(req.query, "editorigin")) {
return waypointUrl({ waypoint: req.query.editorigin });
}
if (req.body && Object.hasOwn(req.body, "editorigin")) {
return waypointUrl({ waypoint: req.body.editorigin });
}
return "";
};
/**
* Data middleware.
*
* Decorates the request with some contextual data about the user's journey
* through the application. This is used by downstream middleware and
* templates.
*
* @param {object} opts Options
* @param {Plan} opts.plan CASA Plan
* @param {ContextEventHandler[]} opts.events Event handlers
* @param {ContextIdGenerator} opts.contextIdGenerator Content ID generator
* @param {boolean} ops.govukRebrand Govuk rebrand feature flag
* @param opts.govukRebrand
* @returns {RequestHandler[]} Middleware functions
*/
export default function dataMiddleware({
plan,
events,
contextIdGenerator,
govukRebrand,
}) {
return [
(req, res, next) => {
/* ------------------------------------------------ Request decorations */
// CASA
req.casa = {
...req?.casa,
// The plan
plan,
// Current journey context, loaded from session, specified by
// `contextid` request parameter
journeyContext:
JourneyContext.extractContextFromRequest(req).addEventListeners(
events,
),
// Edit mode
editMode:
(Object.hasOwn(req.query, "edit") &&
Object.hasOwn(req.query, "editorigin")) ||
(Object.hasOwn(req.body, "edit") &&
Object.hasOwn(req.body, "editorigin")),
editOrigin: editOrigin(req),
};
// Grab chosen language from session
req.casa.journeyContext.nav.language = req.session.language;
// Context ID generator
Object.defineProperty(req, JourneyContext.ID_GENERATOR_REQ_KEY, {
value: contextIdGenerator,
enumerable: false,
writable: false,
});
/* ------------------------------------------------- Template variables */
// Capture mount URL that will be used in generating all browser URLs
const mountUrl = validateUrlPath(`${req.baseUrl}/`.replace(/\/+/g, "/"));
// If this CASA app is mounted on a parameterised route, then all of its
// static assets (served by `staticRouter`) will, by default, be served
// from that dynamic path, for example:
// markup: <link href="{{ mountUrl }}css/static.css" />
// resolved URL: /mount/<some-id-here>/css/static.css
// baseUrl: /mount/<some-id>
//
// From a performance point of view, this is very inefficient as we can't
// take advantage of any intermediate caches. So we instead provide am
// alternative URL path in the `staticMountUrl` property that excludes the
// parameterised element, eg:
// markup: <link href="{{ staticMountUrl }}css/static.css" />
// resolved URL: /mount/css/static.css
// baseUrl: /mount
//
// As the staticRouter is mounted on both the CASA app, and its internal
// Router, the `baseUrl` is different in each case, so we cannot rely
// on it to be consistent. Hence the need for this property, which will
// always be the non-parameterised version of the baseUrl.
const staticMountUrl = validateUrlPath(
`${req.unparameterisedBaseUrl}/`.replace(/\/+/g, "/"),
);
// CASA and userland templates
res.locals.casa = {
mountUrl,
staticMountUrl,
editMode: req.casa.editMode,
editOrigin: req.casa.editOrigin,
};
res.locals.locale = req.language;
// Used by govuk-frontend template
// htmlLang = req.language is provided by i18n-http-middleware
// assetPath = used for linking to static assets in the govuk-frontend module
res.locals.htmlLang = req.language;
const rebrandAssetPath = govukRebrand ? "/rebrand" : "";
res.locals.assetPath = `${staticMountUrl}govuk/assets${rebrandAssetPath}`;
// Function for building URLs. This will be curried with the `mountUrl`,
// `journeyContext`, `edit` and `editOrigin` for convenience. This means
// the template author does not have to be concerned about the current
// "state" when generating URLs, but still has the ability to override
// these curried defaults if needs be.
res.locals.waypointUrl = (args) =>
waypointUrl({
mountUrl,
journeyContext: req.casa.journeyContext,
edit: req.casa.editMode,
editOrigin: req.casa.editOrigin,
...args,
});
next();
},
];
}