@gov-cy/govcy-express-services 1.6.1 → 1.6.3

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": "1.6.1",
3
+ "version": "1.6.3",
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",
@@ -181,4 +181,16 @@ export function cyLoginPolicy(req, res, next) {
181
181
  next
182
182
  );
183
183
  }
184
- /************************************************************************/
184
+ /************************************************************************/
185
+
186
+ export function getUniqueIdentifier(req) {
187
+ // https://dev.azure.com/cyprus-gov-cds/Documentation/_wiki/wikis/Documentation/82/Available-Scopes-and-Claims
188
+ const user = req?.session?.user || {};
189
+
190
+ const id =
191
+ user.unique_identifier ??
192
+ user.legal_unique_identifier ??
193
+ "";
194
+
195
+ return String(id);
196
+ }
@@ -184,7 +184,7 @@ export function govcyReviewPostHandler() {
184
184
  const submissionData = prepareSubmissionData(req, siteId, service);
185
185
 
186
186
  // Prepare submission data for API
187
- const submissionDataAPI = prepareSubmissionDataAPI(submissionData, service);
187
+ const submissionDataAPI = prepareSubmissionDataAPI(submissionData, service, req);
188
188
 
189
189
  logger.debug("Prepared submission data for API:", submissionDataAPI);
190
190
 
@@ -437,10 +437,13 @@ export const staticResources = {
437
437
  {
438
438
  check: "valid",
439
439
  params: {
440
- checkValue: "noSpecialChars",
440
+ checkValue: "nameCY",
441
441
  message: {
442
- el: "Το όνομα σας πρέπει να αποτελείται μόνο από γράμματα, αριθμούς και ορισμένους άλλους χαρακτήρες",
443
- en: "Your name must consist only of letters, numbers and some other characters",
442
+ // el: "Το όνομά σας μπορεί να περιέχει μόνο γράμματα, κενά, παύλες (-), αποστρόφους (’ ‘ ʼ) και τελείες (.)",
443
+ // en: "Your name can only include letters, spaces, hyphens (-), apostrophes (’ ‘ ʼ) and dots (.)",
444
+ // tr: ""
445
+ el: "Το όνομά σας μπορεί να περιέχει μόνο γράμματα, κενά, παύλες (-), αποστρόφους (’ ‘ ʼ) και τελείες (.)",
446
+ en: "Your name can only include letters, spaces, hyphens (-), curly apostrophes (’ ‘ ʼ) and dots (.)",
444
447
  tr: ""
445
448
  }
446
449
  }
@@ -8,6 +8,7 @@ import { getServiceConfigData, getPageConfigData } from "./govcyLoadConfigData.m
8
8
  import { getMultipleThingsEmptyFormData } from "./govcyFormHandling.mjs";
9
9
  import { logger } from "./govcyLogger.mjs";
10
10
  import { createUmdManualPageTemplate } from "../middleware/govcyUpdateMyDetails.mjs"
11
+ import { getUniqueIdentifier } from "../middleware/cyLoginAuth.mjs"
11
12
  import nunjucks from "nunjucks";
12
13
 
13
14
  /**
@@ -101,11 +102,19 @@ export function prepareSubmissionData(req, siteId, service) {
101
102
  // radios conditional elements
102
103
  if (el.element === "radios") {
103
104
  for (const radioItem of el.params.items || []) {
105
+
106
+ const isSelected = radioItem.value === value; // 🔸 to decide to populate only selected
107
+
104
108
  for (const condEl of radioItem.conditionalElements || []) {
105
109
  if (!ALLOWED_FORM_ELEMENTS.includes(condEl.element)) continue;
106
110
  const condId = condEl.params?.id || condEl.params?.name;
107
111
  if (!condId) continue;
108
- let condValue = getValue(condEl, pageUrl, req, siteId, idx);
112
+
113
+ // 🔸only populate if selected, otherwise empty string
114
+ let condValue = isSelected
115
+ ? getValue(condEl, pageUrl, req, siteId, idx)
116
+ : ""; // 🔸 Empty if conditional radio is not selected
117
+
109
118
  itemData[condId] = condValue;
110
119
 
111
120
  if (condEl.element === "fileInput") {
@@ -156,6 +165,8 @@ export function prepareSubmissionData(req, siteId, service) {
156
165
  const condEls = radioItem.conditionalElements;
157
166
  if (!Array.isArray(condEls)) continue;
158
167
 
168
+ const isSelected = radioItem.value === value; // 🔸 to decide to populate only selected
169
+
159
170
  for (const condElement of condEls) {
160
171
  const condType = condElement.element;
161
172
  if (!ALLOWED_FORM_ELEMENTS.includes(condType)) continue;
@@ -164,7 +175,10 @@ export function prepareSubmissionData(req, siteId, service) {
164
175
  if (!condId) continue;
165
176
 
166
177
  // Again: read from session or fallback to ""
167
- const condValue = getValue(condElement, pageUrl, req, siteId) ?? "";
178
+ // 🔸 only populate if selected; otherwise keep empty string
179
+ const condValue = isSelected
180
+ ? getValue(condElement, pageUrl, req, siteId) ?? ""
181
+ : ""; // 🔸 Empty if conditional radio is not selected
168
182
 
169
183
  // Store even if the field was not visible to user
170
184
  // submissionData[pageUrl].formData[condId] = condValue;
@@ -255,9 +269,10 @@ export function prepareSubmissionData(req, siteId, service) {
255
269
  *
256
270
  * @param {object} data data prepared by `prepareSubmissionData`\
257
271
  * @param {object} service service config data
272
+ * @param {object} req request object
258
273
  * @returns {object} The API-ready submission data object with all fields as strings
259
274
  */
260
- export function prepareSubmissionDataAPI(data, service) {
275
+ export function prepareSubmissionDataAPI(data, service, req) {
261
276
 
262
277
  //deep copy data to avoid mutating the original
263
278
  let dataObj = JSON.parse(JSON.stringify(data));
@@ -277,12 +292,20 @@ export function prepareSubmissionDataAPI(data, service) {
277
292
  let pageEmptyData = getMultipleThingsEmptyFormData(page);
278
293
  //replace the dataObj.submissionData[key] with the empty data
279
294
  dataObj.submissionData[key] = [pageEmptyData];
280
-
295
+
281
296
  } catch (error) {
282
297
  logger.error('Error getting pageConfigData:', key);
283
298
  }
284
299
  }
285
300
  }
301
+ // get the Unique Identifier of the user
302
+ const uniqueIdentifier = getUniqueIdentifier(req);
303
+
304
+ // Insert unique identifier as the first item in the array
305
+ dataObj.submissionData = {
306
+ cylogin_unique_identifier: uniqueIdentifier,
307
+ ...(dataObj.submissionData || {})
308
+ };
286
309
  }
287
310
 
288
311
  return {
@@ -923,7 +946,7 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
923
946
  // Custom page
924
947
  // ==========================================================
925
948
  // 🆕 Check for custom pages that should appear after this one
926
- for (const [customKey, customPage] of customPageEntries) {
949
+ for (const [customKey, customPage] of customPageEntries) {
927
950
  const insertAfter = customPage.insertAfterPageUrl;
928
951
  if (insertAfter && insertAfter === pageUrl && Array.isArray(customPage.email)) {
929
952
  pagesInBody.push(customPage.pageUrl); // Track custom page key for later
@@ -939,7 +962,7 @@ export function generateSubmitEmail(service, submissionData, submissionId, req)
939
962
  body.push(...customPage.email);
940
963
  }
941
964
  }
942
-
965
+
943
966
  // ==========================================================
944
967
  }
945
968
 
@@ -29,7 +29,8 @@ function validateValue(value, rules) {
29
29
  alphaNum: (val) => /^[A-Za-zΑ-Ωα-ω\u0370-\u03ff\u1f00-\u1fff0-9\s]+$/.test(val),
30
30
  noSpecialChars: (val) => /^([0-9]|[A-Z]|[a-z]|[α-ω]|[Α-Ω]|[,]|[.]|[-]|[(]|[)]|[?]|[!]|[;]|[:]|[\n]|[\r]|[ _]|[\u0370-\u03ff\u1f00-\u1fff])+$/.test(val),
31
31
  noSpecialCharsEl: (val) => /^([0-9]|[α-ω]|[Α-Ω]|[,]|[.]|[-]|[(]|[)]|[?]|[!]|[;]|[:]|[\n]|[\r]|[ _]|[\u0370-\u03ff\u1f00-\u1fff])+$/.test(val),
32
- name: (val) => /^[A-Za-zΑ-Ωα-ω\u0370-\u03ff\u1f00-\u1fff\s'-]+$/.test(val),
32
+ name: (val) => /^[\p{L}\p{M}\s’‘ʼ.-]+$/u.test(val),
33
+ nameCY: (val) => /^[A-Za-zΑ-Ωα-ωΆ-Ώά-ώΪΫϊΐϋΰÇĞİIŞÖÜçğışöü\s.’‘ʼ-]*$/u.test(val),
33
34
  tel: (val) => /^(?:\+|00)?[\d\s\-()]{8,20}$/.test(val.replace(/[\s\-()]/g, '')),
34
35
  mobile: (val) => /^(?:\+|00)?[\d\s\-()]{8,20}$/.test(val.replace(/[\s\-()]/g, '')),
35
36
  // telCY: (val) => /^(?:\+|00)?357[-\s]?(2|9)\d{7}$/.test(val.replace(/[\s\-()]/g, '')),