@gov-cy/govcy-express-services 0.2.16 β 1.0.0-alpha.1
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 +56 -0
- package/package.json +1 -1
- package/src/index.mjs +4 -3
- package/src/middleware/govcyFormsPostHandler.mjs +7 -1
- package/src/middleware/govcyLoadSubmissionData.mjs +22 -0
- package/src/utils/govcyDataLayer.mjs +49 -1
- package/src/utils/govcyLoadSubmissionDataAPIs.mjs +135 -0
- package/src/utils/govcyTempSave.mjs +70 -0
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ The project is designed to support the [Linear structure](https://gov-cy.github.
|
|
|
32
32
|
- [π€ Site submissions](#-site-submissions)
|
|
33
33
|
- [β
Input validations](#-input-validations)
|
|
34
34
|
- [β
Conditional logic](#-conditional-logic)
|
|
35
|
+
- [πΎ Temporary save](#-temporary-save)
|
|
35
36
|
- [π£οΈ Routes](#%EF%B8%8F-routes)
|
|
36
37
|
- [π¨βπ» Enviromental variables](#-enviromental-variables)
|
|
37
38
|
- [π Security note](#-security-note)
|
|
@@ -55,6 +56,7 @@ The project is designed to support the [Linear structure](https://gov-cy.github.
|
|
|
55
56
|
- Pre-filling posted values (in the same session)
|
|
56
57
|
- Site level API eligibility checks
|
|
57
58
|
- API integration with retry logic for form submissions.
|
|
59
|
+
- Optional temporary save of in-progress form data via configurable API endpoints
|
|
58
60
|
|
|
59
61
|
## π Prerequisites
|
|
60
62
|
- Node.js 20+
|
|
@@ -1412,6 +1414,55 @@ Explanation:
|
|
|
1412
1414
|
- `[].concat(...)`: safely flattens a string or array into an array.
|
|
1413
1415
|
- `.includes('value1')`: checks if the value is selected.
|
|
1414
1416
|
|
|
1417
|
+
### πΎ Temporary save
|
|
1418
|
+
|
|
1419
|
+
The **temporary save** feature allows user progress to be stored in an external API and automatically reloaded on the next visit.
|
|
1420
|
+
This is useful for long forms or cases where users may leave and return later.
|
|
1421
|
+
|
|
1422
|
+
#### 1. Configure the endpoints in your service JSON
|
|
1423
|
+
In your serviceβs `site` object, add both a `submissionGetAPIEndpoint` and `submissionPutAPIEndpoint` entry:
|
|
1424
|
+
|
|
1425
|
+
```json
|
|
1426
|
+
"submissionGetAPIEndpoint": {
|
|
1427
|
+
"url": "TEST_SUBMISSION_GET_API_URL",
|
|
1428
|
+
"method": "GET",
|
|
1429
|
+
"clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
|
|
1430
|
+
"serviceId": "TEST_SUBMISSION_API_SERVICE_ID"
|
|
1431
|
+
},
|
|
1432
|
+
"submissionPutAPIEndpoint": {
|
|
1433
|
+
"url": "TEST_SUBMISSION_PUT_API_URL",
|
|
1434
|
+
"method": "PUT",
|
|
1435
|
+
"clientKey": "TEST_SUBMISSION_API_CLIENT_KEY",
|
|
1436
|
+
"serviceId": "TEST_SUBMISSION_API_SERVICE_ID"
|
|
1437
|
+
}
|
|
1438
|
+
```
|
|
1439
|
+
|
|
1440
|
+
These values should point to environment variables that hold your real endpoint URLs and credentials.
|
|
1441
|
+
|
|
1442
|
+
#### 2. Add environment variables
|
|
1443
|
+
In your `secrets/.env` file (and staging/production configs), define the variables referenced above:
|
|
1444
|
+
|
|
1445
|
+
```dotenv
|
|
1446
|
+
TEST_SUBMISSION_GET_API_URL=https://example.com/api/submissionData
|
|
1447
|
+
TEST_SUBMISSION_PUT_API_URL=https://example.com/api/submissionData
|
|
1448
|
+
TEST_SUBMISSION_API_CLIENT_KEY=12345678901234567890123456789000
|
|
1449
|
+
TEST_SUBMISSION_API_SERVICE_ID=123
|
|
1450
|
+
```
|
|
1451
|
+
|
|
1452
|
+
#### 3. How it works
|
|
1453
|
+
|
|
1454
|
+
- **On first page load** for a site, `govcyLoadSubmissionData` will:
|
|
1455
|
+
1. Call the GET endpoint to retrieve any saved submission.
|
|
1456
|
+
2. If found, populate the sessionβs `inputData` so fields are pre-filled.
|
|
1457
|
+
3. If not found, call the PUT endpoint to create a new temporary record.
|
|
1458
|
+
- **On every form POST**, after successful validation:
|
|
1459
|
+
- The `govcyFormsPostHandler` will fire-and-forget a `PUT` request to update the saved submission with the latest form data.
|
|
1460
|
+
- The payload includes all required submission fields with `submission_data` JSON-stringified.
|
|
1461
|
+
|
|
1462
|
+
#### 4. Backward compatibility
|
|
1463
|
+
If these endpoints are not defined in the service JSON, the temporary save/load logic is skipped entirely.
|
|
1464
|
+
Existing services will continue to work without modification.
|
|
1465
|
+
|
|
1415
1466
|
### π£οΈ Routes
|
|
1416
1467
|
The project uses express.js to serve the following routes:
|
|
1417
1468
|
|
|
@@ -1500,6 +1551,11 @@ TEST_SUBMISSION_API_CLIENT_KEY=12345678901234567890123456789000
|
|
|
1500
1551
|
TEST_SUBMISSION_API_SERVIVE_ID=123
|
|
1501
1552
|
TEST_SUBMISSION_DSF_GTW_KEY=12345678901234567890123456789000
|
|
1502
1553
|
|
|
1554
|
+
# Optional Temporary Save GET and PUT endpoint (test service)
|
|
1555
|
+
TEST_SUBMISSION_GET_API_URL=http://localhost:3002/getTempSubmission
|
|
1556
|
+
TEST_SUBMISSION_PUT_API_URL=http://localhost:3002/save
|
|
1557
|
+
|
|
1558
|
+
|
|
1503
1559
|
# Eligibility checks (optional test APIs)
|
|
1504
1560
|
TEST_ELIGIBILITY_1_API_URL=http://localhost:3002/eligibility1
|
|
1505
1561
|
TEST_ELIGIBILITY_2_API_URL=http://localhost:3002/eligibility2
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gov-cy/govcy-express-services",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
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",
|
package/src/index.mjs
CHANGED
|
@@ -23,6 +23,7 @@ import { serviceConfigDataMiddleware } from './middleware/govcyConfigSiteData.mj
|
|
|
23
23
|
import { govcyManifestHandler } from './middleware/govcyManifestHandler.mjs';
|
|
24
24
|
import { govcyRoutePageHandler } from './middleware/govcyRoutePageHandler.mjs';
|
|
25
25
|
import { govcyServiceEligibilityHandler } from './middleware/govcyServiceEligibilityHandler.mjs';
|
|
26
|
+
import { govcyLoadSubmissionData } from './middleware/govcyLoadSubmissionData.mjs';
|
|
26
27
|
import { isProdOrStaging , getEnvVariable, whatsIsMyEnvironment } from './utils/govcyEnvVariables.mjs';
|
|
27
28
|
import { logger } from "./utils/govcyLogger.mjs";
|
|
28
29
|
|
|
@@ -129,10 +130,10 @@ export default function initializeGovCyExpressService(){
|
|
|
129
130
|
app.get('/:siteId/manifest.json', serviceConfigDataMiddleware, govcyManifestHandler());
|
|
130
131
|
|
|
131
132
|
// π -- ROUTE: Handle route with only siteId (/:siteId or /:siteId/)
|
|
132
|
-
app.get('/:siteId', serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(true),govcyPageHandler(), renderGovcyPage());
|
|
133
|
+
app.get('/:siteId', serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(true),govcyLoadSubmissionData(),govcyPageHandler(), renderGovcyPage());
|
|
133
134
|
|
|
134
135
|
// π -- ROUTE: Add Review Page Route (BEFORE the dynamic route)
|
|
135
|
-
app.get('/:siteId/review',serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(), govcyReviewPageHandler(), renderGovcyPage());
|
|
136
|
+
app.get('/:siteId/review',serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(),govcyLoadSubmissionData(), govcyReviewPageHandler(), renderGovcyPage());
|
|
136
137
|
|
|
137
138
|
// β
π -- ROUTE: Add Success PDF Route (BEFORE the dynamic route)
|
|
138
139
|
app.get('/:siteId/success/pdf',serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(), govcySuccessPageHandler(true), govcyPDFRender());
|
|
@@ -141,7 +142,7 @@ export default function initializeGovCyExpressService(){
|
|
|
141
142
|
app.get('/:siteId/success',serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(), govcySuccessPageHandler(), renderGovcyPage());
|
|
142
143
|
|
|
143
144
|
// π -- ROUTE: Dynamic route to render pages based on siteId and pageUrl, using govcyPageHandler middleware
|
|
144
|
-
app.get('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(true), govcyPageHandler(), renderGovcyPage());
|
|
145
|
+
app.get('/:siteId/:pageUrl', serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(true), govcyLoadSubmissionData(), govcyPageHandler(), renderGovcyPage());
|
|
145
146
|
|
|
146
147
|
// π₯ -- ROUTE: Handle POST requests for review page. The `submit` action
|
|
147
148
|
app.post('/:siteId/review', serviceConfigDataMiddleware, requireAuth, naturalPersonPolicy, govcyServiceEligibilityHandler(), govcyReviewPostHandler());
|
|
@@ -6,6 +6,7 @@ import { logger } from "../utils/govcyLogger.mjs";
|
|
|
6
6
|
import { handleMiddlewareError } from "../utils/govcyUtils.mjs";
|
|
7
7
|
import { getFormData } from "../utils/govcyFormHandling.mjs"
|
|
8
8
|
import { evaluatePageConditions } from "../utils/govcyExpressions.mjs";
|
|
9
|
+
import { tempSaveIfConfigured } from "../utils/govcyTempSave.mjs";
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
/**
|
|
@@ -59,7 +60,12 @@ export function govcyFormsPostHandler() {
|
|
|
59
60
|
|
|
60
61
|
//β€΄οΈ Store validated form data in session
|
|
61
62
|
dataLayer.storePageData(req.session, siteId, pageUrl, formData);
|
|
62
|
-
|
|
63
|
+
|
|
64
|
+
// π Fire-and-forget temporary save (non-blocking)
|
|
65
|
+
(async () => {
|
|
66
|
+
try { await tempSaveIfConfigured(req.session, service, siteId); }
|
|
67
|
+
catch (e) { /* already logged internally */ }
|
|
68
|
+
})();
|
|
63
69
|
|
|
64
70
|
logger.debug("β
Form submitted successfully:", dataLayer.getPageData(req.session, siteId, pageUrl), req);
|
|
65
71
|
logger.info("β
Form submitted successfully:", req.originalUrl);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { logger } from "../utils/govcyLogger.mjs";
|
|
2
|
+
import { govcyLoadSubmissionDataAPIs } from "../utils/govcyLoadSubmissionDataAPIs.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Middleware to load submission data from APIs.
|
|
5
|
+
* This middleware fetches submission data from configured APIs and stores it in the session.
|
|
6
|
+
* @returns {function} Middleware function to load submission data from APIs
|
|
7
|
+
*/
|
|
8
|
+
export function govcyLoadSubmissionData() {
|
|
9
|
+
return async (req, res, next) => {
|
|
10
|
+
try {
|
|
11
|
+
const service = req.serviceData;
|
|
12
|
+
// Extract siteId from request
|
|
13
|
+
const { siteId } = req.params;
|
|
14
|
+
|
|
15
|
+
return await govcyLoadSubmissionDataAPIs(req.session, service, siteId, next);
|
|
16
|
+
|
|
17
|
+
} catch (error) {
|
|
18
|
+
logger.error("Error in govcyLoadSubmissionData middleware:", error.message);
|
|
19
|
+
return next(error); // Pass the error to the next middleware
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
export function initializeSiteData(store, siteId, pageUrl = null) {
|
|
17
17
|
if (!store.siteData) store.siteData = {};
|
|
18
18
|
if (!store.siteData[siteId]) store.siteData[siteId] = {};
|
|
19
|
+
if (!store.siteData[siteId].inputData) store.siteData[siteId].loadData = {};
|
|
19
20
|
if (!store.siteData[siteId].inputData) store.siteData[siteId].inputData = {};
|
|
20
21
|
if (!store.siteData[siteId].submissionData) store.siteData[siteId].submissionData = {};
|
|
21
22
|
|
|
@@ -97,6 +98,33 @@ export function storePageData(store, siteId, pageUrl, formData) {
|
|
|
97
98
|
|
|
98
99
|
store.siteData[siteId].inputData[pageUrl]["formData"] = formData;
|
|
99
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* Stores the page's input data in the data layer
|
|
103
|
+
* *
|
|
104
|
+
* @param {object} store The session store
|
|
105
|
+
* @param {string} siteId The site id
|
|
106
|
+
* @param {object} loadData The form data to be stored
|
|
107
|
+
*/
|
|
108
|
+
export function storeSiteInputData(store, siteId, loadData) {
|
|
109
|
+
// Ensure session structure is initialized
|
|
110
|
+
initializeSiteData(store, siteId);
|
|
111
|
+
|
|
112
|
+
store.siteData[siteId]["inputData"] = loadData;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Stores the page's load data in the data layer
|
|
117
|
+
* *
|
|
118
|
+
* @param {object} store The session store
|
|
119
|
+
* @param {string} siteId The site id
|
|
120
|
+
* @param {object} loadData The form data to be stored
|
|
121
|
+
*/
|
|
122
|
+
export function storeSiteLoadData(store, siteId, loadData) {
|
|
123
|
+
// Ensure session structure is initialized
|
|
124
|
+
initializeSiteData(store, siteId);
|
|
125
|
+
|
|
126
|
+
store.siteData[siteId]["loadData"] = loadData;
|
|
127
|
+
}
|
|
100
128
|
|
|
101
129
|
/**
|
|
102
130
|
* Stores the site validation errors in the data layer
|
|
@@ -156,8 +184,11 @@ export function storeSiteSubmissionData(store, siteId, submissionData) {
|
|
|
156
184
|
// Store the submission data
|
|
157
185
|
store.siteData[siteId].submissionData = submissionData;
|
|
158
186
|
|
|
159
|
-
|
|
187
|
+
// Clear validation errors from the session
|
|
160
188
|
store.siteData[siteId].inputData = {};
|
|
189
|
+
// Clear presaved/temporary save data
|
|
190
|
+
store.siteData[siteId].loadData = {};
|
|
191
|
+
|
|
161
192
|
}
|
|
162
193
|
|
|
163
194
|
|
|
@@ -280,6 +311,23 @@ export function getSiteInputData(store, siteId) {
|
|
|
280
311
|
return null;
|
|
281
312
|
}
|
|
282
313
|
|
|
314
|
+
/**
|
|
315
|
+
* Get the site's load data from the store
|
|
316
|
+
*
|
|
317
|
+
* @param {object} store The session store
|
|
318
|
+
* @param {string} siteId |The site id
|
|
319
|
+
* @returns The site load data or null if none exist.
|
|
320
|
+
*/
|
|
321
|
+
export function getSiteLoadData(store, siteId) {
|
|
322
|
+
const loadData = store?.siteData?.[siteId]?.loadData || {};
|
|
323
|
+
|
|
324
|
+
if (loadData) {
|
|
325
|
+
return loadData;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
|
|
283
331
|
/**
|
|
284
332
|
* Get the site's input data from the store
|
|
285
333
|
*
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { govcyApiRequest } from "./govcyApiRequest.mjs";
|
|
2
|
+
import { logger } from "./govcyLogger.mjs";
|
|
3
|
+
import { getEnvVariable, getEnvVariableBool } from "./govcyEnvVariables.mjs";
|
|
4
|
+
import { handleMiddlewareError } from "./govcyUtils.mjs";
|
|
5
|
+
import * as dataLayer from "./govcyDataLayer.mjs";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Load submission data from configured APIs and store it in the session.
|
|
9
|
+
* @param {object} store The session store
|
|
10
|
+
* @param {object} service The service configuration
|
|
11
|
+
* @param {string} siteId The site id
|
|
12
|
+
* @param {function} next The next middleware function
|
|
13
|
+
*/
|
|
14
|
+
export async function govcyLoadSubmissionDataAPIs(store, service, siteId, next) {
|
|
15
|
+
try {
|
|
16
|
+
|
|
17
|
+
// Get the API endpoints
|
|
18
|
+
const getCfg = service?.site?.submissionGetAPIEndpoint;
|
|
19
|
+
const putCfg = service?.site?.submissionPutAPIEndpoint;
|
|
20
|
+
|
|
21
|
+
//If siteLoadData already exists, skip the API call
|
|
22
|
+
const siteLoadData = dataLayer.getSiteLoadData(store, siteId);
|
|
23
|
+
if (siteLoadData && Object.keys(siteLoadData).length > 0) {
|
|
24
|
+
// Data exists, skip API call
|
|
25
|
+
logger.debug("Load data already exists for site:", siteId);
|
|
26
|
+
return next();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Only continue if both endpoints and required fields are defined
|
|
30
|
+
if (
|
|
31
|
+
getCfg && putCfg &&
|
|
32
|
+
getCfg.clientKey && getCfg.serviceId &&
|
|
33
|
+
putCfg.clientKey && putCfg.serviceId
|
|
34
|
+
){
|
|
35
|
+
const user = dataLayer.getUser(store); // Get the user from the session;
|
|
36
|
+
|
|
37
|
+
// get the API endpoint URL, clientKey, serviceId from the environment variable (handle edge cases)
|
|
38
|
+
const getCfgUrl = getEnvVariable(getCfg?.url || "", false);
|
|
39
|
+
const getCfgClientKey = getEnvVariable(getCfg?.clientKey || "", false);
|
|
40
|
+
const getCfgServiceId = getEnvVariable(getCfg?.serviceId || "", false);
|
|
41
|
+
const getCfgDsfGtwApiKey = getEnvVariable(getCfg?.dsfgtwApiKey || "", "");
|
|
42
|
+
const getCfgParams = getCfg?.params || {};
|
|
43
|
+
const getCfgMethod = (getCfg?.method || "GET").toLowerCase();
|
|
44
|
+
const putCfgUrl = getEnvVariable(putCfg?.url || "", false);
|
|
45
|
+
const putCfgClientKey = getEnvVariable(putCfg?.clientKey || "", false);
|
|
46
|
+
const putCfgServiceId = getEnvVariable(putCfg?.serviceId || "", false);
|
|
47
|
+
const putCfgDsfGtwApiKey = getEnvVariable(putCfg?.dsfgtwApiKey || "", "");
|
|
48
|
+
const putCfgParams = putCfg?.params || {};
|
|
49
|
+
const putCfgMethod = (putCfg?.method || "PUT").toLowerCase();
|
|
50
|
+
|
|
51
|
+
const allowSelfSignedCerts = getEnvVariableBool("ALLOW_SELF_SIGNED_CERTIFICATES",false) ; // Default to false if not set
|
|
52
|
+
|
|
53
|
+
// check necessary values exist
|
|
54
|
+
if (!getCfgUrl || !getCfgClientKey
|
|
55
|
+
|| !putCfgUrl || !putCfgClientKey ) {
|
|
56
|
+
|
|
57
|
+
return handleMiddlewareError(`π¨ Get submission environment variable missing:
|
|
58
|
+
getURl : ${getCfgUrl}, getClientKey : ${getCfgClientKey}
|
|
59
|
+
putURl : ${putCfgUrl}, putClientKey : ${putCfgClientKey}`
|
|
60
|
+
, 500, next)
|
|
61
|
+
}
|
|
62
|
+
// get response form GET submission API
|
|
63
|
+
const getResponse = await govcyApiRequest(
|
|
64
|
+
getCfgMethod,
|
|
65
|
+
getCfgUrl,
|
|
66
|
+
getCfgParams,
|
|
67
|
+
true, // use access token auth
|
|
68
|
+
user,
|
|
69
|
+
{
|
|
70
|
+
accept: "text/plain", // Set Accept header to text/plain
|
|
71
|
+
"client-key": getCfgClientKey, // Set the client key header
|
|
72
|
+
"service-id": getCfgServiceId, // Set the service ID header
|
|
73
|
+
...(getCfgDsfGtwApiKey !== '' && { "dsfgtw-api-key": getCfgDsfGtwApiKey }) // Use the DSF API GTW secret from environment variables
|
|
74
|
+
},
|
|
75
|
+
3,
|
|
76
|
+
allowSelfSignedCerts
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
// If not succeeded, handle error
|
|
80
|
+
if (!getResponse.Succeeded) {
|
|
81
|
+
logger.debug("govcyLoadSubmissionData returned succeeded false",getResponse)
|
|
82
|
+
return handleMiddlewareError(`π¨ govcyLoadSubmissionData returned succeeded false`, 500, next)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// check if getResponse.Data is defined
|
|
86
|
+
if (getResponse.Data) {
|
|
87
|
+
// Store the response in the request for later use
|
|
88
|
+
dataLayer.storeSiteLoadData(store, siteId, getResponse.Data);
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
const parsed = JSON.parse(getResponse.Data.submissionData || "{}");
|
|
92
|
+
if (parsed && typeof parsed === "object") {
|
|
93
|
+
dataLayer.storeSiteInputData(store, siteId, parsed);
|
|
94
|
+
logger.debug(`πΎ Input data restored from saved submission for siteId: ${siteId}`);
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
logger.warn(`β οΈ Failed to parse saved submissionData for siteId: ${siteId}`, err);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// if not call the PUT submission API
|
|
101
|
+
} else {
|
|
102
|
+
// If no data, call the PUT submission API to create it
|
|
103
|
+
const putResponse = await govcyApiRequest(
|
|
104
|
+
putCfgMethod,
|
|
105
|
+
putCfgUrl,
|
|
106
|
+
putCfgParams,
|
|
107
|
+
true, // use access token auth
|
|
108
|
+
user,
|
|
109
|
+
{
|
|
110
|
+
accept: "text/plain", // Set Accept header to text/plain
|
|
111
|
+
"client-key": putCfgClientKey, // Set the client key header
|
|
112
|
+
"service-id": putCfgServiceId, // Set the service ID header
|
|
113
|
+
...(putCfgDsfGtwApiKey !== '' && { "dsfgtw-api-key": putCfgDsfGtwApiKey }) // Use the DSF API GTW secret from environment variables
|
|
114
|
+
},
|
|
115
|
+
3,
|
|
116
|
+
allowSelfSignedCerts
|
|
117
|
+
);
|
|
118
|
+
// If not succeeded, handle error
|
|
119
|
+
if (!putResponse.Succeeded) {
|
|
120
|
+
logger.debug("govcyLoadSubmissionData returned succeeded false",putResponse)
|
|
121
|
+
return handleMiddlewareError(`π¨ govcyLoadSubmissionData returned succeeded false`, 500, next)
|
|
122
|
+
}
|
|
123
|
+
// Store the response in the request for later use
|
|
124
|
+
dataLayer.storeSiteLoadData(store, siteId, putResponse.Data);
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
next();
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.error("Error in govcyLoadSubmissionData middleware:", error.message);
|
|
133
|
+
return next(error); // Pass the error to the next middleware
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// utils/govcyTempSave.mjs
|
|
2
|
+
import { govcyApiRequest } from "./govcyApiRequest.mjs";
|
|
3
|
+
import { getEnvVariable, getEnvVariableBool } from "./govcyEnvVariables.mjs";
|
|
4
|
+
import * as dataLayer from "./govcyDataLayer.mjs";
|
|
5
|
+
import { logger } from "./govcyLogger.mjs";
|
|
6
|
+
/**
|
|
7
|
+
* Temporary save of in-progress form data via configured API endpoints.
|
|
8
|
+
* @param {object} store The session store
|
|
9
|
+
* @param {object} service The service object
|
|
10
|
+
* @param {string} siteId The site id
|
|
11
|
+
*/
|
|
12
|
+
export async function tempSaveIfConfigured(store, service, siteId) {
|
|
13
|
+
// Check if temp save is configured for this service with a PUT endpoint
|
|
14
|
+
const putCfg = service?.site?.submissionPutAPIEndpoint;
|
|
15
|
+
if (!putCfg?.url || !putCfg?.clientKey || !putCfg?.serviceId) return;
|
|
16
|
+
|
|
17
|
+
//Get environment variables
|
|
18
|
+
const allowSelfSignedCerts = getEnvVariableBool("ALLOW_SELF_SIGNED_CERTIFICATES", false);
|
|
19
|
+
const url = getEnvVariable(putCfg.url || "", false);
|
|
20
|
+
const clientKey = getEnvVariable(putCfg.clientKey || "", false);
|
|
21
|
+
const serviceId = getEnvVariable(putCfg.serviceId || "", false);
|
|
22
|
+
const dsfGtwKey = getEnvVariable(putCfg?.dsfgtwApiKey || "", "");
|
|
23
|
+
const method = (putCfg?.method || "PUT").toLowerCase();
|
|
24
|
+
const user = dataLayer.getUser(store);
|
|
25
|
+
|
|
26
|
+
// Prepare minimal temp payload (send whole inputData snapshot)
|
|
27
|
+
const inputData = dataLayer.getSiteInputData(store, siteId) || {};
|
|
28
|
+
const tempPayload = {
|
|
29
|
+
// mirror final submission format: send stringified JSON
|
|
30
|
+
submission_data: JSON.stringify(inputData)
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
if (!url || !clientKey) {
|
|
34
|
+
logger.error("π¨ Temp save API configuration is incomplete:", { url, clientKey })
|
|
35
|
+
return; // don't break UX
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
// Call the API to save temp data
|
|
40
|
+
const resp = await govcyApiRequest(
|
|
41
|
+
method,
|
|
42
|
+
url,
|
|
43
|
+
tempPayload,
|
|
44
|
+
true, // auth with user access token
|
|
45
|
+
user,
|
|
46
|
+
{
|
|
47
|
+
accept: "text/plain",
|
|
48
|
+
"client-key": clientKey,
|
|
49
|
+
"service-id": serviceId,
|
|
50
|
+
...(dsfGtwKey !== "" && { "dsfgtw-api-key": dsfGtwKey }),
|
|
51
|
+
"content-type": "application/json"
|
|
52
|
+
},
|
|
53
|
+
3,
|
|
54
|
+
allowSelfSignedCerts
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (!resp?.Succeeded) {
|
|
58
|
+
logger.warn("Temp save returned Succeeded=false", resp);
|
|
59
|
+
return; // donβt break UX
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
logger.info("β
Temp save successful for site:", siteId, "Response:", resp);
|
|
63
|
+
// Optional: reflect any server state locally (e.g., keep referenceValue in loadData)
|
|
64
|
+
if (resp?.Data) {
|
|
65
|
+
dataLayer.storeSiteLoadData(store, siteId,resp.Data );
|
|
66
|
+
}
|
|
67
|
+
} catch (e) {
|
|
68
|
+
logger.error("Temp save failed (non-blocking):", e?.message);
|
|
69
|
+
}
|
|
70
|
+
}
|