@gov-cy/govcy-express-services 1.0.0-alpha.14 → 1.0.0-alpha.16
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
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|

|
|
4
4
|
[](https://github.com/gov-cy/govcy-express-services/actions/workflows/unit-test.yml)
|
|
5
5
|
[](https://github.com/gov-cy/govcy-express-services/actions/workflows/tag-and-publish-on-version-change.yml)
|
|
6
|
+

|
|
6
7
|
|
|
7
8
|
> ⚠️ **Warning:**
|
|
8
9
|
> This package is **under active development** and is not a finished product. It is intended for testing, acceptance, integration, and browser testing purposes only.
|
|
@@ -12,10 +13,12 @@
|
|
|
12
13
|
> You are responsible for ensuring your own compliance, security, and quality assurance processes.
|
|
13
14
|
|
|
14
15
|
## 📝 Description
|
|
15
|
-
This project is an Express-based project that dynamically renders online service forms using `@gov-cy/govcy-frontend-renderer
|
|
16
|
+
This project is an Express-based project that dynamically renders online service forms using `@gov-cy/govcy-frontend-renderer`, handles data input, validations, renders a review page and submits the data via a submission API. It is designed for developers building government services in Cyprus, enabling them to manage user authentication, form submissions, and OpenID authentication workflows in a timely manner.
|
|
16
17
|
|
|
17
18
|
The project is designed to support the [Linear structure](https://gov-cy.github.io/govcy-design-system-docs/patterns/service_structure/#variant-1---linear-structure) as described in the [Unified Design System](https://gov-cy.github.io/govcy-design-system-docs/).
|
|
18
19
|
|
|
20
|
+
The APIs used for submission, temporary save and file uploads are not part of this project. The project has been designed to work together with the **DSF Submission plarform** and all API calls are based on the **DSF Submission Platform APIs**. This readme file describes the definition of these APIs if you wish to develop your own for your own government back-end solution. For more details about the DSF Submission Platform [contact the DSF team](https://dsf.dmrid.gov.cy/contact/).
|
|
21
|
+
|
|
19
22
|

|
|
20
23
|
|
|
21
24
|
## Table of contents
|
|
@@ -27,15 +30,15 @@ The project is designed to support the [Linear structure](https://gov-cy.github.
|
|
|
27
30
|
- [✅ Best Practices](#-best-practices)
|
|
28
31
|
- [📦 Full installation guide](#-full-installation-guide)
|
|
29
32
|
- [🛠️ Usage](#%EF%B8%8F-usage)
|
|
30
|
-
- [🧩 Dynamic services
|
|
33
|
+
- [🧩 Dynamic services](#-dynamic-services)
|
|
31
34
|
- [🛡️ Site eligibility checks](#%EF%B8%8F-site-eligibility-checks)
|
|
32
35
|
- [📤 Site submissions](#-site-submissions)
|
|
33
36
|
- [✅ Input validations](#-input-validations)
|
|
34
|
-
- [
|
|
37
|
+
- [🔀 Conditional logic](#-conditional-logic)
|
|
35
38
|
- [💾 Temporary save feature](#-temporary-save-feature)
|
|
36
39
|
- [🗃️ Files uploads feature](#%EF%B8%8F-files-uploads-feature)
|
|
37
40
|
- [🛣️ Routes](#%EF%B8%8F-routes)
|
|
38
|
-
- [👨💻
|
|
41
|
+
- [👨💻 Environment variables](#-environment-variables)
|
|
39
42
|
- [🔒 Security note](#-security-note)
|
|
40
43
|
- [❓ Troubleshooting / FAQ](#-troubleshooting--faq)
|
|
41
44
|
- [🙏 Credits](#-credits)
|
|
@@ -45,7 +48,7 @@ The project is designed to support the [Linear structure](https://gov-cy.github.
|
|
|
45
48
|
|
|
46
49
|
## ✨ Features
|
|
47
50
|
- Dynamic form rendering from JSON templates
|
|
48
|
-
- Support for `textInput`, `textArea`, `select`, `radios`, `checkboxes`, `datePicker`, `dateInput`
|
|
51
|
+
- Support for `textInput`, `textArea`, `select`, `radios`, `checkboxes`, `datePicker`, `dateInput`, `fileInput` elements
|
|
49
52
|
- Support for `conditional radios`
|
|
50
53
|
- Dynamic creation of check your answers page
|
|
51
54
|
- OpenID Connect authentication with CY Login
|
|
@@ -58,6 +61,7 @@ The project is designed to support the [Linear structure](https://gov-cy.github.
|
|
|
58
61
|
- Site level API eligibility checks
|
|
59
62
|
- API integration with retry logic for form submissions.
|
|
60
63
|
- Optional temporary save of in-progress form data via configurable API endpoints
|
|
64
|
+
- Optional file uploads via API endpoints
|
|
61
65
|
|
|
62
66
|
## 📋 Prerequisites
|
|
63
67
|
- Node.js 20+
|
|
@@ -138,7 +142,7 @@ The CY Login tokens are used to also connect with the various APIs through [cyCo
|
|
|
138
142
|
|
|
139
143
|
The CY Login settings are configured in the `secrets/.env` file.
|
|
140
144
|
|
|
141
|
-
### 🧩 Dynamic Services
|
|
145
|
+
### 🧩 Dynamic Services
|
|
142
146
|
Services are rendered dynamically using JSON templates stored in the `/data` folder. All the service configuration, pages, routes, and logic is stored in the JSON files. The service will load `data/:siteId.json` to get the form data when a user visits `/:siteId/:pageUrl`. Checkout the [express-service-shema.json](express-service-shema.json) and the example JSON structure of the **[test.json](data/test.json)** file for more details.
|
|
143
147
|
|
|
144
148
|
Here is an example JSON config:
|
|
@@ -749,8 +753,7 @@ Here's an example of a page defined in the JSON file:
|
|
|
749
753
|
"element": "backLink",
|
|
750
754
|
"params": {}
|
|
751
755
|
}
|
|
752
|
-
]
|
|
753
|
-
"params": {}
|
|
756
|
+
]
|
|
754
757
|
},
|
|
755
758
|
{
|
|
756
759
|
"name": "main",
|
|
@@ -1174,7 +1177,7 @@ Content-Type: application/json
|
|
|
1174
1177
|
{
|
|
1175
1178
|
"submission_username": "username",
|
|
1176
1179
|
"submission_email": "email@example.com",
|
|
1177
|
-
"submission_data": "{\"index\":{\"
|
|
1180
|
+
"submission_data": "{\"index\":{\"certificate_select\":[\"birth\",\"permanent_residence\"]}}",
|
|
1178
1181
|
"submission_data_version": "1",
|
|
1179
1182
|
"print_friendly_data": "[{\"pageUrl\":\"index\",\"pageTitle\":{\"el\":\"Επιλογή Εγγάφου\",\"en\":\"Document selection\",\"tr\":\"\"},\"fields\":[{\"id\":\"certificate_select\",\"name\":\"certificate_select\",\"label\":{\"el\":\"Τι έγγραφα επιθυμείτε να εκδώσετε;\",\"en\":\"What documents do you wish to issue?\"},\"value\":[\"birth\",\"permanent_residence\"],\"valueLabel\":[{\"el\":\"Πιστοποιητικό γέννησης\",\"en\":\"Birth certificate\",\"tr\":\"\"},{\"el\":\"Βεβαίωση μόνιμης διαμονής\",\"en\":\"Certificate of permanent residence\",\"tr\":\"\"}]}]}]",
|
|
1180
1183
|
"renderer_data": "{\"element\":\"summaryList\",\"params\":{\"items\":[{\"key\":{\"el\":\"Επιλογή Εγγάφου\",\"en\":\"Document selection\",\"tr\":\"\"},\"value\":[{\"element\":\"summaryList\",\"params\":{\"items\":[{\"key\":{\"el\":\"Τι έγγραφα επιθυμείτε να εκδώσετε;\",\"en\":\"What documents do you wish to issue?\"},\"value\":[{\"element\":\"textElement\",\"params\":{\"text\":{\"en\":\"Birth certificate, Certificate of permanent residence\",\"el\":\"Birth certificate, Certificate of permanent residence\",\"tr\":\"Birth certificate, Certificate of permanent residence\"},\"type\":\"span\"}}]}]}}]}]}}",
|
|
@@ -1260,35 +1263,34 @@ The data is collected from the form elements and the data layer and are sent via
|
|
|
1260
1263
|
"submission_data_version": "0.1", // Submission data version
|
|
1261
1264
|
"submission_data": { // Submission raw data. Object, will be stringified
|
|
1262
1265
|
"index": { // Page level
|
|
1263
|
-
"
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1266
|
+
"id_select": ["id", "arc"], // field level. Could be string or array
|
|
1267
|
+
"id_number": "654654",
|
|
1268
|
+
"arc_number": "",
|
|
1269
|
+
"aka": "232323",
|
|
1270
|
+
"evidenceAttachment": // File attachments contains an object with `fileId` and `sha256`
|
|
1271
|
+
{
|
|
1272
|
+
"fileId": "1234567891234567890",
|
|
1273
|
+
"sha256": "123456789012345678901234567890123456789012345678901234567890123456"
|
|
1274
|
+
}
|
|
1269
1275
|
},
|
|
1270
1276
|
"appointment": {
|
|
1271
|
-
"
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
"fileno_orismenou": ""
|
|
1281
|
-
}
|
|
1277
|
+
"diorismos": "monimos",
|
|
1278
|
+
"fileno_monimos": "3233",
|
|
1279
|
+
"eidikotita_monimos": "1",
|
|
1280
|
+
"fileno_sumvasiouxos": "",
|
|
1281
|
+
"eidikotita_sumvasiouxos": "",
|
|
1282
|
+
"fileno_aoristou": "",
|
|
1283
|
+
"eidikotita_aoristou": "",
|
|
1284
|
+
"program": "",
|
|
1285
|
+
"fileno_orismenou": ""
|
|
1282
1286
|
},
|
|
1283
1287
|
"takeover": {
|
|
1284
|
-
"
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
"reason": "24324dssf"
|
|
1291
|
-
}
|
|
1288
|
+
"date_start_day": "11",
|
|
1289
|
+
"date_start_month": "12",
|
|
1290
|
+
"date_start_year": "2020",
|
|
1291
|
+
"date_on_contract": "date_other",
|
|
1292
|
+
"date_contract": "16/04/2025",
|
|
1293
|
+
"reason": "24324dssf"
|
|
1292
1294
|
}
|
|
1293
1295
|
},
|
|
1294
1296
|
"submission_data_version": "1", // Submission data version
|
|
@@ -1687,6 +1689,7 @@ The project includes input validation for the following elements:
|
|
|
1687
1689
|
- `checkboxes`
|
|
1688
1690
|
- `datePicker`
|
|
1689
1691
|
- `dateInput`
|
|
1692
|
+
- `fileInput` (only required check)
|
|
1690
1693
|
|
|
1691
1694
|
The validation rules for each element are defined in the `"validations` array for each element. The project support the following validations:
|
|
1692
1695
|
|
|
@@ -1742,7 +1745,7 @@ Example:
|
|
|
1742
1745
|
]
|
|
1743
1746
|
```
|
|
1744
1747
|
|
|
1745
|
-
###
|
|
1748
|
+
### 🔀 Conditional logic
|
|
1746
1749
|
|
|
1747
1750
|
The project supports conditional logic on pages. Conditional logic is evaluated using a custom `govcyExpressions.mjs` module, which executes expressions in a safe and scoped context using `new Function`. Only safe data access through the `dataLayer` is allowed. The system uses expressions and session data from the service's [data layer](NOTES.md#data-layer) to decide if a page will be shown or not.
|
|
1748
1751
|
|
|
@@ -2285,7 +2288,13 @@ Here is a sample code section of a page definition with a file input field:
|
|
|
2285
2288
|
- **On a form page with an upload input field**:
|
|
2286
2289
|
- **On first load**: the page displays the fileInput field to choose a file.
|
|
2287
2290
|
- **On choosing a file**: the file is uploaded to the `fileUploadAPIEndpoint` endpoint.
|
|
2288
|
-
- The `fileUploadAPIEndpoint` validates the input for allowed file types and file size. On validation error an error message is displayed.
|
|
2291
|
+
- The `fileUploadAPIEndpoint` validates the input for allowed file types and file size. On validation error an error message is displayed. The following validations are performed:
|
|
2292
|
+
- The fileInput element is defined in the JSON config
|
|
2293
|
+
- API endpoints and environment variables are defined
|
|
2294
|
+
- The page is not skiped because of conditional logic
|
|
2295
|
+
- The file is not empty
|
|
2296
|
+
- The file size must be less than 5MB
|
|
2297
|
+
- The file type must be one of the following: pdf, jpg, jpeg, png
|
|
2289
2298
|
- If `fileUploadAPIEndpoint` returns an success, the file is uploaded temporarilly and the `fileView` element is displayed, with links to `View` or `Delete` the file. The file infomation are store in the data layer of the page.
|
|
2290
2299
|
- **On a form page after upload**:
|
|
2291
2300
|
- The `fileView` element is displayed with links to `View` or `Delete` the file.
|
|
@@ -2300,7 +2309,7 @@ Here is a sample code section of a page definition with a file input field:
|
|
|
2300
2309
|
|
|
2301
2310
|
|
|
2302
2311
|
#### `fileUploadAPIEndpoint` `POST` API Request and Response
|
|
2303
|
-
This API is used to temporarily store the file uploaded by the user.
|
|
2312
|
+
This API is used to temporarily store the file uploaded by the user. The API connects the file with a temporary saved submission, for the specific user and service. It does not create a connection with the actual field on the specific page, that is done by the `submissionPutAPIEndpoint` API.
|
|
2304
2313
|
|
|
2305
2314
|
**Request:**
|
|
2306
2315
|
|
|
@@ -2457,7 +2466,7 @@ To help back-end systems recognize the field as a file, the field's element name
|
|
|
2457
2466
|
```
|
|
2458
2467
|
|
|
2459
2468
|
#### File uploads backward compatibility
|
|
2460
|
-
If these endpoints are not defined in the service JSON, the
|
|
2469
|
+
If these endpoints are not defined in the service JSON, the file upload logic is skipped entirely.
|
|
2461
2470
|
Existing services will continue to work without modification.
|
|
2462
2471
|
|
|
2463
2472
|
### 🛣️ Routes
|
|
@@ -2479,7 +2488,7 @@ The project uses express.js to serve the following routes:
|
|
|
2479
2488
|
#### API routes:
|
|
2480
2489
|
- **`/apis/:siteId/:pageUrl/upload`**: Uploads a file. Used from the client side JS.
|
|
2481
2490
|
|
|
2482
|
-
### 👨💻
|
|
2491
|
+
### 👨💻 Environment variables
|
|
2483
2492
|
The environment variables are defined in:
|
|
2484
2493
|
- **Secret environment variables**: These are secret variables and MUSR NOT be saved in version control. The are saved locally in the `secrets/.env` file and they control the server configuration, authentication, integrations, and development behavior. These variables vary depending on the environment and are defined through the deployment prosses for `staging` and `production`.
|
|
2485
2494
|
- **Non secret environment variables**: These are non secret enviromentat variables and can be saved in version control. These are stored in the root folder of the project:
|
|
@@ -2550,10 +2559,13 @@ TEST_SUBMISSION_API_CLIENT_KEY=12345678901234567890123456789000
|
|
|
2550
2559
|
TEST_SUBMISSION_API_SERVIVE_ID=123
|
|
2551
2560
|
TEST_SUBMISSION_DSF_GTW_KEY=12345678901234567890123456789000
|
|
2552
2561
|
|
|
2553
|
-
# Optional Temporary Save GET and PUT
|
|
2562
|
+
# Optional Temporary Save GET and PUT endpoints (test service)
|
|
2554
2563
|
TEST_SUBMISSION_GET_API_URL=http://localhost:3002/getTempSubmission
|
|
2555
2564
|
TEST_SUBMISSION_PUT_API_URL=http://localhost:3002/save
|
|
2556
2565
|
|
|
2566
|
+
# Optional File Upload and download endpoints (test service)
|
|
2567
|
+
TEST_FILE_UPLOAD_API_URL=http://localhost:3002/fileUpload
|
|
2568
|
+
TEST_FILE_DOWNLOAD_API_URL=http://localhost:3002/fileDownload
|
|
2557
2569
|
|
|
2558
2570
|
# Eligibility checks (optional test APIs)
|
|
2559
2571
|
TEST_ELIGIBILITY_1_API_URL=http://localhost:3002/eligibility1
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gov-cy/govcy-express-services",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.16",
|
|
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",
|
|
@@ -45,8 +45,9 @@
|
|
|
45
45
|
"test:package": "mocha --recursive tests/package/**/*.test.mjs",
|
|
46
46
|
"test:functional": "mocha --timeout 30000 --recursive tests/functional/**/*.test.mjs",
|
|
47
47
|
"test:watch": "mocha --watch --timeout 60000 tests/**/*.test.mjs",
|
|
48
|
-
"coverage": "c8 --reporter=html --reporter=text --reporter=lcov
|
|
49
|
-
"coverage:report": "c8 report"
|
|
48
|
+
"coverage": "c8 --reporter=html --reporter=text --reporter=lcov --reporter=json-summary npm test",
|
|
49
|
+
"coverage:report": "c8 report",
|
|
50
|
+
"coverage:badge": "coverage-badges --output ./coverage-badges.svg"
|
|
50
51
|
},
|
|
51
52
|
"dependencies": {
|
|
52
53
|
"@gov-cy/dsf-email-templates": "^2.1.0",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"c8": "^10.1.3",
|
|
66
67
|
"chai": "^5.2.0",
|
|
67
68
|
"chai-http": "^5.1.1",
|
|
69
|
+
"coverage-badges-cli": "^2.2.0",
|
|
68
70
|
"mocha": "^11.1.0",
|
|
69
71
|
"mochawesome": "^7.1.3",
|
|
70
72
|
"nodemon": "^3.0.2",
|
package/src/auth/cyLoginAuth.mjs
CHANGED
|
@@ -2,7 +2,7 @@ import * as client from 'openid-client';
|
|
|
2
2
|
import { getEnvVariable } from '../utils/govcyEnvVariables.mjs';
|
|
3
3
|
import { logger } from "../utils/govcyLogger.mjs";
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/* c8 ignore start */
|
|
6
6
|
// OpenID Configuration
|
|
7
7
|
const issuerUrl = getEnvVariable('CYLOGIN_ISSUER_URL');
|
|
8
8
|
const clientId = getEnvVariable('CYLOGIN_CLIENT_ID');
|
|
@@ -131,3 +131,4 @@ export function getLogoutUrl(id_token_hint = '') {
|
|
|
131
131
|
|
|
132
132
|
// Export config if needed elsewhere
|
|
133
133
|
export { config };
|
|
134
|
+
/* c8 ignore end */
|
|
@@ -10,6 +10,7 @@ import { handleMiddlewareError } from "../utils/govcyUtils.mjs";
|
|
|
10
10
|
import { errorResponse } from "../utils/govcyApiResponse.mjs";
|
|
11
11
|
import { isApiRequest } from '../utils/govcyApiDetection.mjs';
|
|
12
12
|
|
|
13
|
+
/* c8 ignore start */
|
|
13
14
|
/**
|
|
14
15
|
* Middleware to check if the user is authenticated. If not, redirect to the login page.
|
|
15
16
|
*
|
|
@@ -136,4 +137,5 @@ export function handleLogout() {
|
|
|
136
137
|
res.redirect(logoutUrl);
|
|
137
138
|
});
|
|
138
139
|
};
|
|
139
|
-
}
|
|
140
|
+
}
|
|
141
|
+
/* c8 ignore end */
|
|
@@ -6,6 +6,7 @@ import { logger } from "../utils/govcyLogger.mjs";
|
|
|
6
6
|
* Middleware function to render PDFs using the GovCy Frontend Renderer.
|
|
7
7
|
* This function takes the processed page data and template, and generates the final PDF response.
|
|
8
8
|
*/
|
|
9
|
+
/* c8 ignore start */
|
|
9
10
|
export function govcyPDFRender() {
|
|
10
11
|
return async (req, res) => {
|
|
11
12
|
try {
|
|
@@ -29,4 +30,5 @@ export function govcyPDFRender() {
|
|
|
29
30
|
res.status(500).send('Unable to generate PDF at this time.');
|
|
30
31
|
}
|
|
31
32
|
};
|
|
32
|
-
}
|
|
33
|
+
}
|
|
34
|
+
/* c8 ignore end */
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
export const ALLOWED_FORM_ELEMENTS = ["textInput", "textArea", "select", "radios", "checkboxes", "datePicker", "dateInput","fileInput","fileView"];
|
|
5
5
|
export const ALLOWED_FILE_MIME_TYPES = ['application/pdf', 'image/jpeg', 'image/png'];
|
|
6
6
|
export const ALLOWED_FILE_EXTENSIONS = ['pdf', 'jpg', 'jpeg', 'png'];
|
|
7
|
-
export const ALLOWED_FILE_SIZE_MB =
|
|
7
|
+
export const ALLOWED_FILE_SIZE_MB = 4; // Maximum file size in MB
|
|
8
8
|
export const ALLOWED_MULTER_FILE_SIZE_MB = 10; // Maximum file size in MB
|