Skip to content

Commit fd72131

Browse files
FORMS-2662: Add CHEFS web component (chefs-form-viewer) (bcgov#1768)
* FORMS-2662: Add CHEFS web component (chefs-form-viewer) - Add web component for loading CHEFS forms outside of the hosted environment - Add embedding web component to simplify external hosting - Add routes/paths to isolate webcomponent API from CHEFS API - Comprehensive unit testing - Documentation and demo html files to load chefs-form-viewer * config json updates - NO SECRETS! Signed-off-by: Jason Sherman <[email protected]> * BC Gov Address components plus Advanced Address component (generic) Signed-off-by: Jason Sherman <[email protected]> * simplify cors. chefs-form-viewer-generator to use base url. Signed-off-by: Jason Sherman <[email protected]> * Clean up documentation. Signed-off-by: Jason Sherman <[email protected]> * lint fix Signed-off-by: Jason Sherman <[email protected]> * Sonarqube issues Signed-off-by: Jason Sherman <[email protected]> * Sonarqube issues Signed-off-by: Jason Sherman <[email protected]> * some devcontainer startup checks, was timing out. Signed-off-by: Jason Sherman <[email protected]> * Pulling in changes that we "lost"/overwritten with the Git Guardian debacle. Number component rounding Action/Floating Bar for designer. Signed-off-by: Jason Sherman <[email protected]> * sonarqube issues Signed-off-by: Jason Sherman <[email protected]> * updates to component webpack (basically revert) Signed-off-by: Jason Sherman <[email protected]> * point readme at techdocs Signed-off-by: Jason Sherman <[email protected]> * make for viewer auto reload after submission success by default. Signed-off-by: Jason Sherman <[email protected]> * runtime-auth security middleware. Signed-off-by: Jason Sherman <[email protected]> --------- Signed-off-by: Jason Sherman <[email protected]>
1 parent 251d559 commit fd72131

File tree

135 files changed

+21899
-1046
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+21899
-1046
lines changed

.devcontainer/chefs_local/local.sample.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,5 +112,13 @@
112112
"matchPrecision": "occupant, unit, site, civic_number, intersection, block, street, locality, province",
113113
"precisionPoints": 100
114114
}
115+
},
116+
"webcomponents": {
117+
"assets": {
118+
"roots": "/workspaces/common-hosted-form-service/app/webcomponents/v1/assets"
119+
}
120+
},
121+
"gateway": {
122+
"jwtLifetime": "30m"
115123
}
116124
}

.devcontainer/chefs_local/test.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,5 +111,13 @@
111111
"matchPrecision": "occupant, unit, site, civic_number, intersection, block, street, locality, province",
112112
"precisionPoints": 100
113113
}
114+
},
115+
"webcomponents": {
116+
"assets": {
117+
"roots": "/workspaces/common-hosted-form-service/app/webcomponents/v1/assets"
118+
}
119+
},
120+
"gateway": {
121+
"jwtLifetime": "30m"
114122
}
115123
}

.devcontainer/post-install.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ cd ${WORKSPACE_DIR}/app/frontend
7878
npm run build:formio
7979
npm run deploy:formio
8080

81+
# make an initial build of webcomponents
82+
# need to build the frontend first to get the formio and font awesome assets
83+
cd ${WORKSPACE_DIR}/app/frontend
84+
npm run build
85+
# then build the webcomponents
86+
cd ${WORKSPACE_DIR}/app
87+
npm run webcomponents:build
88+
89+
8190
# copy over the sample files to the image...
8291
if [ ! -f "${CHEFS_LOCAL_DIR}/local.json" ]; then
8392
cp ${CHEFS_LOCAL_DIR}/local.sample.json ${CHEFS_LOCAL_DIR}/local.json
@@ -95,6 +104,7 @@ echo "⏳ Waiting for PostgreSQL to be ready..."
95104
max_attempts=30
96105
attempt=1
97106
while [ $attempt -le $max_attempts ]; do
107+
# Check if PostgreSQL is accepting connections by testing the port
98108
if docker compose -f ${CHEFS_LOCAL_DIR}/docker-compose.yml exec -T postgres pg_isready -U app -d chefs > /dev/null 2>&1; then
99109
echo "✅ PostgreSQL is ready!"
100110
break
@@ -106,6 +116,10 @@ done
106116

