@dwp/govuk-casa 7.0.6 → 8.0.0-alpha1

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.
Files changed (219) hide show
  1. package/README.md +22 -17
  2. package/dist/{casa → assets}/css/casa-ie8.css +1 -1
  3. package/dist/assets/css/casa.css +1 -0
  4. package/dist/casa.d.ts +10 -0
  5. package/dist/casa.js +44 -0
  6. package/dist/lib/CasaTemplateLoader.d.ts +19 -0
  7. package/dist/lib/CasaTemplateLoader.js +57 -0
  8. package/dist/lib/JourneyContext.d.ts +255 -0
  9. package/dist/lib/JourneyContext.js +681 -0
  10. package/dist/lib/MutableRouter.d.ts +155 -0
  11. package/dist/lib/MutableRouter.js +272 -0
  12. package/dist/lib/Plan.d.ts +119 -0
  13. package/dist/lib/Plan.js +382 -0
  14. package/dist/lib/ValidationError.d.ts +70 -0
  15. package/dist/lib/ValidationError.js +156 -0
  16. package/dist/lib/ValidatorFactory.d.ts +24 -0
  17. package/dist/lib/ValidatorFactory.js +87 -0
  18. package/dist/lib/configure.d.ts +205 -0
  19. package/dist/lib/configure.js +215 -0
  20. package/dist/lib/dirname.cjs +1 -0
  21. package/dist/lib/end-session.d.ts +12 -0
  22. package/dist/lib/end-session.js +24 -0
  23. package/dist/lib/field.d.ts +79 -0
  24. package/dist/lib/field.js +223 -0
  25. package/dist/lib/logger.d.ts +8 -0
  26. package/dist/lib/logger.js +19 -0
  27. package/dist/lib/nunjucks-filters.d.ts +26 -0
  28. package/dist/lib/nunjucks-filters.js +112 -0
  29. package/dist/lib/nunjucks.d.ts +22 -0
  30. package/dist/lib/nunjucks.js +49 -0
  31. package/dist/lib/utils.d.ts +22 -0
  32. package/dist/lib/utils.js +44 -0
  33. package/dist/lib/validators/dateObject.d.ts +4 -0
  34. package/dist/lib/validators/dateObject.js +135 -0
  35. package/dist/lib/validators/email.d.ts +4 -0
  36. package/dist/lib/validators/email.js +46 -0
  37. package/dist/lib/validators/inArray.d.ts +4 -0
  38. package/dist/lib/validators/inArray.js +60 -0
  39. package/dist/lib/validators/index.d.ts +21 -0
  40. package/dist/lib/validators/index.js +47 -0
  41. package/dist/lib/validators/nino.d.ts +4 -0
  42. package/dist/lib/validators/nino.js +46 -0
  43. package/dist/lib/validators/postalAddressObject.d.ts +4 -0
  44. package/dist/lib/validators/postalAddressObject.js +123 -0
  45. package/dist/lib/validators/regex.d.ts +4 -0
  46. package/dist/lib/validators/regex.js +40 -0
  47. package/dist/lib/validators/required.d.ts +4 -0
  48. package/dist/lib/validators/required.js +56 -0
  49. package/dist/lib/validators/strlen.d.ts +4 -0
  50. package/dist/lib/validators/strlen.js +51 -0
  51. package/dist/lib/validators/wordCount.d.ts +5 -0
  52. package/dist/lib/validators/wordCount.js +54 -0
  53. package/dist/lib/waypoint-url.d.ts +17 -0
  54. package/dist/lib/waypoint-url.js +46 -0
  55. package/dist/middleware/body-parser.d.ts +1 -0
  56. package/dist/middleware/body-parser.js +24 -0
  57. package/dist/middleware/csrf.d.ts +1 -0
  58. package/dist/middleware/csrf.js +31 -0
  59. package/dist/middleware/data.d.ts +6 -0
  60. package/dist/middleware/data.js +53 -0
  61. package/dist/middleware/dirname.cjs +1 -0
  62. package/dist/middleware/gather-fields.d.ts +5 -0
  63. package/dist/middleware/gather-fields.js +39 -0
  64. package/dist/middleware/i18n.d.ts +4 -0
  65. package/dist/middleware/i18n.js +87 -0
  66. package/dist/middleware/post.d.ts +1 -0
  67. package/dist/middleware/post.js +42 -0
  68. package/dist/middleware/pre.d.ts +3 -0
  69. package/dist/middleware/pre.js +38 -0
  70. package/dist/middleware/progress-journey.d.ts +6 -0
  71. package/dist/middleware/progress-journey.js +82 -0
  72. package/dist/middleware/sanitise-fields.d.ts +5 -0
  73. package/dist/middleware/sanitise-fields.js +48 -0
  74. package/dist/middleware/session.d.ts +10 -0
  75. package/dist/middleware/session.js +115 -0
  76. package/dist/middleware/skip-waypoint.d.ts +5 -0
  77. package/dist/middleware/skip-waypoint.js +40 -0
  78. package/dist/middleware/steer-journey.d.ts +6 -0
  79. package/dist/middleware/steer-journey.js +44 -0
  80. package/dist/middleware/validate-fields.d.ts +7 -0
  81. package/dist/middleware/validate-fields.js +76 -0
  82. package/dist/mjs/esm-wrapper.js +10 -0
  83. package/dist/mjs/package.json +3 -0
  84. package/dist/package.json +3 -0
  85. package/dist/routes/ancillary.d.ts +4 -0
  86. package/dist/routes/ancillary.js +19 -0
  87. package/dist/routes/dirname.cjs +1 -0
  88. package/dist/routes/journey.d.ts +8 -0
  89. package/dist/routes/journey.js +130 -0
  90. package/dist/routes/static.d.ts +26 -0
  91. package/dist/routes/static.js +67 -0
  92. package/package.json +45 -86
  93. package/views/casa/components/checkboxes/template.njk +4 -1
  94. package/views/casa/components/date-input/template.njk +3 -3
  95. package/views/casa/components/journey-form/README.md +3 -1
  96. package/views/casa/components/journey-form/template.njk +1 -1
  97. package/views/casa/components/postal-address-object/template.njk +5 -5
  98. package/views/casa/components/radios/template.njk +1 -1
  99. package/views/casa/layouts/journey.njk +26 -9
  100. package/views/casa/layouts/main.njk +6 -19
  101. package/views/casa/partials/scripts.njk +8 -3
  102. package/views/casa/partials/styles.njk +2 -2
  103. package/casa.js +0 -208
  104. package/definitions/review-page.js +0 -60
  105. package/dist/casa/css/casa.css +0 -1
  106. package/dist/casa/js/casa.js +0 -1
  107. package/index.d.ts +0 -121
  108. package/lib/ConfigIngestor.js +0 -588
  109. package/lib/GatherModifier.js +0 -14
  110. package/lib/I18n.js +0 -160
  111. package/lib/JourneyContext.d.ts +0 -97
  112. package/lib/JourneyContext.js +0 -552
  113. package/lib/JourneyMap.js +0 -233
  114. package/lib/JourneyRoad.js +0 -330
  115. package/lib/Logger.js +0 -59
  116. package/lib/PageDictionary.d.ts +0 -11
  117. package/lib/PageDirectory.js +0 -77
  118. package/lib/Plan.js +0 -423
  119. package/lib/RoadConverter.js +0 -153
  120. package/lib/UserJourney.js +0 -8
  121. package/lib/Util.js +0 -227
  122. package/lib/Validation.js +0 -20
  123. package/lib/bootstrap/end-session.js +0 -44
  124. package/lib/bootstrap/load-definitions.js +0 -64
  125. package/lib/commonBodyParser.js +0 -15
  126. package/lib/enums.js +0 -6
  127. package/lib/gather-modifiers/index.js +0 -7
  128. package/lib/gather-modifiers/trimPostalAddressObject.js +0 -75
  129. package/lib/gather-modifiers/trimWhitespace.js +0 -16
  130. package/lib/utils/createGetRequest.d.ts +0 -5
  131. package/lib/utils/createGetRequest.js +0 -59
  132. package/lib/utils/index.js +0 -11
  133. package/lib/utils/parseRequest.d.ts +0 -5
  134. package/lib/utils/parseRequest.js +0 -72
  135. package/lib/utils/sanitise.js +0 -74
  136. package/lib/utils/validate.js +0 -32
  137. package/lib/validation/ArrayObjectField.js +0 -49
  138. package/lib/validation/ObjectField.js +0 -53
  139. package/lib/validation/SimpleField.d.ts +0 -11
  140. package/lib/validation/SimpleField.js +0 -46
  141. package/lib/validation/ValidationError.d.ts +0 -14
  142. package/lib/validation/ValidationError.js +0 -170
  143. package/lib/validation/ValidatorFactory.d.ts +0 -32
  144. package/lib/validation/ValidatorFactory.js +0 -91
  145. package/lib/validation/index.js +0 -22
  146. package/lib/validation/processor/flattenErrorArray.js +0 -24
  147. package/lib/validation/processor/queue.js +0 -214
  148. package/lib/validation/processor.js +0 -84
  149. package/lib/validation/rules/README.md +0 -3
  150. package/lib/validation/rules/ValidationRules.d.ts +0 -22
  151. package/lib/validation/rules/dateObject.js +0 -156
  152. package/lib/validation/rules/email.js +0 -44
  153. package/lib/validation/rules/inArray.js +0 -61
  154. package/lib/validation/rules/index.js +0 -23
  155. package/lib/validation/rules/nino.js +0 -48
  156. package/lib/validation/rules/optional.js +0 -14
  157. package/lib/validation/rules/postalAddressObject.js +0 -142
  158. package/lib/validation/rules/regex.js +0 -39
  159. package/lib/validation/rules/required.js +0 -57
  160. package/lib/validation/rules/strlen.js +0 -57
  161. package/lib/validation/rules/wordCount.js +0 -61
  162. package/lib/view-filters/formatDateObject.js +0 -35
  163. package/lib/view-filters/includes.js +0 -10
  164. package/lib/view-filters/index.js +0 -23
  165. package/lib/view-filters/mergeObjectsDeep.js +0 -21
  166. package/lib/view-filters/renderAsAttributes.js +0 -33
  167. package/middleware/errors/404.js +0 -12
  168. package/middleware/errors/catch-all.js +0 -27
  169. package/middleware/errors/index.js +0 -9
  170. package/middleware/headers/config-defaults.js +0 -57
  171. package/middleware/headers/headers.js +0 -40
  172. package/middleware/headers/index.js +0 -9
  173. package/middleware/i18n/i18n.js +0 -56
  174. package/middleware/i18n/index.js +0 -16
  175. package/middleware/index.js +0 -55
  176. package/middleware/mount/index.js +0 -9
  177. package/middleware/mount/mount.js +0 -10
  178. package/middleware/nunjucks/environment.js +0 -57
  179. package/middleware/nunjucks/index.js +0 -8
  180. package/middleware/page/csrf.js +0 -37
  181. package/middleware/page/edit-mode.js +0 -52
  182. package/middleware/page/gather.js +0 -75
  183. package/middleware/page/index.js +0 -103
  184. package/middleware/page/journey-continue.js +0 -157
  185. package/middleware/page/journey-rails.js +0 -102
  186. package/middleware/page/prepare-request.js +0 -77
  187. package/middleware/page/render.js +0 -75
  188. package/middleware/page/skip.js +0 -72
  189. package/middleware/page/utils.js +0 -206
  190. package/middleware/page/validate.js +0 -67
  191. package/middleware/session/expiry.js +0 -95
  192. package/middleware/session/genid.js +0 -18
  193. package/middleware/session/index.js +0 -18
  194. package/middleware/session/init.js +0 -25
  195. package/middleware/session/seed.js +0 -50
  196. package/middleware/session/timeout.js +0 -5
  197. package/middleware/static/asset-versions.js +0 -23
  198. package/middleware/static/index.js +0 -104
  199. package/middleware/static/prepare-assets.js +0 -51
  200. package/middleware/static/serve-assets.js +0 -58
  201. package/middleware/variables/index.js +0 -12
  202. package/middleware/variables/variables.js +0 -35
  203. package/src/browserconfig.xml +0 -5
  204. package/src/js/casa.js +0 -132
  205. package/src/scss/_casaElements.scss +0 -11
  206. package/src/scss/_casaGovukTemplateJinjaPolyfill.scss +0 -39
  207. package/src/scss/_casaMountUrl.scss +0 -8
  208. package/src/scss/casa-ie8.scss +0 -3
  209. package/src/scss/casa.scss +0 -14
  210. package/test/unit/templates/README.md +0 -5
  211. package/test/utils/BaseTestWaypoint.js +0 -106
  212. package/test/utils/concatWaypoints.js +0 -26
  213. package/test/utils/index.js +0 -6
  214. package/test/utils/testTraversal.js +0 -90
  215. package/views/casa/partials/cookie_message.njk +0 -3
  216. package/views/casa/partials/phase_banner_alpha.njk +0 -8
  217. package/views/casa/partials/phase_banner_beta.njk +0 -8
  218. package/views/casa/review/page-block.njk +0 -8
  219. package/views/casa/review/review.njk +0 -47
