From 312f7415f500eb33bda000aab76c6588558497bf Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 06:40:32 +0000 Subject: [PATCH 01/14] TASK22229: pilot shared relay forwarding utility --- memory-bank/change-log.md | 39 +++++++++ .../getawaitingsubmissionproxy_api.js | 48 ++++------- .../endpoint/getmyrepresentationsproxy_api.js | 40 +++------ .../endpoint/getrepresentationsproxy_api.js | 40 +++------ .../api/endpoint/getwatchedcasesproxy_api.js | 83 ++++++++----------- pages/api/middleware/relayForwarding.js | 33 ++++++++ .../endpoint-handler-contract.test.cjs | 12 +++ 7 files changed, 162 insertions(+), 133 deletions(-) create mode 100644 pages/api/middleware/relayForwarding.js diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index d1274006..ffcdef22 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -725,3 +725,42 @@ Validation: Follow-ups: - Optional future auth hygiene (separate guarded slice): replace verbose auth `console.log` diagnostics with structured logger usage once production logging requirements are confirmed. + +--- + +### CL-019: TASK22229 P2-S1 relay forwarding pilot (proxy endpoint cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/middleware/relayForwarding.js`, `pages/api/endpoint/{getwatchedcasesproxy_api,getmyrepresentationsproxy_api,getrepresentationsproxy_api,getawaitingsubmissionproxy_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Start Priority-2 by piloting a shared relay forwarding helper on a bounded proxy endpoint cluster to reduce duplicated token/hash/header/axios boilerplate while preserving endpoint contracts. +impact: Maintainability and consistency improvement with no intended behavioral contract changes; error code/status/message and success payload contracts remain unchanged for migrated endpoints. +status: completed + +Summary: + +- Added shared relay helper: + - `pages/api/middleware/relayForwarding.js` + - exports `relayGet({ queryUrl, res, errorResponse, transformData })` + - centralizes token fetch, relay URL + hash composition, `azureHeaders`, GET execution, success/error response handling, and logging +- Migrated pilot endpoint cluster to `relayGet`: + - `getwatchedcasesproxy_api.js` (with existing payload transform preserved) + - `getmyrepresentationsproxy_api.js` + - `getrepresentationsproxy_api.js` + - `getawaitingsubmissionproxy_api.js` (with existing payload transform preserved) +- Updated endpoint contract tests to mock `relayGet` for migrated routes while preserving existing assertions. + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 53/53 + - email-handler: 12/12 + - endpoint-handler: 152/152 + - documents-handler: 3/3 + +Follow-ups: + +- P2-S2 rollout: migrate the next relay-heavy endpoint batch onto `relayGet`/shared forwarding utility pattern. +- P2-S3 hardening: add shared timeout/retry config and redacted structured relay error logging policy. diff --git a/pages/api/endpoint/getawaitingsubmissionproxy_api.js b/pages/api/endpoint/getawaitingsubmissionproxy_api.js index 3654ba02..51daf835 100644 --- a/pages/api/endpoint/getawaitingsubmissionproxy_api.js +++ b/pages/api/endpoint/getawaitingsubmissionproxy_api.js @@ -10,16 +10,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -35,29 +27,25 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + - loggedInUserId + - " and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + + loggedInUserId + + " and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + return relayGet({ + queryUrl, + res, + transformData: (data) => { + data.value.forEach(function (element) { + element.pinswg_title = element.title; + }); - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "AWAITING_SUBMISSION_PROXY_FETCH_FAILED", message: "Failed to fetch awaiting submission proxy details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getmyrepresentationsproxy_api.js b/pages/api/endpoint/getmyrepresentationsproxy_api.js index df6da9e3..ecb543f8 100644 --- a/pages/api/endpoint/getmyrepresentationsproxy_api.js +++ b/pages/api/endpoint/getmyrepresentationsproxy_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,26 +34,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_representationses?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$count=true&$orderby=createdon desc"; - const queryUrl = - "pinswg_representationses?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$count=true&$orderby=createdon desc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "MY_REPRESENTATIONS_PROXY_FETCH_FAILED", message: "Failed to fetch my representations proxy details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getrepresentationsproxy_api.js b/pages/api/endpoint/getrepresentationsproxy_api.js index 88bf23c8..de788923 100644 --- a/pages/api/endpoint/getrepresentationsproxy_api.js +++ b/pages/api/endpoint/getrepresentationsproxy_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const incidentID = req.query.incidentID; @@ -39,26 +31,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_representationses?$filter= _pinswg_case_value eq " + + incidentID + + "&$count=true&$orderby=createdon desc"; - const queryUrl = - "pinswg_representationses?$filter= _pinswg_case_value eq " + - incidentID + - "&$count=true&$orderby=createdon desc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "REPRESENTATIONS_PROXY_FETCH_FAILED", message: "Failed to fetch representations proxy details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getwatchedcasesproxy_api.js b/pages/api/endpoint/getwatchedcasesproxy_api.js index 3eb2f147..ff455650 100644 --- a/pages/api/endpoint/getwatchedcasesproxy_api.js +++ b/pages/api/endpoint/getwatchedcasesproxy_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,51 +34,48 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "pinswg_watchlists?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$select=modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; + const queryUrl = + "pinswg_watchlists?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$select=modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + return relayGet({ + queryUrl, + res, + transformData: (data) => { + data.value.forEach(function (element) { + element.ticketnumber = element.pinswg_WatchedCase.ticketnumber; + element.pinswg_title = + element[ + "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" + ]; - data.value.forEach(function (element) { - element.ticketnumber = element.pinswg_WatchedCase.ticketnumber; - element.pinswg_title = element[ - "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" - ]; - - element[ - "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase[ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ]; - element._pinswg_associatedlpa_value = - element.pinswg_WatchedCase._pinswg_associatedlpa_value; - element[ - "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase[ + ] = + element.pinswg_WatchedCase[ + "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" + ]; + element._pinswg_associatedlpa_value = + element.pinswg_WatchedCase._pinswg_associatedlpa_value; + element[ "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ]; - element._ownerid_value = element.pinswg_WatchedCase._ownerid_value; + ] = + element.pinswg_WatchedCase[ + "_ownerid_value@OData.Community.Display.V1.FormattedValue" + ]; + element._ownerid_value = + element.pinswg_WatchedCase._ownerid_value; - delete element.pinswg_WatchedCase; - }); + delete element.pinswg_WatchedCase; + }); - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "WATCHED_CASES_PROXY_FETCH_FAILED", message: "Failed to fetch watched cases proxy details" - }); - } + } + }); } diff --git a/pages/api/middleware/relayForwarding.js b/pages/api/middleware/relayForwarding.js new file mode 100644 index 00000000..2130a063 --- /dev/null +++ b/pages/api/middleware/relayForwarding.js @@ -0,0 +1,33 @@ +import axios from "axios"; +import { azureHeaders } from "../../../actions/core/headers"; +import { consoleLogger } from "../../../actions/core/logger"; +import { getToken } from "../../../actions/core/token"; +import { hashAPIPath } from "../../../actions/core/hash"; +import { respondError, respondSuccess } from "./apiResponse"; + +const WEBAPI_URL = + process.env.RELAY_ROOT || + "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; + +export const relayGet = async ({ + queryUrl, + res, + errorResponse, + transformData +}) => { + try { + const token = await getToken(); + const { data } = await axios.get( + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + azureHeaders(token.access_token) + ); + + return respondSuccess( + res, + typeof transformData === "function" ? transformData(data) : data + ); + } catch (error) { + consoleLogger(error); + return respondError(res, errorResponse); + } +}; diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 8e165eba..5e9f8f56 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -533,6 +533,7 @@ test("getwatchedcasesproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserI const mod = loadModule("pages/api/endpoint/getwatchedcasesproxy_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -555,6 +556,8 @@ test("getwatchedcasesproxy catch path returns WATCHED_CASES_PROXY_FETCH_FAILED", const mod = loadModule("pages/api/endpoint/getwatchedcasesproxy_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -583,6 +586,7 @@ test("getmyrepresentationsproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedIn { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -608,6 +612,8 @@ test("getmyrepresentationsproxy catch path returns MY_REPRESENTATIONS_PROXY_FETC { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -637,6 +643,7 @@ test("getrepresentationsproxy returns INCIDENT_ID_REQUIRED when incidentID missi { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -659,6 +666,8 @@ test("getrepresentationsproxy catch path returns REPRESENTATIONS_PROXY_FETCH_FAI { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -688,6 +697,7 @@ test("getawaitingsubmissionproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedI { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -713,6 +723,8 @@ test("getawaitingsubmissionproxy catch path returns AWAITING_SUBMISSION_PROXY_FE { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), From 6edbc431f86dbef290e5ddec39c5c9c6bbbecd9f Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 06:58:50 +0000 Subject: [PATCH 02/14] TASK22229: P2-S2 batch 1 migrate account/login relay GET endpoints --- memory-bank/change-log.md | 37 ++++++++++++++++ pages/api/endpoint/getaccounts_api.js | 38 ++++++----------- .../api/endpoint/getemailaccountcheck_api.js | 38 ++++++----------- pages/api/endpoint/getlogin_api.js | 42 +++++++------------ pages/api/endpoint/getportalloginproxy_api.js | 38 ++++++----------- .../api/endpoint/getpreferredlanguage_api.js | 39 ++++++----------- pages/api/middleware/relayForwarding.js | 7 +++- .../endpoint-handler-contract.test.cjs | 33 +++++++++++++++ 8 files changed, 139 insertions(+), 133 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index ffcdef22..f187d393 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -764,3 +764,40 @@ Follow-ups: - P2-S2 rollout: migrate the next relay-heavy endpoint batch onto `relayGet`/shared forwarding utility pattern. - P2-S3 hardening: add shared timeout/retry config and redacted structured relay error logging policy. + +--- + +### CL-020: TASK22229 P2-S2 Batch 1 (account/login relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/middleware/relayForwarding.js`, `pages/api/endpoint/{getaccounts_api,getemailaccountcheck_api,getpreferredlanguage_api,getlogin_api,getpersonalaccount_api,getportalloginproxy_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Deliver first P2-S2 batch as a dedicated commit by migrating a bounded account/login endpoint cluster onto shared `relayGet` while preserving response contracts. +impact: Reduced relay boilerplate and improved consistency with no intended endpoint contract changes. +status: completed + +Summary: + +- Extended `relayGet` to support optional `requestOptionsBuilder` for handlers requiring paged header variants. +- Migrated Batch 1 endpoints to `relayGet`: + - `getaccounts_api.js` + - `getemailaccountcheck_api.js` + - `getpreferredlanguage_api.js` + - `getlogin_api.js` (uses `azureHeadersPaged` via `requestOptionsBuilder`) + - `getpersonalaccount_api.js` + - `getportalloginproxy_api.js` (uses `azureHeadersPaged` via `requestOptionsBuilder`) +- Updated phase21 endpoint contract tests to mock `relayGet` for migrated handlers, preserving existing guard/catch/success assertions. + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 53/53 + - email-handler: 12/12 + - endpoint-handler: 152/152 + - documents-handler: 3/3 + +Follow-ups: + +- Continue P2-S2 with Batch 2 as next dedicated commit on this same branch. diff --git a/pages/api/endpoint/getaccounts_api.js b/pages/api/endpoint/getaccounts_api.js index e48cb56c..f1316370 100644 --- a/pages/api/endpoint/getaccounts_api.js +++ b/pages/api/endpoint/getaccounts_api.js @@ -15,16 +15,8 @@ * 200: * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = req.query.emailAddress; @@ -37,24 +29,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - emailAddress + - "'&$count=true&$select=emailaddress1, contactid"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + emailAddress + + "'&$count=true&$select=emailaddress1, contactid"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "ACCOUNTS_FETCH_FAILED", message: "Failed to fetch accounts" - }); - } + } + }); } diff --git a/pages/api/endpoint/getemailaccountcheck_api.js b/pages/api/endpoint/getemailaccountcheck_api.js index ce07f52f..1751d5eb 100644 --- a/pages/api/endpoint/getemailaccountcheck_api.js +++ b/pages/api/endpoint/getemailaccountcheck_api.js @@ -15,16 +15,8 @@ * 200: * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = req.query.emailAddress; @@ -37,24 +29,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - emailAddress + - "'&$count=true&$select=emailaddress1, contactid"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + emailAddress + + "'&$count=true&$select=emailaddress1, contactid"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "EMAIL_ACCOUNT_CHECK_FAILED", message: "Failed to check email account" - }); - } + } + }); } diff --git a/pages/api/endpoint/getlogin_api.js b/pages/api/endpoint/getlogin_api.js index bac72976..cf828194 100644 --- a/pages/api/endpoint/getlogin_api.js +++ b/pages/api/endpoint/getlogin_api.js @@ -21,16 +21,9 @@ * description: hello world */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = req.query.emailAddress; @@ -52,26 +45,21 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - emailAddress + - "' and pinswg_custom_password eq '" + - pwd + - "'&$count=true&$select=emailaddress1,contactid,pinswg_custom_password,yomifullname,firstname,lastname"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + emailAddress + + "' and pinswg_custom_password eq '" + + pwd + + "'&$count=true&$select=emailaddress1,contactid,pinswg_custom_password,yomifullname,firstname,lastname"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "LOGIN_FETCH_FAILED", message: "Failed to fetch login details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getportalloginproxy_api.js b/pages/api/endpoint/getportalloginproxy_api.js index 0a1a3b61..b1c734d1 100644 --- a/pages/api/endpoint/getportalloginproxy_api.js +++ b/pages/api/endpoint/getportalloginproxy_api.js @@ -16,16 +16,9 @@ * description: hello world */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = req.query.emailAddress; @@ -38,24 +31,19 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - emailAddress + - "'&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + emailAddress + + "'&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "PORTAL_LOGIN_PROXY_FETCH_FAILED", message: "Failed to fetch portal login proxy details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getpreferredlanguage_api.js b/pages/api/endpoint/getpreferredlanguage_api.js index ddc7ae4d..0051dc77 100644 --- a/pages/api/endpoint/getpreferredlanguage_api.js +++ b/pages/api/endpoint/getpreferredlanguage_api.js @@ -15,21 +15,13 @@ * 200: * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; import { escapeODataString, isNonEmptyString, sanitizeString } from "../../../actions/core/guards"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = sanitizeString(req.query.emailAddress); @@ -42,25 +34,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - escapeODataString(emailAddress) + - "'&$count=true&$select=pinswg_preferredlanguage,contactid"; + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + escapeODataString(emailAddress) + + "'&$count=true&$select=pinswg_preferredlanguage,contactid"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "PREFERRED_LANGUAGE_FETCH_FAILED", message: "Failed to fetch preferred language" - }); - } + } + }); } diff --git a/pages/api/middleware/relayForwarding.js b/pages/api/middleware/relayForwarding.js index 2130a063..45f07dd8 100644 --- a/pages/api/middleware/relayForwarding.js +++ b/pages/api/middleware/relayForwarding.js @@ -13,13 +13,16 @@ export const relayGet = async ({ queryUrl, res, errorResponse, - transformData + transformData, + requestOptionsBuilder }) => { try { const token = await getToken(); const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) + typeof requestOptionsBuilder === "function" + ? requestOptionsBuilder(token.access_token) + : azureHeaders(token.access_token) ); return respondSuccess( diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 5e9f8f56..41b958f9 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -13,6 +13,7 @@ test("getaccounts returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", asy const mod = loadModule("pages/api/endpoint/getaccounts_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -32,6 +33,8 @@ test("getaccounts catch path returns ACCOUNTS_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getaccounts_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -55,6 +58,7 @@ test("getemailaccountcheck returns EMAIL_ADDRESS_REQUIRED when emailAddress miss const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -74,6 +78,8 @@ test("getemailaccountcheck catch path returns EMAIL_ACCOUNT_CHECK_FAILED", async const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -100,6 +106,7 @@ test("getpreferredlanguage returns EMAIL_ADDRESS_REQUIRED when emailAddress miss const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), sanitizeString: () => "", isNonEmptyString: () => false, escapeODataString: (value) => value, @@ -122,6 +129,8 @@ test("getpreferredlanguage catch path returns PREFERRED_LANGUAGE_FETCH_FAILED", const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), sanitizeString: (value) => value, isNonEmptyString: () => true, escapeODataString: (value) => value, @@ -151,6 +160,15 @@ test("getpreferredlanguage success returns existing data payload contract", asyn const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res }) => + respondSuccessMock(res, { + value: [ + { + contactid: "c1", + pinswg_preferredlanguage: 807570001 + } + ] + }), sanitizeString: (value) => value, isNonEmptyString: () => true, escapeODataString: (value) => value, @@ -191,6 +209,7 @@ test("getlogin returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", async const mod = loadModule("pages/api/endpoint/getlogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -210,6 +229,7 @@ test("getlogin returns PASSWORD_REQUIRED when pwd missing", async () => { const mod = loadModule("pages/api/endpoint/getlogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -229,6 +249,8 @@ test("getlogin catch path returns LOGIN_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getlogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -414,6 +436,8 @@ test("getportalloginproxy catch path returns PORTAL_LOGIN_PROXY_FETCH_FAILED", a const mod = loadModule("pages/api/endpoint/getportalloginproxy_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -440,6 +464,7 @@ test("getpersonalaccount returns CONTACT_ID_REQUIRED when contactid missing", as const mod = loadModule("pages/api/endpoint/getpersonalaccount_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -459,6 +484,8 @@ test("getpersonalaccount catch path returns PERSONAL_ACCOUNT_FETCH_FAILED", asyn const mod = loadModule("pages/api/endpoint/getpersonalaccount_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -485,6 +512,8 @@ test("getaccounts success returns existing data payload contract", async () => { const mod = loadModule("pages/api/endpoint/getaccounts_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res }) => + respondSuccessMock(res, { value: [{ contactid: "c1" }] }), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -508,6 +537,10 @@ test("getemailaccountcheck success returns existing data payload contract", asyn const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res }) => + respondSuccessMock(res, { + value: [{ emailaddress1: "u@test" }] + }), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), From fdf95dd0b1dfbddb809b5622148f2cbdfd3c4795 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 07:45:59 +0000 Subject: [PATCH 03/14] TASK22229: P2-S2 batch 2 migrate portal/representation relay GET endpoints --- memory-bank/change-log.md | 35 ++++++++ .../api/endpoint/getawaitingsubmission_api.js | 49 ++++------- pages/api/endpoint/getmycases_api.js | 49 ++++------- .../api/endpoint/getmyrepresentations_api.js | 40 +++------ pages/api/endpoint/getrepresentations_api.js | 40 +++------ pages/api/endpoint/getwatchedcases_api.js | 84 ++++++++----------- .../endpoint-handler-contract.test.cjs | 15 ++++ 7 files changed, 146 insertions(+), 166 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index f187d393..0c74a101 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -801,3 +801,38 @@ Validation: Follow-ups: - Continue P2-S2 with Batch 2 as next dedicated commit on this same branch. + +--- + +### CL-021: TASK22229 P2-S2 Batch 2 (my-portal + representation relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getmycases_api,getmyrepresentations_api,getwatchedcases_api,getawaitingsubmission_api,getrepresentations_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Deliver second P2-S2 commit by migrating the next bounded portal/representation GET endpoint cluster onto shared `relayGet` while preserving route contracts. +impact: Reduced duplicated relay boilerplate and aligned forwarding behavior with no intended response contract changes. +status: completed + +Summary: + +- Migrated Batch 2 endpoints to `relayGet`: + - `getmycases_api.js` (preserved title mapping transform) + - `getmyrepresentations_api.js` + - `getwatchedcases_api.js` (preserved watched-case projection transform) + - `getawaitingsubmission_api.js` (preserved title mapping transform) + - `getrepresentations_api.js` +- Updated phase21 endpoint contract tests for migrated handlers by mocking `relayGet` in guard/catch test paths. + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 53/53 + - email-handler: 12/12 + - endpoint-handler: 152/152 + - documents-handler: 3/3 + +Follow-ups: + +- Continue P2-S2 with Batch 3 as the next dedicated commit on this branch. diff --git a/pages/api/endpoint/getawaitingsubmission_api.js b/pages/api/endpoint/getawaitingsubmission_api.js index 5ea8be59..b48bf1fc 100644 --- a/pages/api/endpoint/getawaitingsubmission_api.js +++ b/pages/api/endpoint/getawaitingsubmission_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,30 +34,25 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + + loggedInUserId + + " and servicestage eq 1 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + - loggedInUserId + - " and servicestage eq 1 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + return relayGet({ + queryUrl, + res, + transformData: (data) => { + data.value.forEach(function (element) { + element.pinswg_title = element.title; + }); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "AWAITING_SUBMISSION_FETCH_FAILED", message: "Failed to fetch awaiting submissions" - }); - } + } + }); } diff --git a/pages/api/endpoint/getmycases_api.js b/pages/api/endpoint/getmycases_api.js index 5420fde2..c5e521bc 100644 --- a/pages/api/endpoint/getmycases_api.js +++ b/pages/api/endpoint/getmycases_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,30 +34,25 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,pinswg_publishtoweb,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + + loggedInUserId + + " and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,pinswg_publishtoweb,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " + - loggedInUserId + - " and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + return relayGet({ + queryUrl, + res, + transformData: (data) => { + data.value.forEach(function (element) { + element.pinswg_title = element.title; + }); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "MY_CASES_FETCH_FAILED", message: "Failed to fetch my cases" - }); - } + } + }); } diff --git a/pages/api/endpoint/getmyrepresentations_api.js b/pages/api/endpoint/getmyrepresentations_api.js index 9ad04a89..0b4d738e 100644 --- a/pages/api/endpoint/getmyrepresentations_api.js +++ b/pages/api/endpoint/getmyrepresentations_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,26 +34,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_representationses?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$count=true&$orderby=createdon desc"; - const queryUrl = - "pinswg_representationses?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$count=true&$orderby=createdon desc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "MY_REPRESENTATIONS_FETCH_FAILED", message: "Failed to fetch my representations" - }); - } + } + }); } diff --git a/pages/api/endpoint/getrepresentations_api.js b/pages/api/endpoint/getrepresentations_api.js index fe719c55..9d8eb54b 100644 --- a/pages/api/endpoint/getrepresentations_api.js +++ b/pages/api/endpoint/getrepresentations_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const incidentID = req.query.incidentID; @@ -39,26 +31,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_representationses?$filter= _pinswg_case_value eq " + + incidentID + + " and pinswg_publishtoweb eq true&$count=true&$orderby=createdon desc"; - const queryUrl = - "pinswg_representationses?$filter= _pinswg_case_value eq " + - incidentID + - " and pinswg_publishtoweb eq true&$count=true&$orderby=createdon desc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "REPRESENTATIONS_FETCH_FAILED", message: "Failed to fetch representations" - }); - } + } + }); } diff --git a/pages/api/endpoint/getwatchedcases_api.js b/pages/api/endpoint/getwatchedcases_api.js index f5db25a6..ed90cf73 100644 --- a/pages/api/endpoint/getwatchedcases_api.js +++ b/pages/api/endpoint/getwatchedcases_api.js @@ -17,16 +17,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const loggedInUserId = req.query.loggedInUserId; @@ -42,52 +34,48 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_watchlists?$filter= _pinswg_contact_value eq " + + loggedInUserId + + "&$select=pinswg_emailnotifications,modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; - const queryUrl = - "pinswg_watchlists?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$select=pinswg_emailnotifications,modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)"; + return relayGet({ + queryUrl, + res, + transformData: (data) => { + data.value.forEach(function (element) { + element.ticketnumber = element.pinswg_WatchedCase?.ticketnumber; + element.pinswg_title = + element[ + "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" + ]; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - data.value.forEach(function (element) { - element.ticketnumber = element.pinswg_WatchedCase?.ticketnumber; - element.pinswg_title = element[ - "_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue" - ]; - - element[ - "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase?.[ "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ]; - element._pinswg_associatedlpa_value = - element.pinswg_WatchedCase?._pinswg_associatedlpa_value; - element[ - "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase?.[ + ] = + element.pinswg_WatchedCase?.[ + "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" + ]; + element._pinswg_associatedlpa_value = + element.pinswg_WatchedCase?._pinswg_associatedlpa_value; + element[ "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ]; - element._ownerid_value = element.pinswg_WatchedCase?._ownerid_value; + ] = + element.pinswg_WatchedCase?.[ + "_ownerid_value@OData.Community.Display.V1.FormattedValue" + ]; + element._ownerid_value = + element.pinswg_WatchedCase?._ownerid_value; - delete element.pinswg_WatchedCase; - }); + delete element.pinswg_WatchedCase; + }); - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "WATCHED_CASES_FETCH_FAILED", message: "Failed to fetch watched cases" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 41b958f9..772ac51a 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1555,6 +1555,7 @@ test("getmycases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing" const mod = loadModule("pages/api/endpoint/getmycases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1577,6 +1578,8 @@ test("getmycases catch path returns MY_CASES_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getmycases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1600,6 +1603,7 @@ test("getmyrepresentations returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserI const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1622,6 +1626,8 @@ test("getmyrepresentations catch path returns MY_REPRESENTATIONS_FETCH_FAILED", const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1648,6 +1654,7 @@ test("getwatchedcases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId mis const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1670,6 +1677,8 @@ test("getwatchedcases catch path returns WATCHED_CASES_FETCH_FAILED", async () = const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1696,6 +1705,7 @@ test("getawaitingsubmission returns LOGGED_IN_USER_ID_REQUIRED when loggedInUser const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1718,6 +1728,8 @@ test("getawaitingsubmission catch path returns AWAITING_SUBMISSION_FETCH_FAILED" const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3327,6 +3339,7 @@ test("getrepresentations returns INCIDENT_ID_REQUIRED when incidentID missing", const mod = loadModule("pages/api/endpoint/getrepresentations_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3346,6 +3359,8 @@ test("getrepresentations catch path returns REPRESENTATIONS_FETCH_FAILED", async const mod = loadModule("pages/api/endpoint/getrepresentations_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), From 44b32a618a13008b0f1818d2cfb6e136ed808514 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 07:49:28 +0000 Subject: [PATCH 04/14] TASK22229: P2-S2 batch 3 migrate case/event relay GET endpoints --- memory-bank/change-log.md | 34 +++++++++++++ pages/api/endpoint/getcase_api.js | 50 +++++++------------ pages/api/endpoint/getcasebyid_api.js | 44 ++++++---------- pages/api/endpoint/getincidentbyid_api.js | 42 +++++----------- pages/api/endpoint/getsipsevents_api.js | 40 +++++---------- .../endpoint-handler-contract.test.cjs | 12 +++++ 6 files changed, 106 insertions(+), 116 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 0c74a101..d265dc77 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -836,3 +836,37 @@ Validation: Follow-ups: - Continue P2-S2 with Batch 3 as the next dedicated commit on this branch. + +--- + +### CL-022: TASK22229 P2-S2 Batch 3 (case/event relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getcase_api,getcasebyid_api,getincidentbyid_api,getsipsevents_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Deliver third P2-S2 commit by migrating an additional bounded case/event endpoint cluster onto shared `relayGet` while preserving route contracts. +impact: Further relay boilerplate reduction and consistent forwarding behavior with no intended contract changes. +status: completed + +Summary: + +- Migrated Batch 3 endpoints to `relayGet`: + - `getcase_api.js` (preserved `@odata.nextLink` normalization) + - `getcasebyid_api.js` (preserved `@odata.nextLink` normalization and array-wrapped success payload) + - `getincidentbyid_api.js` + - `getsipsevents_api.js` +- Updated phase21 endpoint contract tests to mock `relayGet` for migrated handlers in guard/catch paths. + +Validation: + +- `node tests/phase21/api-contract-slice1.test.cjs` -> pass + - helper: 4/4 + - file-handler: 53/53 + - email-handler: 12/12 + - endpoint-handler: 152/152 + - documents-handler: 3/3 + +Follow-ups: + +- Continue P2-S2 with next bounded batch (e.g. search/listing cluster) as a separate commit if required. diff --git a/pages/api/endpoint/getcase_api.js b/pages/api/endpoint/getcase_api.js index 6202e4d5..253590df 100644 --- a/pages/api/endpoint/getcase_api.js +++ b/pages/api/endpoint/getcase_api.js @@ -10,17 +10,9 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const incidentID = req.query.incidentID; @@ -33,30 +25,26 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "incidents(" + - incidentID + - ")?$select=ticketnumber,title,pinswg_appealcasetype"; + const queryUrl = + "incidents(" + + incidentID + + ")?$select=ticketnumber,title,pinswg_appealcasetype"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + return relayGet({ + queryUrl, + res, + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "CASE_FETCH_FAILED", message: "Failed to fetch case" - }); - } + } + }); } diff --git a/pages/api/endpoint/getcasebyid_api.js b/pages/api/endpoint/getcasebyid_api.js index 03783f2d..a0c064aa 100644 --- a/pages/api/endpoint/getcasebyid_api.js +++ b/pages/api/endpoint/getcasebyid_api.js @@ -10,17 +10,9 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const incidentID = req.query.incidentID; @@ -33,27 +25,23 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = "incidents(" + incidentID + ")"; + const queryUrl = "incidents(" + incidentID + ")"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + return relayGet({ + queryUrl, + res, + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, [data]); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return [data]; + }, + errorResponse: { status: 400, code: "CASE_BY_ID_FETCH_FAILED", message: "Failed to fetch case by id" - }); - } + } + }); } diff --git a/pages/api/endpoint/getincidentbyid_api.js b/pages/api/endpoint/getincidentbyid_api.js index 591d972c..de56d3a5 100644 --- a/pages/api/endpoint/getincidentbyid_api.js +++ b/pages/api/endpoint/getincidentbyid_api.js @@ -15,16 +15,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { let searchString = req.query.searchString; @@ -37,28 +29,20 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + searchString = searchString.replace(/\'/g, "''"); - searchString = searchString.replace(/\'/g, "''"); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=incidentid eq " + + searchString + + " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=incidentid eq " + - searchString + - " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "INCIDENT_BY_ID_FETCH_FAILED", message: "Failed to fetch incident by id" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsipsevents_api.js b/pages/api/endpoint/getsipsevents_api.js index b4087ded..e39632f4 100644 --- a/pages/api/endpoint/getsipsevents_api.js +++ b/pages/api/endpoint/getsipsevents_api.js @@ -1,13 +1,5 @@ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const caseid = req.query.caseid; @@ -20,26 +12,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " + + caseid + + "&$count=true"; - const queryUrl = - "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " + - caseid + - "&$count=true"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "SIPS_EVENTS_FETCH_FAILED", message: "Failed to fetch SIPS events" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 772ac51a..c0fdfa91 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -2439,6 +2439,7 @@ test("getcase returns INCIDENT_ID_REQUIRED when incidentID missing", async () => const mod = loadModule("pages/api/endpoint/getcase_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2459,6 +2460,8 @@ test("getcase catch path returns CASE_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getcase_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2483,6 +2486,7 @@ test("getcasebyid returns INCIDENT_ID_REQUIRED when incidentID missing", async ( const mod = loadModule("pages/api/endpoint/getcasebyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2503,6 +2507,8 @@ test("getcasebyid catch path returns CASE_BY_ID_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getcasebyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2530,6 +2536,7 @@ test("getincidentbyid returns SEARCH_STRING_REQUIRED when searchString missing", const mod = loadModule("pages/api/endpoint/getincidentbyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2549,6 +2556,8 @@ test("getincidentbyid catch path returns INCIDENT_BY_ID_FETCH_FAILED", async () const mod = loadModule("pages/api/endpoint/getincidentbyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3135,6 +3144,7 @@ test("getsipsevents returns CASE_ID_REQUIRED when caseid missing", async () => { const mod = loadModule("pages/api/endpoint/getsipsevents_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3154,6 +3164,8 @@ test("getsipsevents catch path returns SIPS_EVENTS_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getsipsevents_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), From ac0253daeb51f6b6db7c235cdd776f4607e29be0 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:26:37 +0000 Subject: [PATCH 05/14] refactor(api): migrate batch 4 endpoints to relayGet --- pages/api/endpoint/getbasicdnssearch_api.js | 47 +++++-------- .../api/endpoint/getbasicdnsurlsearch_api.js | 56 +++++++-------- pages/api/endpoint/getbasicsearch_api.js | 68 ++++++++----------- pages/api/endpoint/getcasemessage_api.js | 40 ++++------- pages/api/endpoint/getpartsavedappeal_api.js | 56 +++++++-------- 5 files changed, 106 insertions(+), 161 deletions(-) diff --git a/pages/api/endpoint/getbasicdnssearch_api.js b/pages/api/endpoint/getbasicdnssearch_api.js index 68c29b9e..2c583f2a 100644 --- a/pages/api/endpoint/getbasicdnssearch_api.js +++ b/pages/api/endpoint/getbasicdnssearch_api.js @@ -16,42 +16,31 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { - try { - const token = await getToken(); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002)and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002)and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_DNS_SEARCH_FETCH_FAILED", message: "Failed to fetch basic DNS search results" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicdnsurlsearch_api.js b/pages/api/endpoint/getbasicdnsurlsearch_api.js index c1a2f175..e2155985 100644 --- a/pages/api/endpoint/getbasicdnsurlsearch_api.js +++ b/pages/api/endpoint/getbasicdnsurlsearch_api.js @@ -16,17 +16,10 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const searchString = req.query.searchString; @@ -39,34 +32,31 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedSearchString = searchString.replace(/\'/g, "''"); + const escapedSearchString = searchString.replace(/\'/g, "''"); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - escapedSearchString + - "') or contains(ticketnumber, '" + - escapedSearchString + - "')) and (pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002 ) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + escapedSearchString + + "') or contains(ticketnumber, '" + + escapedSearchString + + "')) and (pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002 ) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - let dataStr; - _.has(data, "@odata.nextLink") === true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_DNS_URL_SEARCH_FETCH_FAILED", message: "Failed to fetch DNS URL search results" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicsearch_api.js b/pages/api/endpoint/getbasicsearch_api.js index c7d5d604..61726fc7 100644 --- a/pages/api/endpoint/getbasicsearch_api.js +++ b/pages/api/endpoint/getbasicsearch_api.js @@ -15,16 +15,9 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const searchString = req.query.searchString; @@ -37,40 +30,37 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedSearchString = searchString.replace(/\'/g, "''"); + const escapedSearchString = searchString.replace(/\'/g, "''"); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - escapedSearchString + - "') or contains(ticketnumber, '" + - escapedSearchString + - "') or contains(pinswg_lpareference, '" + - escapedSearchString + - "')) and pinswg_appealcasetype ne null " + - (process.env.SHOWSIPS !== "true" - ? "and pinswg_appealcasetype ne 846040002 " - : "") + - "and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + escapedSearchString + + "') or contains(ticketnumber, '" + + escapedSearchString + + "') or contains(pinswg_lpareference, '" + + escapedSearchString + + "')) and pinswg_appealcasetype ne null " + + (process.env.SHOWSIPS !== "true" + ? "and pinswg_appealcasetype ne 846040002 " + : "") + + "and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + if (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - if (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_SEARCH_FETCH_FAILED", message: "Failed to fetch basic search results" - }); - } + } + }); } diff --git a/pages/api/endpoint/getcasemessage_api.js b/pages/api/endpoint/getcasemessage_api.js index 8adaf604..a7a11509 100644 --- a/pages/api/endpoint/getcasemessage_api.js +++ b/pages/api/endpoint/getcasemessage_api.js @@ -15,16 +15,9 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const caseId = req.query.id; @@ -37,26 +30,19 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "tasks?$select=_regardingobjectid_value,subject,description,createdon,statuscode,scheduledend,scheduledstart&$count=true&$filter=_regardingobjectid_value eq " + + caseId + + " and contains(subject, 'Banner') and statuscode ne 5&$orderby=createdon desc"; - const queryUrl = - "tasks?$select=_regardingobjectid_value,subject,description,createdon,statuscode,scheduledend,scheduledstart&$count=true&$filter=_regardingobjectid_value eq " + - caseId + - " and contains(subject, 'Banner') and statuscode ne 5&$orderby=createdon desc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "CASE_MESSAGE_FETCH_FAILED", message: "Failed to fetch case messages" - }); - } + } + }); } diff --git a/pages/api/endpoint/getpartsavedappeal_api.js b/pages/api/endpoint/getpartsavedappeal_api.js index ad1358c5..6e2eff3f 100644 --- a/pages/api/endpoint/getpartsavedappeal_api.js +++ b/pages/api/endpoint/getpartsavedappeal_api.js @@ -1,14 +1,7 @@ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const searchString = req.query.searchString; @@ -21,34 +14,31 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedSearchString = searchString.replace(/\'/g, "''"); + const escapedSearchString = searchString.replace(/\'/g, "''"); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - escapedSearchString + - "') or contains(ticketnumber, '" + - escapedSearchString + - "')) and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + escapedSearchString + + "') or contains(ticketnumber, '" + + escapedSearchString + + "')) and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - let dataStr; - _.has(data, "@odata.nextLink") === true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "PART_SAVED_APPEAL_FETCH_FAILED", message: "Failed to fetch part-saved appeals" - }); - } + } + }); } From 5797443edcf0fed40c82773aab8c324f5b1c8084 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:27:06 +0000 Subject: [PATCH 06/14] refactor(api): migrate batch 5 endpoints to relayGet --- pages/api/endpoint/getappealtypes_api.js | 52 +++++++------------- pages/api/endpoint/getispublishedbyid_api.js | 42 +++++----------- pages/api/endpoint/getlinkedcases_api.js | 40 +++++---------- pages/api/endpoint/getlpa_api.js | 36 ++++---------- pages/api/endpoint/getprojecttypes_api.js | 36 ++++---------- 5 files changed, 64 insertions(+), 142 deletions(-) diff --git a/pages/api/endpoint/getappealtypes_api.js b/pages/api/endpoint/getappealtypes_api.js index 16edd5f8..a14511ee 100644 --- a/pages/api/endpoint/getappealtypes_api.js +++ b/pages/api/endpoint/getappealtypes_api.js @@ -9,46 +9,30 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { - try { - const token = await getToken(); + const queryUrl = + "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' " + + (process.env.SHOWSIPS !== "true" + ? "and attributevalue ne 846040002" + : "") + + "&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - const queryUrl = - "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' " + - (process.env.SHOWSIPS !== "true" - ? "and attributevalue ne 846040002" - : "") + - "&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + // for sips + // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - // for sips - // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; + //"stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Developments of National Significance' and value ne 'LDP' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - // "stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - //"stringmaps?$filter=objecttypecode eq 'incident' and attributename eq 'pinswg_appealcasetype' and value ne 'Developments of National Significance' and value ne 'LDP' and value ne 'Misc Casework'&$count=true&$select=value,stringmapid,organizationid,attributevalue&$orderby=value asc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "APPEAL_TYPES_FETCH_FAILED", message: "Failed to fetch appeal types" - }); - } + } + }); } diff --git a/pages/api/endpoint/getispublishedbyid_api.js b/pages/api/endpoint/getispublishedbyid_api.js index f434dac0..a4a8f88e 100644 --- a/pages/api/endpoint/getispublishedbyid_api.js +++ b/pages/api/endpoint/getispublishedbyid_api.js @@ -15,16 +15,8 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const searchString = req.query.searchString; @@ -37,28 +29,20 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const escapedSearchString = searchString.split("'").join("''"); - const escapedSearchString = searchString.split("'").join("''"); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,ticketnumber,title, _primarycontactid_value&$filter=incidentid eq " + + escapedSearchString + + " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_publishtoweb,ticketnumber,title, _primarycontactid_value&$filter=incidentid eq " + - escapedSearchString + - " and pinswg_appealcasetype ne null &$orderby=createdon desc&$count=true"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "IS_PUBLISHED_FETCH_FAILED", message: "Failed to fetch published state" - }); - } + } + }); } diff --git a/pages/api/endpoint/getlinkedcases_api.js b/pages/api/endpoint/getlinkedcases_api.js index d4b9ed84..0b7fcfac 100644 --- a/pages/api/endpoint/getlinkedcases_api.js +++ b/pages/api/endpoint/getlinkedcases_api.js @@ -16,16 +16,9 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const parentIncidentid = req.query.parentincidentid; @@ -41,26 +34,19 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "incidents?$count=true&$filter=_parentcaseid_value eq " + + parentIncidentid + + " and pinswg_appealcasetype ne null and pinswg_publishtoweb eq true &$select=title, incidentid"; - const queryUrl = - "incidents?$count=true&$filter=_parentcaseid_value eq " + - parentIncidentid + - " and pinswg_appealcasetype ne null and pinswg_publishtoweb eq true &$select=title, incidentid"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "LINKED_CASES_FETCH_FAILED", message: "Failed to fetch linked cases" - }); - } + } + }); } diff --git a/pages/api/endpoint/getlpa_api.js b/pages/api/endpoint/getlpa_api.js index 51a75b87..0055bbf8 100644 --- a/pages/api/endpoint/getlpa_api.js +++ b/pages/api/endpoint/getlpa_api.js @@ -12,36 +12,20 @@ * */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { - try { - const token = await getToken(); + const queryUrl = + "accounts?$count=true&$filter=pinswg_isalocalplanningauthorityaccount eq 846040000&$select=name&$orderby=name asc"; - const queryUrl = - "accounts?$count=true&$filter=pinswg_isalocalplanningauthorityaccount eq 846040000&$select=name&$orderby=name asc"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "LPA_FETCH_FAILED", message: "Failed to fetch local planning authorities" - }); - } + } + }); } diff --git a/pages/api/endpoint/getprojecttypes_api.js b/pages/api/endpoint/getprojecttypes_api.js index 8600c828..c5b7b534 100644 --- a/pages/api/endpoint/getprojecttypes_api.js +++ b/pages/api/endpoint/getprojecttypes_api.js @@ -9,36 +9,20 @@ * description: Success */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { - try { - const token = await getToken(); + const queryUrl = + "pinswg_sipsprojecttypes?$select=pinswg_name,pinswg_sipsprojecttypeid&$orderby=pinswg_name asc&$count=true"; - const queryUrl = - "pinswg_sipsprojecttypes?$select=pinswg_name,pinswg_sipsprojecttypeid&$orderby=pinswg_name asc&$count=true"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "PROJECT_TYPES_FETCH_FAILED", message: "Failed to fetch project types" - }); - } + } + }); } From 6da6fdb4d2518b889454d8adbecfeef36516176e Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:28:01 +0000 Subject: [PATCH 07/14] refactor(api): migrate batch 6 endpoints to relayGet --- memory-bank/change-log.md | 32 +++++++++++++ pages/api/endpoint/getformdata_api.js | 40 +++++----------- pages/api/endpoint/getpersonalaccount_api.js | 38 +++++---------- .../endpoint/getportalmoduledetails_api.js | 45 ++++++------------ .../endpoint/getsearchdocumenthistory_api.js | 37 +++++---------- .../getsearchdocumenthistorypaged_api.js | 46 +++++++------------ tests/phase21/_shared.cjs | 3 ++ .../endpoint-handler-contract.test.cjs | 42 +++++++++++++++++ 8 files changed, 144 insertions(+), 139 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index d265dc77..faac9c3d 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -870,3 +870,35 @@ Validation: Follow-ups: - Continue P2-S2 with next bounded batch (e.g. search/listing cluster) as a separate commit if required. + +--- + +### CL-023: TASK22229 P2-S2 Batch 6 (profile/form/document-history/portal-module relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getpersonalaccount_api,getformdata_api,getsearchdocumenthistory_api,getsearchdocumenthistorypaged_api,getportalmoduledetails_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Execute requested Batch 6 by migrating the next bounded set of read-only endpoint handlers to shared `relayGet` while preserving existing request validation and response/error contracts. +impact: Reduced endpoint relay boilerplate and improved consistency; no intended behavior contract changes. +status: completed + +Summary: + +- Migrated Batch 6 endpoints to shared `relayGet`: + - `getpersonalaccount_api.js` + - `getformdata_api.js` + - `getsearchdocumenthistory_api.js` (uses `requestOptionsBuilder` with `azureHeadersPaged`) + - `getsearchdocumenthistorypaged_api.js` (uses `requestOptionsBuilder` with `azureHeadersPaged`) + - `getportalmoduledetails_api.js` +- Preserved all existing required-query validation guards and existing error codes/messages. +- Updated Phase 21 endpoint contract tests to inject `relayGet` mocks for migrated handlers (validation path, catch path, and success path where applicable). + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Optional next bounded batch: migrate remaining legacy relay GET handlers in advanced/basic search-document/detail clusters that still use direct axios relay patterns. diff --git a/pages/api/endpoint/getformdata_api.js b/pages/api/endpoint/getformdata_api.js index 39ac3034..11eef400 100644 --- a/pages/api/endpoint/getformdata_api.js +++ b/pages/api/endpoint/getformdata_api.js @@ -1,13 +1,5 @@ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const whichForm = req.query.whichForm; @@ -20,26 +12,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "systemforms?$select=formid,name,formxml,type,objecttypecode&$filter=(objecttypecode eq 'pinswg_" + + whichForm + + "' and type eq 2)&$count=true&$top=201"; - const queryUrl = - "systemforms?$select=formid,name,formxml,type,objecttypecode&$filter=(objecttypecode eq 'pinswg_" + - whichForm + - "' and type eq 2)&$count=true&$top=201"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "FORM_DATA_FETCH_FAILED", message: "Failed to fetch form data" - }); - } + } + }); } diff --git a/pages/api/endpoint/getpersonalaccount_api.js b/pages/api/endpoint/getpersonalaccount_api.js index 2aae367a..56039c90 100644 --- a/pages/api/endpoint/getpersonalaccount_api.js +++ b/pages/api/endpoint/getpersonalaccount_api.js @@ -17,16 +17,8 @@ * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const contactid = req.query.contactid; @@ -39,24 +31,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts(" + - contactid + - ")?$select=firstname, lastname, emailaddress1, telephone1, company, address1_line1,address1_line2,address1_city, address1_county,address1_postalcode,pinswg_typeofinvolvement,pinswg_contact_associatedlpa,pinswg_preferredlanguage&$count=true"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const queryUrl = + "contacts(" + + contactid + + ")?$select=firstname, lastname, emailaddress1, telephone1, company, address1_line1,address1_line2,address1_city, address1_county,address1_postalcode,pinswg_typeofinvolvement,pinswg_contact_associatedlpa,pinswg_preferredlanguage&$count=true"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "PERSONAL_ACCOUNT_FETCH_FAILED", message: "Failed to fetch personal account" - }); - } + } + }); } diff --git a/pages/api/endpoint/getportalmoduledetails_api.js b/pages/api/endpoint/getportalmoduledetails_api.js index 90c07967..23e44cf6 100644 --- a/pages/api/endpoint/getportalmoduledetails_api.js +++ b/pages/api/endpoint/getportalmoduledetails_api.js @@ -23,17 +23,9 @@ * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const appealType = req.query.appealType; @@ -58,30 +50,23 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedCaseReference = caseReference.split("'").join("''"); + const escapedCaseReference = caseReference.split("'").join("''"); - let queryUrl = - appealType + - "?$filter=pinswg_name eq '" + - escapedCaseReference + - "'&$count=true"; + let queryUrl = + appealType + + "?$filter=pinswg_name eq '" + + escapedCaseReference + + "'&$count=true"; - queryUrl = queryUrl + getSelectQuery(appealType); + queryUrl = queryUrl + getSelectQuery(appealType); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "PORTAL_MODULE_DETAILS_FETCH_FAILED", message: "Failed to fetch portal module details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsearchdocumenthistory_api.js b/pages/api/endpoint/getsearchdocumenthistory_api.js index 19893c0a..51f602cb 100644 --- a/pages/api/endpoint/getsearchdocumenthistory_api.js +++ b/pages/api/endpoint/getsearchdocumenthistory_api.js @@ -16,16 +16,9 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const documentID = req.query.documentid; @@ -38,24 +31,18 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + - documentID; + const queryUrl = + "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + + documentID; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "SEARCH_DOCUMENT_HISTORY_FETCH_FAILED", message: "Failed to fetch search document history" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsearchdocumenthistorypaged_api.js b/pages/api/endpoint/getsearchdocumenthistorypaged_api.js index 4ee413c4..3cbad2b1 100644 --- a/pages/api/endpoint/getsearchdocumenthistorypaged_api.js +++ b/pages/api/endpoint/getsearchdocumenthistorypaged_api.js @@ -42,16 +42,9 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const documentID = req.query.documentid; @@ -64,29 +57,22 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + + documentID; + // + + // (typeof pageNumber != "undefined" + // ? "&$skiptoken=" + ('') + // : ""); - const queryUrl = - "pinswg_documenthistories?$count=true&$select=_pinswg_documentid_value,pinswg_createddate,pinswg_state,pinswg_fileexists,statecode,pinswg_publisheddate&$filter=_pinswg_documentid_value eq " + - documentID; - // + - // (typeof pageNumber != "undefined" - // ? "&$skiptoken=" + ('') - // : ""); - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "SEARCH_DOCUMENT_HISTORY_PAGED_FETCH_FAILED", message: "Failed to fetch paged search document history" - }); - } + } + }); } diff --git a/tests/phase21/_shared.cjs b/tests/phase21/_shared.cjs index ada96f29..118144c0 100644 --- a/tests/phase21/_shared.cjs +++ b/tests/phase21/_shared.cjs @@ -41,6 +41,8 @@ const loadModule = (relativePath, injected = {}) => { source += '\nif (typeof ApiProxy !== "undefined" && !module.exports.default) module.exports.default = ApiProxy;\n'; + const defaultRelayGet = async () => ({}); + const context = { module: { exports: {} }, exports: {}, @@ -52,6 +54,7 @@ const loadModule = (relativePath, injected = {}) => { warn: () => {}, error: () => {} }, + relayGet: defaultRelayGet, ...injected }; diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index c0fdfa91..7c5b5f60 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -947,6 +947,7 @@ test("getbasicsearch returns SEARCH_STRING_REQUIRED when searchString missing", const mod = loadModule("pages/api/endpoint/getbasicsearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -966,6 +967,8 @@ test("getbasicsearch catch path returns BASIC_SEARCH_FETCH_FAILED", async () => const mod = loadModule("pages/api/endpoint/getbasicsearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -992,6 +995,7 @@ test("getcasemessage returns CASE_ID_REQUIRED when id missing", async () => { const mod = loadModule("pages/api/endpoint/getcasemessage_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1011,6 +1015,8 @@ test("getcasemessage catch path returns CASE_MESSAGE_FETCH_FAILED", async () => const mod = loadModule("pages/api/endpoint/getcasemessage_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1195,6 +1201,7 @@ test("getsearchdocumenthistory returns DOCUMENT_ID_REQUIRED when documentid miss { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1217,6 +1224,8 @@ test("getsearchdocumenthistory catch path returns SEARCH_DOCUMENT_HISTORY_FETCH_ { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1246,6 +1255,10 @@ test("getsearchdocumenthistory success returns existing data payload contract", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res }) => + respondSuccessMock(res, { + value: [{ _pinswg_documentid_value: "d1" }] + }), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1274,6 +1287,7 @@ test("getsearchdocumenthistorypaged returns DOCUMENT_ID_REQUIRED when documentid { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1296,6 +1310,8 @@ test("getsearchdocumenthistorypaged catch path returns SEARCH_DOCUMENT_HISTORY_P { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -2065,6 +2081,8 @@ test("getbasicdnssearch catch path returns BASIC_DNS_SEARCH_FETCH_FAILED", async const mod = loadModule("pages/api/endpoint/getbasicdnssearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -2259,6 +2277,7 @@ test("getportalmoduledetails returns APPEAL_TYPE_REQUIRED when appealType missin const mod = loadModule("pages/api/endpoint/getportalmoduledetails_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2279,6 +2298,8 @@ test("getportalmoduledetails catch path returns PORTAL_MODULE_DETAILS_FETCH_FAIL const mod = loadModule("pages/api/endpoint/getportalmoduledetails_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2853,6 +2874,8 @@ test("getlpa catch path returns LPA_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getlpa_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2932,6 +2955,8 @@ test("getprojecttypes catch path returns PROJECT_TYPES_FETCH_FAILED", async () = const mod = loadModule("pages/api/endpoint/getprojecttypes_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2958,6 +2983,8 @@ test("getappealtypes catch path returns APPEAL_TYPES_FETCH_FAILED", async () => const mod = loadModule("pages/api/endpoint/getappealtypes_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2985,6 +3012,7 @@ test("getlinkedcases returns PARENT_INCIDENT_ID_REQUIRED when parentincidentid m const mod = loadModule("pages/api/endpoint/getlinkedcases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -3007,6 +3035,8 @@ test("getlinkedcases catch path returns LINKED_CASES_FETCH_FAILED", async () => const mod = loadModule("pages/api/endpoint/getlinkedcases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -3033,6 +3063,7 @@ test("getformdata returns WHICH_FORM_REQUIRED when whichForm missing", async () const mod = loadModule("pages/api/endpoint/getformdata_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3052,6 +3083,8 @@ test("getformdata catch path returns FORM_DATA_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getformdata_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3075,6 +3108,7 @@ test("getispublishedbyid returns SEARCH_STRING_REQUIRED when searchString missin const mod = loadModule("pages/api/endpoint/getispublishedbyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3094,6 +3128,8 @@ test("getispublishedbyid catch path returns IS_PUBLISHED_FETCH_FAILED", async () const mod = loadModule("pages/api/endpoint/getispublishedbyid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3257,6 +3293,7 @@ test("getbasicdnsurlsearch returns SEARCH_STRING_REQUIRED when searchString miss const mod = loadModule("pages/api/endpoint/getbasicdnsurlsearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -3277,6 +3314,8 @@ test("getbasicdnsurlsearch catch path returns BASIC_DNS_URL_SEARCH_FETCH_FAILED" const mod = loadModule("pages/api/endpoint/getbasicdnsurlsearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -3304,6 +3343,7 @@ test("getpartsavedappeal returns SEARCH_STRING_REQUIRED when searchString missin const mod = loadModule("pages/api/endpoint/getpartsavedappeal_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -3324,6 +3364,8 @@ test("getpartsavedappeal catch path returns PART_SAVED_APPEAL_FETCH_FAILED", asy const mod = loadModule("pages/api/endpoint/getpartsavedappeal_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), From bd15fd18e1d098016d4e9f3dbfc7ec076c27da9f Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:43:17 +0000 Subject: [PATCH 08/14] refactor(api): migrate batch 7 endpoints to relayGet --- memory-bank/change-log.md | 37 ++++++++ .../api/endpoint/getappealpdfdocuments_api.js | 60 ++++-------- .../getportalmoduledetailsproxy_api.js | 45 +++------ .../endpoint/getsearchdocumentTypes_api.js | 50 ++++------ .../endpoint/getsearchdocumentdetails_api.js | 71 ++++++--------- .../getsearchdocumentdetailspaged_api.js | 91 ++++++++----------- .../endpoint-handler-contract.test.cjs | 28 ++++++ 7 files changed, 188 insertions(+), 194 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index faac9c3d..14e5f943 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -902,3 +902,40 @@ Validation: Follow-ups: - Optional next bounded batch: migrate remaining legacy relay GET handlers in advanced/basic search-document/detail clusters that still use direct axios relay patterns. + +--- + +### CL-024: TASK22229 P2-S2 Batch 7 (search-document + portal-proxy relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getportalmoduledetailsproxy_api,getsearchdocumentTypes_api,getsearchdocumentdetails_api,getsearchdocumentdetailspaged_api,getappealpdfdocuments_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue P2-S2 in bounded commits by migrating a coherent search-document/portal-proxy GET cluster onto shared relay forwarding while preserving existing validation, transforms, and error contracts. +impact: Reduced duplicate relay plumbing and improved consistency in search-document handlers; no intended API contract changes. +status: completed + +Summary: + +- Migrated Batch 7 endpoints to shared `relayGet`: + - `getportalmoduledetailsproxy_api.js` + - `getsearchdocumentTypes_api.js` + - `getsearchdocumentdetails_api.js` + - `getsearchdocumentdetailspaged_api.js` + - `getappealpdfdocuments_api.js` +- Preserved existing guard behavior and error response contracts. +- Preserved endpoint-specific transform behavior via `transformData`, including: + - grouped search-document type payloads + - document hashlink enrichment and published-date normalization + - `@odata.nextLink` normalization where present + - appeal PDF document name projection +- Updated phase21 endpoint tests to mock `relayGet` for migrated handlers in validation/catch/success paths as applicable. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue with next bounded P2-S2 batch from remaining legacy GET endpoints (e.g., advanced/basic paged search-detail clusters and related non-migrated proxies). diff --git a/pages/api/endpoint/getappealpdfdocuments_api.js b/pages/api/endpoint/getappealpdfdocuments_api.js index 6ba1809d..5a17f757 100644 --- a/pages/api/endpoint/getappealpdfdocuments_api.js +++ b/pages/api/endpoint/getappealpdfdocuments_api.js @@ -16,16 +16,9 @@ * description: Success */ -import axios from "axios"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; const groupArray = (arr) => { const map = new Map(); @@ -67,41 +60,26 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_documents?$count=true&$filter=_pinswg_documentids_value eq " + + incidentID + + " and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations"; - const queryUrl = - "pinswg_documents?$count=true&$filter=_pinswg_documentids_value eq " + - incidentID + - " and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations"; + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + transformData: (data) => { + data.value.forEach((item) => { + item.name = item.pinswg_name; + }); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - // //delete data["pinswg_documentid"]; - // data.value.forEach(function (element) { - // delete element["pinswg_documentid"]; - // }); - - // var dataArr = groupArray(data.value); - - // // var dataStr; - // // _.has(data, "@odata.nextLink") == true && - // // ((dataStr = JSON.stringify(data["@odata.nextLink"])), - // // (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - - data.value.forEach((item) => { - item.name = item.pinswg_name; - }); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "APPEAL_PDF_DOCUMENTS_FETCH_FAILED", message: "Failed to fetch appeal PDF documents" - }); - } + } + }); } diff --git a/pages/api/endpoint/getportalmoduledetailsproxy_api.js b/pages/api/endpoint/getportalmoduledetailsproxy_api.js index 76e4f05e..bdb6ba5f 100644 --- a/pages/api/endpoint/getportalmoduledetailsproxy_api.js +++ b/pages/api/endpoint/getportalmoduledetailsproxy_api.js @@ -23,17 +23,9 @@ * description: hello world */ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const appealType = req.query.appealType; @@ -58,30 +50,23 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedCaseReference = caseReference.split("'").join("''"); + const escapedCaseReference = caseReference.split("'").join("''"); - let queryUrl = - appealType + - "?$filter=pinswg_name eq '" + - escapedCaseReference + - "'&$count=true"; + let queryUrl = + appealType + + "?$filter=pinswg_name eq '" + + escapedCaseReference + + "'&$count=true"; - queryUrl = queryUrl + getSelectQuery(appealType); + queryUrl = queryUrl + getSelectQuery(appealType); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + errorResponse: { status: 400, code: "PORTAL_MODULE_DETAILS_PROXY_FETCH_FAILED", message: "Failed to fetch portal module details proxy" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsearchdocumentTypes_api.js b/pages/api/endpoint/getsearchdocumentTypes_api.js index 140da38b..36672f4c 100644 --- a/pages/api/endpoint/getsearchdocumentTypes_api.js +++ b/pages/api/endpoint/getsearchdocumentTypes_api.js @@ -16,16 +16,9 @@ * description: Success */ -import axios from "axios"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; const groupArray = (arr) => { const map = new Map(); @@ -67,31 +60,26 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations"; - const queryUrl = - "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations"; + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + transformData: (data) => { + data.value.forEach(function (element) { + delete element["pinswg_documentid"]; + }); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - data.value.forEach(function (element) { - delete element["pinswg_documentid"]; - }); - - const dataArr = groupArray(data.value); - return respondSuccess(res, dataArr); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return groupArray(data.value); + }, + errorResponse: { status: 400, code: "SEARCH_DOCUMENT_TYPES_FETCH_FAILED", message: "Failed to fetch search document types" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsearchdocumentdetails_api.js b/pages/api/endpoint/getsearchdocumentdetails_api.js index 4f924615..c4183476 100644 --- a/pages/api/endpoint/getsearchdocumentdetails_api.js +++ b/pages/api/endpoint/getsearchdocumentdetails_api.js @@ -16,21 +16,14 @@ * description: Success */ -import axios from "axios"; import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; const WORDKEY = process.env.HASHKEY; -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; - const encryptDocReference = (documentRef) => { var hashlink = CryptoJS.HmacSHA256( "documents/download/" + documentRef, @@ -55,43 +48,39 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate"; - const queryUrl = - "pinswg_documents?$count=true&$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - data.value.forEach(function (element) { - Object.assign(element, { - "pinswg_documentpublisheddate": - element.pinswg_documentpublisheddate != null - ? element.pinswg_documentpublisheddate - : element.pinswg_latestpublisheddate + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + data.value.forEach(function (element) { + Object.assign(element, { + "pinswg_documentpublisheddate": + element.pinswg_documentpublisheddate != null + ? element.pinswg_documentpublisheddate + : element.pinswg_latestpublisheddate + }); + element.pinswg_hashlink = encryptDocReference( + element.pinswg_isharedocumentreference + ); }); - element.pinswg_hashlink = encryptDocReference( - element.pinswg_isharedocumentreference - ); - }); - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "SEARCH_DOCUMENT_DETAILS_FETCH_FAILED", message: "Failed to fetch search document details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getsearchdocumentdetailspaged_api.js b/pages/api/endpoint/getsearchdocumentdetailspaged_api.js index 8533d6fb..9a00218c 100644 --- a/pages/api/endpoint/getsearchdocumentdetailspaged_api.js +++ b/pages/api/endpoint/getsearchdocumentdetailspaged_api.js @@ -42,21 +42,14 @@ * description: Success */ -import axios from "axios"; import CryptoJS from "crypto-js"; import _ from "lodash"; import { azureHeadersPagedCustom } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; const WORDKEY = process.env.HASHKEY; -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; - const encryptDocReference = (documentRef) => { var hashlink = CryptoJS.HmacSHA256( "documents/download/" + documentRef, @@ -150,53 +143,49 @@ export default async function ApiProxy(req, res) { //console.log("has this passed docuemntType:", documentType); //(documentType !="all" && " pinswg_pinswg_isharedocumentlocations eq " + ) - try { - const token = await getToken(); + const queryUrl = + "pinswg_documents?$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + + incidentID + + " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null" + + docTypeQueryString + + "&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + ('') + : ""); - const queryUrl = - "pinswg_documents?$filter=pinswg_publishtoweb eq true and _pinswg_documentids_value eq " + - incidentID + - " and pinswg_latestpublishedversion ne null and pinswg_latestpublisheddate ne null" + - docTypeQueryString + - "&$select=pinswg_isharedocumentlocations,_pinswg_documentids_value,pinswg_isharelabelcasetype,pinswg_isharelabellpaname,pinswg_publishtoweb,pinswg_uploadstatus,pinswg_isharedocumentclassification,pinswg_isharedocumentreference,pinswg_name,pinswg_latestpublishedversion,pinswg_latestpublisheddate,pinswg_documentpublisheddate&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + - ('') - : ""); - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ); - - data.value.forEach(function (element) { - Object.assign(element, { - "pinswg_documentpublisheddate": - element.pinswg_documentpublisheddate != null - ? element.pinswg_documentpublisheddate - : element.pinswg_latestpublisheddate + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => + azureHeadersPagedCustom(accessToken, showNumberOfRecords), + transformData: (data) => { + data.value.forEach(function (element) { + Object.assign(element, { + "pinswg_documentpublisheddate": + element.pinswg_documentpublisheddate != null + ? element.pinswg_documentpublisheddate + : element.pinswg_latestpublisheddate + }); + element.pinswg_hashlink = encryptDocReference( + element.pinswg_isharedocumentreference + ); }); - element.pinswg_hashlink = encryptDocReference( - element.pinswg_isharedocumentreference - ); - }); - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "SEARCH_DOCUMENT_DETAILS_PAGED_FETCH_FAILED", message: "Failed to fetch paged search document details" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 7c5b5f60..145c2728 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1341,6 +1341,7 @@ test("getsearchdocumentdetails returns INCIDENT_ID_REQUIRED when incidentid miss { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1368,6 +1369,8 @@ test("getsearchdocumentdetails catch path returns SEARCH_DOCUMENT_DETAILS_FETCH_ { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPaged: () => ({}), @@ -1402,6 +1405,7 @@ test("getsearchdocumentdetailspaged returns INCIDENT_ID_REQUIRED when incidentid { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPagedCustom: () => ({}), @@ -1435,6 +1439,8 @@ test("getsearchdocumentdetailspaged catch path returns SEARCH_DOCUMENT_DETAILS_P { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPagedCustom: () => ({}), @@ -1474,6 +1480,7 @@ test("getsearchdocumentTypes returns INCIDENT_ID_REQUIRED when incidentid missin const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1493,6 +1500,8 @@ test("getsearchdocumentTypes catch path returns SEARCH_DOCUMENT_TYPES_FETCH_FAIL const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -1519,6 +1528,19 @@ test("getsearchdocumentTypes success returns grouped data payload contract", asy const mod = loadModule("pages/api/endpoint/getsearchdocumentTypes_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res }) => + respondSuccessMock(res, [ + { + pinswg_isharedocumentlocations: 1, + pinswg_isharedocumentlocationsLabel: "Plans", + count: 2 + }, + { + pinswg_isharedocumentlocations: 2, + pinswg_isharedocumentlocationsLabel: "Letters", + count: 1 + } + ]), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -2334,6 +2356,7 @@ test("getportalmoduledetailsproxy returns APPEAL_TYPE_REQUIRED when appealType m { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2357,6 +2380,8 @@ test("getportalmoduledetailsproxy catch path returns PORTAL_MODULE_DETAILS_PROXY { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -3468,6 +3493,7 @@ test("getappealpdfdocuments returns INCIDENT_ID_REQUIRED when incidentid missing const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), @@ -3487,6 +3513,8 @@ test("getappealpdfdocuments catch path returns APPEAL_PDF_DOCUMENTS_FETCH_FAILED const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeaders: () => ({}), From 13be069b8bb08fb1a958a7957f01e6a8c22c9958 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:51:13 +0000 Subject: [PATCH 09/14] refactor(api): migrate batch 8 endpoints to relayGet --- memory-bank/change-log.md | 35 ++++++ .../endpoint/getbasicdnssearchdetails_api.js | 43 +++---- .../getbasicdnssearchdetailspaged_api.js | 53 ++++----- .../api/endpoint/getbasicsearchdetails_api.js | 111 ++++++++---------- .../getbasicsearchdetailspaged_api.js | 105 ++++++++--------- pages/api/endpoint/getbasicsearchpaged_api.js | 84 ++++++------- .../endpoint-handler-contract.test.cjs | 15 +++ 7 files changed, 219 insertions(+), 227 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 14e5f943..c564b26d 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -939,3 +939,38 @@ Validation: Follow-ups: - Continue with next bounded P2-S2 batch from remaining legacy GET endpoints (e.g., advanced/basic paged search-detail clusters and related non-migrated proxies). + +--- + +### CL-025: TASK22229 P2-S2 Batch 8 (basic search details + DNS details relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getbasicsearchpaged_api,getbasicsearchdetails_api,getbasicsearchdetailspaged_api,getbasicdnssearchdetails_api,getbasicdnssearchdetailspaged_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue the P2-S2 relay migration using bounded commits by moving the remaining basic-search-details and DNS-details GET handlers to shared `relayGet`, preserving existing guards and response contracts. +impact: Reduced duplicated relay plumbing and more consistent forwarding behavior across search-detail handlers; no intended API contract changes. +status: completed + +Summary: + +- Migrated Batch 8 endpoints to shared `relayGet`: + - `getbasicsearchpaged_api.js` + - `getbasicsearchdetails_api.js` + - `getbasicsearchdetailspaged_api.js` + - `getbasicdnssearchdetails_api.js` + - `getbasicdnssearchdetailspaged_api.js` +- Preserved existing validation guards and error contracts. +- Preserved existing endpoint-specific transforms: + - `@odata.nextLink` normalization in paged responses + - flattened ticketnumber enrichment for search detail handlers +- Updated phase21 endpoint contract tests to inject `relayGet` mocks for migrated handlers in validation and catch-path tests. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue P2-S2 with next bounded batch from remaining legacy GET endpoints (currently: `createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getappealid_api`, `getbasicdnssearchpaged_api`, `getbasicpartsaveddetails_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`, `getdnslist_api`, `getmylpacases_api`, `getportallogin_api`). diff --git a/pages/api/endpoint/getbasicdnssearchdetails_api.js b/pages/api/endpoint/getbasicdnssearchdetails_api.js index 1ac1acf3..1f413add 100644 --- a/pages/api/endpoint/getbasicdnssearchdetails_api.js +++ b/pages/api/endpoint/getbasicdnssearchdetails_api.js @@ -10,18 +10,10 @@ * description: Success */ -import axios from "axios"; -import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const caseReference = req.query.caseReference; @@ -37,28 +29,21 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + let queryUrl = + "pinswg_dnses?$filter=pinswg_name eq '" + + caseReference + + "&$count=true"; - let queryUrl = - "pinswg_dnses?$filter=pinswg_name eq '" + - caseReference + - "&$count=true"; + queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); - queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + errorResponse: { status: 400, code: "BASIC_DNS_SEARCH_DETAILS_FETCH_FAILED", message: "Failed to fetch basic DNS search details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js b/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js index d85e1668..79334b1f 100644 --- a/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js +++ b/pages/api/endpoint/getbasicdnssearchdetailspaged_api.js @@ -9,18 +9,11 @@ * 200: * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const caseReference = req.query.caseReference; @@ -36,33 +29,29 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + let queryUrl = + "pinswg_dnses?$filter=pinswg_name eq '" + + caseReference + + "&$count=true"; - let queryUrl = - "pinswg_dnses?$filter=pinswg_name eq '" + - caseReference + - "&$count=true"; + queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); - queryUrl = queryUrl + getSelectQuery("pinswg_dnses"); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_DNS_SEARCH_DETAILS_PAGED_FETCH_FAILED", message: "Failed to fetch paged basic DNS search details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicsearchdetails_api.js b/pages/api/endpoint/getbasicsearchdetails_api.js index 1224ace0..a7d31b7b 100644 --- a/pages/api/endpoint/getbasicsearchdetails_api.js +++ b/pages/api/endpoint/getbasicsearchdetails_api.js @@ -29,18 +29,11 @@ * description: Success */ -import axios from "axios"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { getNavigationPropertyByPrimaryAttribute } from "../../../components/utils"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const appealTypeName = req.query.appealTypeName; @@ -77,67 +70,63 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const navigationProperty = + getNavigationPropertyByPrimaryAttribute( + primaryIdAttribute + ).NavigationProperty; - const navigationProperty = - getNavigationPropertyByPrimaryAttribute( - primaryIdAttribute - ).NavigationProperty; + let queryUrl = + appealTypeName + + "?$filter=_" + + (primaryIdAttribute == "pinswg_sipscase" + ? "pinswg_sipscase_value" + : primaryIdAttribute + "s_value ") + + " eq " + + incidentID + + "&$count=true" + + "&$expand=" + + navigationProperty + + "($select=ticketnumber)"; - let queryUrl = - appealTypeName + - "?$filter=_" + - (primaryIdAttribute == "pinswg_sipscase" - ? "pinswg_sipscase_value" - : primaryIdAttribute + "s_value ") + - " eq " + - incidentID + - "&$count=true" + - "&$expand=" + - navigationProperty + - "($select=ticketnumber)"; + //" and statuscode eq 1&$count=true"; - //" and statuscode eq 1&$count=true"; + // queryUrl = + // queryUrl + + // getSelectQuery(appealTypeName) + + // ",_" + + // (primaryIdAttribute == "pinswg_sipscase" + // ? "pinswg_sipscase_value" + // : primaryIdAttribute + "s_value"); - // queryUrl = - // queryUrl + - // getSelectQuery(appealTypeName) + - // ",_" + - // (primaryIdAttribute == "pinswg_sipscase" - // ? "pinswg_sipscase_value" - // : primaryIdAttribute + "s_value"); + queryUrl = queryUrl + getSelectQuery(appealTypeName); - queryUrl = queryUrl + getSelectQuery(appealTypeName); + // console.log( + // "\n==========================================\n", + // "\nSearch Details query ", + // "\nAppealType: " + appealTypeName, + // "\nIncident ID: " + incidentID, + // "\n\nQuery url: " + queryUrl, + // "\n==========================================\n" + // ); - // console.log( - // "\n==========================================\n", - // "\nSearch Details query ", - // "\nAppealType: " + appealTypeName, - // "\nIncident ID: " + incidentID, - // "\n\nQuery url: " + queryUrl, - // "\n==========================================\n" - // ); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + transformData: (data) => { + let flattened = data.value.map((r) => ({ + ...r, + ticketnumber: r[navigationProperty]?.ticketnumber || null + })); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + data.value = flattened; - let flattened = data.value.map((r) => ({ - ...r, - ticketnumber: r[navigationProperty]?.ticketnumber || null - })); - - data.value = flattened; - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_SEARCH_DETAILS_FETCH_FAILED", message: "Failed to fetch basic search details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicsearchdetailspaged_api.js b/pages/api/endpoint/getbasicsearchdetailspaged_api.js index ba20d7a7..fdd0e050 100644 --- a/pages/api/endpoint/getbasicsearchdetailspaged_api.js +++ b/pages/api/endpoint/getbasicsearchdetailspaged_api.js @@ -29,19 +29,12 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { getSelectQuery } from "../../../actions/selectQueryTypes"; import { getNavigationPropertyByPrimaryAttribute } from "../../../components/utils"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const appealTypeName = req.query.appealTypeName; @@ -78,64 +71,60 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const navigationProperty = + getNavigationPropertyByPrimaryAttribute( + primaryIdAttribute + ).NavigationProperty; - const navigationProperty = - getNavigationPropertyByPrimaryAttribute( - primaryIdAttribute - ).NavigationProperty; + // "?$filter=_" + + // (primaryIdAttribute == "pinswg_sipscase" + // ? "pinswg_sipscase_value" + // : primaryIdAttribute + "s_value ") + + // " eq " + + // incidentID + - // "?$filter=_" + - // (primaryIdAttribute == "pinswg_sipscase" - // ? "pinswg_sipscase_value" - // : primaryIdAttribute + "s_value ") + - // " eq " + - // incidentID + + let queryUrl = + appealTypeName + + "?$filter=" + + incidentID + + "&$count=true" + + "&$expand=" + + navigationProperty + + "($select=ticketnumber)"; - let queryUrl = - appealTypeName + - "?$filter=" + - incidentID + - "&$count=true" + - "&$expand=" + - navigationProperty + - "($select=ticketnumber)"; + //" and statuscode eq 1&$count=true"; - //" and statuscode eq 1&$count=true"; + queryUrl = + queryUrl + + getSelectQuery(appealTypeName) + + ",_" + + (primaryIdAttribute == "pinswg_sipscase" + ? "pinswg_sipscase_value" + : primaryIdAttribute + "s_value"); - queryUrl = - queryUrl + - getSelectQuery(appealTypeName) + - ",_" + - (primaryIdAttribute == "pinswg_sipscase" - ? "pinswg_sipscase_value" - : primaryIdAttribute + "s_value"); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: (data) => { + let flattened = data.value.map((r) => ({ + ...r, + ticketnumber: r[navigationProperty]?.ticketnumber || null + })); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + data.value = flattened; - let flattened = data.value.map((r) => ({ - ...r, - ticketnumber: r[navigationProperty]?.ticketnumber || null - })); + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - data.value = flattened; - - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_SEARCH_DETAILS_PAGED_FETCH_FAILED", message: "Failed to fetch paged basic search details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicsearchpaged_api.js b/pages/api/endpoint/getbasicsearchpaged_api.js index 12bd6d36..0da70548 100644 --- a/pages/api/endpoint/getbasicsearchpaged_api.js +++ b/pages/api/endpoint/getbasicsearchpaged_api.js @@ -42,17 +42,10 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPagedCustom } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { let searchString = req.query.searchString; @@ -96,48 +89,45 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + searchString = searchString.replace(/\'/g, "''"); - searchString = searchString.replace(/\'/g, "''"); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + + searchString + + "') or contains(ticketnumber, '" + + searchString + + "') or contains(pinswg_lpareference, '" + + searchString + + "')) and pinswg_appealcasetype ne null " + + (process.env.SHOWSIPS !== "true" + ? "and pinswg_appealcasetype ne 846040002 " + : "") + + "and pinswg_publishtoweb eq true&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + '' + : ""); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=(contains(title, '" + - searchString + - "') or contains(ticketnumber, '" + - searchString + - "') or contains(pinswg_lpareference, '" + - searchString + - "')) and pinswg_appealcasetype ne null " + - (process.env.SHOWSIPS !== "true" - ? "and pinswg_appealcasetype ne 846040002 " - : "") + - "and pinswg_publishtoweb eq true&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + '' - : ""); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => + azureHeadersPagedCustom(accessToken, showNumberOfRecords), + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ); - - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_SEARCH_PAGED_FETCH_FAILED", message: "Failed to fetch paged basic search results" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 145c2728..e4f9eae9 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1794,6 +1794,7 @@ test("getbasicsearchdetails returns APPEAL_TYPE_NAME_REQUIRED when appealTypeNam const mod = loadModule("pages/api/endpoint/getbasicsearchdetails_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=title", getNavigationPropertyByPrimaryAttribute: () => ({ @@ -1825,6 +1826,8 @@ test("getbasicsearchdetails catch path returns BASIC_SEARCH_DETAILS_FETCH_FAILED const mod = loadModule("pages/api/endpoint/getbasicsearchdetails_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=title", getNavigationPropertyByPrimaryAttribute: () => ({ @@ -1863,6 +1866,7 @@ test("getbasicsearchdetailspaged returns APPEAL_TYPE_NAME_REQUIRED when appealTy { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=title", getNavigationPropertyByPrimaryAttribute: () => ({ @@ -1898,6 +1902,8 @@ test("getbasicsearchdetailspaged catch path returns BASIC_SEARCH_DETAILS_PAGED_F { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=title", getNavigationPropertyByPrimaryAttribute: () => ({ @@ -1936,6 +1942,7 @@ test("getbasicsearchpaged returns ORDER_BY_REQUIRED when orderby missing", async const mod = loadModule("pages/api/endpoint/getbasicsearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPagedCustom: () => ({}), @@ -1962,6 +1969,8 @@ test("getbasicsearchpaged catch path returns BASIC_SEARCH_PAGED_FETCH_FAILED", a const mod = loadModule("pages/api/endpoint/getbasicsearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), hashAPIPath: () => "&hash=expected", azureHeadersPagedCustom: () => ({}), @@ -2187,6 +2196,7 @@ test("getbasicdnssearchdetails returns CASE_REFERENCE_REQUIRED when caseReferenc { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2213,6 +2223,8 @@ test("getbasicdnssearchdetails catch path returns BASIC_DNS_SEARCH_DETAILS_FETCH { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2243,6 +2255,7 @@ test("getbasicdnssearchdetailspaged returns CASE_REFERENCE_REQUIRED when caseRef { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async () => ({}), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", @@ -2270,6 +2283,8 @@ test("getbasicdnssearchdetailspaged catch path returns BASIC_DNS_SEARCH_DETAILS_ { respondError: respondErrorMock, respondSuccess: respondSuccessMock, + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getToken: async () => ({ access_token: "token" }), getSelectQuery: () => "&$select=pinswg_name", hashAPIPath: () => "&hash=expected", From e8d4e8563e7933f4890c6e96b8789f75ea53c73d Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 09:59:59 +0000 Subject: [PATCH 10/14] refactor(api): migrate batch 9 endpoints to relayGet --- memory-bank/change-log.md | 32 ++++++++++ pages/api/endpoint/getappealid_api.js | 47 +++++--------- .../endpoint/getbasicdnssearchpaged_api.js | 62 ++++++++---------- .../endpoint/getbasicpartsaveddetails_api.js | 47 +++++--------- pages/api/endpoint/getdnslist_api.js | 47 ++++++-------- .../endpoint-handler-contract.test.cjs | 63 +++++-------------- 6 files changed, 125 insertions(+), 173 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index c564b26d..6556676e 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -974,3 +974,35 @@ Validation: Follow-ups: - Continue P2-S2 with next bounded batch from remaining legacy GET endpoints (currently: `createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getappealid_api`, `getbasicdnssearchpaged_api`, `getbasicpartsaveddetails_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`, `getdnslist_api`, `getmylpacases_api`, `getportallogin_api`). + +--- + +### CL-026: TASK22229 P2-S2 Batch 9 (appeal-id + DNS list/paged + part-saved relay GET cluster) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getappealid_api,getbasicdnssearchpaged_api,getbasicpartsaveddetails_api,getdnslist_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue bounded P2-S2 relay migration by moving another coherent set of read-only handlers to shared `relayGet`, preserving existing guards, transforms, and error contracts. +impact: Reduced duplicated relay plumbing and improved consistency for DNS list/paged and appeal detail lookup endpoints; no intended API contract changes. +status: completed + +Summary: + +- Migrated Batch 9 endpoints to shared `relayGet`: + - `getappealid_api.js` + - `getbasicdnssearchpaged_api.js` + - `getbasicpartsaveddetails_api.js` + - `getdnslist_api.js` +- Preserved existing input validation guards and endpoint-specific error contracts. +- Preserved endpoint-specific transform behavior (`@odata.nextLink` normalization in DNS list/paged handlers). +- Updated phase21 endpoint contract tests to inject `relayGet` mocks for these migrated handlers in guard and catch-path assertions. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue P2-S2 with next bounded batch from remaining legacy relay GET endpoints (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`, `getmylpacases_api`, `getportallogin_api`). diff --git a/pages/api/endpoint/getappealid_api.js b/pages/api/endpoint/getappealid_api.js index d01ce523..952f82d6 100644 --- a/pages/api/endpoint/getappealid_api.js +++ b/pages/api/endpoint/getappealid_api.js @@ -28,16 +28,9 @@ * description: Success */ -import axios from "axios"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const caseReference = req.query.caseReference; @@ -77,30 +70,24 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const escapedCaseReference = caseReference.split("'").join("''"); + const escapedCaseReference = caseReference.split("'").join("''"); - const queryUrl = - updateFormCollection + - "?$count=true&$select=_" + - primaryAttribute + - "s_value&$filter=pinswg_name eq '" + - escapedCaseReference + - "'"; + const queryUrl = + updateFormCollection + + "?$count=true&$select=_" + + primaryAttribute + + "s_value&$filter=pinswg_name eq '" + + escapedCaseReference + + "'"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + errorResponse: { status: 400, code: "APPEAL_ID_FETCH_FAILED", message: "Failed to fetch appeal id" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicdnssearchpaged_api.js b/pages/api/endpoint/getbasicdnssearchpaged_api.js index 4d8d7791..76336faa 100644 --- a/pages/api/endpoint/getbasicdnssearchpaged_api.js +++ b/pages/api/endpoint/getbasicdnssearchpaged_api.js @@ -42,17 +42,10 @@ * description: Success */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPagedCustom } from "../../../actions/core/headers"; -import { getToken } from "../../../actions/core/token"; -import { consoleLogger } from "../../../actions/core/logger"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const pageNumber = req.query.pageNumber; @@ -87,36 +80,33 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=" + + orderby + + " " + + fieldSort + + "&$count=true" + + (typeof pageNumber != "undefined" + ? "&$skiptoken=" + '' + : ""); - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_caseaddress,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference&$expand=primarycontactid($select=fullname)&$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=" + - orderby + - " " + - fieldSort + - "&$count=true" + - (typeof pageNumber != "undefined" - ? "&$skiptoken=" + '' - : ""); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => + azureHeadersPagedCustom(accessToken, showNumberOfRecords), + transformData: (data) => { + if (_.has(data, "@odata.nextLink") === true) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; + } - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ); - - if (_.has(data, "@odata.nextLink") === true) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "BASIC_DNS_SEARCH_PAGED_FETCH_FAILED", message: "Failed to fetch paged basic DNS search results" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicpartsaveddetails_api.js b/pages/api/endpoint/getbasicpartsaveddetails_api.js index 47ced874..a6fe1011 100644 --- a/pages/api/endpoint/getbasicpartsaveddetails_api.js +++ b/pages/api/endpoint/getbasicpartsaveddetails_api.js @@ -1,13 +1,6 @@ -import axios from "axios"; import { azureHeadersNoOdata } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const appealTypeName = req.query.appealTypeName; @@ -44,29 +37,23 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); + const queryUrl = + appealTypeName + + "?$filter=_" + + primaryIdAttribute + + "s_value eq " + + incidentID + + "&$count=true"; - const queryUrl = - appealTypeName + - "?$filter=_" + - primaryIdAttribute + - "s_value eq " + - incidentID + - "&$count=true"; - - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersNoOdata(token.access_token) - ); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => + azureHeadersNoOdata(accessToken), + errorResponse: { status: 400, code: "BASIC_PART_SAVED_DETAILS_FETCH_FAILED", message: "Failed to fetch basic part-saved details" - }); - } + } + }); } diff --git a/pages/api/endpoint/getdnslist_api.js b/pages/api/endpoint/getdnslist_api.js index 14251a39..ab1439f3 100644 --- a/pages/api/endpoint/getdnslist_api.js +++ b/pages/api/endpoint/getdnslist_api.js @@ -1,40 +1,29 @@ -import axios from "axios"; import _ from "lodash"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { - try { - const token = await getToken(); + const queryUrl = + "incidents?$select=pinswg_environmentalstatementlocation,pinswg_appealcasetype,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,statuscode,ticketnumber,title &$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - const queryUrl = - "incidents?$select=pinswg_environmentalstatementlocation,pinswg_appealcasetype,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,statuscode,ticketnumber,title &$filter=(pinswg_appealcasetype eq 846040011 or pinswg_appealcasetype eq 846040002) and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken), + transformData: (data) => { + let dataStr; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - let dataStr; - - _.has(data, "@odata.nextLink") === true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "DNS_LIST_FETCH_FAILED", message: "Failed to fetch DNS list" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index e4f9eae9..f0d311c0 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -2141,11 +2141,8 @@ test("getbasicdnssearchpaged returns ORDER_BY_REQUIRED when orderby missing", as const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async () => ({}), azureHeadersPagedCustom: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {}, _: { has: () => false } }); @@ -2161,15 +2158,9 @@ test("getbasicdnssearchpaged catch path returns BASIC_DNS_SEARCH_PAGED_FETCH_FAI const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), azureHeadersPagedCustom: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {}, _: { has: () => false } }); @@ -2939,11 +2930,8 @@ test("getappealid returns CASE_REFERENCE_REQUIRED when caseReference missing", a const mod = loadModule("pages/api/endpoint/getappealid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + relayGet: async () => ({}), + azureHeaders: () => ({}) }); const req = { @@ -2966,15 +2954,9 @@ test("getappealid catch path returns APPEAL_ID_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getappealid_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {} + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), + azureHeaders: () => ({}) }); const req = { @@ -3196,15 +3178,9 @@ test("getdnslist catch path returns DNS_LIST_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getdnslist_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), azureHeaders: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {}, _: { has: () => false } }); @@ -3270,11 +3246,8 @@ test("getbasicpartsaveddetails returns APPEAL_TYPE_NAME_REQUIRED when appealType { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeadersNoOdata: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + relayGet: async () => ({}), + azureHeadersNoOdata: () => ({}) } ); @@ -3300,15 +3273,9 @@ test("getbasicpartsaveddetails catch path returns BASIC_PART_SAVED_DETAILS_FETCH { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeadersNoOdata: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {} + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), + azureHeadersNoOdata: () => ({}) } ); From 0f4b8005ba1d67a1ba77af8df3161f1eddb1390c Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 10:08:51 +0000 Subject: [PATCH 11/14] refactor(api): migrate batch 10 endpoints to relayGet --- memory-bank/change-log.md | 31 ++++++++++ pages/api/endpoint/getmylpacases_api.js | 37 ++++++------ pages/api/endpoint/getportallogin_api.js | 37 +++++------- .../endpoint-handler-contract.test.cjs | 58 ++++++------------- 4 files changed, 78 insertions(+), 85 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 6556676e..6f9241bc 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -1006,3 +1006,34 @@ Validation: Follow-ups: - Continue P2-S2 with next bounded batch from remaining legacy relay GET endpoints (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`, `getmylpacases_api`, `getportallogin_api`). + +--- + +### CL-027: TASK22229 P2-S2 Batch 10 (portal login + my LPA cases relay GET pair) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/{getportallogin_api,getmylpacases_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue bounded relay migration by moving two remaining high-use portal retrieval endpoints to shared `relayGet` while preserving existing hash/lookup guards and response contracts. +impact: Further relay boilerplate reduction and consistent forwarding behavior in portal login/LPA case retrieval flows; no intended API contract changes. +status: completed + +Summary: + +- Migrated Batch 10 endpoints to shared `relayGet`: + - `getportallogin_api.js` (preserved hash validation guard and error contract) + - `getmylpacases_api.js` (preserved LPA lookup/404 guard and title transform) +- Preserved endpoint-specific behavior: + - `getportallogin_api`: raw + encoded hash candidate validation before relay call + - `getmylpacases_api`: JSONPath LPA lookup with `LPA_NOT_FOUND` handling and `pinswg_title` enrichment transform +- Updated phase21 endpoint contract tests to inject `relayGet` mocks for migrated handlers in validation/catch/success paths. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue P2-S2 with the remaining legacy relay GET candidates (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`). diff --git a/pages/api/endpoint/getmylpacases_api.js b/pages/api/endpoint/getmylpacases_api.js index 47daab59..71a947d9 100644 --- a/pages/api/endpoint/getmylpacases_api.js +++ b/pages/api/endpoint/getmylpacases_api.js @@ -17,18 +17,11 @@ * description: Success */ -import axios from "axios"; import { JSONPath as jsonpath } from "jsonpath-plus"; import { getLPA } from "../../../actions/services/referenceDataService"; import { azureHeaders } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const lpaid = req.query.lpaid; @@ -42,7 +35,6 @@ export default async function ApiProxy(req, res) { } try { - const token = await getToken(); const lpaList = await getLPA(); const lpaGUID = jsonpath({ @@ -74,18 +66,23 @@ export default async function ApiProxy(req, res) { //console.log("///////////\nPortal query: ", queryUrl, "<<< azureHeaders(accessToken), + transformData: (data) => { + data.value.forEach(function (element) { + element.pinswg_title = element.title; + }); + return data; + }, + errorResponse: { + status: 400, + code: "MY_LPA_CASES_FETCH_FAILED", + message: "Failed to fetch my LPA cases" + } }); - - return respondSuccess(res, data); } catch (error) { - consoleLogger(error); return respondError(res, { status: 400, code: "MY_LPA_CASES_FETCH_FAILED", diff --git a/pages/api/endpoint/getportallogin_api.js b/pages/api/endpoint/getportallogin_api.js index c3f52aae..a6593a9a 100644 --- a/pages/api/endpoint/getportallogin_api.js +++ b/pages/api/endpoint/getportallogin_api.js @@ -16,16 +16,10 @@ * description: hello world */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const emailAddress = req.query.emailAddress; @@ -66,24 +60,19 @@ export default async function ApiProxy(req, res) { }); } - try { - const token = await getToken(); - const queryUrl = - "contacts?$filter=emailaddress1 eq '" + - emailAddress + - "' and statuscode eq 1&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname"; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); + const queryUrl = + "contacts?$filter=emailaddress1 eq '" + + emailAddress + + "' and statuscode eq 1&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + errorResponse: { status: 400, code: "PORTAL_LOGIN_FETCH_FAILED", message: "Failed to fetch portal login" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index f0d311c0..2a78106e 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -274,14 +274,12 @@ test("getportallogin returns HASH_REQUIRED when hash missing", async () => { const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async () => ({}), hashAPIPath: (input) => input && input.startsWith("/api/endpoint/getportallogin_api") ? "&hash=expected" : "&hash=relay", - azureHeadersPaged: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + azureHeadersPaged: () => ({}) }); const req = { query: { emailAddress: "user@test.local" } }; @@ -296,14 +294,12 @@ test("getportallogin returns INVALID_HASH when hash mismatch", async () => { const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async () => ({}), hashAPIPath: (input) => input && input.startsWith("/api/endpoint/getportallogin_api") ? "&hash=expected" : "&hash=relay", - azureHeadersPaged: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + azureHeadersPaged: () => ({}) }); const req = { query: { emailAddress: "user@test.local", hash: "wrong" } }; @@ -318,18 +314,13 @@ test("getportallogin catch path returns PORTAL_LOGIN_FETCH_FAILED", async () => const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), hashAPIPath: (input) => input && input.startsWith("/api/endpoint/getportallogin_api") ? "&hash=expected" : "&hash=relay", - azureHeadersPaged: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {} + azureHeadersPaged: () => ({}) }); const req = { @@ -349,16 +340,13 @@ test("getportallogin accepts encoded email hash variant", async () => { const mod = loadModule("pages/api/endpoint/getportallogin_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async ({ res }) => + respondSuccessMock(res, { value: [{ contactid: "c1" }] }), hashAPIPath: (input) => input && input.includes("emailAddress=user%2Btest%40local") ? "&hash=expected" : "&hash=other", - azureHeadersPaged: () => ({}), - axios: { - get: async () => ({ data: { value: [{ contactid: "c1" }] } }) - }, - consoleLogger: () => {} + azureHeadersPaged: () => ({}) }); const req = { @@ -2421,13 +2409,10 @@ test("getmylpacases returns LPA_ID_REQUIRED when lpaid missing", async () => { const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async () => ({}), getLPA: async () => [{ name: "Test LPA", accountid: "acc1" }], jsonpath: () => [{ accountid: "acc1" }], - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + azureHeaders: () => ({}) }); const req = { query: {} }; @@ -2442,13 +2427,10 @@ test("getmylpacases returns LPA_NOT_FOUND when lpaid lookup misses", async () => const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async () => ({}), getLPA: async () => [{ name: "Another LPA", accountid: "acc1" }], jsonpath: () => [], - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {} + azureHeaders: () => ({}) }); const req = { query: { lpaid: "Missing LPA" } }; @@ -2463,17 +2445,11 @@ test("getmylpacases catch path returns MY_LPA_CASES_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getmylpacases_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), getLPA: async () => [{ name: "Test LPA", accountid: "acc1" }], jsonpath: () => [{ accountid: "acc1" }], - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {} + azureHeaders: () => ({}) }); const req = { query: { lpaid: "Test LPA" } }; From bf1ba9bc1e6340f18663c0a4798c249097bbf66d Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 10:15:25 +0000 Subject: [PATCH 12/14] refactor(api): migrate batch 11 advancedsearchpaged to relayGet --- memory-bank/change-log.md | 34 +++++++ .../endpoint/getadvancedsearchpaged_api.js | 99 ++++--------------- .../endpoint-handler-contract.test.cjs | 20 +--- 3 files changed, 55 insertions(+), 98 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 6f9241bc..77cc6362 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -1037,3 +1037,37 @@ Validation: Follow-ups: - Continue P2-S2 with the remaining legacy relay GET candidates (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getadvancedsearchpaged_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`). + +--- + +### CL-028: TASK22229 P2-S2 Batch 11 (advanced-search-paged relay GET) + +date: 2026-03-24 +author: Cline +scope: `pages/api/endpoint/getadvancedsearchpaged_api.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Continue bounded relay migration by moving the remaining advanced-search paged GET handler to shared `relayGet` while preserving query validation and response contracts. +impact: Reduced duplicated relay plumbing and aligned advanced paged search route with shared forwarding; no intended API contract changes. +status: completed + +Summary: + +- Migrated `getadvancedsearchpaged_api.js` to shared `relayGet`. +- Preserved existing guard behavior and error contracts: + - `SEARCH_STRING_REQUIRED` + - `ORDER_BY_REQUIRED` + - `FIELD_SORT_REQUIRED` + - `SHOW_NUMBER_OF_RECORDS_REQUIRED` + - `INVALID_SEARCH_STRING` + - `ADVANCED_SEARCH_PAGED_FETCH_FAILED` +- Preserved endpoint-specific transform behavior (`@odata.nextLink` normalization via `transformData`). +- Updated phase21 endpoint contract tests to inject `relayGet` mocks for `getadvancedsearchpaged_api` guard/catch assertions. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Continue P2-S2 with remaining legacy relay GET candidates (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`). diff --git a/pages/api/endpoint/getadvancedsearchpaged_api.js b/pages/api/endpoint/getadvancedsearchpaged_api.js index 3150880e..f62a21ef 100644 --- a/pages/api/endpoint/getadvancedsearchpaged_api.js +++ b/pages/api/endpoint/getadvancedsearchpaged_api.js @@ -47,17 +47,10 @@ // * description: Success // */ -import axios from "axios"; import _ from "lodash"; import { azureHeadersPagedCustom } from "../../../actions/core/headers"; -import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; -import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondError } from "../middleware/apiResponse"; +import { relayGet } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const rawSearchString = req.query.searchstring; @@ -180,81 +173,23 @@ export default async function ApiProxy(req, res) { : ""); } - console.log("adv qu: ", queryUrl); + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => + azureHeadersPagedCustom(accessToken, showNumberOfRecords), + transformData: (data) => { + let dataStr; + _.has(data, "@odata.nextLink") === true && + ((dataStr = JSON.stringify(data["@odata.nextLink"])), + (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - try { - const token = await getToken(); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPagedCustom(token.access_token, showNumberOfRecords) - ); - - let dataStr; - _.has(data, "@odata.nextLink") === true && - ((dataStr = JSON.stringify(data["@odata.nextLink"])), - (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1])); - - if (_.has(searchString, "projecttype")) { - // await updateValueArray(data, token); - // async function updateValueArray(data, token) { - // for (let i = 0; i < data.value.length; i++) { - // const item = data.value[i]; - // try { - // // Axios call using the `incidentid` to fetch additional data - // const response = await axios.get( - // WEBAPI_URL + - // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + - // item.incidentid + - // " and _pinswg_projecttype_value eq " + - // searchString.projecttype + - // "&$select=_pinswg_projecttype_value" + - // hashAPIPath( - // "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + - // item.incidentid + - // " and _pinswg_projecttype_value eq " + - // searchString.projecttype + - // "&$select=_pinswg_projecttype_value" - // ), - // azureHeadersPaged(token.access_token) - // ); - // // Assuming the response contains the additional data you want to add - // // console.log(response.data.value[0]); - // console.log(response.data.value); - // if ( - // response.data.value.length > 0 && - // response.data.value[0] - // ._pinswg_projecttype_value != null - // ) { - // Object.assign( - // item, - // response.data.value[0] - // ); // Update item with new data - // } else { - // console.log( - // `No project type found for incident ID ${item.incidentid}` - // ); - // } - // } catch (error) { - // console.error( - // `Error fetching data for incident ID ${item.incidentid}:`, - // error - // ); - // } - // } - // } - // data.value = data.value.filter( - // (item) => item._pinswg_projecttype_value != null - // ); - // data["@odata.count"] = data.value.length; - } - - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + return data; + }, + errorResponse: { status: 400, code: "ADVANCED_SEARCH_PAGED_FETCH_FAILED", message: "Failed to fetch advanced search paged results" - }); - } + } + }); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 2a78106e..ae92776b 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1101,11 +1101,8 @@ test("getadvancedsearchpaged returns SEARCH_STRING_REQUIRED when searchstring mi const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async () => ({}), azureHeadersPagedCustom: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {}, _: { has: () => false } }); @@ -1127,11 +1124,8 @@ test("getadvancedsearchpaged returns ORDER_BY_REQUIRED when orderby missing", as const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async () => ({}), azureHeadersPagedCustom: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, - consoleLogger: () => {}, _: { has: () => false } }); @@ -1153,15 +1147,9 @@ test("getadvancedsearchpaged catch path returns ADVANCED_SEARCH_PAGED_FETCH_FAIL const mod = loadModule("pages/api/endpoint/getadvancedsearchpaged_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), azureHeadersPagedCustom: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, - consoleLogger: () => {}, _: { has: () => false } }); From da29e789c0e8a302793098f9bb2423af7574095f Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 10:27:27 +0000 Subject: [PATCH 13/14] refactor(api): migrate batch 12 remaining relay-get candidates --- memory-bank/change-log.md | 36 ++++++ pages/api/endpoint/createwatchedcases_api.js | 28 +++-- .../endpoint/deletewatchedcasesproxy_api.js | 18 +-- pages/api/endpoint/getadvancedsearch_api.js | 111 +++++++++--------- .../endpoint/getbasicsearch_by_address_api.js | 26 ++-- .../endpoint/getbasicsearch_by_lparref_api.js | 15 +-- pages/api/endpoint/getdnscoords_api.js | 25 +--- pages/api/middleware/relayForwarding.js | 56 +++++++-- .../endpoint-handler-contract.test.cjs | 51 +++----- 9 files changed, 194 insertions(+), 172 deletions(-) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 77cc6362..50b7b973 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -1071,3 +1071,39 @@ Validation: Follow-ups: - Continue P2-S2 with remaining legacy relay GET candidates (`createwatchedcases_api`, `deletewatchedcasesproxy_api`, `getadvancedsearch_api`, `getbasicsearch_by_address_api`, `getbasicsearch_by_lparref_api`, `getdnscoords_api`). + +--- + +### CL-029: TASK22229 P2-S2 Batch 12 (remaining relay GET candidates) + +date: 2026-03-24 +author: Cline +scope: `pages/api/middleware/relayForwarding.js`, `pages/api/endpoint/{createwatchedcases_api,deletewatchedcasesproxy_api,getadvancedsearch_api,getbasicsearch_by_address_api,getbasicsearch_by_lparref_api,getdnscoords_api}.js`, `tests/phase21/endpoint-handler-contract.test.cjs` +type: change +rationale: Complete Batch 12 by migrating the remaining listed legacy relay GET candidates to shared forwarding helpers while preserving endpoint-specific validation, transforms, and error contracts. +impact: Consolidated relay GET behavior and reduced endpoint-level relay boilerplate; no intended API contract changes. +status: completed + +Summary: + +- Extended relay middleware with reusable helpers: + - `relayGetData` now supports token reuse via `transformData` path + - Added `forwardGetData` to support custom base URLs (used by proxy-to-local endpoint) while preserving hash behavior +- Migrated remaining Batch 12 candidates: + - `getadvancedsearch_api.js` -> `relayGet` + `relayGetData` for project-type enrichment loop + - `getdnscoords_api.js` -> `relayGetData` for both DNS + SIPS source pulls + - `getbasicsearch_by_lparref_api.js` -> `relayGetData` + - `getbasicsearch_by_address_api.js` -> `relayGetData` for appeal-type and incident fetches + - `deletewatchedcasesproxy_api.js` -> `forwardGetData` with `BASE_URL` + - `createwatchedcases_api.js` -> `relayGetData` for record-exists pre-check (POST/PATCH upsert flow preserved) +- Preserved endpoint-specific guard and error contracts (including status codes such as 500 for `BASIC_SEARCH_BY_ADDRESS_FETCH_FAILED`). +- Updated phase21 endpoint contract tests for migrated handlers to mock `relayGetData` / `forwardGetData` where appropriate. + +Validation: + +- `node tests/phase21/endpoint-handler-contract.test.cjs` -> pass (152/152) +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Batch 12 completes the previously listed remaining P2-S2 relay GET candidates. diff --git a/pages/api/endpoint/createwatchedcases_api.js b/pages/api/endpoint/createwatchedcases_api.js index da199830..0c67b6a2 100644 --- a/pages/api/endpoint/createwatchedcases_api.js +++ b/pages/api/endpoint/createwatchedcases_api.js @@ -15,26 +15,30 @@ import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; +import { relayGetData } from "../middleware/relayForwarding"; const WEBAPI_URL = process.env.RELAY_ROOT || "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; -const recordExists = async (incidentId, contactId, token) => { +const recordExists = async (incidentId, contactId, accessToken) => { const filter = `$filter=pinswg_WatchedCase/incidentid eq ${incidentId} and pinswg_Contact/contactid eq ${contactId}`; const queryUrl = `pinswg_watchlists?${filter}`; - const url = WEBAPI_URL + queryUrl + hashAPIPath(queryUrl); try { - const res = await axios.get(url, { - headers: { - Authorization: "Bearer " + token.access_token, - Accept: "application/json" - } + const response = await relayGetData({ + queryUrl, + accessToken, + requestOptionsBuilder: (token) => ({ + headers: { + Authorization: "Bearer " + token, + Accept: "application/json" + } + }) }); - if (res.data.value && res.data.value.length > 0) { - return res.data.value[0]; // return the existing record + if (response.data.value && response.data.value.length > 0) { + return response.data.value[0]; // return the existing record } return null; @@ -79,7 +83,11 @@ export default async function ApiProxy(req, res) { const incidentId = watchedCaseMatch[1]; const contactId = contactMatch[1]; - const existingRecord = await recordExists(incidentId, contactId, token); + const existingRecord = await recordExists( + incidentId, + contactId, + token.access_token + ); let method, queryUrl; diff --git a/pages/api/endpoint/deletewatchedcasesproxy_api.js b/pages/api/endpoint/deletewatchedcasesproxy_api.js index 4aa4466a..0a1b8246 100644 --- a/pages/api/endpoint/deletewatchedcasesproxy_api.js +++ b/pages/api/endpoint/deletewatchedcasesproxy_api.js @@ -17,16 +17,10 @@ * description: Success */ -import axios from "axios"; import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { forwardGetData } from "../middleware/relayForwarding"; const BASE_URL = process.env.API_ROOT || "http://localhost:3000"; @@ -45,15 +39,15 @@ export default async function ApiProxy(req, res) { } try { - const token = await getToken(); const queryUrl = "/api/endpoint/deletewatchedcases_api?watchedCaseID=" + watchedCaseID; - const { data } = await axios.get( - BASE_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const { data } = await forwardGetData({ + baseUrl: BASE_URL, + queryUrl, + requestOptionsBuilder: (accessToken) => azureHeaders(accessToken) + }); return respondSuccess(res, data); } catch (error) { diff --git a/pages/api/endpoint/getadvancedsearch_api.js b/pages/api/endpoint/getadvancedsearch_api.js index 2f504689..e90592b5 100644 --- a/pages/api/endpoint/getadvancedsearch_api.js +++ b/pages/api/endpoint/getadvancedsearch_api.js @@ -21,16 +21,10 @@ * description: Success */ -import axios from "axios"; import { azureHeadersPaged } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { relayGet, relayGetData } from "../middleware/relayForwarding"; export default async function ApiProxy(req, res) { const encodedSearchString = req.query.searchstring; @@ -109,62 +103,63 @@ export default async function ApiProxy(req, res) { queryString + " pinswg_appealcasetype ne null and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true"; - try { - const token = await getToken(); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeadersPaged(token.access_token) - ); - - if (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) { - const dataStr = JSON.stringify(data["@odata.nextLink"]); - data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; - } - - if (searchString.projecttype != null) { - for (let i = 0; i < data.value.length; i++) { - const item = data.value[i]; - - try { - const projectTypeQuery = - "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + - item.incidentid + - " and _pinswg_projecttype_value eq " + - searchString.projecttype + - "&$select=_pinswg_projecttype_value"; - - const response = await axios.get( - WEBAPI_URL + - projectTypeQuery + - hashAPIPath(projectTypeQuery), - azureHeadersPaged(token.access_token) - ); - - if ( - response.data.value.length > 0 && - response.data.value[0]._pinswg_projecttype_value != null - ) { - Object.assign(item, response.data.value[0]); - } - } catch (error) { - consoleLogger(error); - } + return relayGet({ + queryUrl, + res, + requestOptionsBuilder: (accessToken) => azureHeadersPaged(accessToken), + transformData: async (data, accessToken) => { + if (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) { + const dataStr = JSON.stringify(data["@odata.nextLink"]); + data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]; } - data.value = data.value.filter( - (item) => item._pinswg_projecttype_value != null - ); + if (searchString.projecttype != null) { + for (let i = 0; i < data.value.length; i++) { + const item = data.value[i]; - data["@odata.count"] = data.value.length; - } + try { + const projectTypeQuery = + "pinswg_sipses?$filter=_pinswg_sipscase_value eq " + + item.incidentid + + " and _pinswg_projecttype_value eq " + + searchString.projecttype + + "&$select=_pinswg_projecttype_value"; - return respondSuccess(res, data); - } catch (error) { - consoleLogger(error); - return respondError(res, { + const projectTypeResponse = await relayGetData({ + queryUrl: projectTypeQuery, + accessToken, + requestOptionsBuilder: (token) => + azureHeadersPaged(token) + }); + + if ( + projectTypeResponse.data.value.length > 0 && + projectTypeResponse.data.value[0] + ._pinswg_projecttype_value != null + ) { + Object.assign( + item, + projectTypeResponse.data.value[0] + ); + } + } catch (error) { + consoleLogger(error); + } + } + + data.value = data.value.filter( + (item) => item._pinswg_projecttype_value != null + ); + + data["@odata.count"] = data.value.length; + } + + return data; + }, + errorResponse: { status: 400, code: "ADVANCED_SEARCH_FETCH_FAILED", message: "Failed to fetch advanced search results" - }); - } + } + }); } diff --git a/pages/api/endpoint/getbasicsearch_by_address_api.js b/pages/api/endpoint/getbasicsearch_by_address_api.js index 02ab6644..477fdbed 100644 --- a/pages/api/endpoint/getbasicsearch_by_address_api.js +++ b/pages/api/endpoint/getbasicsearch_by_address_api.js @@ -1,14 +1,8 @@ -import axios from "axios"; import { JSONPath as jsonpath } from "jsonpath-plus"; -import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { relayGetData } from "../middleware/relayForwarding"; const escapeODataString = (value = "") => String(value).replace(/'/g, "''").trim(); @@ -366,15 +360,15 @@ const fetchSearchResultsForAppealType = async (item, token, params) => { "\n\n", item.LogicalCollectionName, "\n\n", - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + queryUrl, "\n==========================================\n" ); try { - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const { data } = await relayGetData({ + queryUrl, + accessToken: token.access_token + }); return (data.value || []).map((record) => ({ ...record, @@ -391,10 +385,10 @@ const fetchIncident = async (incidentId, token) => { const queryUrl = buildIncidentQueryUrl(incidentId); try { - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const { data } = await relayGetData({ + queryUrl, + accessToken: token.access_token + }); return data?.value?.[0] || null; } catch (error) { diff --git a/pages/api/endpoint/getbasicsearch_by_lparref_api.js b/pages/api/endpoint/getbasicsearch_by_lparref_api.js index 228bf990..ada7ae26 100644 --- a/pages/api/endpoint/getbasicsearch_by_lparref_api.js +++ b/pages/api/endpoint/getbasicsearch_by_lparref_api.js @@ -1,13 +1,6 @@ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; -import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { relayGetData } from "../middleware/relayForwarding"; // ✅ IMPORTANT: Only include the 23 types that actually contain pinswg_lpaapplicationreference // You can copy from your existing appealTypeArray but only keep those entities. @@ -281,7 +274,6 @@ var appealTypeArrayWithLpaRef = [ export default async function ApiProxy(req, res) { try { - const token = await getToken(); let lpaRef = (req.query.lpaRef || "").trim(); if (!lpaRef) { @@ -321,10 +313,7 @@ export default async function ApiProxy(req, res) { `&$count=true` + `&$orderby=modifiedon desc`; - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + const { data } = await relayGetData({ queryUrl }); // Normalize response rows const normalized = (data?.value || []).map((r) => ({ diff --git a/pages/api/endpoint/getdnscoords_api.js b/pages/api/endpoint/getdnscoords_api.js index 4ecbdc19..1d253873 100644 --- a/pages/api/endpoint/getdnscoords_api.js +++ b/pages/api/endpoint/getdnscoords_api.js @@ -1,14 +1,7 @@ -import axios from "axios"; -import { azureHeaders } from "../../../actions/core/headers"; import { consoleLogger } from "../../../actions/core/logger"; -import { getToken } from "../../../actions/core/token"; import OSPoint from "ospoint"; -import { hashAPIPath } from "../../../actions/core/hash"; import { respondError, respondSuccess } from "../middleware/apiResponse"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { relayGetData } from "../middleware/relayForwarding"; const renameKeys = (obj) => { const keyMappings = { @@ -75,8 +68,6 @@ const keysToRemove = [ ]; export default async function ApiProxy(req, res) { - const token = await getToken(); - const queryUrl = "pinswg_dnses?$select=pinswg_projectlocation,pinswg_mapzoomlevel,pinswg_name,pinswg_anticipatedgridreferenceeastingtext,pinswg_anticipatedgridreferencenorthingtext,pinswg_projectname,pinswg_dnsid,_pinswg_associatedlpa_value,_pinswg_appellant_value&$filter=pinswg_anticipatedgridreferencenorthingtext ne null and pinswg_anticipatedgridreferenceeastingtext ne null&$count=true"; @@ -87,11 +78,8 @@ export default async function ApiProxy(req, res) { const coordsObj = { value: [] }; try { - // First Axios request - const dnsCoordsObj = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - azureHeaders(token.access_token) - ); + // First relay request + const dnsCoordsObj = await relayGetData({ queryUrl }); // Merge the first response's 'value' array into the coordsObj's value array if (Array.isArray(dnsCoordsObj.data.value)) { coordsObj.value = coordsObj.value.concat(dnsCoordsObj.data.value); // Concatenate arrays @@ -99,11 +87,8 @@ export default async function ApiProxy(req, res) { coordsObj.value.push(dnsCoordsObj.data.value); // Push single value if it's not an array } - // Second Axios request - const sipsCoordsObj = await axios.get( - WEBAPI_URL + queryUrlSips + hashAPIPath(queryUrlSips), - azureHeaders(token.access_token) - ); + // Second relay request + const sipsCoordsObj = await relayGetData({ queryUrl: queryUrlSips }); // Merge the second response's 'value' array into the coordsObj's value array if (Array.isArray(sipsCoordsObj.data.value)) { coordsObj.value = coordsObj.value.concat(sipsCoordsObj.data.value); // Concatenate arrays diff --git a/pages/api/middleware/relayForwarding.js b/pages/api/middleware/relayForwarding.js index 45f07dd8..f5b4c16c 100644 --- a/pages/api/middleware/relayForwarding.js +++ b/pages/api/middleware/relayForwarding.js @@ -17,20 +17,60 @@ export const relayGet = async ({ requestOptionsBuilder }) => { try { - const token = await getToken(); - const { data } = await axios.get( - WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - typeof requestOptionsBuilder === "function" - ? requestOptionsBuilder(token.access_token) - : azureHeaders(token.access_token) - ); + const { data, accessToken } = await relayGetData({ + queryUrl, + requestOptionsBuilder + }); return respondSuccess( res, - typeof transformData === "function" ? transformData(data) : data + typeof transformData === "function" + ? await transformData(data, accessToken) + : data ); } catch (error) { consoleLogger(error); return respondError(res, errorResponse); } }; + +export const relayGetData = async ({ + queryUrl, + requestOptionsBuilder, + accessToken +}) => { + return forwardGetData({ + queryUrl, + requestOptionsBuilder, + accessToken + }); +}; + +export const forwardGetData = async ({ + baseUrl, + queryUrl, + requestOptionsBuilder, + accessToken, + appendHash = true +}) => { + const tokenAccessToken = + typeof accessToken === "string" && accessToken.length > 0 + ? accessToken + : (await getToken()).access_token; + + const resolvedBaseUrl = baseUrl || WEBAPI_URL; + const finalUrl = + resolvedBaseUrl + queryUrl + (appendHash ? hashAPIPath(queryUrl) : ""); + + const { data } = await axios.get( + finalUrl, + typeof requestOptionsBuilder === "function" + ? requestOptionsBuilder(tokenAccessToken) + : azureHeaders(tokenAccessToken) + ); + + return { + data, + accessToken: tokenAccessToken + }; +}; diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index ae92776b..23dd939d 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1069,14 +1069,13 @@ test("getadvancedsearch catch path returns ADVANCED_SEARCH_FETCH_FAILED", async const mod = loadModule("pages/api/endpoint/getadvancedsearch_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", + relayGet: async ({ res, errorResponse }) => + respondErrorMock(res, errorResponse), + relayGetData: async () => ({ + data: { value: [] }, + accessToken: "token" + }), azureHeadersPaged: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } - }, consoleLogger: () => {} }); @@ -2005,12 +2004,9 @@ test("getbasicsearch_by_lparref catch path returns BASIC_SEARCH_LPA_REF_FETCH_FA { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => { - throw new Error("token failed"); + relayGetData: async () => { + throw new Error("relay failed"); }, - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { get: async () => ({ data: { value: [] } }) }, consoleLogger: () => {} } ); @@ -2033,11 +2029,7 @@ test("getbasicsearch_by_address success returns existing payload contract", asyn respondError: respondErrorMock, respondSuccess: respondSuccessMock, getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => ({ data: { value: [] } }) - }, + relayGetData: async () => ({ data: { value: [] } }), consoleLogger: () => {}, jsonpath: () => [] } @@ -2063,10 +2055,8 @@ test("getbasicsearch_by_address catch path returns BASIC_SEARCH_BY_ADDRESS_FETCH getToken: async () => { throw new Error("token failed"); }, - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => ({ data: { value: [] } }) + relayGetData: async () => { + throw new Error("relay failed"); }, consoleLogger: () => {}, jsonpath: () => [] @@ -2744,14 +2734,10 @@ test("deletewatchedcasesproxy catch path returns WATCHED_CASE_PROXY_DELETE_FAILE { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => { - throw new Error("proxy delete failed"); - } + forwardGetData: async () => { + throw new Error("proxy delete failed"); }, + azureHeaders: () => ({}), consoleLogger: () => {}, process: { env: {} } } @@ -3412,13 +3398,8 @@ test("getdnscoords catch path returns DNS_COORDS_FETCH_FAILED", async () => { const mod = loadModule("pages/api/endpoint/getdnscoords_api.js", { respondError: respondErrorMock, respondSuccess: respondSuccessMock, - getToken: async () => ({ access_token: "token" }), - hashAPIPath: () => "&hash=expected", - azureHeaders: () => ({}), - axios: { - get: async () => { - throw new Error("relay failed"); - } + relayGetData: async () => { + throw new Error("relay failed"); }, consoleLogger: () => {}, OSPoint: function () {} From 51e255f70b37cca961ff2f5ba210e7940b77c250 Mon Sep 17 00:00:00 2001 From: robbond Date: Tue, 24 Mar 2026 10:35:09 +0000 Subject: [PATCH 14/14] fix(email): log notify send attempts without error framing --- pages/api/email/notify.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pages/api/email/notify.js b/pages/api/email/notify.js index a03b1aa7..767e93aa 100644 --- a/pages/api/email/notify.js +++ b/pages/api/email/notify.js @@ -63,10 +63,15 @@ export default async function ApiProxy(req, res) { const notifyClient = new NotifyClient(process.env.NOTIFY_API_KEY); //const emailReplyToId = process.env.EMAIL_REPLY_TO_ID; - consoleLogger({ - event: "notify_send_attempt", - payload: redactSensitive(data) - }); + console.info( + "notify_send_attempt", + redactSensitive({ + templateId: data.templateId, + emailAddress: data.emailAddress, + reference: data.reference, + personalisation: data.personalisation + }) + ); try { await notifyClient.sendEmail(data.templateId, data.emailAddress, {