@gov-cy/govcy-express-services 0.2.12 → 0.2.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gov-cy/govcy-express-services",
3
- "version": "0.2.12",
3
+ "version": "0.2.14",
4
4
  "description": "An Express-based system that dynamically renders services using @gov-cy/govcy-frontend-renderer and posts data to a submission API.",
5
5
  "author": "DMRID - DSF Team",
6
6
  "license": "MIT",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@gov-cy/dsf-email-templates": "^2.1.0",
51
- "@gov-cy/govcy-frontend-renderer": "^1.17.2",
51
+ "@gov-cy/govcy-frontend-renderer": "^1.18.0",
52
52
  "axios": "^1.9.0",
53
53
  "cookie-parser": "^1.4.7",
54
54
  "dotenv": "^16.3.1",
@@ -85,6 +85,8 @@ export function govcyReviewPostHandler() {
85
85
  // Prepare submission data for API
86
86
  const submissionDataAPI = prepareSubmissionDataAPI(submissionData);
87
87
 
88
+ logger.debug("Prepared submission data for API:", submissionDataAPI);
89
+
88
90
  // Call the API to submit the data
89
91
  const response = await govcyApiRequest(
90
92
  "post", // Use POST method
@@ -4,6 +4,7 @@ import * as dataLayer from "./govcyDataLayer.mjs";
4
4
  import { DSFEmailRenderer } from '@gov-cy/dsf-email-templates';
5
5
  import { ALLOWED_FORM_ELEMENTS } from "./govcyConstants.mjs";
6
6
  import { evaluatePageConditions } from "./govcyExpressions.mjs";
7
+ import { logger } from "./govcyLogger.mjs";
7
8
 
8
9
  /**
9
10
  * Prepares the submission data for the service, including raw data, print-friendly data, and renderer data.
@@ -19,17 +20,78 @@ export function prepareSubmissionData(req, siteId, service) {
19
20
 
20
21
  // ----- Conditional logic comes here
21
22
  // Filter site input data based on active pages only
22
- const rawData = {};
23
+ // const rawData = {};
24
+ // for (const page of service.pages) {
25
+ // const shouldInclude = evaluatePageConditions(page, req.session, siteId, req).result === true;
26
+ // if (shouldInclude) {
27
+ // const pageUrl = page.pageData.url;
28
+ // const formData = dataLayer.getPageData(req.session, siteId, pageUrl);
29
+ // if (formData && Object.keys(formData).length > 0) {
30
+ // rawData[pageUrl] = { formData };
31
+ // }
32
+ // }
33
+ // }
34
+
35
+ // ----- consistent data model for submission_data (CONFIG-BASED)
36
+ const submissionData = {};
37
+
38
+ // Loop through every page in the service definition
23
39
  for (const page of service.pages) {
24
- const shouldInclude = evaluatePageConditions(page, req.session, siteId, req).result === true;
25
- if (shouldInclude) {
26
- const pageUrl = page.pageData.url;
27
- const formData = dataLayer.getPageData(req.session, siteId, pageUrl);
28
- if (formData && Object.keys(formData).length > 0) {
29
- rawData[pageUrl] = { formData };
40
+ const pageUrl = page.pageData.url || "";
41
+
42
+ // Find the <form> element in the page
43
+ let formElement = null;
44
+ for (const section of page.pageTemplate.sections || []) {
45
+ formElement = section.elements.find(el => el.element === "form");
46
+ if (formElement) break;
47
+ }
48
+
49
+ if (!formElement) continue; // ⛔ Skip pages without a <form> element
50
+
51
+ submissionData[pageUrl] = { formData: {} }; // ✅ Now initialize only if a form is present
52
+
53
+ // Traverse the form elements inside the form
54
+ for (const element of formElement.params.elements || []) {
55
+ const elType = element.element;
56
+
57
+ // ✅ Skip non-input elements like buttons
58
+ if (!ALLOWED_FORM_ELEMENTS.includes(elType)) continue;
59
+
60
+ const elId = element.params?.id || element.params?.name;
61
+ if (!elId) continue; // ⛔ Skip elements with no id/name
62
+
63
+ // 🟢 Use helper to get session value (or "" fallback if missing)
64
+ const value = getValue(element, pageUrl, req, siteId) ?? "";
65
+
66
+ // Store in submissionData
67
+ submissionData[pageUrl].formData[elId] = value;
68
+
69
+
70
+ // 🔄 If radios with conditionalElements, walk ALL options
71
+ if (elType === "radios" && Array.isArray(element.params?.items)) {
72
+ for (const radioItem of element.params.items) {
73
+ const condEls = radioItem.conditionalElements;
74
+ if (!Array.isArray(condEls)) continue;
75
+
76
+ for (const condElement of condEls) {
77
+ const condType = condElement.element;
78
+ if (!ALLOWED_FORM_ELEMENTS.includes(condType)) continue;
79
+
80
+ const condId = condElement.params?.id || condElement.params?.name;
81
+ if (!condId) continue;
82
+
83
+ // Again: read from session or fallback to ""
84
+ const condValue = getValue(condElement, pageUrl, req, siteId) ?? "";
85
+
86
+ // Store even if the field was not visible to user
87
+ submissionData[pageUrl].formData[condId] = condValue;
88
+ }
89
+ }
90
+ }
30
91
  }
31
92
  }
32
- }
93
+ logger.debug("Submission Data prepared:", submissionData);
94
+ // ----- END config-based stable submission_data block
33
95
 
34
96
  // Get the print-friendly data from the session store
35
97
  const printFriendlyData = preparePrintFriendlyData(req, siteId, service);
@@ -40,7 +102,7 @@ export function prepareSubmissionData(req, siteId, service) {
40
102
  return {
41
103
  submission_username: dataLayer.getUser(req.session).name,
42
104
  submission_email: dataLayer.getUser(req.session).email,
43
- submission_data: rawData, // Raw data as submitted by the user in each page
105
+ submission_data: submissionData, // Raw data as submitted by the user in each page
44
106
  submission_data_version: service.site?.submission_data_version || "", // The submission data version
45
107
  print_friendly_data: printFriendlyData, // Print-friendly data
46
108
  renderer_data: reviewSummaryList, // Renderer data of the summary list
@@ -89,123 +151,6 @@ export function preparePrintFriendlyData(req, siteId, service) {
89
151
 
90
152
  const allowedElements = ALLOWED_FORM_ELEMENTS;
91
153
 
92
- /**
93
- * Helper function to retrieve date raw input.
94
- *
95
- * @param {string} pageUrl The page URL
96
- * @param {string} name The name of the form element
97
- * @returns {string} The raw date input in ISO format (YYYY-MM-DD) or an empty string if not found
98
- */
99
- function getDateInputISO(pageUrl, name) {
100
- const day = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_day`);
101
- const month = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_month`);
102
- const year = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_year`);
103
- if (!day || !month || !year) return "";
104
-
105
- // Pad day and month with leading zero if needed
106
- const paddedDay = String(day).padStart(2, "0");
107
- const paddedMonth = String(month).padStart(2, "0");
108
-
109
- return `${year}-${paddedMonth}-${paddedDay}`; // ISO format: YYYY-MM-DD
110
- }
111
-
112
- /**
113
- * Helper function to retrieve date input in DMY format.
114
- *
115
- * @param {string} pageUrl The page URL
116
- * @param {string} name The name of the form element
117
- * @returns {string} The raw date input in DMY format (DD/MM/YYYY) or an empty string if not found
118
- */
119
- function getDateInputDMY(pageUrl, name) {
120
- const day = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_day`);
121
- const month = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_month`);
122
- const year = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_year`);
123
- if (!day || !month || !year) return "";
124
- return `${day}/${month}/${year}`; // EU format: DD/MM/YYYY
125
- }
126
-
127
- /**
128
- * Helper function to create a field object.
129
- *
130
- * @param {object} formElement The form element object
131
- * @param {string} value The value of the form element
132
- * @param {object} valueLabel The label of the form element
133
- * @returns {object} The field object containing id, label, value, and valueLabel
134
- */
135
- function createFieldObject(formElement, value, valueLabel) {
136
- return {
137
- id: formElement.params?.id || "",
138
- name: formElement.params?.name || "",
139
- label: formElement.params.label
140
- || formElement.params.legend
141
- || govcyResources.getSameMultilingualObject(service.site.languages, formElement.params.name),
142
- value: value,
143
- valueLabel: valueLabel
144
- };
145
- }
146
-
147
- /**
148
- * Helper function to retrieve the value of a form element from the session.
149
- *
150
- * @param {object} formElement The form element object
151
- * @param {string} pageUrl The page URL
152
- * @returns {string} The value of the form element from the session or an empty string if not found
153
- */
154
- function getValue(formElement, pageUrl) {
155
- // handle raw value
156
- let value = ""
157
- if (formElement.element === "dateInput") {
158
- value = getDateInputISO(pageUrl, formElement.params.name);
159
- } else {
160
- value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name);
161
- }
162
- return value;
163
- }
164
-
165
- /**
166
- * Helper function to get the label of a form element based on its value and type.
167
- *
168
- * @param {object} formElement The form element object
169
- * @param {string} value The value of the form element
170
- * @param {string} pageUrl The page URL
171
- * @returns {object} The label of the form element based on the value and element type
172
- */
173
- function getValueLabel(formElement, value, pageUrl) {
174
- //handle checkboxes label
175
- if (formElement.element === "checkboxes") {
176
- if (Array.isArray(value)) {
177
- // loop through each value and find the corresponding item
178
- return value.map(v => {
179
- // find the item
180
- const item = formElement.params.items.find(i => i.value === v);
181
- return item?.text || govcyResources.getSameMultilingualObject(service.site.languages, "");
182
- });
183
- } else if (typeof value === "string") {
184
- const matchedItem = formElement.params.items.find(item => item.value === value);
185
- if (matchedItem) {
186
- return matchedItem.text;
187
- } else {
188
- return govcyResources.getSameMultilingualObject(service.site.languages, "")
189
- }
190
- }
191
- }
192
-
193
- // handle radios and select labels
194
- if (formElement.element === "radios" || formElement.element === "select") {
195
- const item = formElement.params.items.find(i => i.value === value);
196
- return item?.text || govcyResources.getSameMultilingualObject(service.site.languages, "");
197
- }
198
-
199
- // handle dateInput
200
- if (formElement.element === "dateInput") {
201
- const formattedDate = getDateInputDMY(pageUrl, formElement.params.name);
202
- return govcyResources.getSameMultilingualObject(service.site.languages, formattedDate);
203
- }
204
-
205
- // textInput, textArea, etc.
206
- return govcyResources.getSameMultilingualObject(service.site.languages, value);
207
- }
208
-
209
154
  // loop through each page in the service
