@dwp/govuk-casa 9.0.0 → 9.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/dist/assets/css/casa.css +1 -1
- package/dist/assets/css/casa.css.map +1 -1
- package/dist/casa.d.ts +122 -95
- package/dist/casa.js +119 -86
- package/dist/casa.js.map +1 -1
- package/dist/lib/CasaTemplateLoader.d.ts +4 -4
- package/dist/lib/CasaTemplateLoader.js +16 -16
- package/dist/lib/CasaTemplateLoader.js.map +1 -1
- package/dist/lib/JourneyContext.d.ts +38 -40
- package/dist/lib/JourneyContext.js +81 -75
- package/dist/lib/JourneyContext.js.map +1 -1
- package/dist/lib/MutableRouter.d.ts +40 -41
- package/dist/lib/MutableRouter.js +64 -71
- package/dist/lib/MutableRouter.js.map +1 -1
- package/dist/lib/Plan.d.ts +29 -26
- package/dist/lib/Plan.js +85 -71
- package/dist/lib/Plan.js.map +1 -1
- package/dist/lib/ValidationError.d.ts +16 -15
- package/dist/lib/ValidationError.js +21 -20
- package/dist/lib/ValidationError.js.map +1 -1
- package/dist/lib/ValidatorFactory.d.ts +15 -13
- package/dist/lib/ValidatorFactory.js +14 -12
- package/dist/lib/ValidatorFactory.js.map +1 -1
- package/dist/lib/configuration-ingestor.d.ts +37 -40
- package/dist/lib/configuration-ingestor.js +93 -93
- package/dist/lib/configuration-ingestor.js.map +1 -1
- package/dist/lib/configure.d.ts +6 -6
- package/dist/lib/configure.js +14 -12
- package/dist/lib/configure.js.map +1 -1
- package/dist/lib/constants.d.ts +1 -3
- package/dist/lib/constants.js +9 -11
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/context-id-generators.d.ts +3 -5
- package/dist/lib/context-id-generators.js +7 -6
- package/dist/lib/context-id-generators.js.map +1 -1
- package/dist/lib/end-session.d.ts +4 -4
- package/dist/lib/end-session.js +5 -5
- package/dist/lib/field.d.ts +20 -18
- package/dist/lib/field.js +35 -48
- package/dist/lib/field.js.map +1 -1
- package/dist/lib/index.d.ts +13 -13
- package/dist/lib/logger.d.ts +7 -6
- package/dist/lib/logger.js +7 -7
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/mount.d.ts +5 -5
- package/dist/lib/mount.js +11 -10
- package/dist/lib/mount.js.map +1 -1
- package/dist/lib/nunjucks-filters.d.ts +10 -12
- package/dist/lib/nunjucks-filters.js +35 -35
- package/dist/lib/nunjucks-filters.js.map +1 -1
- package/dist/lib/nunjucks.d.ts +7 -5
- package/dist/lib/nunjucks.js +10 -8
- package/dist/lib/nunjucks.js.map +1 -1
- package/dist/lib/utils.d.ts +19 -19
- package/dist/lib/utils.js +62 -55
- package/dist/lib/utils.js.map +1 -1
- package/dist/lib/validators/dateObject.d.ts +29 -22
- package/dist/lib/validators/dateObject.js +58 -49
- package/dist/lib/validators/dateObject.js.map +1 -1
- package/dist/lib/validators/email.d.ts +4 -4
- package/dist/lib/validators/email.js +4 -4
- package/dist/lib/validators/inArray.d.ts +4 -4
- package/dist/lib/validators/inArray.js +7 -8
- package/dist/lib/validators/inArray.js.map +1 -1
- package/dist/lib/validators/index.d.ts +10 -10
- package/dist/lib/validators/index.js +1 -3
- package/dist/lib/validators/index.js.map +1 -1
- package/dist/lib/validators/nino.d.ts +9 -8
- package/dist/lib/validators/nino.js +14 -10
- package/dist/lib/validators/nino.js.map +1 -1
- package/dist/lib/validators/postalAddressObject.d.ts +37 -24
- package/dist/lib/validators/postalAddressObject.js +65 -46
- package/dist/lib/validators/postalAddressObject.js.map +1 -1
- package/dist/lib/validators/range.d.ts +12 -8
- package/dist/lib/validators/range.js +11 -9
- package/dist/lib/validators/range.js.map +1 -1
- package/dist/lib/validators/regex.d.ts +4 -4
- package/dist/lib/validators/regex.js +5 -5
- package/dist/lib/validators/required.d.ts +6 -6
- package/dist/lib/validators/required.js +9 -11
- package/dist/lib/validators/required.js.map +1 -1
- package/dist/lib/validators/strlen.d.ts +12 -8
- package/dist/lib/validators/strlen.js +13 -11
- package/dist/lib/validators/strlen.js.map +1 -1
- package/dist/lib/validators/wordCount.d.ts +12 -8
- package/dist/lib/validators/wordCount.js +15 -11
- package/dist/lib/validators/wordCount.js.map +1 -1
- package/dist/lib/waypoint-url.d.ts +16 -13
- package/dist/lib/waypoint-url.js +39 -36
- package/dist/lib/waypoint-url.js.map +1 -1
- package/dist/middleware/body-parser.d.ts +1 -1
- package/dist/middleware/body-parser.js +6 -6
- package/dist/middleware/body-parser.js.map +1 -1
- package/dist/middleware/data.d.ts +1 -1
- package/dist/middleware/data.js +8 -7
- package/dist/middleware/data.js.map +1 -1
- package/dist/middleware/gather-fields.d.ts +2 -2
- package/dist/middleware/gather-fields.js +6 -4
- package/dist/middleware/gather-fields.js.map +1 -1
- package/dist/middleware/i18n.js +13 -15
- package/dist/middleware/i18n.js.map +1 -1
- package/dist/middleware/post.js +30 -18
- package/dist/middleware/post.js.map +1 -1
- package/dist/middleware/pre.d.ts +2 -2
- package/dist/middleware/pre.js +46 -27
- package/dist/middleware/pre.js.map +1 -1
- package/dist/middleware/progress-journey.d.ts +1 -1
- package/dist/middleware/progress-journey.js +5 -5
- package/dist/middleware/progress-journey.js.map +1 -1
- package/dist/middleware/sanitise-fields.d.ts +1 -1
- package/dist/middleware/sanitise-fields.js +13 -11
- package/dist/middleware/sanitise-fields.js.map +1 -1
- package/dist/middleware/serve-first-waypoint.d.ts +3 -3
- package/dist/middleware/serve-first-waypoint.js +8 -6
- package/dist/middleware/serve-first-waypoint.js.map +1 -1
- package/dist/middleware/session.js +14 -11
- package/dist/middleware/session.js.map +1 -1
- package/dist/middleware/skip-waypoint.d.ts +1 -1
- package/dist/middleware/skip-waypoint.js +3 -3
- package/dist/middleware/skip-waypoint.js.map +1 -1
- package/dist/middleware/steer-journey.d.ts +1 -1
- package/dist/middleware/steer-journey.js +16 -14
- package/dist/middleware/steer-journey.js.map +1 -1
- package/dist/middleware/strip-proxy-path.d.ts +1 -1
- package/dist/middleware/strip-proxy-path.js +3 -3
- package/dist/middleware/strip-proxy-path.js.map +1 -1
- package/dist/middleware/validate-fields.d.ts +1 -1
- package/dist/middleware/validate-fields.js +2 -5
- package/dist/middleware/validate-fields.js.map +1 -1
- package/dist/routes/ancillary.d.ts +3 -3
- package/dist/routes/ancillary.js +4 -4
- package/dist/routes/ancillary.js.map +1 -1
- package/dist/routes/journey.d.ts +2 -2
- package/dist/routes/journey.js +91 -39
- package/dist/routes/journey.js.map +1 -1
- package/dist/routes/static.d.ts +7 -5
- package/dist/routes/static.js +20 -19
- package/dist/routes/static.js.map +1 -1
- package/package.json +19 -18
- package/src/casa.js +133 -100
- package/src/lib/CasaTemplateLoader.js +24 -19
- package/src/lib/JourneyContext.js +138 -107
- package/src/lib/MutableRouter.js +72 -74
- package/src/lib/Plan.js +145 -97
- package/src/lib/ValidationError.js +25 -21
- package/src/lib/ValidatorFactory.js +17 -13
- package/src/lib/configuration-ingestor.js +147 -110
- package/src/lib/configure.js +34 -32
- package/src/lib/constants.js +9 -11
- package/src/lib/context-id-generators.js +40 -43
- package/src/lib/end-session.js +6 -6
- package/src/lib/field.js +69 -58
- package/src/lib/index.js +12 -12
- package/src/lib/logger.js +9 -9
- package/src/lib/mount.js +70 -74
- package/src/lib/nunjucks-filters.js +56 -59
- package/src/lib/nunjucks.js +23 -18
- package/src/lib/utils.js +78 -57
- package/src/lib/validators/dateObject.js +71 -60
- package/src/lib/validators/email.js +8 -8
- package/src/lib/validators/inArray.js +10 -11
- package/src/lib/validators/index.js +12 -14
- package/src/lib/validators/nino.js +29 -15
- package/src/lib/validators/postalAddressObject.js +87 -63
- package/src/lib/validators/range.js +14 -12
- package/src/lib/validators/regex.js +8 -8
- package/src/lib/validators/required.js +16 -16
- package/src/lib/validators/strlen.js +16 -14
- package/src/lib/validators/wordCount.js +22 -14
- package/src/lib/waypoint-url.js +64 -46
- package/src/middleware/body-parser.js +10 -10
- package/src/middleware/csrf.js +1 -1
- package/src/middleware/data.js +28 -24
- package/src/middleware/gather-fields.js +10 -9
- package/src/middleware/i18n.js +35 -37
- package/src/middleware/post.js +41 -21
- package/src/middleware/pre.js +62 -41
- package/src/middleware/progress-journey.js +32 -18
- package/src/middleware/sanitise-fields.js +43 -20
- package/src/middleware/serve-first-waypoint.js +14 -12
- package/src/middleware/session.js +74 -61
- package/src/middleware/skip-waypoint.js +7 -9
- package/src/middleware/steer-journey.js +40 -28
- package/src/middleware/strip-proxy-path.js +8 -7
- package/src/middleware/validate-fields.js +5 -12
- package/src/routes/ancillary.js +5 -7
- package/src/routes/journey.js +159 -85
- package/src/routes/static.js +62 -29
- package/views/casa/components/character-count/README.md +2 -2
- package/views/casa/components/checkboxes/README.md +6 -6
- package/views/casa/components/date-input/README.md +7 -7
- package/views/casa/components/input/README.md +2 -2
- package/views/casa/components/journey-form/README.md +33 -14
- package/views/casa/components/postal-address-object/README.md +4 -4
- package/views/casa/components/radios/README.md +6 -6
- package/views/casa/components/select/README.md +6 -6
- package/views/casa/components/textarea/README.md +2 -2
- package/views/casa/layouts/main.njk +2 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
// This sits in front of all other middleware and prevents the user from
|
|
2
2
|
// "jumping ahead" in the Plan.
|
|
3
3
|
|
|
4
|
-
import waypointUrl from
|
|
5
|
-
import logger from
|
|
4
|
+
import waypointUrl from "../lib/waypoint-url.js";
|
|
5
|
+
import logger from "../lib/logger.js";
|
|
6
6
|
|
|
7
|
-
const log = logger(
|
|
7
|
+
const log = logger("middleware:steer-journey");
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
+
* @typedef {import("../lib/Plan")} Plan
|
|
10
11
|
* @access private
|
|
11
|
-
* @typedef {import('../lib/Plan')} Plan
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
/**
|
|
@@ -20,10 +20,7 @@ const log = logger('middleware:steer-journey');
|
|
|
20
20
|
* @param {Plan} obj.plan CASA Plan
|
|
21
21
|
* @returns {void}
|
|
22
22
|
*/
|
|
23
|
-
export default ({
|
|
24
|
-
waypoint,
|
|
25
|
-
plan,
|
|
26
|
-
}) => [
|
|
23
|
+
export default ({ waypoint, plan }) => [
|
|
27
24
|
(req, res, next) => {
|
|
28
25
|
const mountUrl = `${req.baseUrl}/`;
|
|
29
26
|
|
|
@@ -32,25 +29,38 @@ export default ({
|
|
|
32
29
|
const traversed = plan.traverse(req.casa.journeyContext);
|
|
33
30
|
if (traversed.indexOf(waypoint) === -1) {
|
|
34
31
|
const redirectTo = traversed[traversed.length - 1];
|
|
35
|
-
log.trace(
|
|
32
|
+
log.trace(
|
|
33
|
+
`Attempted to access "${waypoint}" when not in the journey; redirecting to "${redirectTo}"`,
|
|
34
|
+
);
|
|
36
35
|
|
|
37
|
-
return res.redirect(
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
return res.redirect(
|
|
37
|
+
302,
|
|
38
|
+
waypointUrl({
|
|
39
|
+
waypoint: redirectTo,
|
|
40
|
+
mountUrl,
|
|
41
|
+
journeyContext: req.casa.journeyContext,
|
|
42
|
+
edit: req.casa.editMode,
|
|
43
|
+
editOrigin: req.casa.editOrigin,
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
// Edit mode
|
|
47
49
|
// Cannot be in edit mode if we're already on the `editorigin` URL
|
|
48
50
|
if (req.casa.editMode) {
|
|
49
|
-
const { pathname: currentPathname } = new URL(
|
|
50
|
-
|
|
51
|
+
const { pathname: currentPathname } = new URL(
|
|
52
|
+
req.originalUrl,
|
|
53
|
+
"https://placeholder.test/",
|
|
54
|
+
);
|
|
55
|
+
const { pathname: editOriginPathname } = new URL(
|
|
56
|
+
req.casa.editOrigin,
|
|
57
|
+
"https://placeholder.test/",
|
|
58
|
+
);
|
|
51
59
|
|
|
52
60
|
if (editOriginPathname === currentPathname) {
|
|
53
|
-
log.debug(
|
|
61
|
+
log.debug(
|
|
62
|
+
`Disabling edit mode as we are on the edit origin (${req.casa.editOrigin})`,
|
|
63
|
+
);
|
|
54
64
|
req.casa.editMode = false;
|
|
55
65
|
req.casa.editOrigin = undefined;
|
|
56
66
|
}
|
|
@@ -64,16 +74,18 @@ export default ({
|
|
|
64
74
|
// Calculate URL for the "back" link
|
|
65
75
|
const [prevRoute] = plan.traversePrevRoutes(req.casa.journeyContext, {
|
|
66
76
|
startWaypoint: waypoint,
|
|
67
|
-
stopCondition: () =>
|
|
77
|
+
stopCondition: () => true, // stop at the first one
|
|
68
78
|
});
|
|
69
|
-
res.locals.casa.journeyPreviousUrl = prevRoute.target
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
res.locals.casa.journeyPreviousUrl = prevRoute.target
|
|
80
|
+
? waypointUrl({
|
|
81
|
+
mountUrl,
|
|
82
|
+
journeyContext: req.casa.journeyContext,
|
|
83
|
+
waypoint: prevRoute.target,
|
|
84
|
+
routeName: "prev",
|
|
85
|
+
edit: req.casa.editMode,
|
|
86
|
+
editOrigin: req.casa.editOrigin,
|
|
87
|
+
})
|
|
88
|
+
: undefined;
|
|
77
89
|
|
|
78
90
|
return next();
|
|
79
91
|
},
|
|
@@ -14,13 +14,11 @@
|
|
|
14
14
|
//
|
|
15
15
|
// See docs in `docs/guides/setup-behind-a-proxy.md`
|
|
16
16
|
|
|
17
|
-
import logger from
|
|
17
|
+
import logger from "../lib/logger.js";
|
|
18
18
|
|
|
19
|
-
const log = logger(
|
|
19
|
+
const log = logger("casa:middleware:strip-proxy-path");
|
|
20
20
|
|
|
21
|
-
export default ({
|
|
22
|
-
mountUrl = '/',
|
|
23
|
-
}) => [
|
|
21
|
+
export default ({ mountUrl = "/" }) => [
|
|
24
22
|
(req, res, next) => {
|
|
25
23
|
// TODO:
|
|
26
24
|
// We _may_ have to start tracking the various prefix in order to differentiate
|
|
@@ -28,7 +26,7 @@ export default ({
|
|
|
28
26
|
|
|
29
27
|
// Assume everything before `mountUrl` is the proxy path prefix and remove it
|
|
30
28
|
req.originalBaseUrl = req.originalBaseUrl ?? req.baseUrl;
|
|
31
|
-
req.baseUrl = mountUrl.replace(/\/$/,
|
|
29
|
+
req.baseUrl = mountUrl.replace(/\/$/, "");
|
|
32
30
|
|
|
33
31
|
// If the app has been mounted directly on the specific `mountUrl`, then
|
|
34
32
|
// there's nothing we need to do and can let this request pass-through.
|
|
@@ -43,7 +41,10 @@ export default ({
|
|
|
43
41
|
// req.url will already have the proxy prefix and mountUrl removed.
|
|
44
42
|
/* eslint-disable security/detect-non-literal-regexp */
|
|
45
43
|
log.trace(`req.originalUrl before proxy stripping: ${req.originalUrl}`);
|
|
46
|
-
req.originalUrl = req.originalUrl.replace(
|
|
44
|
+
req.originalUrl = req.originalUrl.replace(
|
|
45
|
+
new RegExp(`^/.+?${mountUrl}`),
|
|
46
|
+
mountUrl,
|
|
47
|
+
);
|
|
47
48
|
log.trace(`req.originalUrl after proxy stripping: ${req.originalUrl}`);
|
|
48
49
|
/* eslint-enable security/detect-non-literal-regexp */
|
|
49
50
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Validate the data captured in the journey context
|
|
2
|
-
import JourneyContext from
|
|
3
|
-
import { REQUEST_PHASE_VALIDATE } from
|
|
2
|
+
import JourneyContext from "../lib/JourneyContext.js";
|
|
3
|
+
import { REQUEST_PHASE_VALIDATE } from "../lib/constants.js";
|
|
4
4
|
|
|
5
5
|
const updateContext = ({
|
|
6
6
|
waypoint,
|
|
@@ -21,13 +21,9 @@ const updateContext = ({
|
|
|
21
21
|
casaRequestPhase: REQUEST_PHASE_VALIDATE,
|
|
22
22
|
},
|
|
23
23
|
});
|
|
24
|
-
}
|
|
24
|
+
};
|
|
25
25
|
|
|
26
|
-
export default ({
|
|
27
|
-
waypoint,
|
|
28
|
-
fields = [],
|
|
29
|
-
plan,
|
|
30
|
-
}) => [
|
|
26
|
+
export default ({ waypoint, fields = [], plan }) => [
|
|
31
27
|
(req, res, next) => {
|
|
32
28
|
const mountUrl = `${req.baseUrl}/`;
|
|
33
29
|
|
|
@@ -50,10 +46,7 @@ export default ({
|
|
|
50
46
|
};
|
|
51
47
|
/* eslint-enable security/detect-object-injection */
|
|
52
48
|
|
|
53
|
-
errors = [
|
|
54
|
-
...errors,
|
|
55
|
-
...field.runValidators(context),
|
|
56
|
-
];
|
|
49
|
+
errors = [...errors, ...field.runValidators(context)];
|
|
57
50
|
}
|
|
58
51
|
|
|
59
52
|
// Validation passed with no errors
|
package/src/routes/ancillary.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import MutableRouter from
|
|
1
|
+
import MutableRouter from "../lib/MutableRouter.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @typedef {object} AncillaryRouterOptions Options to configure static router
|
|
@@ -8,19 +8,17 @@ import MutableRouter from '../lib/MutableRouter.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* Create an instance of the ancillary router.
|
|
10
10
|
*
|
|
11
|
-
* @access private
|
|
12
11
|
* @param {AncillaryRouterOptions} options Options
|
|
13
12
|
* @returns {MutableRouter} ExpressJS Router instance
|
|
13
|
+
* @access private
|
|
14
14
|
*/
|
|
15
|
-
export default function ancillaryRouter({
|
|
16
|
-
sessionTtl,
|
|
17
|
-
}) {
|
|
15
|
+
export default function ancillaryRouter({ sessionTtl }) {
|
|
18
16
|
// Router
|
|
19
17
|
const router = new MutableRouter();
|
|
20
18
|
|
|
21
19
|
// Session timeout
|
|
22
|
-
router.all(
|
|
23
|
-
res.render(
|
|
20
|
+
router.all("/session-timeout", (req, res) => {
|
|
21
|
+
res.render("casa/session-timeout.njk", {
|
|
24
22
|
sessionTtl: Math.floor(sessionTtl / 60),
|
|
25
23
|
});
|
|
26
24
|
});
|
package/src/routes/journey.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
/* eslint-disable object-curly-newline,max-len */
|
|
2
|
-
import MutableRouter from
|
|
3
|
-
import skipWaypointMiddlewareFactory from
|
|
4
|
-
import steerJourneyMiddlewareFactory from
|
|
5
|
-
import sanitiseFieldsMiddlewareFactory from
|
|
6
|
-
import gatherFieldsMiddlewareFactory from
|
|
7
|
-
import validateFieldsMiddlewareFactory from
|
|
8
|
-
import progressJourneyMiddlewareFactory from
|
|
9
|
-
import waypointUrl from
|
|
10
|
-
import logger from
|
|
11
|
-
import { resolveMiddlewareHooks } from
|
|
12
|
-
import { CONFIG_ERROR_VISIBILITY_ALWAYS } from
|
|
13
|
-
|
|
14
|
-
const log = logger(
|
|
2
|
+
import MutableRouter from "../lib/MutableRouter.js";
|
|
3
|
+
import skipWaypointMiddlewareFactory from "../middleware/skip-waypoint.js";
|
|
4
|
+
import steerJourneyMiddlewareFactory from "../middleware/steer-journey.js";
|
|
5
|
+
import sanitiseFieldsMiddlewareFactory from "../middleware/sanitise-fields.js";
|
|
6
|
+
import gatherFieldsMiddlewareFactory from "../middleware/gather-fields.js";
|
|
7
|
+
import validateFieldsMiddlewareFactory from "../middleware/validate-fields.js";
|
|
8
|
+
import progressJourneyMiddlewareFactory from "../middleware/progress-journey.js";
|
|
9
|
+
import waypointUrl from "../lib/waypoint-url.js";
|
|
10
|
+
import logger from "../lib/logger.js";
|
|
11
|
+
import { resolveMiddlewareHooks } from "../lib/utils.js";
|
|
12
|
+
import { CONFIG_ERROR_VISIBILITY_ALWAYS } from "../lib/constants.js";
|
|
13
|
+
|
|
14
|
+
const log = logger("routes:journey");
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
+
* @param {import("../casa.js").GlobalHook} GlobalHook
|
|
17
18
|
* @access private
|
|
18
|
-
* @param {import('../casa.js').GlobalHook} GlobalHook
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
+
* @param {import("../casa.js").Page} Page
|
|
22
23
|
* @access private
|
|
23
|
-
* @param {import('../casa.js').Page} Page
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
+
* @param {import("../casa.js").Plan} Plan
|
|
27
28
|
* @access private
|
|
28
|
-
* @param {import('../casa.js').Plan} Plan
|
|
29
29
|
*/
|
|
30
30
|
|
|
31
31
|
/**
|
|
@@ -38,49 +38,58 @@ const log = logger('routes:journey');
|
|
|
38
38
|
|
|
39
39
|
const renderMiddlewareFactory = (view, contextFactory) => [
|
|
40
40
|
(req, res, next) => {
|
|
41
|
-
res.render(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
41
|
+
res.render(
|
|
42
|
+
view,
|
|
43
|
+
{
|
|
44
|
+
// Common template variables for both GET and POST requests
|
|
45
|
+
inEditMode: req.casa.editMode,
|
|
46
|
+
editOriginUrl: req.casa.editOrigin,
|
|
47
|
+
activeContextId: req.casa.journeyContext.identity.id,
|
|
48
|
+
...contextFactory(req),
|
|
49
|
+
},
|
|
50
|
+
(err, templateString) => {
|
|
51
|
+
if (err) {
|
|
52
|
+
next(err);
|
|
53
|
+
} else {
|
|
54
|
+
res.send(templateString);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
);
|
|
54
58
|
},
|
|
55
59
|
];
|
|
56
60
|
|
|
57
61
|
/**
|
|
58
|
-
*
|
|
62
|
+
* Generate page validation error
|
|
59
63
|
*
|
|
60
|
-
* @param {object} errors
|
|
61
|
-
* @param {object} req
|
|
62
|
-
* @returns {object[]}
|
|
64
|
+
* @param {object} errors Object of page validation error
|
|
65
|
+
* @param {object} req Casa request object
|
|
66
|
+
* @returns {object[]} Array of error objects
|
|
63
67
|
*/
|
|
64
|
-
const generateGovukErrors = (errors, req) =>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
const generateGovukErrors = (errors, req) =>
|
|
69
|
+
Object.values(errors || {}).map(([error]) => ({
|
|
70
|
+
text: req.t(error.summary, error.variables),
|
|
71
|
+
href: error.fieldHref,
|
|
72
|
+
}));
|
|
68
73
|
/**
|
|
69
|
-
*
|
|
74
|
+
* Handle errorVisibility flag and function and return boolean
|
|
70
75
|
*
|
|
71
|
-
* @param {
|
|
72
|
-
* @param {
|
|
73
|
-
* @returns {boolean}
|
|
76
|
+
* @param {object} req Casa request object
|
|
77
|
+
* @param {symbol | Function} errorVisibility ErrorVisibility config option
|
|
78
|
+
* @returns {boolean} True if errorVisibility is "always" or function condition
|
|
79
|
+
* true
|
|
74
80
|
*/
|
|
75
|
-
const resolveErrorVisibility = (req, errorVisibility) =>
|
|
81
|
+
const resolveErrorVisibility = (req, errorVisibility) =>
|
|
82
|
+
typeof errorVisibility === "function"
|
|
83
|
+
? errorVisibility({ req })
|
|
84
|
+
: errorVisibility === CONFIG_ERROR_VISIBILITY_ALWAYS;
|
|
76
85
|
|
|
77
86
|
/**
|
|
78
87
|
* Create an instance of the router for all waypoints visited during a Journey
|
|
79
88
|
* through the Plan.
|
|
80
89
|
*
|
|
81
|
-
* @access private
|
|
82
90
|
* @param {JourneyRouterOptions} opts Options
|
|
83
91
|
* @returns {MutableRouter} Router
|
|
92
|
+
* @access private
|
|
84
93
|
*/
|
|
85
94
|
export default function journeyRouter({
|
|
86
95
|
globalHooks,
|
|
@@ -94,7 +103,7 @@ export default function journeyRouter({
|
|
|
94
103
|
|
|
95
104
|
// Special "_" route which handles redirecting the user between sub-apps
|
|
96
105
|
// /app1/_/?refmount=app2&route=prev
|
|
97
|
-
router.all(
|
|
106
|
+
router.all("/_", (req, res) => {
|
|
98
107
|
const mountUrl = `${req.baseUrl}/`;
|
|
99
108
|
const refmount = req.query?.refmount;
|
|
100
109
|
const route = req.query?.route;
|
|
@@ -104,7 +113,7 @@ export default function journeyRouter({
|
|
|
104
113
|
const fallback = waypointUrl({
|
|
105
114
|
mountUrl,
|
|
106
115
|
waypoint: plan.traverse(req.casa.journeyContext, {
|
|
107
|
-
stopCondition: () =>
|
|
116
|
+
stopCondition: () => true, // we only need one; stop at the first
|
|
108
117
|
})[0],
|
|
109
118
|
});
|
|
110
119
|
|
|
@@ -113,37 +122,49 @@ export default function journeyRouter({
|
|
|
113
122
|
// the user to our Plan and we don't intend to link back.
|
|
114
123
|
if (!plan.getWaypoints().includes(refmount)) {
|
|
115
124
|
redirectTo = fallback;
|
|
116
|
-
} else if (route ===
|
|
117
|
-
const routes = plan.traversePrevRoutes(req.casa.journeyContext, {
|
|
118
|
-
|
|
125
|
+
} else if (route === "prev") {
|
|
126
|
+
const routes = plan.traversePrevRoutes(req.casa.journeyContext, {
|
|
127
|
+
startWaypoint: refmount,
|
|
128
|
+
});
|
|
129
|
+
redirectTo = routes.length
|
|
130
|
+
? waypointUrl({ mountUrl, waypoint: routes[0].target })
|
|
131
|
+
: fallback;
|
|
119
132
|
} else {
|
|
120
|
-
const routes = plan.traverseNextRoutes(req.casa.journeyContext, {
|
|
133
|
+
const routes = plan.traverseNextRoutes(req.casa.journeyContext, {
|
|
134
|
+
startWaypoint: refmount,
|
|
135
|
+
});
|
|
121
136
|
if (routes[0].target !== null) {
|
|
122
|
-
redirectTo = routes.length
|
|
137
|
+
redirectTo = routes.length
|
|
138
|
+
? waypointUrl({ mountUrl, waypoint: routes[0].target })
|
|
139
|
+
: fallback;
|
|
123
140
|
} else {
|
|
124
141
|
redirectTo = fallback;
|
|
125
142
|
}
|
|
126
143
|
}
|
|
127
144
|
|
|
128
145
|
// Carry over any params
|
|
129
|
-
const url = new URL(redirectTo,
|
|
146
|
+
const url = new URL(redirectTo, "https://placeholder.test/");
|
|
130
147
|
const searchParams = new URLSearchParams(req.query);
|
|
131
|
-
searchParams.delete(
|
|
132
|
-
searchParams.delete(
|
|
148
|
+
searchParams.delete("refmount");
|
|
149
|
+
searchParams.delete("route");
|
|
133
150
|
url.search = searchParams.toString();
|
|
134
|
-
redirectTo = `${url.pathname.replace(/\/+/g,
|
|
151
|
+
redirectTo = `${url.pathname.replace(/\/+/g, "/")}${url.search}`;
|
|
135
152
|
|
|
136
153
|
log.trace(`Redirect to ${redirectTo}`);
|
|
137
154
|
return res.redirect(redirectTo);
|
|
138
155
|
});
|
|
139
156
|
|
|
140
157
|
// Create GET / POST routes for each page
|
|
141
|
-
const commonMiddleware = [
|
|
142
|
-
...csrfMiddleware,
|
|
143
|
-
];
|
|
158
|
+
const commonMiddleware = [...csrfMiddleware];
|
|
144
159
|
|
|
145
160
|
pages.forEach((page) => {
|
|
146
|
-
const {
|
|
161
|
+
const {
|
|
162
|
+
waypoint,
|
|
163
|
+
view,
|
|
164
|
+
hooks: pageHooks = [],
|
|
165
|
+
fields,
|
|
166
|
+
errorVisibility,
|
|
167
|
+
} = page;
|
|
147
168
|
const waypointPath = `/${waypoint}`;
|
|
148
169
|
|
|
149
170
|
let commonWaypointMiddleware = [
|
|
@@ -167,22 +188,40 @@ export default function journeyRouter({
|
|
|
167
188
|
...commonMiddleware,
|
|
168
189
|
...commonWaypointMiddleware,
|
|
169
190
|
|
|
170
|
-
...resolveMiddlewareHooks(
|
|
191
|
+
...resolveMiddlewareHooks("journey.presteer", waypointPath, [
|
|
192
|
+
...globalHooks,
|
|
193
|
+
...pageHooks,
|
|
194
|
+
]),
|
|
171
195
|
...steerJourneyMiddlewareFactory({ waypoint, plan }),
|
|
172
|
-
...resolveMiddlewareHooks(
|
|
173
|
-
|
|
174
|
-
|
|
196
|
+
...resolveMiddlewareHooks("journey.poststeer", waypointPath, [
|
|
197
|
+
...globalHooks,
|
|
198
|
+
...pageHooks,
|
|
199
|
+
]),
|
|
200
|
+
|
|
201
|
+
...resolveMiddlewareHooks("journey.prerender", waypointPath, [
|
|
202
|
+
...globalHooks,
|
|
203
|
+
...pageHooks,
|
|
204
|
+
]),
|
|
175
205
|
renderMiddlewareFactory(view, (req) => {
|
|
176
|
-
const displayErrors =
|
|
177
|
-
|
|
206
|
+
const displayErrors =
|
|
207
|
+
resolveErrorVisibility(req, globalErrorVisibility) ||
|
|
208
|
+
resolveErrorVisibility(req, errorVisibility);
|
|
209
|
+
const errors =
|
|
210
|
+
displayErrors &&
|
|
211
|
+
(req.casa.journeyContext.getValidationErrorsForPageByField(
|
|
212
|
+
waypoint,
|
|
213
|
+
) ??
|
|
214
|
+
Object.create(null));
|
|
178
215
|
const govukErrors = displayErrors && generateGovukErrors(errors, req);
|
|
179
216
|
|
|
180
|
-
return
|
|
217
|
+
return {
|
|
181
218
|
formUrl: waypointUrl({ mountUrl: `${req.baseUrl}/`, waypoint }),
|
|
182
219
|
formData: req.casa.journeyContext.getDataForPage(waypoint),
|
|
183
|
-
formErrors:
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
formErrors:
|
|
221
|
+
Object.keys(errors).length && displayErrors ? errors : null,
|
|
222
|
+
formErrorsGovukArray:
|
|
223
|
+
govukErrors.length && displayErrors ? govukErrors : null,
|
|
224
|
+
};
|
|
186
225
|
}),
|
|
187
226
|
);
|
|
188
227
|
|
|
@@ -191,35 +230,70 @@ export default function journeyRouter({
|
|
|
191
230
|
...commonMiddleware,
|
|
192
231
|
...commonWaypointMiddleware,
|
|
193
232
|
|
|
194
|
-
...resolveMiddlewareHooks(
|
|
233
|
+
...resolveMiddlewareHooks("journey.presteer", waypointPath, [
|
|
234
|
+
...globalHooks,
|
|
235
|
+
...pageHooks,
|
|
236
|
+
]),
|
|
195
237
|
...steerJourneyMiddlewareFactory({ waypoint, plan }),
|
|
196
|
-
...resolveMiddlewareHooks(
|
|
197
|
-
|
|
198
|
-
|
|
238
|
+
...resolveMiddlewareHooks("journey.poststeer", waypointPath, [
|
|
239
|
+
...globalHooks,
|
|
240
|
+
...pageHooks,
|
|
241
|
+
]),
|
|
242
|
+
|
|
243
|
+
...resolveMiddlewareHooks("journey.presanitise", waypointPath, [
|
|
244
|
+
...globalHooks,
|
|
245
|
+
...pageHooks,
|
|
246
|
+
]),
|
|
199
247
|
...sanitiseFieldsMiddlewareFactory({ waypoint, fields }),
|
|
200
|
-
...resolveMiddlewareHooks(
|
|
201
|
-
|
|
202
|
-
|
|
248
|
+
...resolveMiddlewareHooks("journey.postsanitise", waypointPath, [
|
|
249
|
+
...globalHooks,
|
|
250
|
+
...pageHooks,
|
|
251
|
+
]),
|
|
252
|
+
|
|
253
|
+
...resolveMiddlewareHooks("journey.pregather", waypointPath, [
|
|
254
|
+
...globalHooks,
|
|
255
|
+
...pageHooks,
|
|
256
|
+
]),
|
|
203
257
|
...gatherFieldsMiddlewareFactory({ waypoint, fields }),
|
|
204
|
-
...resolveMiddlewareHooks(
|
|
205
|
-
|
|
206
|
-
|
|
258
|
+
...resolveMiddlewareHooks("journey.postgather", waypointPath, [
|
|
259
|
+
...globalHooks,
|
|
260
|
+
...pageHooks,
|
|
261
|
+
]),
|
|
262
|
+
|
|
263
|
+
...resolveMiddlewareHooks("journey.prevalidate", waypointPath, [
|
|
264
|
+
...globalHooks,
|
|
265
|
+
...pageHooks,
|
|
266
|
+
]),
|
|
207
267
|
...validateFieldsMiddlewareFactory({ waypoint, fields, plan }),
|
|
208
|
-
...resolveMiddlewareHooks(
|
|
268
|
+
...resolveMiddlewareHooks("journey.postvalidate", waypointPath, [
|
|
269
|
+
...globalHooks,
|
|
270
|
+
...pageHooks,
|
|
271
|
+
]),
|
|
209
272
|
|
|
210
273
|
// If there were validation errors, jump out of this route and into the
|
|
211
274
|
// next, where the errors will be rendered
|
|
212
|
-
(req, res, next) =>
|
|
213
|
-
|
|
214
|
-
|
|
275
|
+
(req, res, next) =>
|
|
276
|
+
req.casa.journeyContext.hasValidationErrorsForPage(waypoint)
|
|
277
|
+
? next("route")
|
|
278
|
+
: next(),
|
|
279
|
+
|
|
280
|
+
...resolveMiddlewareHooks("journey.preredirect", waypointPath, [
|
|
281
|
+
...globalHooks,
|
|
282
|
+
...pageHooks,
|
|
283
|
+
]),
|
|
215
284
|
...progressJourneyMiddlewareFactory({ waypoint, plan }),
|
|
216
285
|
);
|
|
217
286
|
|
|
218
287
|
router.post(
|
|
219
288
|
waypointPath,
|
|
220
|
-
...resolveMiddlewareHooks(
|
|
289
|
+
...resolveMiddlewareHooks("journey.prerender", waypointPath, [
|
|
290
|
+
...globalHooks,
|
|
291
|
+
...pageHooks,
|
|
292
|
+
]),
|
|
221
293
|
renderMiddlewareFactory(view, (req) => {
|
|
222
|
-
const errors =
|
|
294
|
+
const errors =
|
|
295
|
+
req.casa.journeyContext.getValidationErrorsForPageByField(waypoint) ??
|
|
296
|
+
Object.create(null);
|
|
223
297
|
|
|
224
298
|
// This is a convenience for the template. The `govukErrorSummary` macro
|
|
225
299
|
// requires the errors be in a particular format, so here we provide our
|
|
@@ -228,7 +302,7 @@ export default function journeyRouter({
|
|
|
228
302
|
// first one is shown.
|
|
229
303
|
// Disabling security/detect-object-injection rule because both `errors`
|
|
230
304
|
// and the `k` property are known entities
|
|
231
|
-
const govukErrors = generateGovukErrors(errors, req)
|
|
305
|
+
const govukErrors = generateGovukErrors(errors, req);
|
|
232
306
|
|
|
233
307
|
return {
|
|
234
308
|
formUrl: waypointUrl({ mountUrl: `${req.baseUrl}/`, waypoint }),
|