From 56872e083c9fccdb356c140fe24b192ffadfbd90 Mon Sep 17 00:00:00 2001 From: Viswam Date: Thu, 24 Jul 2025 15:12:06 -0700 Subject: [PATCH 01/40] Changes for Submit --- portal/generatePortalIntegratedHandler.js | 6 ++- portal/savePortalFormDataHandler.js | 65 +++++++++++++++++++++++ routes.js | 2 + 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 portal/savePortalFormDataHandler.js diff --git a/portal/generatePortalIntegratedHandler.js b/portal/generatePortalIntegratedHandler.js index 55b5658..210be3f 100644 --- a/portal/generatePortalIntegratedHandler.js +++ b/portal/generatePortalIntegratedHandler.js @@ -24,7 +24,9 @@ async function generatePortalIntegratedTemplate(req, res) { if(!targetApp) { return res.status(400).send({ error: 'Unknown app ID' }); } - const paramsFromPortal = await getParametersFromPortal(targetApp,token,userId); + const paramsFromPortal = await getParametersFromPortal(targetApp,token,userId); + console.log("paramsFromPortal",paramsFromPortal); + if(!paramsFromPortal) { return res.status(400).send({ error: 'Parameters not found to generate form' }); @@ -71,4 +73,6 @@ async function constructFormJson(params) { return fullJSON; } + + module.exports = generatePortalIntegratedTemplate; \ No newline at end of file diff --git a/portal/savePortalFormDataHandler.js b/portal/savePortalFormDataHandler.js new file mode 100644 index 0000000..dee7b05 --- /dev/null +++ b/portal/savePortalFormDataHandler.js @@ -0,0 +1,65 @@ +async function submitForPortalAction (req,res) { + console.log("in here submitForPortalAction"); + + const { tokenId, savedForm ,config} = req.body; + console.log("tokenId",tokenId); + console.log("savedForm",savedForm); + console.log("config",config); +try{ + if (!config?.actions || !Array.isArray(config.actions)) { + return res + .status(400) + .send({ error: getErrorMessage("FORM_ID_REQUIRED") }); + } + for (const action of config.actions) { + if (action.actionType === 'endpoint') { + await handleEndpointAction(tokenId,action, savedForm); + } else if (action.actionType === 'email') { + await handleEmailAction(action, formData); + } else { + console.warn('Unexpected action type:', action.type); + return res + .status(400) + .send({ error: "Unexpected action type" }); + } + } + + res.json({ status: 'success' }); +} catch(error) { + return res + .status(400) + .send({ error: "Error in executing action type" }); +} +} +async function handleEndpointAction(tokenId,action, formData) { + console.log("action",action); + try { + const url = `${action.host}${action.path}`; + const headers = Object.fromEntries(action.headers.map(h => Object.entries(h)[0])); + + + const savedJson = { + "token": tokenId, + "formJson": formData + }; + const actionBody = Object.fromEntries( + (action.body || []).map(b => Object.entries(b)[0]) + ); + const payload = { ...savedJson, ...actionBody }; + const response = await fetch(url, { + method: action.type, + headers: { ...headers, Authorization: `Bearer ${action.authentication}`, 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + if (!response.ok) { + const text = await response.text(); + console.log("Error",text) + throw new Error(`Endpoint error: ${response.status} ${text}`); + } +} catch(error) { + console.error('Error in handleEndPoint:', error); + throw error; +} +} +module.exports = submitForPortalAction; \ No newline at end of file diff --git a/routes.js b/routes.js index a98c753..86bf927 100644 --- a/routes.js +++ b/routes.js @@ -9,6 +9,7 @@ const renderRouter = require("./renderHandler"); const {generatePDFFromHTML,generatePDFFromURL,generatePDFFromJSON,loadSavedJson } = require("./generatePDFHandler"); const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegratedHandler.js"); +const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const getFormsFromFormTemplate = require("./formRepoHandler"); @@ -145,5 +146,6 @@ router.post("/loadSavedJson", loadSavedJson); router.post("/generatePortalForm", generatePortalIntegratedTemplate); router.post("/generateNewTemplate", generateNewTemplate); router.use('/pdfRender', renderRouter); +router.use('/submitForPortalAction', submitForPortalAction); module.exports = router; \ No newline at end of file From 55dcf15ddbfa2eed8997958c0e52090eba3f33fd Mon Sep 17 00:00:00 2001 From: Viswam Date: Fri, 25 Jul 2025 15:51:25 -0700 Subject: [PATCH 02/40] Changes for Submit action 2799 --- errorHandling/errorMessages.json | 21 +++- portal/generatePortalIntegratedHandler.js | 24 ++--- portal/loadPortalDataHandler.js | 24 ++--- portal/savePortalFormDataHandler.js | 117 +++++++++++----------- 4 files changed, 99 insertions(+), 87 deletions(-) diff --git a/errorHandling/errorMessages.json b/errorHandling/errorMessages.json index 12207ff..6306e74 100644 --- a/errorHandling/errorMessages.json +++ b/errorHandling/errorMessages.json @@ -12,7 +12,12 @@ "FORM_NOT_FOUND_IN_REQUEST":"The form is not found in the incoming request. Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Error generating the form .Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "PARAMS_NOT_FOUND":"Parameters not found to generate form", + "UNKNOWN_ORIGIN_SERVER":"Unknown Origin Server", + "NO_ACTION_FOUND":"No action found", + "UNKNOWN_ACTION":"Unknown action encountered", + "ERROR_IN_EXECUTING_ACTION":"Error in executing action type" }, "test": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -27,7 +32,12 @@ "FORM_NOT_FOUND_IN_REQUEST":"Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "PARAMS_NOT_FOUND":"Parameters not found to generate form", + "UNKNOWN_ORIGIN_SERVER":"Unknown Origin Server", + "NO_ACTION_FOUND":"No action found", + "UNKNOWN_ACTION":"Unknown action encountered", + "ERROR_IN_EXECUTING_ACTION":"Error in executing action type" }, "prod": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -42,6 +52,11 @@ "FORM_NOT_FOUND_IN_REQUEST":"Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "PARAMS_NOT_FOUND":"Parameters not found to generate form", + "UNKNOWN_ORIGIN_SERVER":"Unknown Origin Server", + "NO_ACTION_FOUND":"No action found", + "UNKNOWN_ACTION":"Unknown action encountered", + "ERROR_IN_EXECUTING_ACTION":"Error in executing action type" } } \ No newline at end of file diff --git a/portal/generatePortalIntegratedHandler.js b/portal/generatePortalIntegratedHandler.js index 1b4edd0..582b08d 100644 --- a/portal/generatePortalIntegratedHandler.js +++ b/portal/generatePortalIntegratedHandler.js @@ -8,36 +8,29 @@ const {getParametersFromPortal , expireTokenInPortal} = require("./loadPortalDa async function generatePortalIntegratedTemplate(req, res) { try { const params = req.body; - const token = params["id"]; + const token = params["id"]; + const userId = "test"; if (!token ) { return res .status(400) .send({ error: 'No token found' }); - } - const userId = "test"; - const portalId = params["portalId"]; - //const targetApp = appConfig[portalId]; + } const rawHost = (req.get("X-Original-Server") || req.hostname); const targetApp = appConfig[rawHost]; if(!targetApp) { - return res.status(400).send({ error: 'Unknown app ID' }); + return res.status(400).send({ error: getErrorMessage("UNKNOWN_ORIGIN_SERVER") }); } - const paramsFromPortal = await getParametersFromPortal(targetApp,token,userId); - console.log("paramsFromPortal",paramsFromPortal); - + const paramsFromPortal = await getParametersFromPortal(targetApp,token,userId); if(!paramsFromPortal) { - return res.status(400).send({ error: 'Parameters not found to generate form' }); + return res.status(400).send({ error: getErrorMessage("PARAMS_NOT_FOUND") }); } - const formJson = await constructFormJson(paramsFromPortal); if (formJson != null) { - //expire token - const isTokenExpired = await expireTokenInPortal(targetApp,token,userId); - console.log("isTokenExpired >> ",isTokenExpired); + res.status(200).send({ save_data: formJson }); @@ -48,9 +41,10 @@ async function generatePortalIntegratedTemplate(req, res) { } } catch (error) { console.error(`Error generating the form:`, error); + return res .status(400) - .send({ error: getErrorMessage("GENERATE_ERROR_MSG") }); + .send({ error: getErrorMessage("FORM_CANNOT_BE_GENERATED") }); } } diff --git a/portal/loadPortalDataHandler.js b/portal/loadPortalDataHandler.js index 799e83f..303309d 100644 --- a/portal/loadPortalDataHandler.js +++ b/portal/loadPortalDataHandler.js @@ -1,13 +1,11 @@ const axios = require("axios"); const { getErrorMessage } = require("../errorHandling/errorHandler.js"); -async function getParametersFromPortal(portal,token, userId) { - //call another api from portal to get the params - //TODO - add header for validation +async function getParametersFromPortal(portal,token, userId) { let parametersForForm = ""; try { - const urlForValidateTokenAndGetParams= portal.baseUrl+ (portal.getParametersEndpoint || process.env.PORTAL_VALIDATE_TOKEN_ENDPOINT); - console.log("urlForValidateTokenAndGetParams",urlForValidateTokenAndGetParams); + const urlForValidateTokenAndGetParams= portal.apiHost+ (portal.getParametersEndpoint || process.env.PORTAL_VALIDATE_TOKEN_ENDPOINT); + console.log("urlForValidateTokenAndGetParams >>",urlForValidateTokenAndGetParams); const response = await axios.post(`${urlForValidateTokenAndGetParams}`, { token, @@ -18,14 +16,16 @@ async function getParametersFromPortal(portal,token, userId) { 'Content-Type': 'application/json' } } - ); - - parametersForForm = response.data; + ); + return response.data; + + } catch (err) { - console.log( 'Failed to contact target app', err ); - return parametersForForm; + console.log( 'Failed to contact target app', err ); + + throw err; } - return parametersForForm; + } async function expireTokenInPortal(portal,token, userId) { @@ -34,7 +34,7 @@ async function expireTokenInPortal(portal,token, userId) { let isTokenExpired = false; try { - const urlForExpiringToken = portal.baseUrl + (portal.expireTokenEndPoint || process.env.PORTAL_EXPIRE_TOKEN_ENDPOINT);; + const urlForExpiringToken = portal.apiHost + (portal.expireTokenEndPoint || process.env.PORTAL_EXPIRE_TOKEN_ENDPOINT);; console.log("urlForExpiringToken",urlForExpiringToken); const response = await axios.post(`${urlForExpiringToken}`, { diff --git a/portal/savePortalFormDataHandler.js b/portal/savePortalFormDataHandler.js index dee7b05..57b1f20 100644 --- a/portal/savePortalFormDataHandler.js +++ b/portal/savePortalFormDataHandler.js @@ -1,65 +1,68 @@ +const appConfig = require('../appConfig.js'); async function submitForPortalAction (req,res) { - console.log("in here submitForPortalAction"); + const { tokenId, savedForm ,config} = req.body; + try{ + + if (!config?.actions || !Array.isArray(config.actions)) { + return res + .status(400) + .send({ error: getErrorMessage("NO_ACTION_FOUND")}); + } + const rawHost = (req.get("X-Original-Server") || req.hostname); + const portalConfig = appConfig[rawHost] || Object.values(appConfig).find(cfg => { + try { + return new URL(cfg.apiHost).hostname === rawHost; + } catch { + return false; + } + }) || {}; + + for (const action of config.actions) { + if (action.actionType === 'endpoint') { + await handleEndpointAction(tokenId,action, savedForm,portalConfig); + } else { + console.warn('Unexpected action type:', action.type); + return res + .status(400) + .send({ error: getErrorMessage("UNKNOWN_ACTION") }); + } + } - const { tokenId, savedForm ,config} = req.body; - console.log("tokenId",tokenId); - console.log("savedForm",savedForm); - console.log("config",config); -try{ - if (!config?.actions || !Array.isArray(config.actions)) { - return res - .status(400) - .send({ error: getErrorMessage("FORM_ID_REQUIRED") }); - } - for (const action of config.actions) { - if (action.actionType === 'endpoint') { - await handleEndpointAction(tokenId,action, savedForm); - } else if (action.actionType === 'email') { - await handleEmailAction(action, formData); - } else { - console.warn('Unexpected action type:', action.type); + res.json({ status: 'success' }); + } catch(error) { return res - .status(400) - .send({ error: "Unexpected action type" }); + .status(400) + .send({ error: getErrorMessage("ERROR_IN_EXECUTING_ACTION") }); } - } - - res.json({ status: 'success' }); -} catch(error) { - return res - .status(400) - .send({ error: "Error in executing action type" }); -} } -async function handleEndpointAction(tokenId,action, formData) { - console.log("action",action); - try { - const url = `${action.host}${action.path}`; - const headers = Object.fromEntries(action.headers.map(h => Object.entries(h)[0])); - +async function handleEndpointAction(tokenId,action, formData, portalConfig) { + + try { + const portalHost = portalConfig["apiHost"] || action.host; + const url = portalHost+action.path; + const headers = Object.fromEntries(action.headers.map(h => Object.entries(h)[0])); - const savedJson = { - "token": tokenId, - "formJson": formData - }; - const actionBody = Object.fromEntries( - (action.body || []).map(b => Object.entries(b)[0]) - ); - const payload = { ...savedJson, ...actionBody }; - const response = await fetch(url, { - method: action.type, - headers: { ...headers, Authorization: `Bearer ${action.authentication}`, 'Content-Type': 'application/json' }, - body: JSON.stringify(payload) - }); - - if (!response.ok) { - const text = await response.text(); - console.log("Error",text) - throw new Error(`Endpoint error: ${response.status} ${text}`); - } -} catch(error) { - console.error('Error in handleEndPoint:', error); - throw error; -} + const savedJson = { + "token": tokenId, + "formJson": formData + }; + const actionBody = Object.fromEntries( + (action.body || []).map(b => Object.entries(b)[0]) + ); + const payload = { ...savedJson, ...actionBody }; + const response = await fetch(url, { + method: action.type, + headers: { ...headers, Authorization: `Bearer ${action.authentication}`, 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + if (!response.ok) { + const text = await response.text(); + throw new Error(`Endpoint error: ${response.status} ${text}`); + } + } catch(error) { + console.error('Error in handleEndPoint:', error); + throw error; + } } module.exports = submitForPortalAction; \ No newline at end of file From 40729714ce100bea154a2572e1c5a94f2dd583af Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:26:41 -0700 Subject: [PATCH 03/40] added support for databinding conditions --- databindingsHandler.js | 135 +++++++++++++++++++++++++---------------- 1 file changed, 84 insertions(+), 51 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index c2f5d26..d36f3ba 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -21,7 +21,7 @@ async function populateDatabindings(formJson, params) { } // Map the API data to the form fields - const boundData = bindDataToFields(formJson, apiData); + const boundData = bindDataToFields(formJson, apiData, params); return boundData; } catch (error) { @@ -51,7 +51,50 @@ async function fetchDataFromSources(dataSources, params) { return data; } -function bindDataToFields(formJson, fetchedData) { +function getBindingValue(bindings, fetchedData, params = {}) { + // Normalize singular binding to array if needed + let list; + if (Array.isArray(bindings)) { + list = bindings; + } else if (bindings && bindings.source && bindings.path) { + list = [{ + order: 0, + condition: '', + source: bindings.source, + path: bindings.path + }]; + } else { + return null; + } + + const sortedBindings = list.slice().sort((a, b) => (a.order || 0) - (b.order || 0)); + + // Evaluate each binding + for (const binding of sortedBindings) { + // Parse condition + const [conditionField, valuesListString = ''] = (binding.condition || '').split('=').map(str => str.trim()); + + // Evaluate condition + if (conditionField) { + const allowedValues = valuesListString.split(',').map(str => str.trim()); + const currentParam = params[conditionField]; + if (!allowedValues.includes(currentParam)) { + continue; + } + } + + const sourceData = fetchedData[binding.source] || {}; + const results = JSONPath({ path: binding.path, json: sourceData }); + + if (Array.isArray(results) && results.length > 0 && results[0] != null && results[0] !== '') { + return results[0]; + } + } + + return null; +} + +function bindDataToFields(formJson, fetchedData, params = {}) { const formData = {}; const processItemsForDatabinding = (items) => { @@ -59,72 +102,62 @@ function bindDataToFields(formJson, fetchedData) { if (field.type === 'container' && field.containerItems) { processItemsForDatabinding(field.containerItems); - } else if (field.databindings != null) { - - const dataSourceName = field.databindings.source; - const dataPath = field.databindings.path; - const fetchedSourceData = fetchedData[dataSourceName]; - let valueFromPath = ""; - - // Fetch the value from the fetched data - if (fetchedSourceData) { - valueFromPath = JSONPath(dataPath, fetchedSourceData); - // If the field is a group, handle groupItems - if (field.type === 'group' && field.groupItems) { - // Create an array to store groupData objects for each item in valueFromPath - const groupDataArray = valueFromPath.map((pathObj, index) => { - const groupData = {}; - - // For each item in valueFromPath, iterate over the group fields - field.groupItems.forEach(groupItem => { - groupItem.fields.forEach(groupField => { - if (groupField.databindings) { - const fieldBindings = groupField.databindings.path; - const fieldIdInGroup = `${field.id}-${index}-${groupField.id}`; - // Assign data from pathObj or an empty string if not available - groupData[fieldIdInGroup] = transformValueToBindIfNeeded(groupField, pathObj[fieldBindings]) || ''; - } + + } else if (field.databindings) { + // Use new array‑based bindings if present, otherwise wrap the legacy binding + const bindings = Array.isArray(field.databindings) + ? field.databindings + : [ field.databindings ]; + const raw = getBindingValue(bindings, fetchedData, params); + + if (field.type === 'group' && field.groupItems) { + const groupDataArray = raw && Array.isArray(raw) + ? raw.map((pathObj, index) => { + const groupData = {}; + field.groupItems.forEach(groupItem => { + groupItem.fields.forEach(groupField => { + if (groupField.databindings) { + const gf = Array.isArray(groupField.databindings) ? groupField.databindings[0] : groupField.databindings; + const fieldIdInGroup = `${field.id}-${index}-${groupField.id}`; + const subVal = JSONPath({ path: gf.path, json: pathObj })[0]; + groupData[fieldIdInGroup] = transformValueToBindIfNeeded(groupField, subVal) || ''; + } + }); }); - }); - return groupData; - }); - - // Assign the groupDataArray to formData for this field's ID - formData[field.id] = groupDataArray; - } else { - formData[field.id] = valueFromPath.length > 0 ? transformValueToBindIfNeeded(field, valueFromPath[0]) : null; - } + return groupData; + }) + : []; + formData[field.id] = groupDataArray; + + } else { + formData[field.id] = raw != null + ? transformValueToBindIfNeeded(field, raw) + : null; } - } else if (field.type === 'group' && field.groupItems && !field.repeater) { + } + + else if (field.type === 'group' && field.groupItems && !field.repeater) { const transformedItem = {}; - //get the databindings from individual filed from non-repeating group field.groupItems.forEach(groupItem => { groupItem.fields.forEach(groupField => { if (groupField.databindings) { - const dataSourceName = groupField.databindings.source; - const dataPath = groupField.databindings.path; - const fetchedSourceData = fetchedData[dataSourceName]; - const fieldIdInGroup = `${field.id}-0-${groupField.id}`; - if (fetchedSourceData) { - const valueFromPathForGroupField = JSONPath(dataPath, fetchedSourceData); - //do date conversion here uisng a function - chceking the type as date and then checking format and applying - transformedItem[fieldIdInGroup] = valueFromPathForGroupField.length > 0 ? transformValueToBindIfNeeded(groupField, valueFromPathForGroupField[0]) : null; // Replace with actual value + const gf = Array.isArray(groupField.databindings) ? groupField.databindings[0] : groupField.databindings; + const fetchedSourceData = fetchedData[gf.source] || {}; + const vals = JSONPath({ path: gf.path, json: fetchedSourceData }); + const id = `${field.id}-0-${groupField.id}`; + transformedItem[id] = vals.length > 0 ? transformValueToBindIfNeeded(groupField, vals[0]): null; } - } }); }); formData[field.id] = [transformedItem]; } }); - } + }; if (formJson?.data?.items) { processItemsForDatabinding(formJson.data.items); } - // Iterate through fields - formJson.data.items.forEach - return formData; } From 5cd10beced9d18c83613aae584c188432488e1c9 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Mon, 28 Jul 2025 11:41:04 -0700 Subject: [PATCH 04/40] Update formatting --- databindingsHandler.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index d36f3ba..b413813 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -104,10 +104,8 @@ function bindDataToFields(formJson, fetchedData, params = {}) { processItemsForDatabinding(field.containerItems); } else if (field.databindings) { - // Use new array‑based bindings if present, otherwise wrap the legacy binding - const bindings = Array.isArray(field.databindings) - ? field.databindings - : [ field.databindings ]; + // Use new array‑based bindings, otherwise wrap the legacy binding + const bindings = Array.isArray(field.databindings) ? field.databindings : [ field.databindings ]; const raw = getBindingValue(bindings, fetchedData, params); if (field.type === 'group' && field.groupItems) { From 027288b7dbaf119d784100363d1011754c8b3509 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Thu, 31 Jul 2025 15:25:08 -0700 Subject: [PATCH 05/40] Update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 68f5a24..4d85769 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ /node_modules .env /common/* +.idea/ \ No newline at end of file From 2e526164cfda2af7ccd43c190dcf837c562da07d Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Thu, 31 Jul 2025 15:33:10 -0700 Subject: [PATCH 06/40] Update error messages. --- errorHandling/errorMessages.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/errorHandling/errorMessages.json b/errorHandling/errorMessages.json index 12207ff..474abd8 100644 --- a/errorHandling/errorMessages.json +++ b/errorHandling/errorMessages.json @@ -12,7 +12,9 @@ "FORM_NOT_FOUND_IN_REQUEST":"The form is not found in the incoming request. Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Error generating the form .Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "INVALID_FORM_ID":"Invalid Form ID.", + "INVALID_TOOL":"Invalid Tool." }, "test": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -27,7 +29,9 @@ "FORM_NOT_FOUND_IN_REQUEST":"Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "INVALID_FORM_ID":"Invalid Form ID.", + "INVALID_TOOL":"Invalid Tool." }, "prod": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -42,6 +46,8 @@ "FORM_NOT_FOUND_IN_REQUEST":"Try again later and report to support, if the issue is not solved", "GENERATE_ERROR_MSG":"Close this window and try again in a few seconds", "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", - "FORM_CANNOT_BE_GENERATED":"The form cannot be generated." + "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", + "INVALID_FORM_ID":"Invalid Form ID.", + "INVALID_TOOL":"Invalid Tool." } } \ No newline at end of file From 2d4b00b37b146d9817b516c00cca80ccdf684711 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Thu, 31 Jul 2025 15:36:26 -0700 Subject: [PATCH 07/40] Add validation --- generateHandler.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/generateHandler.js b/generateHandler.js index 9d4fd3c..8d6044b 100644 --- a/generateHandler.js +++ b/generateHandler.js @@ -141,6 +141,18 @@ async function generateNewTemplate(req, res) { .send({ error: getErrorMessage("FORM_ALREADY_FINALIZED") }); } + if (icm_metadata["Template"] !== template_id) { + return res + .status(400) + .send({ error: getErrorMessage("INVALID_FORM_ID") }); + } + + if (icm_metadata["Tool"]?.toLowerCase() !== "formfoundry") { + return res + .status(400) + .send({ error: getErrorMessage("INVALID_TOOL") }); + } + const formJson = await constructFormJson(template_id, params); if (formJson != null) { From 2519e17c84739e4fb0c8b09afdb032a44858b17c Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Fri, 1 Aug 2025 08:47:39 -0700 Subject: [PATCH 08/40] refactor databindings --- databindingsHandler.js | 87 ++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 46 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index b413813..8861a7a 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -97,65 +97,60 @@ function getBindingValue(bindings, fetchedData, params = {}) { function bindDataToFields(formJson, fetchedData, params = {}) { const formData = {}; - const processItemsForDatabinding = (items) => { - items.forEach(field => { + //helper function to bind group + function bindRowsToGroup(field, rows) { + return rows.map((rowObj, index) => { + const row = {}; + const binding = Array.isArray(field.databindings) ? field.databindings[0] : field.databindings || null; + const localDatabindings = binding ? { ...fetchedData, [binding.source]: rowObj } : { ...fetchedData }; + + field.groupItems.forEach(groupItem => + groupItem.fields.forEach(groupField => { + if (!groupField.databindings) return; + const fieldIdInGroup = `${field.id}-${index}-${groupField.id}`; + const subVal = getBindingValue(groupField.databindings,localDatabindings,params); + row[fieldIdInGroup] = transformValueToBindIfNeeded(groupField, subVal) || ''; + }) + ); + return row; + }); + } + function processItemsForDatabinding(items) { + items.forEach(field => { + // containers if (field.type === 'container' && field.containerItems) { - processItemsForDatabinding(field.containerItems); - - } else if (field.databindings) { - // Use new array‑based bindings, otherwise wrap the legacy binding - const bindings = Array.isArray(field.databindings) ? field.databindings : [ field.databindings ]; - const raw = getBindingValue(bindings, fetchedData, params); - - if (field.type === 'group' && field.groupItems) { - const groupDataArray = raw && Array.isArray(raw) - ? raw.map((pathObj, index) => { - const groupData = {}; - field.groupItems.forEach(groupItem => { - groupItem.fields.forEach(groupField => { - if (groupField.databindings) { - const gf = Array.isArray(groupField.databindings) ? groupField.databindings[0] : groupField.databindings; - const fieldIdInGroup = `${field.id}-${index}-${groupField.id}`; - const subVal = JSONPath({ path: gf.path, json: pathObj })[0]; - groupData[fieldIdInGroup] = transformValueToBindIfNeeded(groupField, subVal) || ''; - } - }); - }); - return groupData; - }) - : []; - formData[field.id] = groupDataArray; + return processItemsForDatabinding(field.containerItems); + } + // groups + if (field.type === 'group' && field.groupItems) { + if (field.repeater) { + // repeatable group + const binding = Array.isArray(field.databindings) ? field.databindings[0] : field.databindings || null; + const rows = binding ? JSONPath({ path: binding.path, json: fetchedData }) || [] : []; + formData[field.id] = bindRowsToGroup(field, rows); } else { - formData[field.id] = raw != null - ? transformValueToBindIfNeeded(field, raw) - : null; + // single-instance group + const binding = Array.isArray(field.databindings) ? field.databindings[0]: field.databindings || null; + const rows = binding? (JSONPath({ path: binding.path, json: fetchedData }) || []).slice(0, 1) : [{}]; + formData[field.id] = bindRowsToGroup(field, rows); } + return; } - else if (field.type === 'group' && field.groupItems && !field.repeater) { - const transformedItem = {}; - field.groupItems.forEach(groupItem => { - groupItem.fields.forEach(groupField => { - if (groupField.databindings) { - const gf = Array.isArray(groupField.databindings) ? groupField.databindings[0] : groupField.databindings; - const fetchedSourceData = fetchedData[gf.source] || {}; - const vals = JSONPath({ path: gf.path, json: fetchedSourceData }); - const id = `${field.id}-0-${groupField.id}`; - transformedItem[id] = vals.length > 0 ? transformValueToBindIfNeeded(groupField, vals[0]): null; - } - }); - }); - formData[field.id] = [transformedItem]; + //Single fields + if (field.databindings) { + const raw = getBindingValue(field.databindings, fetchedData, params); + formData[field.id] = raw != null ? transformValueToBindIfNeeded(field, raw) : null; } }); }; - if (formJson?.data?.items) { + if (Array.isArray(formJson?.data?.items)) { processItemsForDatabinding(formJson.data.items); } - + // console.dir(formData, { depth: null, colors: true }); return formData; } From 0f585eaa5d442bc5eb2f121ac19a6b144c7a4ed7 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 1 Aug 2025 11:51:23 -0700 Subject: [PATCH 09/40] Pass Template and Tool properties. --- saveICMdataHandler.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/saveICMdataHandler.js b/saveICMdataHandler.js index 748e04e..0f0efeb 100644 --- a/saveICMdataHandler.js +++ b/saveICMdataHandler.js @@ -19,6 +19,9 @@ async function getICMAttachmentStatus(attachment_id, username, params) { return_data["Locked by Id"] = ""; return_data["DocFileName"] = ""; return_data["Office Name"] = ""; + return_data["Template"] = ""; + return_data["Tool"] = ""; + if (!attachment_id || attachment_id == "") { return return_data; } @@ -45,6 +48,8 @@ async function getICMAttachmentStatus(attachment_id, username, params) { return_data["Locked by Id"] = response.data["Locked by Id"]; return_data["DocFileName"] = response.data["DocFileName"]; return_data["Office Name"] = response.data["Office Name"]; + return_data["Template"] = response.data["Template"]; + return_data["Tool"] = response.data["Tool"]; return return_data; } catch (error) { From 22605eba3a4049fcebb29a5420af12075beae08c Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Fri, 1 Aug 2025 12:47:41 -0700 Subject: [PATCH 10/40] Updated schema to support new Klamm schema --- schema/form_definition.yaml | 70 +++++++++++++++++++++++-------------- schema/saved_json.yaml | 3 +- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/schema/form_definition.yaml b/schema/form_definition.yaml index 392badb..a7f4345 100644 --- a/schema/form_definition.yaml +++ b/schema/form_definition.yaml @@ -7,11 +7,11 @@ required: properties: version: anyOf: - - type: string - - type: number + - type: string + - type: number ministry_id: anyOf: - - type: string + - type: string - type: number id: type: string @@ -25,13 +25,13 @@ properties: type: string deployed_to: anyOf: - - type: string + - type: string - type: "null" dataSources: type: array items: type: object - default: [] + default: [] data: type: object required: @@ -64,8 +64,8 @@ definitions: type: string mask: anyOf: - - type: string - - type: "null" + - type: string + - type: "null" codeContext: type: object properties: @@ -90,7 +90,7 @@ definitions: type: string required: - text - - value + - value groupItems: type: array items: @@ -101,7 +101,7 @@ definitions: items: $ref: "#/definitions/Item" repeater: - type: boolean + type: boolean helperText: anyOf: - type: string @@ -142,7 +142,7 @@ definitions: value: anyOf: - type: string - - type: number + - type: number - type: boolean errorMessage: anyOf: @@ -151,7 +151,7 @@ definitions: required: - type - value - - errorMessage + - errorMessage conditions: type: array items: @@ -169,30 +169,48 @@ definitions: required: - type - value - additionalProperties: false + additionalProperties: false webStyles: anyOf: - type: "null" - type: object - additionalProperties: - type: string + additionalProperties: + anyOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array + - type: "null" pdfStyles: anyOf: - type: "null" - type: object - additionalProperties: - type: string + additionalProperties: + anyOf: + - type: string + - type: number + - type: boolean + - type: object + - type: array + - type: "null" databindings: - type: object - properties: - path: - type: string - source: - type: string - required: - - path - - source - additionalProperties: false + type: array + items: + type: object + properties: + path: + type: string + source: + type: string + order: + type: number + condition: + type: string + required: + - path + - source + additionalProperties: false containerItems: type: array items: diff --git a/schema/saved_json.yaml b/schema/saved_json.yaml index 7712db5..6011102 100644 --- a/schema/saved_json.yaml +++ b/schema/saved_json.yaml @@ -1,3 +1,4 @@ + type: object required: - data @@ -13,7 +14,6 @@ properties: - type: number - type: object - type: array - - type: "null" items: type: object additionalProperties: @@ -23,6 +23,7 @@ properties: - type: number - type: object - type: "null" + - type: "null" form_definition: $ref: "form_definition_schema" metadata: From c13f52a8d80e83d38e29024f36c2ad530603bdd0 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 1 Aug 2025 14:32:27 -0700 Subject: [PATCH 11/40] Add area validation. --- errorHandling/errorMessages.json | 9 ++++++--- generateHandler.js | 8 ++++++++ saveICMdataHandler.js | 2 ++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/errorHandling/errorMessages.json b/errorHandling/errorMessages.json index 474abd8..21045cd 100644 --- a/errorHandling/errorMessages.json +++ b/errorHandling/errorMessages.json @@ -14,7 +14,8 @@ "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", "INVALID_FORM_ID":"Invalid Form ID.", - "INVALID_TOOL":"Invalid Tool." + "INVALID_TOOL":"Invalid Tool.", + "INVALID_AREA":"Invalid Area." }, "test": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -31,7 +32,8 @@ "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", "INVALID_FORM_ID":"Invalid Form ID.", - "INVALID_TOOL":"Invalid Tool." + "INVALID_TOOL":"Invalid Tool.", + "INVALID_AREA":"Invalid Area." }, "prod": { "FORM_ID_REQUIRED": "Close this window and try again in a few seconds", @@ -48,6 +50,7 @@ "FORM_ALREADY_FINALIZED":"The form you are trying to generate is already finalized", "FORM_CANNOT_BE_GENERATED":"The form cannot be generated.", "INVALID_FORM_ID":"Invalid Form ID.", - "INVALID_TOOL":"Invalid Tool." + "INVALID_TOOL":"Invalid Tool.", + "INVALID_AREA":"Invalid Area." } } \ No newline at end of file diff --git a/generateHandler.js b/generateHandler.js index 8d6044b..01918ea 100644 --- a/generateHandler.js +++ b/generateHandler.js @@ -153,6 +153,14 @@ async function generateNewTemplate(req, res) { .send({ error: getErrorMessage("INVALID_TOOL") }); } + let requestArea = params["area"]; + let icmArea = icm_metadata["Categorie"]; + if (requestArea && requestArea.toLowerCase() !== icmArea?.toLowerCase()) { + return res + .status(400) + .send({ error: getErrorMessage("INVALID_AREA") }); + } + const formJson = await constructFormJson(template_id, params); if (formJson != null) { diff --git a/saveICMdataHandler.js b/saveICMdataHandler.js index 0f0efeb..5c508ac 100644 --- a/saveICMdataHandler.js +++ b/saveICMdataHandler.js @@ -21,6 +21,7 @@ async function getICMAttachmentStatus(attachment_id, username, params) { return_data["Office Name"] = ""; return_data["Template"] = ""; return_data["Tool"] = ""; + return_data["Categorie"] = ""; if (!attachment_id || attachment_id == "") { return return_data; @@ -50,6 +51,7 @@ async function getICMAttachmentStatus(attachment_id, username, params) { return_data["Office Name"] = response.data["Office Name"]; return_data["Template"] = response.data["Template"]; return_data["Tool"] = response.data["Tool"]; + return_data["Categorie"] = response.data["Categorie"]; return return_data; } catch (error) { From 68e09aa3749c014a85216b49f1e4b4fccd9f8458 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Tue, 5 Aug 2025 11:34:34 -0700 Subject: [PATCH 12/40] Update form definition to support old databindings --- schema/form_definition.yaml | 51 +++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/schema/form_definition.yaml b/schema/form_definition.yaml index a7f4345..d988ba6 100644 --- a/schema/form_definition.yaml +++ b/schema/form_definition.yaml @@ -195,22 +195,41 @@ definitions: - type: array - type: "null" databindings: - type: array - items: - type: object - properties: - path: - type: string - source: - type: string - order: - type: number - condition: - type: string - required: - - path - - source - additionalProperties: false + oneOf: + - type: array + items: + type: object + properties: + path: + type: string + source: + type: string + order: + type: number + condition: + anyOf: + - type: string + - type: "null" + required: + - path + - source + additionalProperties: false + - type: object + properties: + path: + type: string + source: + type: string + order: + type: number + condition: + anyOf: + - type: string + - type: "null" + required: + - path + - source + additionalProperties: false containerItems: type: array items: From 5073992171fc12e7c8b852c22e7970f8228817df Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Tue, 5 Aug 2025 16:15:21 -0700 Subject: [PATCH 13/40] Fix the area validation logic. --- generateHandler.js | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/generateHandler.js b/generateHandler.js index 01918ea..f764690 100644 --- a/generateHandler.js +++ b/generateHandler.js @@ -153,12 +153,20 @@ async function generateNewTemplate(req, res) { .send({ error: getErrorMessage("INVALID_TOOL") }); } - let requestArea = params["area"]; - let icmArea = icm_metadata["Categorie"]; - if (requestArea && requestArea.toLowerCase() !== icmArea?.toLowerCase()) { - return res - .status(400) - .send({ error: getErrorMessage("INVALID_AREA") }); + const requestArea = params.area?.toLowerCase(); + + if (requestArea) { + const icmArea = icm_metadata.Categorie?.toLowerCase(); + + const isServiceRequest = requestArea === "service request"; + + const isAreaValid = + (isServiceRequest && icmArea === "application") || + (!isServiceRequest && requestArea === icmArea); + + if (!isAreaValid) { + return res.status(400).send({ error: getErrorMessage("INVALID_AREA") }); + } } const formJson = await constructFormJson(template_id, params); From 4725f4014abb62c4dc719982bce88a102e56d1ef Mon Sep 17 00:00:00 2001 From: Viswam Date: Wed, 6 Aug 2025 16:07:32 -0700 Subject: [PATCH 14/40] Changes for Open Form in Portal --- portal/loadPortalDataHandler.js | 31 ++++++++++++++++- portal/loadPortalIntegratedHandler.js | 48 +++++++++++++++++++++++++++ routes.js | 3 +- 3 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 portal/loadPortalIntegratedHandler.js diff --git a/portal/loadPortalDataHandler.js b/portal/loadPortalDataHandler.js index 303309d..0c8eabf 100644 --- a/portal/loadPortalDataHandler.js +++ b/portal/loadPortalDataHandler.js @@ -55,4 +55,33 @@ async function expireTokenInPortal(portal,token, userId) { return isTokenExpired; } -module.exports = {getParametersFromPortal , expireTokenInPortal}; \ No newline at end of file +async function getSavedFormFromPortal(portal,token, userId) { + let parametersForForm = ""; + + try { + const urlForValidateTokenAndGetJson= portal.apiHost+ (portal.getSavedJsonEndpoint || process.env.PORTAL_VALIDATE_TOKEN_ENDPOINT); + console.log("urlForValidateTokenAndGetJson >>",urlForValidateTokenAndGetJson); + const response = await axios.post(`${urlForValidateTokenAndGetJson}`, + { + token, + userId + }, + { + headers: { + 'Content-Type': 'application/json' + } + } + ); + //TODO: verify the json against schema or some other sanity checks + return response.data; + + + } catch (err) { + console.log( 'Failed to contact target app', err ); + + throw err; + } + +} + +module.exports = {getParametersFromPortal , expireTokenInPortal, getSavedFormFromPortal }; \ No newline at end of file diff --git a/portal/loadPortalIntegratedHandler.js b/portal/loadPortalIntegratedHandler.js new file mode 100644 index 0000000..0b7c2b1 --- /dev/null +++ b/portal/loadPortalIntegratedHandler.js @@ -0,0 +1,48 @@ +const { keycloakForFormRepo } = require("../keycloak.js"); +const axios = require("axios"); +const getFormFromFormTemplate = require("../formRepoHandler.js"); +const { getErrorMessage } = require("../errorHandling/errorHandler.js"); +const appConfig = require('../appConfig.js'); +const {getSavedFormFromPortal} = require("./loadPortalDataHandler.js"); + +async function loadPortalIntegratedForm(req, res) { + try { + const params = req.body; + const token = params["id"]; + const userId = "test"; + if (!token ) { + return res + .status(400) + .send({ error: 'No token found' }); + } + + const rawHost = (req.get("X-Original-Server") || req.hostname); + const targetApp = appConfig[rawHost]; + + if(!targetApp) { + return res.status(400).send({ error: getErrorMessage("UNKNOWN_ORIGIN_SERVER") }); + } + const formJson = await getSavedFormFromPortal(targetApp,token,userId); + + if(!formJson) { + return res.status(400).send({ error: getErrorMessage("FORM_NOT_FOUND", { templateId: template_id }) }); + } + //the formJson is a base64 string . Converting to json here. + const savedJson = Buffer.from(formJson["form"], 'base64').toString('utf-8'); + const data = JSON.parse(savedJson); + res.status(200).send(data); + + } catch (error) { + console.error(`Error generating the form:`, error); + + return res + .status(400) + .send({ error: getErrorMessage("FORM_CANNOT_BE_GENERATED") }); + } +} + + + + + +module.exports = loadPortalIntegratedForm; \ No newline at end of file diff --git a/routes.js b/routes.js index 86bf927..252d79d 100644 --- a/routes.js +++ b/routes.js @@ -10,6 +10,7 @@ const renderRouter = require("./renderHandler"); const {generatePDFFromHTML,generatePDFFromURL,generatePDFFromJSON,loadSavedJson } = require("./generatePDFHandler"); const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegratedHandler.js"); const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); +const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); const getFormsFromFormTemplate = require("./formRepoHandler"); @@ -147,5 +148,5 @@ router.post("/generatePortalForm", generatePortalIntegratedTemplate); router.post("/generateNewTemplate", generateNewTemplate); router.use('/pdfRender', renderRouter); router.use('/submitForPortalAction', submitForPortalAction); - +router.post("/loadPortalForm", loadPortalIntegratedForm); module.exports = router; \ No newline at end of file From 6980189ad9505ad48febf7aa06d1a7fdb7dc97a3 Mon Sep 17 00:00:00 2001 From: Viswam Date: Thu, 7 Aug 2025 14:14:30 -0700 Subject: [PATCH 15/40] Update the actionType to actionType as per coming from form --- portal/savePortalFormDataHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/portal/savePortalFormDataHandler.js b/portal/savePortalFormDataHandler.js index 57b1f20..0bc06d4 100644 --- a/portal/savePortalFormDataHandler.js +++ b/portal/savePortalFormDataHandler.js @@ -18,10 +18,10 @@ async function submitForPortalAction (req,res) { }) || {}; for (const action of config.actions) { - if (action.actionType === 'endpoint') { + if (action.action_type === 'endpoint') { await handleEndpointAction(tokenId,action, savedForm,portalConfig); } else { - console.warn('Unexpected action type:', action.type); + console.warn('Unexpected action type:', action.action_type); return res .status(400) .send({ error: getErrorMessage("UNKNOWN_ACTION") }); From 314ce43e67acaf57f1a8481a4e15095911839cf2 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 8 Aug 2025 09:20:50 -0700 Subject: [PATCH 16/40] Add new endpoint to process json clob data --- icmJsonClobHandler.js | 75 +++++++++++++++++++++++++++++++++++++++++++ routes.js | 49 +++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 icmJsonClobHandler.js diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js new file mode 100644 index 0000000..1cc6424 --- /dev/null +++ b/icmJsonClobHandler.js @@ -0,0 +1,75 @@ +const axios = require("axios"); +const { keycloakForSiebel } = require("./keycloak.js"); + +function processICMJsonItem(item, index) { + console.log(">>> ICMJsonClob (raw):\n\n", item.ICMJsonClob); + console.log("--------------------------------------------------"); + + const trimmedClob = item.ICMJsonClob.trim(); + if (!trimmedClob.startsWith('{') && !trimmedClob.startsWith('[')) { + console.log(`Item ${index + 1}: ICMJsonClob is not a valid JSON`); + return; + } + + try { + const parsedClob = JSON.parse(item.ICMJsonClob); + console.log("ICMJsonClob (parsed):\n\n", JSON.stringify(parsedClob, null, 2)); + } catch (parseError) { + console.log(`Item ${index + 1}: Failed to parse JSON: ${parseError.message}`); + } +} + +async function getData() { + try { + const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); + + const headers = { + Authorization: `Bearer ${grant.id_token.token}`, + "X-ICM-TrustedUsername": "DMEIRELE", + "Content-Type": "application/json" + }; + + const url = "https://sieblabm.apps.gov.bc.ca/ffdy/v1.0/data/ICMJSONClobBO/ICMJSONClobBC" + + const queryParams = { + ViewMode: "Catalog", + workspace: "dev_sadmin_bz_1", + searchspec: `("SR Id" = "1-54SIG7F")` + }; + + const response = await axios.get(url, { + headers, + params: queryParams + }); + + console.log("Status:", response.status + " " + response.statusText); + + if (response.data && response.data.items && Array.isArray(response.data.items)) { + response.data.items.forEach((item, index) => { + processICMJsonItem(item, index); + }); + } else { + console.log("No items found in response"); + } + + return { + success: true, + totalItems: response.data?.items?.length || 0, + statusCode: response.status, + statusText: response.statusText + }; + + } catch (error) { + console.error("API call failed:", error.message); + return { + success: false, + error: error.message, + totalItems: 0, + errors: [error.message] + }; + } +} + +module.exports = { + getData +}; \ No newline at end of file diff --git a/routes.js b/routes.js index 252d79d..624616a 100644 --- a/routes.js +++ b/routes.js @@ -12,13 +12,33 @@ const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegra const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); - const getFormsFromFormTemplate = require("./formRepoHandler"); +const {getData} = require("./icmJsonClobHandler"); const router = express.Router(); const FORM_SERVER_URL = process.env.FORMSERVERURL; const ENDPOINT_URL = process.env.ENDPOINTURL; +const localhostOnlyMiddleware = (req, res, next) => { + const clientIP = req.ip || + req.connection.remoteAddress || + req.socket.remoteAddress || + (req.connection.socket ? req.connection.socket.remoteAddress : null); + + const actualIP = clientIP?.replace('::ffff:', '') || clientIP; + + const isLocalhost = clientIP === '127.0.0.1' || + clientIP === '::1' || + clientIP === 'localhost' || + actualIP?.startsWith('172.') || // Docker default bridge network + actualIP?.startsWith('192.168.') || // Docker custom networks + actualIP?.startsWith('10.'); // Docker swarm networks + + if (!isLocalhost) { + return res.status(403).json({ error: 'Localhost access only' }); + } + next(); +}; // Form Map const formMap = new Map(); @@ -115,8 +135,7 @@ router.post("/generate", generateTemplate); router.get("/getAllForms", async (request, response) => { try { - const grant = - await keycloakForFormRepo.grantManager.obtainFromClientCredentials(); + const grant = await keycloakForFormRepo.grantManager.obtainFromClientCredentials(); let endpointUrl = `http://localhost:3030/api/forms-list`; const forms = await axios.get(endpointUrl, { @@ -134,10 +153,31 @@ router.get("/getAllForms", async (request, response) => { }); +router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { + try { + const result = await getData(); + + if (result.success) { + res.status(200).json({ + message: "Success." + }); + } else { + res.status(500).json({ + error: "Error." + }); + } + } catch (error) { + console.error("Error:", error.message); + res.status(500).json({ + error: "Internal server error", + message: error.message + }); + } +}); + // clear the locked by flags in ICM for the form, used when form is closed router.post("/clearICMLockedFlag", clearICMLockedFlag); - router.post("/generatePDFFromJson", generatePDFFromJSON); // Generate route @@ -149,4 +189,5 @@ router.post("/generateNewTemplate", generateNewTemplate); router.use('/pdfRender', renderRouter); router.use('/submitForPortalAction', submitForPortalAction); router.post("/loadPortalForm", loadPortalIntegratedForm); + module.exports = router; \ No newline at end of file From 03963e0fdacd220f6c8fd26254e07668a270c6bd Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 8 Aug 2025 17:11:30 -0700 Subject: [PATCH 17/40] Process clob data data. --- icmJsonClobHandler.js | 87 ++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 1cc6424..4c2b64d 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -1,75 +1,92 @@ const axios = require("axios"); const { keycloakForSiebel } = require("./keycloak.js"); -function processICMJsonItem(item, index) { - console.log(">>> ICMJsonClob (raw):\n\n", item.ICMJsonClob); - console.log("--------------------------------------------------"); - - const trimmedClob = item.ICMJsonClob.trim(); - if (!trimmedClob.startsWith('{') && !trimmedClob.startsWith('[')) { - console.log(`Item ${index + 1}: ICMJsonClob is not a valid JSON`); - return; +function processIcmJsonClobData(jsonData) { + // Create a copy to avoid mutating the original instance + const processedData = JSON.parse(JSON.stringify(jsonData)); + + if (processedData.items && Array.isArray(processedData.items)) { + processedData.items = processedData.items.map(item => { + if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { + try { + item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); + delete item.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${item.Id}: ${error.message}`); + item.ICMJsonClobParsed = null; + } + } + + return item; + }); } + else { + if (processedData.ICMJsonClob && typeof processedData.ICMJsonClob === 'string') { + try { + processedData.ICMJsonClobParsed = JSON.parse(processedData.ICMJsonClob); + delete processedData.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${processedData.Id}: ${error.message}`); + processedData.ICMJsonClobParsed = null; + } + } - try { - const parsedClob = JSON.parse(item.ICMJsonClob); - console.log("ICMJsonClob (parsed):\n\n", JSON.stringify(parsedClob, null, 2)); - } catch (parseError) { - console.log(`Item ${index + 1}: Failed to parse JSON: ${parseError.message}`); + return processedData; } + + return processedData; } -async function getData() { +async function fetchIcmJsonClobData(attachmentId) { try { const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); + const username = process.env.SIEBEL_ICM_TRUSTED_USERNAME; + const headers = { Authorization: `Bearer ${grant.id_token.token}`, - "X-ICM-TrustedUsername": "DMEIRELE", + "X-ICM-TrustedUsername": username, "Content-Type": "application/json" }; - const url = "https://sieblabm.apps.gov.bc.ca/ffdy/v1.0/data/ICMJSONClobBO/ICMJSONClobBC" + const url = process.env.SIEBEL_ICM_BASE_URL; const queryParams = { ViewMode: "Catalog", - workspace: "dev_sadmin_bz_1", - searchspec: `("SR Id" = "1-54SIG7F")` + searchspec: attachmentId // "UserFieldCLOB"="1-123ABC" }; - const response = await axios.get(url, { + return await axios.get(url, { headers, - params: queryParams + params: queryParams, + timeout: 30000 }); - console.log("Status:", response.status + " " + response.statusText); + } catch (error) { + throw new Error(`Failed to fetch ICM data for SR ID ${attachmentId}: ${error.message}`); + } +} - if (response.data && response.data.items && Array.isArray(response.data.items)) { - response.data.items.forEach((item, index) => { - processICMJsonItem(item, index); - }); - } else { - console.log("No items found in response"); - } +async function getProcessedData(attachmentId) { + try { + const response = await fetchIcmJsonClobData(attachmentId); + const processedResult = processIcmJsonClobData(response.data); return { success: true, - totalItems: response.data?.items?.length || 0, - statusCode: response.status, - statusText: response.statusText + data: processedResult }; } catch (error) { console.error("API call failed:", error.message); + return { success: false, - error: error.message, - totalItems: 0, - errors: [error.message] + error: error.message }; } } module.exports = { - getData + getProcessedData }; \ No newline at end of file From d96e3d69a1500a25d7123c6dfc6e64c43feeefc1 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 18:48:11 -0700 Subject: [PATCH 18/40] Update router.js --- routes.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/routes.js b/routes.js index 624616a..00a8d82 100644 --- a/routes.js +++ b/routes.js @@ -13,7 +13,7 @@ const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); const getFormsFromFormTemplate = require("./formRepoHandler"); -const {getData} = require("./icmJsonClobHandler"); +const {getProcessedData} = require("./icmJsonClobHandler"); const router = express.Router(); const FORM_SERVER_URL = process.env.FORMSERVERURL; @@ -154,24 +154,18 @@ router.get("/getAllForms", async (request, response) => { }); router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { + try { - const result = await getData(); + const result = await getProcessedData(req.query.attachmentId); if (result.success) { - res.status(200).json({ - message: "Success." - }); + res.status(200).json(result.data); } else { - res.status(500).json({ - error: "Error." - }); + res.status(500).json({ error: "Error." }); } } catch (error) { console.error("Error:", error.message); - res.status(500).json({ - error: "Internal server error", - message: error.message - }); + res.status(500).json({ error: "Internal server error", message: error.message }); } }); From 5afb6594de177ea26674751e28ca4e3a089b88f4 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 18:48:55 -0700 Subject: [PATCH 19/40] Refactor processIcmJsonClobData --- icmJsonClobHandler.js | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 4c2b64d..83906d8 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -1,40 +1,30 @@ const axios = require("axios"); const { keycloakForSiebel } = require("./keycloak.js"); +function parseIcmJsonClob(item) { + if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { + try { + item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); + delete item.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${item.Id || 'unknown'}: ${error.message}`); + item.ICMJsonClobParsed = null; + } + } + return item; +} + function processIcmJsonClobData(jsonData) { + // Create a copy to avoid mutating the original instance const processedData = JSON.parse(JSON.stringify(jsonData)); if (processedData.items && Array.isArray(processedData.items)) { - processedData.items = processedData.items.map(item => { - if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { - try { - item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); - delete item.ICMJsonClob; - } catch (error) { - console.warn(`Failed to parse ICMJsonClob for item ${item.Id}: ${error.message}`); - item.ICMJsonClobParsed = null; - } - } - - return item; - }); - } - else { - if (processedData.ICMJsonClob && typeof processedData.ICMJsonClob === 'string') { - try { - processedData.ICMJsonClobParsed = JSON.parse(processedData.ICMJsonClob); - delete processedData.ICMJsonClob; - } catch (error) { - console.warn(`Failed to parse ICMJsonClob for item ${processedData.Id}: ${error.message}`); - processedData.ICMJsonClobParsed = null; - } - } - + processedData.items = processedData.items.map(parseIcmJsonClob); return processedData; } - return processedData; + return parseIcmJsonClob(processedData); } async function fetchIcmJsonClobData(attachmentId) { From 21bf42a4070f2592cdbf97b75c86a729fe65f3d8 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 23:04:50 -0700 Subject: [PATCH 20/40] Code cleanup. --- routes.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/routes.js b/routes.js index 00a8d82..1b2e0d1 100644 --- a/routes.js +++ b/routes.js @@ -11,8 +11,7 @@ const {generatePDFFromHTML,generatePDFFromURL,generatePDFFromJSON,loadSavedJson const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegratedHandler.js"); const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); - -const getFormsFromFormTemplate = require("./formRepoHandler"); +require("./formRepoHandler"); const {getProcessedData} = require("./icmJsonClobHandler"); const router = express.Router(); @@ -154,7 +153,6 @@ router.get("/getAllForms", async (request, response) => { }); router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { - try { const result = await getProcessedData(req.query.attachmentId); From ccd46ea1a2440b4aaeb9fd048411cbba9a726f16 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Mon, 11 Aug 2025 09:22:27 -0700 Subject: [PATCH 21/40] Use existing settings for siebel url and username --- icmJsonClobHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 83906d8..4fcd622 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -31,7 +31,7 @@ async function fetchIcmJsonClobData(attachmentId) { try { const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); - const username = process.env.SIEBEL_ICM_TRUSTED_USERNAME; + const username = process.env.TRUSTED_USERNAME; const headers = { Authorization: `Bearer ${grant.id_token.token}`, @@ -39,7 +39,7 @@ async function fetchIcmJsonClobData(attachmentId) { "Content-Type": "application/json" }; - const url = process.env.SIEBEL_ICM_BASE_URL; + const url = process.env.SIEBEL_ICM_API_HOST; const queryParams = { ViewMode: "Catalog", From accf5f8996258622ad4d6377ea54837b78281128 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Tue, 12 Aug 2025 11:16:53 -0700 Subject: [PATCH 22/40] Fix URL path --- icmJsonClobHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 4fcd622..b5ee5c0 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -39,7 +39,7 @@ async function fetchIcmJsonClobData(attachmentId) { "Content-Type": "application/json" }; - const url = process.env.SIEBEL_ICM_API_HOST; + const url = `${process.env.SIEBEL_ICM_API_HOST}${process.env.ICM_JSON_CLOB_ENDPONT}`; const queryParams = { ViewMode: "Catalog", From b91ad5384d136b918aa1018e884e091eaecddb24 Mon Sep 17 00:00:00 2001 From: NicolaSDPR1 Date: Tue, 12 Aug 2025 15:42:38 -0700 Subject: [PATCH 23/40] (ADO-3158) truncate json UUID before XML creation, remove index/offest and parent values from children keys --- saveICMdataHandler.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/saveICMdataHandler.js b/saveICMdataHandler.js index 5c508ac..f6e719c 100644 --- a/saveICMdataHandler.js +++ b/saveICMdataHandler.js @@ -133,8 +133,30 @@ async function saveICMdata(req, res) { saveJson["DocFileExt"] = "json"; saveJson["Doc Attachment Id"] = Buffer.from(savedFormParam).toString('base64');//savedForm is saved as attachment let saveData = JSON.parse(savedFormParam)["data"];// This is the data part of the savedJson + const truncatedKeysSaveData = {}; + for(let oldKey in saveData) { //This begins trunicating the JSON keys for XML (UUID should be first 8 characters) + const stringLength = oldKey.length; + const newKey = oldKey.substring(0, stringLength-28); + if (Array.isArray(saveData[oldKey]) > 0 && Object.keys(saveData[oldKey]).length > 0) { //This trunicates child/dependant objects + const childrenArray = []; + for(let i = 0; i < saveData[oldKey].length; i++) { + const truncatedChildrenKeys = {}; + for (let oldChildKey in saveData[oldKey][i]) { + const childStringLength = oldChildKey.length; + const newChildKey = oldChildKey.substring(stringLength+3, childStringLength-28); + truncatedChildrenKeys[newChildKey] = saveData[oldKey][i][oldChildKey]; + } + childrenArray.push(truncatedChildrenKeys); + } + const wrapperKey = {} + wrapperKey[newKey] = childrenArray; + truncatedKeysSaveData[`${newKey}-List`] = wrapperKey // Add a wrapper around the children/dependecies + } else { + truncatedKeysSaveData[newKey] = saveData[oldKey]; //Data is added to new JSON with the truncated key + } + } let builder = new xml2js.Builder(); - saveJson["XML Hierarchy"] = builder.buildObject(saveData); + saveJson["XML Hierarchy"] = builder.buildObject(truncatedKeysSaveData); //let url = buildUrlWithParams('SIEBEL_ICM_API_HOST', 'fwd/v1.0/data/DT Form Instance Thin/DT Form Instance Thin/' + attachment_id + '/', ''); let url = buildUrlWithParams(params["apiHost"], params["saveEndpoint"] + attachment_id + '/', params); try { From 41355cd86852c8ee8396a9ee1d93a8dd82d0b842 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 8 Aug 2025 09:20:50 -0700 Subject: [PATCH 24/40] Add new endpoint to process json clob data --- icmJsonClobHandler.js | 75 +++++++++++++++++++++++++++++++++++++++++++ routes.js | 49 +++++++++++++++++++++++++--- 2 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 icmJsonClobHandler.js diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js new file mode 100644 index 0000000..1cc6424 --- /dev/null +++ b/icmJsonClobHandler.js @@ -0,0 +1,75 @@ +const axios = require("axios"); +const { keycloakForSiebel } = require("./keycloak.js"); + +function processICMJsonItem(item, index) { + console.log(">>> ICMJsonClob (raw):\n\n", item.ICMJsonClob); + console.log("--------------------------------------------------"); + + const trimmedClob = item.ICMJsonClob.trim(); + if (!trimmedClob.startsWith('{') && !trimmedClob.startsWith('[')) { + console.log(`Item ${index + 1}: ICMJsonClob is not a valid JSON`); + return; + } + + try { + const parsedClob = JSON.parse(item.ICMJsonClob); + console.log("ICMJsonClob (parsed):\n\n", JSON.stringify(parsedClob, null, 2)); + } catch (parseError) { + console.log(`Item ${index + 1}: Failed to parse JSON: ${parseError.message}`); + } +} + +async function getData() { + try { + const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); + + const headers = { + Authorization: `Bearer ${grant.id_token.token}`, + "X-ICM-TrustedUsername": "DMEIRELE", + "Content-Type": "application/json" + }; + + const url = "https://sieblabm.apps.gov.bc.ca/ffdy/v1.0/data/ICMJSONClobBO/ICMJSONClobBC" + + const queryParams = { + ViewMode: "Catalog", + workspace: "dev_sadmin_bz_1", + searchspec: `("SR Id" = "1-54SIG7F")` + }; + + const response = await axios.get(url, { + headers, + params: queryParams + }); + + console.log("Status:", response.status + " " + response.statusText); + + if (response.data && response.data.items && Array.isArray(response.data.items)) { + response.data.items.forEach((item, index) => { + processICMJsonItem(item, index); + }); + } else { + console.log("No items found in response"); + } + + return { + success: true, + totalItems: response.data?.items?.length || 0, + statusCode: response.status, + statusText: response.statusText + }; + + } catch (error) { + console.error("API call failed:", error.message); + return { + success: false, + error: error.message, + totalItems: 0, + errors: [error.message] + }; + } +} + +module.exports = { + getData +}; \ No newline at end of file diff --git a/routes.js b/routes.js index 252d79d..624616a 100644 --- a/routes.js +++ b/routes.js @@ -12,13 +12,33 @@ const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegra const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); - const getFormsFromFormTemplate = require("./formRepoHandler"); +const {getData} = require("./icmJsonClobHandler"); const router = express.Router(); const FORM_SERVER_URL = process.env.FORMSERVERURL; const ENDPOINT_URL = process.env.ENDPOINTURL; +const localhostOnlyMiddleware = (req, res, next) => { + const clientIP = req.ip || + req.connection.remoteAddress || + req.socket.remoteAddress || + (req.connection.socket ? req.connection.socket.remoteAddress : null); + + const actualIP = clientIP?.replace('::ffff:', '') || clientIP; + + const isLocalhost = clientIP === '127.0.0.1' || + clientIP === '::1' || + clientIP === 'localhost' || + actualIP?.startsWith('172.') || // Docker default bridge network + actualIP?.startsWith('192.168.') || // Docker custom networks + actualIP?.startsWith('10.'); // Docker swarm networks + + if (!isLocalhost) { + return res.status(403).json({ error: 'Localhost access only' }); + } + next(); +}; // Form Map const formMap = new Map(); @@ -115,8 +135,7 @@ router.post("/generate", generateTemplate); router.get("/getAllForms", async (request, response) => { try { - const grant = - await keycloakForFormRepo.grantManager.obtainFromClientCredentials(); + const grant = await keycloakForFormRepo.grantManager.obtainFromClientCredentials(); let endpointUrl = `http://localhost:3030/api/forms-list`; const forms = await axios.get(endpointUrl, { @@ -134,10 +153,31 @@ router.get("/getAllForms", async (request, response) => { }); +router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { + try { + const result = await getData(); + + if (result.success) { + res.status(200).json({ + message: "Success." + }); + } else { + res.status(500).json({ + error: "Error." + }); + } + } catch (error) { + console.error("Error:", error.message); + res.status(500).json({ + error: "Internal server error", + message: error.message + }); + } +}); + // clear the locked by flags in ICM for the form, used when form is closed router.post("/clearICMLockedFlag", clearICMLockedFlag); - router.post("/generatePDFFromJson", generatePDFFromJSON); // Generate route @@ -149,4 +189,5 @@ router.post("/generateNewTemplate", generateNewTemplate); router.use('/pdfRender', renderRouter); router.use('/submitForPortalAction', submitForPortalAction); router.post("/loadPortalForm", loadPortalIntegratedForm); + module.exports = router; \ No newline at end of file From c7859fd9ec8ed3abfb728318598d37711ecaf5b5 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 8 Aug 2025 17:11:30 -0700 Subject: [PATCH 25/40] Process clob data data. --- icmJsonClobHandler.js | 87 ++++++++++++++++++++++++++----------------- 1 file changed, 52 insertions(+), 35 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 1cc6424..4c2b64d 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -1,75 +1,92 @@ const axios = require("axios"); const { keycloakForSiebel } = require("./keycloak.js"); -function processICMJsonItem(item, index) { - console.log(">>> ICMJsonClob (raw):\n\n", item.ICMJsonClob); - console.log("--------------------------------------------------"); - - const trimmedClob = item.ICMJsonClob.trim(); - if (!trimmedClob.startsWith('{') && !trimmedClob.startsWith('[')) { - console.log(`Item ${index + 1}: ICMJsonClob is not a valid JSON`); - return; +function processIcmJsonClobData(jsonData) { + // Create a copy to avoid mutating the original instance + const processedData = JSON.parse(JSON.stringify(jsonData)); + + if (processedData.items && Array.isArray(processedData.items)) { + processedData.items = processedData.items.map(item => { + if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { + try { + item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); + delete item.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${item.Id}: ${error.message}`); + item.ICMJsonClobParsed = null; + } + } + + return item; + }); } + else { + if (processedData.ICMJsonClob && typeof processedData.ICMJsonClob === 'string') { + try { + processedData.ICMJsonClobParsed = JSON.parse(processedData.ICMJsonClob); + delete processedData.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${processedData.Id}: ${error.message}`); + processedData.ICMJsonClobParsed = null; + } + } - try { - const parsedClob = JSON.parse(item.ICMJsonClob); - console.log("ICMJsonClob (parsed):\n\n", JSON.stringify(parsedClob, null, 2)); - } catch (parseError) { - console.log(`Item ${index + 1}: Failed to parse JSON: ${parseError.message}`); + return processedData; } + + return processedData; } -async function getData() { +async function fetchIcmJsonClobData(attachmentId) { try { const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); + const username = process.env.SIEBEL_ICM_TRUSTED_USERNAME; + const headers = { Authorization: `Bearer ${grant.id_token.token}`, - "X-ICM-TrustedUsername": "DMEIRELE", + "X-ICM-TrustedUsername": username, "Content-Type": "application/json" }; - const url = "https://sieblabm.apps.gov.bc.ca/ffdy/v1.0/data/ICMJSONClobBO/ICMJSONClobBC" + const url = process.env.SIEBEL_ICM_BASE_URL; const queryParams = { ViewMode: "Catalog", - workspace: "dev_sadmin_bz_1", - searchspec: `("SR Id" = "1-54SIG7F")` + searchspec: attachmentId // "UserFieldCLOB"="1-123ABC" }; - const response = await axios.get(url, { + return await axios.get(url, { headers, - params: queryParams + params: queryParams, + timeout: 30000 }); - console.log("Status:", response.status + " " + response.statusText); + } catch (error) { + throw new Error(`Failed to fetch ICM data for SR ID ${attachmentId}: ${error.message}`); + } +} - if (response.data && response.data.items && Array.isArray(response.data.items)) { - response.data.items.forEach((item, index) => { - processICMJsonItem(item, index); - }); - } else { - console.log("No items found in response"); - } +async function getProcessedData(attachmentId) { + try { + const response = await fetchIcmJsonClobData(attachmentId); + const processedResult = processIcmJsonClobData(response.data); return { success: true, - totalItems: response.data?.items?.length || 0, - statusCode: response.status, - statusText: response.statusText + data: processedResult }; } catch (error) { console.error("API call failed:", error.message); + return { success: false, - error: error.message, - totalItems: 0, - errors: [error.message] + error: error.message }; } } module.exports = { - getData + getProcessedData }; \ No newline at end of file From 7007a8240072a1ec8e2af676f58a7876b093638f Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 18:48:11 -0700 Subject: [PATCH 26/40] Update router.js --- routes.js | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/routes.js b/routes.js index 624616a..00a8d82 100644 --- a/routes.js +++ b/routes.js @@ -13,7 +13,7 @@ const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); const getFormsFromFormTemplate = require("./formRepoHandler"); -const {getData} = require("./icmJsonClobHandler"); +const {getProcessedData} = require("./icmJsonClobHandler"); const router = express.Router(); const FORM_SERVER_URL = process.env.FORMSERVERURL; @@ -154,24 +154,18 @@ router.get("/getAllForms", async (request, response) => { }); router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { + try { - const result = await getData(); + const result = await getProcessedData(req.query.attachmentId); if (result.success) { - res.status(200).json({ - message: "Success." - }); + res.status(200).json(result.data); } else { - res.status(500).json({ - error: "Error." - }); + res.status(500).json({ error: "Error." }); } } catch (error) { console.error("Error:", error.message); - res.status(500).json({ - error: "Internal server error", - message: error.message - }); + res.status(500).json({ error: "Internal server error", message: error.message }); } }); From 5c9e4493e292ad63e6d9e933bdaf8bd0248e830b Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 18:48:55 -0700 Subject: [PATCH 27/40] Refactor processIcmJsonClobData --- icmJsonClobHandler.js | 42 ++++++++++++++++-------------------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 4c2b64d..83906d8 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -1,40 +1,30 @@ const axios = require("axios"); const { keycloakForSiebel } = require("./keycloak.js"); +function parseIcmJsonClob(item) { + if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { + try { + item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); + delete item.ICMJsonClob; + } catch (error) { + console.warn(`Failed to parse ICMJsonClob for item ${item.Id || 'unknown'}: ${error.message}`); + item.ICMJsonClobParsed = null; + } + } + return item; +} + function processIcmJsonClobData(jsonData) { + // Create a copy to avoid mutating the original instance const processedData = JSON.parse(JSON.stringify(jsonData)); if (processedData.items && Array.isArray(processedData.items)) { - processedData.items = processedData.items.map(item => { - if (item.ICMJsonClob && typeof item.ICMJsonClob === 'string') { - try { - item.ICMJsonClobParsed = JSON.parse(item.ICMJsonClob); - delete item.ICMJsonClob; - } catch (error) { - console.warn(`Failed to parse ICMJsonClob for item ${item.Id}: ${error.message}`); - item.ICMJsonClobParsed = null; - } - } - - return item; - }); - } - else { - if (processedData.ICMJsonClob && typeof processedData.ICMJsonClob === 'string') { - try { - processedData.ICMJsonClobParsed = JSON.parse(processedData.ICMJsonClob); - delete processedData.ICMJsonClob; - } catch (error) { - console.warn(`Failed to parse ICMJsonClob for item ${processedData.Id}: ${error.message}`); - processedData.ICMJsonClobParsed = null; - } - } - + processedData.items = processedData.items.map(parseIcmJsonClob); return processedData; } - return processedData; + return parseIcmJsonClob(processedData); } async function fetchIcmJsonClobData(attachmentId) { From d5d83452ca9c69ef5ccc833fc4ac8ac174aa4a54 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Sat, 9 Aug 2025 23:04:50 -0700 Subject: [PATCH 28/40] Code cleanup. --- routes.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/routes.js b/routes.js index 00a8d82..1b2e0d1 100644 --- a/routes.js +++ b/routes.js @@ -11,8 +11,7 @@ const {generatePDFFromHTML,generatePDFFromURL,generatePDFFromJSON,loadSavedJson const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegratedHandler.js"); const submitForPortalAction = require("./portal/savePortalFormDataHandler.js"); const loadPortalIntegratedForm = require("./portal/loadPortalIntegratedHandler.js"); - -const getFormsFromFormTemplate = require("./formRepoHandler"); +require("./formRepoHandler"); const {getProcessedData} = require("./icmJsonClobHandler"); const router = express.Router(); @@ -154,7 +153,6 @@ router.get("/getAllForms", async (request, response) => { }); router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { - try { const result = await getProcessedData(req.query.attachmentId); From c7cd6eac41f80a288bd263039f878b7fcce0263a Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Mon, 11 Aug 2025 09:22:27 -0700 Subject: [PATCH 29/40] Use existing settings for siebel url and username --- icmJsonClobHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 83906d8..4fcd622 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -31,7 +31,7 @@ async function fetchIcmJsonClobData(attachmentId) { try { const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); - const username = process.env.SIEBEL_ICM_TRUSTED_USERNAME; + const username = process.env.TRUSTED_USERNAME; const headers = { Authorization: `Bearer ${grant.id_token.token}`, @@ -39,7 +39,7 @@ async function fetchIcmJsonClobData(attachmentId) { "Content-Type": "application/json" }; - const url = process.env.SIEBEL_ICM_BASE_URL; + const url = process.env.SIEBEL_ICM_API_HOST; const queryParams = { ViewMode: "Catalog", From 82dfe4081be4c2de3b7d75bfbb6e90cc0556d805 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Tue, 12 Aug 2025 11:16:53 -0700 Subject: [PATCH 30/40] Fix URL path --- icmJsonClobHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 4fcd622..b5ee5c0 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -39,7 +39,7 @@ async function fetchIcmJsonClobData(attachmentId) { "Content-Type": "application/json" }; - const url = process.env.SIEBEL_ICM_API_HOST; + const url = `${process.env.SIEBEL_ICM_API_HOST}${process.env.ICM_JSON_CLOB_ENDPONT}`; const queryParams = { ViewMode: "Catalog", From c6ccad03f5c7e0dceda97e708f8218dc508042da Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Fri, 15 Aug 2025 13:35:22 -0700 Subject: [PATCH 31/40] Update databindings host/endpoint processing --- databindingsHandler.js | 43 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index 8861a7a..b5d27e6 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -40,6 +40,7 @@ async function fetchDataFromSources(dataSources, params) { let updatedParams = updateParams(source.params || {}, params, data); const response = await readJsonFormApi(source, { ...params, ...updatedParams }); + console.log("Response:",response); data[source.name] = response; } catch (error) { @@ -199,6 +200,7 @@ async function readJsonFormApi(datasource, pathParams) { } // Store response data + console.log("Response:",response); return ensureObjectOrArray(response.data); } catch (error) { @@ -210,6 +212,10 @@ async function readJsonFormApi(datasource, pathParams) { function buildUrlWithParams(host, endpoint, pathVariables) { const hostFromEnv = getHost(pathVariables,host); const endpointFromEnv = getEndpoint(endpoint); + + if (!hostFromEnv) throw new Error("API host not resolved"); + if (!endpointFromEnv) throw new Error("API endpoint not resolved"); + let url = `${hostFromEnv}${endpointFromEnv}`; // Replace any placeholder variables like @@attachmentId Object.keys(pathVariables).forEach(key => { @@ -222,18 +228,23 @@ function buildUrlWithParams(host, endpoint, pathVariables) { function getHost(params, host) { - // Use host from environment variable if available, otherwise fall back to JSON - try { - return params["apiHost"]; - } catch { - return process.env[host]; + // Use process.env if present + const fromEnv = resolveMaybeEnv(host); + if (fromEnv) return fromEnv; + + //Fall back to path params + if (params && typeof params.apiHost === "string" && params.apiHost.trim()) { + return params.apiHost.trim(); } + + return host ? (process.env[host] ?? host) : null; } function getEndpoint(endpoint) { // Use endpoint from environment variable if available, otherwise fall back to JSON - return process.env[endpoint] || endpoint; + return resolveMaybeEnv(endpoint) ?? endpoint; } + function buildBodyWithParams(bodyFromJson, pathVariables) { let bodyString = JSON.stringify(bodyFromJson); @@ -316,5 +327,25 @@ function updateParams(params, pathParams = {}, allFetchedData = {}) { return updated; } +function resolveMaybeEnv(str) { + if (typeof str !== "string" || !str) { + return null; + } + + // Check if value is in the form "process.env.VAR_NAME" + const envPattern = /^process\.env\.(.+)$/; + const match = str.match(envPattern); + + if (!match) { + return str; + } + + const envVarName = match[1]; + const envValue = process.env[envVarName]; + + return envValue != null ? envValue : str; +} + + module.exports.populateDatabindings = populateDatabindings; module.exports.buildUrlWithParams = buildUrlWithParams; From 6fac249d27daa2d2524c69f77ab7e851855e0bf0 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Fri, 15 Aug 2025 14:46:26 -0700 Subject: [PATCH 32/40] Update the attachement id value. --- icmJsonClobHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index b5ee5c0..0d47e7f 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -43,7 +43,7 @@ async function fetchIcmJsonClobData(attachmentId) { const queryParams = { ViewMode: "Catalog", - searchspec: attachmentId // "UserFieldCLOB"="1-123ABC" + SearchSpec: `UserFieldCLOB=\"${attachmentId}\"` }; return await axios.get(url, { From f09d46aa4342decd517e8fec5f4f8185200145fd Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Mon, 18 Aug 2025 09:09:44 -0700 Subject: [PATCH 33/40] Create a second endpoint with only the answers in the response --- icmJsonClobHandler.js | 23 ++++++++++++++++++++--- routes.js | 17 ++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 0d47e7f..2a9aace 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -57,16 +57,33 @@ async function fetchIcmJsonClobData(attachmentId) { } } -async function getProcessedData(attachmentId) { +function extractICMJsonClobParsed(data) { + if (data.items && Array.isArray(data.items)) { + return data.items + .map(item => item.ICMJsonClobParsed) + .filter(parsed => parsed !== null && parsed !== undefined); + } + + if (data.ICMJsonClobParsed !== null && data.ICMJsonClobParsed !== undefined) { + return data.ICMJsonClobParsed; + } + + return []; +} + +async function getProcessedData(attachmentId, returnAnswersOnly = true) { try { const response = await fetchIcmJsonClobData(attachmentId); - const processedResult = processIcmJsonClobData(response.data); + let processedResult = processIcmJsonClobData(response.data); + + if (returnAnswersOnly) { + processedResult = extractICMJsonClobParsed(processedResult); + } return { success: true, data: processedResult }; - } catch (error) { console.error("API call failed:", error.message); diff --git a/routes.js b/routes.js index 1b2e0d1..01c3fc5 100644 --- a/routes.js +++ b/routes.js @@ -154,7 +154,7 @@ router.get("/getAllForms", async (request, response) => { router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { try { - const result = await getProcessedData(req.query.attachmentId); + const result = await getProcessedData(req.query.attachmentId, false); if (result.success) { res.status(200).json(result.data); @@ -167,6 +167,21 @@ router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { } }); +router.get("/processIcmJsonClob2", localhostOnlyMiddleware, async (req, res) => { + try { + const result = await getProcessedData(req.query.attachmentId, true); + + if (result.success) { + res.status(200).json(result.data); + } else { + res.status(500).json({ error: "Error." }); + } + } catch (error) { + console.error("Error:", error.message); + res.status(500).json({ error: "Internal server error", message: error.message }); + } +}); + // clear the locked by flags in ICM for the form, used when form is closed router.post("/clearICMLockedFlag", clearICMLockedFlag); From 37fdee5e5c10202fb5c328f2748161518b586bf4 Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Mon, 18 Aug 2025 09:12:48 -0700 Subject: [PATCH 34/40] Rename answers endpoint --- routes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routes.js b/routes.js index 01c3fc5..656b4ea 100644 --- a/routes.js +++ b/routes.js @@ -167,7 +167,7 @@ router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { } }); -router.get("/processIcmJsonClob2", localhostOnlyMiddleware, async (req, res) => { +router.get("/processIcmJsonClobAnswers", localhostOnlyMiddleware, async (req, res) => { try { const result = await getProcessedData(req.query.attachmentId, true); From 308dd8149f1b66c92f25b0a24eb251e7aabb88da Mon Sep 17 00:00:00 2001 From: Danilo Meireles Date: Mon, 18 Aug 2025 10:37:00 -0700 Subject: [PATCH 35/40] Update response object. --- icmJsonClobHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 2a9aace..0b291f4 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -60,12 +60,12 @@ async function fetchIcmJsonClobData(attachmentId) { function extractICMJsonClobParsed(data) { if (data.items && Array.isArray(data.items)) { return data.items - .map(item => item.ICMJsonClobParsed) + .map(item => item.ICMJsonClobParsed.Answers) .filter(parsed => parsed !== null && parsed !== undefined); } if (data.ICMJsonClobParsed !== null && data.ICMJsonClobParsed !== undefined) { - return data.ICMJsonClobParsed; + return data.ICMJsonClobParsed.Answers; } return []; From c373f4640262ed6e97f72874ba95b4b7d46328a3 Mon Sep 17 00:00:00 2001 From: NicolaSDPR1 Date: Tue, 19 Aug 2025 09:55:02 -0700 Subject: [PATCH 36/40] Quick fix using XMLDEC to ensure the beginning xml tag does not include standalone and encoding --- saveICMdataHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/saveICMdataHandler.js b/saveICMdataHandler.js index f6e719c..9834e0e 100644 --- a/saveICMdataHandler.js +++ b/saveICMdataHandler.js @@ -155,7 +155,7 @@ async function saveICMdata(req, res) { truncatedKeysSaveData[newKey] = saveData[oldKey]; //Data is added to new JSON with the truncated key } } - let builder = new xml2js.Builder(); + let builder = new xml2js.Builder({xmldec: { version: '1.0' }}); saveJson["XML Hierarchy"] = builder.buildObject(truncatedKeysSaveData); //let url = buildUrlWithParams('SIEBEL_ICM_API_HOST', 'fwd/v1.0/data/DT Form Instance Thin/DT Form Instance Thin/' + attachment_id + '/', ''); let url = buildUrlWithParams(params["apiHost"], params["saveEndpoint"] + attachment_id + '/', params); From dfa66b7138e27f70b8958972e34af29ef3feaac1 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Wed, 20 Aug 2025 09:01:55 -0700 Subject: [PATCH 37/40] Remove answer drilldown --- icmJsonClobHandler.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 0b291f4..2a9aace 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -60,12 +60,12 @@ async function fetchIcmJsonClobData(attachmentId) { function extractICMJsonClobParsed(data) { if (data.items && Array.isArray(data.items)) { return data.items - .map(item => item.ICMJsonClobParsed.Answers) + .map(item => item.ICMJsonClobParsed) .filter(parsed => parsed !== null && parsed !== undefined); } if (data.ICMJsonClobParsed !== null && data.ICMJsonClobParsed !== undefined) { - return data.ICMJsonClobParsed.Answers; + return data.ICMJsonClobParsed; } return []; From c1568092cb21561a8d19192bbac97e6e55fd2a53 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Wed, 20 Aug 2025 14:11:23 -0700 Subject: [PATCH 38/40] Use apiHost for portal endpoints --- databindingsHandler.js | 1 + icmJsonClobHandler.js | 8 ++++---- routes.js | 7 +++++-- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index b5d27e6..c14b2cc 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -188,6 +188,7 @@ async function readJsonFormApi(datasource, pathParams) { const headers = { Authorization: `Bearer ${grant.id_token.token}`, "X-ICM-TrustedUsername": username, + "X-API-Host": apiHost, } if (type.toUpperCase() === 'GET') { // For GET requests, add params directly in axios config diff --git a/icmJsonClobHandler.js b/icmJsonClobHandler.js index 2a9aace..a17dcd3 100644 --- a/icmJsonClobHandler.js +++ b/icmJsonClobHandler.js @@ -27,7 +27,7 @@ function processIcmJsonClobData(jsonData) { return parseIcmJsonClob(processedData); } -async function fetchIcmJsonClobData(attachmentId) { +async function fetchIcmJsonClobData(attachmentId, apiHost) { try { const grant = await keycloakForSiebel.grantManager.obtainFromClientCredentials(); @@ -39,7 +39,7 @@ async function fetchIcmJsonClobData(attachmentId) { "Content-Type": "application/json" }; - const url = `${process.env.SIEBEL_ICM_API_HOST}${process.env.ICM_JSON_CLOB_ENDPONT}`; + const url = `${apiHost}${process.env.ICM_JSON_CLOB_ENDPONT}`; const queryParams = { ViewMode: "Catalog", @@ -71,9 +71,9 @@ function extractICMJsonClobParsed(data) { return []; } -async function getProcessedData(attachmentId, returnAnswersOnly = true) { +async function getProcessedData(attachmentId, apiHost, returnAnswersOnly = true) { try { - const response = await fetchIcmJsonClobData(attachmentId); + const response = await fetchIcmJsonClobData(attachmentId, apiHost); let processedResult = processIcmJsonClobData(response.data); if (returnAnswersOnly) { diff --git a/routes.js b/routes.js index 656b4ea..0cbd7f2 100644 --- a/routes.js +++ b/routes.js @@ -6,6 +6,7 @@ const { generateTemplate,generateNewTemplate } = require("./generateHandler"); const { saveICMdata, loadICMdata, clearICMLockedFlag } = require("./saveICMdataHandler"); const { getUsername } = require("./usernameHandler.js"); const renderRouter = require("./renderHandler"); +const appCfg = require('./appConfig.js'); const {generatePDFFromHTML,generatePDFFromURL,generatePDFFromJSON,loadSavedJson } = require("./generatePDFHandler"); const generatePortalIntegratedTemplate = require("./portal/generatePortalIntegratedHandler.js"); @@ -154,7 +155,8 @@ router.get("/getAllForms", async (request, response) => { router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { try { - const result = await getProcessedData(req.query.attachmentId, false); + const apiHost = req.get("X-API-Host"); + const result = await getProcessedData(req.query.attachmentId, apiHost, false); if (result.success) { res.status(200).json(result.data); @@ -169,7 +171,8 @@ router.get("/processIcmJsonClob", localhostOnlyMiddleware, async (req, res) => { router.get("/processIcmJsonClobAnswers", localhostOnlyMiddleware, async (req, res) => { try { - const result = await getProcessedData(req.query.attachmentId, true); + const apiHost = req.get("X-API-Host"); + const result = await getProcessedData(req.query.attachmentId, apiHost, true); if (result.success) { res.status(200).json(result.data); From 0ad7e44317cbebc4e27c9ffb87a06aa12d507d63 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Thu, 21 Aug 2025 10:48:38 -0700 Subject: [PATCH 39/40] Update resource limits --- helm/values.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/helm/values.yaml b/helm/values.yaml index 650c430..4d65148 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -11,7 +11,13 @@ service: configMapName: commlayer -resources: {} +resources: + requests: + memory: "128Mi" + cpu: "50m" + limits: + memory: "512Mi" + cpu: "250m" nodeSelector: {} From 64dc7aca6a90bfdf76ba72d55a2b38c383fef668 Mon Sep 17 00:00:00 2001 From: David Okulski <32730627+DavidOkulski@users.noreply.github.com> Date: Thu, 21 Aug 2025 13:40:28 -0700 Subject: [PATCH 40/40] remove logging --- databindingsHandler.js | 4 ++-- routes.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/databindingsHandler.js b/databindingsHandler.js index c14b2cc..1a3a65e 100644 --- a/databindingsHandler.js +++ b/databindingsHandler.js @@ -40,7 +40,7 @@ async function fetchDataFromSources(dataSources, params) { let updatedParams = updateParams(source.params || {}, params, data); const response = await readJsonFormApi(source, { ...params, ...updatedParams }); - console.log("Response:",response); + //console.log("Response:",response); data[source.name] = response; } catch (error) { @@ -201,7 +201,7 @@ async function readJsonFormApi(datasource, pathParams) { } // Store response data - console.log("Response:",response); + //console.log("Response:",response); return ensureObjectOrArray(response.data); } catch (error) { diff --git a/routes.js b/routes.js index 0cbd7f2..3a1a28a 100644 --- a/routes.js +++ b/routes.js @@ -120,7 +120,7 @@ router.get("/xml", async (req, res) => { // Save data route router.post("/saveData", async (request, response) => { - console.log(request.body); + //console.log(request.body); response.status(200).json("{success!}"); }); @@ -143,7 +143,7 @@ router.get("/getAllForms", async (request, response) => { Authorization: `Bearer ${grant.access_token.token}`, }, }); - console.log("forms", forms); + //console.log("forms", forms); response.json(forms.data); } catch (err) {