210
155
  // and extract the form data from the session
211
156
  for (const page of service.pages) {
@@ -228,11 +173,11 @@ export function preparePrintFriendlyData(req, siteId, service) {
228
173
  if (!allowedElements.includes(formElement.element)) continue;
229
174
 
230
175
  // handle raw value
231
- let rawValue = getValue(formElement, page.pageData.url);
176
+ let rawValue = getValue(formElement, page.pageData.url, req, siteId);
232
177
 
233
178
  //create the field object and push it to the fields array
234
179
  // value of the field is handled by getValueLabel function
235
- const field = createFieldObject(formElement, rawValue, getValueLabel(formElement, rawValue, page.pageData.url));
180
+ const field = createFieldObject(formElement, rawValue, getValueLabel(formElement, rawValue, page.pageData.url, req, siteId, service), service);
236
181
  fields.push(field);
237
182
 
238
183
  // Handle conditional elements (only for radios for now)
@@ -246,11 +191,11 @@ export function preparePrintFriendlyData(req, siteId, service) {
246
191
  if (!allowedElements.includes(condEl.element)) continue;
247
192
 
248
193
  // handle raw value
249
- let condValue = getValue(condEl, page.pageData.url);
194
+ let condValue = getValue(condEl, page.pageData.url, req, siteId);
250
195
 
251
196
  //create the field object and push it to the fields array
252
197
  // value of the field is handled by getValueLabel function
253
- const field = createFieldObject(condEl, condValue, getValueLabel(condEl, condValue, page.pageData.url));
198
+ const field = createFieldObject(condEl, condValue, getValueLabel(condEl, condValue, page.pageData.url, req, siteId, service), service);
254
199
  fields.push(field);
255
200
  }
256
201
  }
@@ -272,6 +217,132 @@ export function preparePrintFriendlyData(req, siteId, service) {
272
217
  }
273
218
 
274
219
  //------------------------------- Helper Functions -------------------------------//
220
+ /**
221
+ * Helper function to retrieve date raw input.
222
+ *
223
+ * @param {string} pageUrl The page URL
224
+ * @param {string} name The name of the form element
225
+ * @param {object} req The request object
226
+ * @param {string} siteId The site ID
227
+ * @returns {string} The raw date input in ISO format (YYYY-MM-DD) or an empty string if not found
228
+ */
229
+ function getDateInputISO(pageUrl, name, req, siteId) {
230
+ const day = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_day`);
231
+ const month = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_month`);
232
+ const year = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_year`);
233
+ if (!day || !month || !year) return "";
234
+
235
+ // Pad day and month with leading zero if needed
236
+ const paddedDay = String(day).padStart(2, "0");
237
+ const paddedMonth = String(month).padStart(2, "0");
238
+
239
+ return `${year}-${paddedMonth}-${paddedDay}`; // ISO format: YYYY-MM-DD
240
+ }
241
+
242
+ /**
243
+ * Helper function to retrieve date input in DMY format.
244
+ *
245
+ * @param {string} pageUrl The page URL
246
+ * @param {string} name The name of the form element
247
+ * @param {object} req The request object
248
+ * @param {string} siteId The site ID
249
+ * @returns {string} The raw date input in DMY format (DD/MM/YYYY) or an empty string if not found
250
+ */
251
+ function getDateInputDMY(pageUrl, name, req, siteId) {
252
+ const day = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_day`);
253
+ const month = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_month`);
254
+ const year = dataLayer.getFormDataValue(req.session, siteId, pageUrl, `${name}_year`);
255
+ if (!day || !month || !year) return "";
256
+ return `${day}/${month}/${year}`; // EU format: DD/MM/YYYY
257
+ }
258
+
259
+ /**
260
+ * Helper function to create a field object.
261
+ *
262
+ * @param {object} formElement The form element object
263
+ * @param {string} value The value of the form element
264
+ * @param {object} valueLabel The label of the form element
265
+ * @param {object} service The service object
266
+ * @returns {object} The field object containing id, label, value, and valueLabel
267
+ */
268
+ function createFieldObject(formElement, value, valueLabel, service) {
269
+ return {
270
+ id: formElement.params?.id || "",
271
+ name: formElement.params?.name || "",
272
+ label: formElement.params.label
273
+ || formElement.params.legend
274
+ || govcyResources.getSameMultilingualObject(service.site.languages, formElement.params.name),
275
+ value: value,
276
+ valueLabel: valueLabel
277
+ };
278
+ }
279
+
280
+ /**
281
+ * Helper function to retrieve the value of a form element from the session.
282
+ *
283
+ * @param {object} formElement The form element object
284
+ * @param {string} pageUrl The page URL
285
+ * @param {object} req The request object
286
+ * @param {string} siteId The site ID
287
+ * @returns {string} The value of the form element from the session or an empty string if not found
288
+ */
289
+ function getValue(formElement, pageUrl, req, siteId) {
290
+ // handle raw value
291
+ let value = ""
292
+ if (formElement.element === "dateInput") {
293
+ value = getDateInputISO(pageUrl, formElement.params.name, req, siteId);
294
+ } else {
295
+ value = dataLayer.getFormDataValue(req.session, siteId, pageUrl, formElement.params.name);
296
+ }
297
+ return value;
298
+ }
299
+
300
+ /**
301
+ * Helper function to get the label of a form element based on its value and type.
302
+ *
303
+ * @param {object} formElement The form element object
304
+ * @param {string} value The value of the form element
305
+ * @param {string} pageUrl The page URL
306
+ * @param {object} req The request object
307
+ * @param {string} siteId The site ID
308
+ * @param {object} service The service object
309
+ * @returns {object} The label of the form element based on the value and element type
310
+ */
311
+ function getValueLabel(formElement, value, pageUrl, req, siteId, service) {
312
+ //handle checkboxes label
313
+ if (formElement.element === "checkboxes") {
314
+ if (Array.isArray(value)) {
315
+ // loop through each value and find the corresponding item
316
+ return value.map(v => {
317
+ // find the item
318
+ const item = formElement.params.items.find(i => i.value === v);
319
+ return item?.text || govcyResources.getSameMultilingualObject(service.site.languages, "");
320
+ });
321
+ } else if (typeof value === "string") {
322
+ const matchedItem = formElement.params.items.find(item => item.value === value);
323
+ if (matchedItem) {
324
+ return matchedItem.text;
325
+ } else {
326
+ return govcyResources.getSameMultilingualObject(service.site.languages, "")
327
+ }
328
+ }
329
+ }
330
+
331
+ // handle radios and select labels
332
+ if (formElement.element === "radios" || formElement.element === "select") {
333
+ const item = formElement.params.items.find(i => i.value === value);
334
+ return item?.text || govcyResources.getSameMultilingualObject(service.site.languages, "");
335
+ }
336
+
337
+ // handle dateInput
338
+ if (formElement.element === "dateInput") {
339
+ const formattedDate = getDateInputDMY(pageUrl, formElement.params.name, req, siteId);
340
+ return govcyResources.getSameMultilingualObject(service.site.languages, formattedDate);
341
+ }
342
+
343
+ // textInput, textArea, etc.
344
+ return govcyResources.getSameMultilingualObject(service.site.languages, value);
345
+ }
275
346
 
276
347
  /**
277
348
  * Helper function to get the item value of checkboxes based on the selected value.