107117
if [ $attempt -gt $max_attempts ]; then
108118
echo "❌ PostgreSQL failed to start within expected time"
119+
echo "📋 PostgreSQL container status:"
120+
docker compose -f ${CHEFS_LOCAL_DIR}/docker-compose.yml ps postgres
121+
echo "📋 PostgreSQL logs:"
122+
docker compose -f ${CHEFS_LOCAL_DIR}/docker-compose.yml logs postgres
109123
exit 1
110124
fi
111125

.devcontainer/webcomponents.sh

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#!/bin/bash
2+
3+
# Define explicit paths - handle running from .devcontainer directory
4+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+
WORKSPACE_ROOT="${WORKSPACE_ROOT:-$(dirname "${SCRIPT_DIR}")}"
6+
7+
echo "📍 Script directory: ${SCRIPT_DIR}"
8+
echo "📍 Workspace root: ${WORKSPACE_ROOT}"
9+
10+
# make an initial build of formio components and ready them for frontend
11+
cd ${WORKSPACE_ROOT}/app/frontend
12+
npm run build:formio
13+
npm run deploy:formio
14+
15+
# make an initial build of webcomponents
16+
# need to build the frontend first to get the formio and font awesome assets
17+
cd ${WORKSPACE_ROOT}/app/frontend
18+
npm run build
19+
# then build the webcomponents
20+
cd ${WORKSPACE_ROOT}/app
21+
npm run webcomponents:build
22+
23+