@@ -1,102 +0,0 @@
1
- /**
2
- * Ensure the user is on the right track in their journey through the app. This
3
- * middleware prevents skipping pages in the journey without having filled in
4
- * required data in the preceding pages.
5
- *
6
- * Enhances `res.locals.casa` with:
7
- * string journeyPreviousUrl = Absolute URL to the previous page in the journey
8
- * (if applicable).
9
- */
10
-
11
- const createLogger = require('../../lib/Logger.js');
12
- const JourneyContext = require('../../lib/JourneyContext.js');
13
- const { createGetRequest } = require('../../lib/utils/index.js');
14
-
15
- module.exports = (mountUrl = '/', plan) => {
16
- if (!plan) {
17
- return (req, res, next) => next();
18
- }
19
-
20
- return (req, res, next) => {
21
- const logger = createLogger('page.journey-rails');
22
- logger.setSessionId(req.session.id);
23
-
24
- req.casa = req.casa || Object.create(null);
25
-
26
- // We can skip to next route handler if the waypoint doesn't feature in the
27
- // defined user journey.
28
- if (!plan.containsWaypoint(req.casa.journeyWaypointId)) {
29
- next();
30
- return;
31
- }
32
-
33
- // Traverse all routes from a given origin. This gives us the list of
34
- // waypoints that the user could have legitimately visited.
35
- const traversalOptions = {
36
- startWaypoint: req.casa.journeyOrigin.waypoint,
37
- };
38
- const traversed = plan.traverse(
39
- req.casa.journeyContext || new JourneyContext(),
40
- traversalOptions,
41
- );
42
-
43
- // Common attributes for generating links
44
- const requestAttributes = {
45
- mountUrl,
46
- editMode: req.inEditMode,
47
- editOrigin: req.editOriginUrl,
48
- contextId: req.casa.journeyContext.identity.id,
49
- };
50
-
51
- // The requested waypoint cannot be found in the list of traversed routes;
52
- // send the user back to their last visited waypoint
53
- const currentUrlIndex = traversed.indexOf(req.casa.journeyWaypointId);
54
- if (currentUrlIndex === -1) {
55
- const redirectUrl = createGetRequest({
56
- ...requestAttributes,
57
- waypoint: `${req.casa.journeyOrigin.originId || ''}/${traversed[traversed.length - 1]}`,
58
- });
59
- logger.debug('Traversal redirect: %s to %s', req.casa.journeyWaypointId, redirectUrl);
60
- res.status(302).redirect(`${redirectUrl}#`);
61
- return;
62
- }
63
-
64
- // The requested waypoint is reachable via the traversed routes; generate a
65
- // previous waypoint URL that can be used to navigate back.
66
- if (currentUrlIndex > 0) {
67
- res.locals.casa.journeyPreviousUrl = createGetRequest({
68
- ...requestAttributes,
69
- waypoint: `${req.casa.journeyOrigin.originId || ''}/${traversed[currentUrlIndex - 1]}`,
70
- });
71
- next();
72
- return;
73
- }
74
-
75
- // We're at the very first waypoint that is reachable from the given origin;
76
- // check if there is a "prev" route available that points to a different
77
- // origin, that we can use to generate a URL pointing to that waypoint.
78
- // First, attempt to traverse backwards; this will ensure the route conditions
79
- // are taken into account during that traversal.
80
- // If no valid route is found with that method, fallback to using the first
81
- // `prev` route that we can find.
82
- const stopCondition = (r) => (r.label.targetOrigin !== undefined);
83
- let [previousRoute] = plan.traversePrevRoutes(req.casa.journeyContext, {
84
- startWaypoint: req.casa.journeyWaypointId,
85
- stopCondition,
86
- }).filter(stopCondition);
87
-
88
- if (!previousRoute) {
89
- [previousRoute] = plan.getPrevOutwardRoutes(req.casa.journeyWaypointId).filter(stopCondition);
90
- }
91
-
92
- if (previousRoute) {
93
- res.locals.casa.journeyPreviousUrl = createGetRequest({
94
- ...requestAttributes,
95
- mountUrl,
96
- waypoint: `${previousRoute.label.targetOrigin}/${previousRoute.target}`,
97
- });
98
- }
99
-
100
- next();
101
- };
102
- };
@@ -1,77 +0,0 @@
1
- /**
2
- * Enhances `req.casa` with:
3
- * string journeyWaypointId = Converts requested URL into a string suitable for
4
- * use as a journey waypoint ID (i.e. Remove trailing slashes)
5
- * object journeyOrigin = The origin from which traversals should start
6
- * Plan plan = The grand Plan.
7
- */
8
-
9
- const { parseRequest, createGetRequest } = require('../../lib/utils/index.js');
10
-
11
- function getSingleOriginMeta(origins, casaRequest) {
12
- // Even though single-origin Plans won't generally generate URLs with the
13
- // origin ID included, there is a chance that could happen. So here we're
14
- // making sure to ignore it if it's there.
15
- const [originOrWaypoint, waypoint] = casaRequest.waypoint.split('/');
16
-
17
- return {
18
- // Set blank originId to prevent generated URLs containing an origin
19
- origin: { ...origins[0], originId: '' },
20
- waypoint: originOrWaypoint === origins[0].originId ? waypoint : originOrWaypoint,
21
- };
22
- }
23
-
24
- function getMultiOriginMeta(origins, casaRequest) {
25
- const [originId, waypoint] = casaRequest.waypoint.split('/');
26
-
27
- return {
28
- origin: origins.find((o) => o.originId === originId),
29
- waypoint,
30
- };
31
- }
32
-
33
- module.exports = (mountUrl, plan) => {
34
- // Prepare for extracting metadata from each request
35
- const origins = plan.getOrigins();
36
- const getOriginAndWaypoint = (origins.length === 1 ? getSingleOriginMeta : getMultiOriginMeta)
37
- .bind(null, origins);
38
-
39
- return (req, res, next) => {
40
- // Extract the Plan origin and current waypoint from the request
41
- const { origin, waypoint } = getOriginAndWaypoint(parseRequest(req));
42
-
43
- // Define read-only properties
44
- req.casa = req.casa || Object.create(null);
45
- Object.defineProperty(req.casa, 'plan', {
46
- value: plan,
47
- configurable: false,
48
- enumerable: true,
49
- writable: false,
50
- });
51
- Object.defineProperty(req.casa, 'journeyOrigin', {
52
- value: origin,
53
- configurable: false,
54
- enumerable: true,
55
- writable: false,
56
- });
57
- Object.defineProperty(req.casa, 'journeyWaypointId', {
58
- value: waypoint,
59
- configurable: false,
60
- enumerable: true,
61
- writable: false,
62
- });
63
-
64
- // Utility function for creating links from within templates
65
- res.locals.makeLink = (args) => (createGetRequest({
66
- mountUrl,
67
- waypoint: `${origin.originId}/${waypoint}`,
68
- contextId: req.casa.journeyContext.isDefault() ? '' : req.casa.journeyContext.identity.id,
69
- editMode: req.inEditMode,
70
- editOrigin: req.editOriginUrl,
71
- ...args,
72
- }));
73
-
74
- // Next middleware
75
- next();
76
- };
77
- };
@@ -1,75 +0,0 @@
1
- const createLogger = require('../../lib/Logger.js');
2
- const { executeHook } = require('./utils.js');
3
-
4
- module.exports = (pageMeta = {}) => (req, res, next) => {
5
- const logger = createLogger('page.render');
6
- logger.setSessionId(req.session.id);
7
- const pageId = pageMeta.id;
8
- const activeContextId = req.casa.journeyContext.isDefault()
9
- ? undefined
10
- : req.casa.journeyContext.identity.id;
11
-
12
- req.casa = req.casa || Object.create(null);
13
-
14
- function renderErrorCallback(err, templateString) {
15
- if (err) {
16
- logger.error(err);
17
- next(err);
18
- } else {
19
- res.send(templateString);
20
- }
21
- }
22
-
23
- function renderGET() {
24
- res.render(pageMeta.view, {
25
- formData: req.casa.journeyContext.getDataForPage(pageMeta),
26
- inEditMode: req.inEditMode,
27
- editOriginUrl: req.editOriginUrl,
28
- editSearchParams: req.editSearchParams,
29
- activeContextId,
30
- }, renderErrorCallback);
31
- }
32
-
33
- function renderPOST() {
34
- const errors = req.casa.journeyContext.getValidationErrorsForPage(pageId)
35
- || Object.create(null);
36
-
37
- // This is a convenience for the template. The `govukErrorSummary` macro
38
- // requires the errors be in a particular format, so here we provide our
39
- // errors in that format.
40
- const govukErrors = Object.keys(errors).map((k) => ({
41
- text: req.i18nTranslator.t(errors[k][0].summary, errors[k][0].variables),
42
- href: errors[k][0].fieldHref,
43
- }));
44
-
45
- // We reflect all POSTed data in req.body rather than just the gathered
46
- // journey data because the template may have custom fields that aren't part
47
- // of the formal page data, but are important to how the page functions.
48
- res.render(pageMeta.view, {
49
- formData: req.body,
50
- formErrors: Object.keys(errors).length ? errors : null,
51
- formErrorsGovukArray: govukErrors.length ? govukErrors : null,
52
- inEditMode: req.inEditMode,
53
- editOriginUrl: req.editOriginUrl,
54
- editSearchParams: req.editSearchParams,
55
- activeContextId,
56
- }, renderErrorCallback);
57
- }
58
-
59
- return executeHook(logger, req, res, pageMeta, 'prerender').then(() => {
60
- logger.trace(
61
- 'Rendering view for %s (editmode=%s, method=%s, contextId=%s)',
62
- pageId,
63
- req.inEditMode ? 'true' : 'false',
64
- req.method,
65
- req.casa.journeyContext.identity.id,
66
- );
67
- if (req.method.toLowerCase() === 'post') {
68
- renderPOST();
69
- } else {
70
- renderGET();
71
- }
72
- }).catch((err) => {
73
- next(err);
74
- });
75
- };
@@ -1,72 +0,0 @@
1
- /**
2
- * Mark a waypoint as having been viewed, and the user having made an explicit
3
- * action to pass through it, onto a subsequent page.
4
- *
5
- * You may also need to modify the "follow" functions on each of the out-edges
6
- * leading away from the skipped waypoint, so they understand to allow the skip.
7
- *
8
- * It is important that this function overwrites _all_ other data on the
9
- * specified waypoint.
10
- *
11
- * NOTE: All waypoints up to the point of the one being marked must have been
12
- * visited beforehand, so this middleware must come after journey-rails.
13
- *
14
- * Format:
15
- * /current-waypoint?skipto=next-waypoint.
16
- */
17
-
18
- const JourneyContext = require('../../lib/JourneyContext.js');
19
- const createLogger = require('../../lib/Logger.js');
20
- const { createGetRequest } = require('../../lib/utils/index.js');
21
-
22
- module.exports = (mountUrl) => (req, res, next) => {
23
- const logger = createLogger('page.skip');
24
- logger.setSessionId(req.session.id);
25
-
26
- req.casa = req.casa || Object.create(null);
27
-
28
- const { skipto } = req.query;
29
- const { journeyOrigin = { originId: '' } } = req.casa;
30
-
31
- // Validate arguments
32
- if (skipto === undefined) {
33
- next();
34
- return;
35
- }
36
- if (typeof skipto !== 'string' || !skipto.match(/^[a-z0-9-]{1,200}$/i)) {
37
- logger.error('Invalid skip waypoint');
38
- res.status(400).send('Invalid waypoint');
39
- return;
40
- }
41
-
42
- // Inject a special "__skipped__" data item into the waypoint's page data,
43
- // overwriting all other data therein.
44
- logger.info('Marking waypoint %s as skipped', req.casa.journeyWaypointId);
45
- req.casa.journeyContext.clearValidationErrorsForPage(req.casa.journeyWaypointId);
46
- req.casa.journeyContext.setDataForPage(req.casa.journeyWaypointId, {
47
- __skipped__: true,
48
- });
49
-
50
- // Persist changes to session
51
- JourneyContext.putContext(req.session, req.casa.journeyContext);
52
-
53
- // Attach edit and contextId flags to the redirect URL, if applicable
54
- // const redirectUrl
55
- const redirectUrl = createGetRequest({
56
- mountUrl,
57
- waypoint: `${journeyOrigin.originId || ''}/${skipto}`,
58
- editMode: req.inEditMode,
59
- editOrigin: req.editOriginUrl,
60
- contextId: req.casa.journeyContext.identity.id,
61
- });
62
-
63
- // Save session and send user on their way
64
- req.session.save((err) => {
65
- if (err) {
66
- logger.error(err);
67
- next(err);
68
- } else {
69
- res.status(302).redirect(redirectUrl);
70
- }
71
- });
72
- };
@@ -1,206 +0,0 @@
1
- const { isObjectWithKeys, isObjectType, normalizeHtmlObjectPath } = require('../../lib/Util.js');
2
- const JourneyContext = require('../../lib/JourneyContext.js');
3
-
4
- /**
5
- * Converts an array of functions to a nested callback, eg:
6
- * [ | function nested(req, res, next) {-
7
- * function a(req, res, next) {- | a(req, res, () => {-
8
- * ... | ...
9
- * next(); | b(req, res, () => {-
10
- * }, | ...
11
- * function b(req, res, next) {- | c(req, res, () => {-
12
- * ... | ...
13
- * next(); | next();
14
- * }, | });
15
- * function c(req, res, next) {- | })
16
- * ... | });
17
- * next(); | }
18
- * }, |
19
- * ] |.
20
- *
21
- * @param {object} logger Request-specific Logger instance.
22
- * @param {string} hookName Name of the hook being called.
23
- * @param {string} waypointId ID of waypoint.
24
- * @param {Array} hooks Array of middleware like functions.
25
- * @returns {Function} Nested function.
26
- */
27
- function nestHooks(logger, hookName, waypointId, hooks) {
28
- return hooks.reduce((inital, hook, hookNumber) => {
29
- if (typeof hook === 'function') {
30
- return (req, res, next) => {
31
- inital(req, res, () => {
32
- logger.trace('Running %s hook %d for %s', hookName, hookNumber + 1, waypointId);
33
- hook(req, res, next);
34
- })
35
- }
36
- }
37
- return inital;
38
- }, (req, res, next) => next());
39
- }
40
-
41
- /**
42
- * Generic wrapper for executing one of the page hooks.
43
- *
44
- * The returned Promise will always resolve unless the hook function ends the
45
- * response with something like `res.send()`.
46
- *
47
- * @param {object} logger Request-specific Logger instance.
48
- * @param {object} req Express request.
49
- * @param {object} res Express response.
50
- * @param {object} pageMeta Metadata of page being processed.
51
- * @param {string} hookName Name of hook to execute.
52
- * @returns {Promise} Promise.
53
- */
54
- function executeHook(logger, req = {}, res = {}, pageMeta = {}, hookName = '') {
55
- return new Promise((resolve, reject) => {
56
- const hooks = pageMeta && pageMeta.hooks ? pageMeta.hooks : Object.create(null);
57
- const { journeyWaypointId } = req.casa || Object.create(null);
58
- if (Array.isArray(hooks[hookName])) {
59
- const nestedHooks = nestHooks(logger, hookName, req.casa.journeyWaypointId, hooks[hookName]);
60
- // Will not resolve if any hook executes `res.send()`
61
- logger.trace('Running nested %s hooks for %s', hookName, journeyWaypointId);
62
- nestedHooks(req, res, resolve);
63
- } else if (typeof hooks[hookName] === 'function') {
64
- logger.trace('Run %s hook for %s', hookName, journeyWaypointId);
65
- hooks[hookName](req, res, (err) => {
66
- // Will not resolve if hook executes `res.send()`
67
- if (err) {
68
- reject(err);
69
- } else {
70
- resolve();
71
- }
72
- });
73
- } else {
74
- logger.trace('No %s hook for %s', hookName, journeyWaypointId);
75
- resolve();
76
- }
77
- });
78
- }
79
-
80
- /**
81
- * Apply the `sanitise()` method of each validator rule defined in the
82
- * fieldValidator. The output of each sanitisation is passed as input to the
83
- * next.
84
- *
85
- * `context` contains the `fieldName`, `waypointId` and `journeyContext`.
86
- *
87
- * @param {SimpleFieldValidatorConfig} fieldValidator Validator
88
- * @param {any} data Data to be sanitised
89
- * @param {any} context Data context
90
- * @returns {any} Sanitised data
91
- */
92
- function applyValidatorSanitiser(fieldValidator, data, context) {
93
- // Apply each valdiator's `sanitise` method
94
- let sanitisedData = data;
95
- fieldValidator.validators.forEach((validatorObj) => {
96
- sanitisedData = validatorObj.sanitise(sanitisedData, context);
97
- });
98
-
99
- return sanitisedData;
100
- }
101
-
102
- /**
103
- * Extract the data that will be saved to session, removing data that does not
104
- * have an associated field validator.
105
- *
106
- * Where no validators are defined for the requested waypoint, we will store
107
- * nothing in the session.
108
- *
109
- * @param {object} logger Request-specific logger instance.
110
- * @param {object} pageMeta Page meta object.
111
- * @param {object} data Data to be pruned.
112
- * @param {JourneyContext} journeyContext Request's journey context.
113
- * @returns {object} The pruned data.
114
- * @throws {TypeError} When given invalid argument types.
115
- */
116
- function extractSessionableData(
117
- logger,
118
- // pageWaypointId,
119
- // fieldValidators = {},
120
- pageMeta,
121
- data = {},
122
- journeyContext,
123
- ) {
124
- if (!isObjectWithKeys(logger, ['warn'])) {
125
- throw new TypeError('Expected logger to be a configured logging object');
126
- }
127
-
128
- if (!isObjectType(pageMeta)) {
129
- throw new TypeError('Expected pageMeta to be an object');
130
- }
131
-
132
- const { fieldValidators } = pageMeta;
133
- if (!isObjectWithKeys(fieldValidators)) {
134
- throw new TypeError('Expected pageMeta.fieldValidators to be an object');
135
- }
136
-
137
- if (!isObjectWithKeys(data)) {
138
- throw new TypeError('Expected data to be an object');
139
- }
140
-
141
- if (Object.keys(fieldValidators).length === 0) {
142
- logger.debug(
143
- 'No field validators defined for "%s" waypoint. Will use an empty object.',
144
- pageMeta.id,
145
- );
146
- return Object.create(null);
147
- }
148
-
149
- // Prune data that does not have an associated field valdiator.
150
- // Conditional functions expect the gathered data to be available via a
151
- // JourneyContext instance. Therefore we need to create a duplicate of the
152
- // `journeyContext`, and bundle `data` into it.
153
- const journeyContextWrapper = JourneyContext.fromObject(journeyContext.toObject());
154
- journeyContextWrapper.setDataForPage(pageMeta, data);
155
- const prunedData = Object.create(null);
156
- Object.keys(fieldValidators).forEach((k) => {
157
- if (
158
- typeof data[k] !== 'undefined'
159
- && fieldValidators[k].condition({
160
- fieldName: normalizeHtmlObjectPath(k),
161
- waypointId: pageMeta.id,
162
- journeyContext: journeyContextWrapper,
163
- })
164
- ) {
165
- prunedData[k] = applyValidatorSanitiser(
166
- fieldValidators[k],
167
- data[k],
168
- {
169
- fieldName: normalizeHtmlObjectPath(k),
170
- waypointId: pageMeta.id,
171
- journeyContext: journeyContextWrapper,
172
- },
173
- );
174
- }
175
- });
176
-
177
- return prunedData;
178
- }
179
-
180
- /**
181
- * Run modifying functions against the specified field.
182
- *
183
- * @param {object} fieldValue Value to modify.
184
- * @param {Array | Function} gatherModifiers Either an array of functions or a single function.
185
- * @returns {any} Modified value.
186
- */
187
- function runGatherModifiers(fieldValue, gatherModifiers = []) {
188
- const modifiers = Array.isArray(gatherModifiers) ? gatherModifiers : [gatherModifiers];
189
-
190
- let fValue = fieldValue;
191
- modifiers.forEach((m) => {
192
- fValue = typeof m === 'function' ? m({
193
- fieldValue: fValue,
194
- }) : fValue;
195
- });
196
-
197
- return fValue;
198
- }
199
-
200
- module.exports = {
201
- applyValidatorSanitiser,
202
- executeHook,
203
- extractSessionableData,
204
- nestHooks,
205
- runGatherModifiers,
206
- };
@@ -1,67 +0,0 @@
1
- const createLogger = require('../../lib/Logger');
2
- const Validation = require('../../lib/validation/index.js');
3
- const JourneyContext = require('../../lib/JourneyContext.js');
4
- const { executeHook } = require('./utils.js');
5
-
6
- module.exports = (pageMeta = {}) => (req, res, next) => {
7
- const logger = createLogger('page.validate');
8
- logger.setSessionId(req.session.id);
9
- const pageId = pageMeta.id;
10
-
11
- req.casa = req.casa || Object.create(null);
12
-
13
- /**
14
- * Run validation process.
15
- *
16
- * @returns {Promise} Promise.
17
- */
18
- function runValidation() {
19
- let result;
20
- if (pageMeta && pageMeta.fieldValidators) {
21
- logger.trace('Run validation for %s', pageId);
22
- result = Validation.processor({
23
- waypointId: pageId,
24
- pageMeta,
25
- // fieldValidators: pageMeta.fieldValidators,
26
- journeyContext: req.casa.journeyContext,
27
- reduceErrors: true,
28
- });
29
- } else {
30
- result = Promise.resolve();
31
- }
32
- return result;
33
- }
34
-
35
- // Promise
36
- return executeHook(logger, req, res, pageMeta, 'prevalidate')
37
- .then(runValidation)
38
- .then(() => (executeHook(logger, req, res, pageMeta, 'postvalidate')))
39
- .then(() => {
40
- // Validation has passed, so clear any validation errors currently stored
41
- // against the page and persist to session
42
- req.casa.journeyContext.clearValidationErrorsForPage(pageId);
43
- JourneyContext.putContext(req.session, req.casa.journeyContext);
44
-
45
- // The next middleware handler is responsible for moving the user onto the
46
- // correct next waypoint.
47
- next();
48
- })
49
- .catch((errors) => {
50
- // If it's a real error (i.e. thrown by interpreter rather than application)
51
- // we want to capture that earlier. Otherwise, treat as a validation error
52
- if (errors instanceof Error) {
53
- logger.trace('Passing through system error on waypoint %s: %s', pageId, errors.message);
54
- next(errors);
55
- return;
56
- }
57
-
58
- // Store validation results so they can be used during future traversals
59
- // TODO: Handle possible exceptions thrown by the below; e.g. if `errors`
60
- // are not in valid format, an exception is thrown by `setValidationErrorsForPage`
61
- logger.trace('Storing validation errors on waypoint %s', pageId);
62
- req.casa.journeyContext.setValidationErrorsForPage(pageId, errors);
63
- JourneyContext.putContext(req.session, req.casa.journeyContext);
64
-
65
- next();
66
- });
67
- }
@@ -1,95 +0,0 @@
1
- // If the store session has expired, clean up and let the user know. It's
2
- // important to clear the cookie, using the same options that were used when
3
- // the cookie was created by expressSession above - see
4
- // http://expressjs.com/en/api.html#res.clearCookie
5
- // Session store implementations should have their own clean-up methods to
6
- // clear out expired sessions, but just in case we also use a `dateExpire`
7
- // attribute in the session to forcefully destroy server-side sessions after
8
- // a defined amount of time.
9
-
10
- const qs = require('querystring');
11
- const url = require('url');
12
- const mwInit = require('./init.js');
13
-
14
- module.exports = (logger, mountUrl = '/', sessionExpiryController, sessionConfig = {}) => (req, res, next) => {
15
- let redirectPath = `${mountUrl}session-timeout`;
16
-
17
- // Session already destroyed, or on timeout page
18
- if (typeof req.session === 'undefined' || req.path === redirectPath) {
19
- next();
20
- return;
21
- }
22
-
23
- // Update manual expiry timestamp
24
- let oldSessionId;
25
- if (req.casaSessionExpired) {
26
- logger.debug('Auto-removed session %s will be cleared up', req.casaSessionExpired);
27
- oldSessionId = req.casaSessionExpired;
28
- delete req.casaSessionExpired;
29
- } else if (req.session.dateExpire && new Date(req.session.dateExpire).getTime() <= Date.now()) {
30
- logger.debug('Expired session %s will be destroyed', req.sessionID);
31
- oldSessionId = req.sessionID;
32
- } else {
33
- const ttlMilliseconds = sessionConfig.ttl ? parseInt(sessionConfig.ttl, 10) * 1000 : 0;
34
- req.session.dateExpire = new Date(Date.now() + ttlMilliseconds).toISOString();
35
- next();
36
- return;
37
- }
38
-
39
- // Optional redirect after destroy
40
- function onAfterDestroy() {
41
- if (!redirectPath || res.headersSent) {
42
- return;
43
- }
44
-
45
- // Add current path to redirect query
46
- if (req.originalUrl) {
47
- const currentUrl = url.parse(req.originalUrl, true);
48
- const redirectUrl = url.parse(redirectPath, true);
49
-
50
- // Remove existing referrer query
51
- if (currentUrl.query.referer) {
52
- delete currentUrl.query.referer;
53
-
54
- // Rebuild or remove query string
55
- currentUrl.search = currentUrl.query.length
56
- ? `?${qs.stringify(currentUrl.query)}`
57
- : null;
58
- }
59
-
60
- // Append referrer, rebuild path
61
- redirectUrl.search = `?${qs.stringify({ referer: url.format(currentUrl) })}`;
62
- redirectPath = url.format(redirectUrl);
63
- }
64
-
65
- // Redirect to session timeout
66
- res.status(302).redirect(`${redirectPath}#`);
67
- }
68
-
69
- // Destroy session
70
- logger.debug('Destroying expired session %s (tmp new ID %s)', oldSessionId, req.sessionID);
71
- req.session.destroy((err) => {
72
- if (err) {
73
- logger.error('Failed to destory session. Error: %s', err.message);
74
- }
75
-
76
- // Always clear cookie
77
- res.clearCookie(sessionConfig.name, {
78
- path: sessionConfig.cookiePath,
79
- sameSite: sessionConfig.cookieSameSite,
80
- httpOnly: true,
81
- secure: sessionConfig.secure,
82
- maxAge: null,
83
- });
84
-
85
- // Custom expiry controller
86
- if (typeof sessionExpiryController === 'function') {
87
- sessionExpiryController(req, res, () => {
88
- redirectPath = undefined;
89
- mwInit(logger, sessionConfig)(req, res, next);
90
- });
91
- }
92
-
93
- process.nextTick(onAfterDestroy);
94
- });
95
- }