.dockerignore

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,13 @@ Thumbs.db
107107
tests/functional/cypress/videos/
108108
tests/functional/cypress/screenshots/
109109
tests/functional/cypress/downloads/
110+
111+
# Do not send demo or minified viewer to build context
112+
# app/frontend/public/embed/chefs-form-viewer-demo.html
113+
app/frontend/public/embed/chefs-form-viewer.min.js
114+
app/frontend/public/embed/chefs-form-viewer.min.js.map
115+
app/frontend/public/embed/chefs-form-viewer-embed.min.js
116+
app/frontend/public/embed/chefs-form-viewer-embed.min.js.map
117+
app/frontend/public/embed/chefs-index.css
118+
app/frontend/public/embed/chefs-theme.css
119+
app/webcomponents/*

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,14 @@ node_modules
1616
/package-lock.json
1717

1818

19-
19+
# Embed viewer build artifacts
20+
app/frontend/public/embed/chefs-form-viewer.min.js
21+
app/frontend/public/embed/chefs-form-viewer.min.js.map
22+
app/frontend/public/embed/chefs-form-viewer-embed.min.js
23+
app/frontend/public/embed/chefs-form-viewer-embed.min.js.map
24+
app/frontend/public/embed/chefs-index.css
25+
app/frontend/public/embed/chefs-theme.css
26+
app/webcomponents/*
2027

2128
# local env files
2229
local.*
@@ -32,6 +39,7 @@ yarn-error.log*
3239

3340
# Editor directories and files
3441
.idea
42+
.cursor
3543
*.iml
3644
*.suo
3745
*.ntvs*

.vscode/launch.json

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"name": "CHEFS API",
2626
"outputCapture": "std",
2727
"request": "launch",
28-
"runtimeArgs": ["run", "serve"],
28+
"runtimeArgs": ["run", "serve", "--trace-warnings"],
2929
"runtimeExecutable": "npm",
3030
"type": "node",
3131
"env": {
@@ -35,7 +35,7 @@
3535
{
3636
"cwd": "${workspaceFolder}/app/frontend",
3737
"env": {
38-
"VITE_CHEFS_GEO_ADDRESS_APIURL": "http://localhost:8080/app/api/v1/bcgeoaddress/advance/address",
38+
"VITE_CHEFS_GEO_ADDRESS_APIURL": "../api/v1/bcgeoaddress/advance/address",
3939
"VITE_CHEFSTOURURL": "https://www.youtube.com/embed/obOhyYusMjM",
4040
"VITE_CONTACT": "[email protected]",
4141
"VITE_FRONTEND_BASEPATH": "/app",
@@ -77,6 +77,23 @@
7777
"program": "${workspaceFolder}/app/node_modules/jest/bin/jest"
7878
}
7979
},
80+
{
81+
"type": "node",
82+
"request": "launch",
83+
"name": "Jest: current file (no coverage)",
84+
"env": { "NODE_ENV": "test" },
85+
"program": "${workspaceFolder}/app/node_modules/.bin/jest",
86+
"args": [
87+
"${file}",
88+
"--config",
89+
"${workspaceFolder}/app/jest.config.js",
90+
"--coverage=false"
91+
],
92+
"console": "integratedTerminal",
93+
"windows": {
94+
"program": "${workspaceFolder}/app/node_modules/jest/bin/jest"
95+
}
96+
},
8097
{
8198
"type": "node",
8299
"request": "launch",

.vscode/tasks.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,27 @@
138138
"panel": "shared"
139139
},
140140
"problemMatcher": []
141+
},
142+
{
143+
"label": "Refresh Webcomponents",
144+
"type": "shell",
145+
"command": "${workspaceFolder}/.devcontainer/webcomponents.sh",
146+
"options": {
147+
"cwd": "${workspaceFolder}",
148+
"env": {
149+
"WORKSPACE_ROOT": "${workspaceFolder}"
150+
}
151+
},
152+
"group": "build",
153+
"presentation": {
154+
"echo": true,
155+
"reveal": "always",
156+
"focus": false,
157+
"panel": "shared",
158+
"showReuseMessage": true,
159+
"clear": false
160+
},
161+
"problemMatcher": []
141162
}
142163
],
143164
"version": "2.0.0"

Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ COPY . /opt/app-root/src
99

1010
RUN npm run all:ci \
1111
&& npm run all:build \
12+
&& cd ../components && npm run build \
13+
&& cd ../app \
14+
&& npm run webcomponents:build \
1215
&& npm run frontend:purge \
13-
&& npm run components:clean \
1416
&& npm run components:purge \
1517
&& mkdir /.npm \
1618
&& chgrp -R 0 /.npm \
1719
&& chmod -R g=u /.npm
1820

1921
EXPOSE 8000
2022

21-
CMD ["npm", "run", "start"]
23+
CMD ["npm", "run", "start"]

app/app.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,18 @@
11
const compression = require('compression');
22
const config = require('config');
33
const express = require('express');
4-
const path = require('path');
4+
const path = require('node:path');
55
const Problem = require('api-problem');
6-
const querystring = require('querystring');
6+
const querystring = require('node:querystring');
7+
const clsRtracer = require('cls-rtracer');
78

89
const log = require('./src/components/log')(module.filename);
910
const httpLogger = require('./src/components/log').httpLogger;
1011
const middleware = require('./src/forms/common/middleware');
1112
const rateLimiter = require('./src/forms/common/middleware').apiKeyRateLimiter;
1213
const v1Router = require('./src/routes/v1');
14+
const webcomponentRouter = require('./src/webcomponents');
15+
const gatewayRouter = require('./src/gateway');
1316

1417
const DataConnection = require('./src/db/dataConnection');
1518
const dataConnection = new DataConnection();
@@ -37,6 +40,11 @@ app.set('trust proxy', 1);
3740

3841
app.set('x-powered-by', false);
3942

43+
// Add correlation ID middleware early in the chain
44+
// useHeader: true - Use existing X-Request-ID header if present, otherwise generate one
45+
// echoHeader: true - Add the request ID to response headers
46+
app.use(clsRtracer.expressMiddleware({ enableRequestId: true, useHeader: true, echoHeader: true }));
47+
4048
// Skip if running tests
4149
if (process.env.NODE_ENV !== 'test') {
4250
// Initialize connections and exit if unsuccessful
@@ -54,12 +62,12 @@ app.use((req, res, next) => {
5462
return next();
5563
}
5664
const status = statusService.getStatus();
57-
if (status.stopped) {
65+
if (status.ready) {
66+
next();
67+
} else if (status.stopped) {
5868
new Problem(503, { details: { message: 'Server is shutting down', ...status } }).send(res);
59-
} else if (!status.ready) {
60-
new Problem(503, { details: { message: 'Server is not ready', ...status } }).send(res);
6169
} else {
62-
next();
70+
new Problem(503, { details: { message: 'Server is not ready', ...status } }).send(res);
6371
}
6472
});
6573

@@ -99,6 +107,13 @@ apiRouter.get('/api', (_req, res) => {
99107
// Host API endpoints
100108
apiRouter.use(config.get('server.apiPath'), v1Router);
101109
app.use(config.get('server.basePath'), apiRouter);
110+
111+
// Host web component endpoints (separate from API) and apply rate limiting
112+
app.use(`${config.get('server.basePath')}/webcomponents`, rateLimiter, webcomponentRouter);
113+
114+
// Host gateway endpoints (separate from API) and apply rate limiting
115+
app.use(`${config.get('server.basePath')}/gateway`, rateLimiter, gatewayRouter);
116+
102117
app.use(middleware.errorHandler);
103118

104119
// Host the static frontend assets

0 commit comments

Comments
 (0)