refactor(api): migrate batch 9 endpoints to relayGet

This commit is contained in:
2026-03-24 09:59:59 +00:00
parent 13be069b8b
commit e8d4e8563e
6 changed files with 125 additions and 173 deletions
+32
View File
@@ -974,3 +974,35 @@ Validation:
Follow-ups: 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`). - 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`).
+17 -30
View File
@@ -28,16 +28,9 @@
* description: Success * description: Success
*/ */
import axios from "axios";
import { azureHeaders } from "../../../actions/core/headers"; import { azureHeaders } from "../../../actions/core/headers";
import { consoleLogger } from "../../../actions/core/logger"; import { respondError } from "../middleware/apiResponse";
import { getToken } from "../../../actions/core/token"; import { relayGet } from "../middleware/relayForwarding";
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/";
export default async function ApiProxy(req, res) { export default async function ApiProxy(req, res) {
const caseReference = req.query.caseReference; const caseReference = req.query.caseReference;
@@ -77,30 +70,24 @@ export default async function ApiProxy(req, res) {
}); });
} }
try { const escapedCaseReference = caseReference.split("'").join("''");
const token = await getToken();
const escapedCaseReference = caseReference.split("'").join("''");
const queryUrl = const queryUrl =
updateFormCollection + updateFormCollection +
"?$count=true&$select=_" + "?$count=true&$select=_" +
primaryAttribute + primaryAttribute +
"s_value&$filter=pinswg_name eq '" + "s_value&$filter=pinswg_name eq '" +
escapedCaseReference + escapedCaseReference +
"'"; "'";
const { data } = await axios.get( return relayGet({
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), queryUrl,
azureHeaders(token.access_token) res,
); requestOptionsBuilder: (accessToken) => azureHeaders(accessToken),
errorResponse: {
return respondSuccess(res, data);
} catch (error) {
consoleLogger(error);
return respondError(res, {
status: 400, status: 400,
code: "APPEAL_ID_FETCH_FAILED", code: "APPEAL_ID_FETCH_FAILED",
message: "Failed to fetch appeal id" message: "Failed to fetch appeal id"
}); }
} });
} }
@@ -42,17 +42,10 @@
* description: Success * description: Success
*/ */
import axios from "axios";
import _ from "lodash"; import _ from "lodash";
import { azureHeadersPagedCustom } from "../../../actions/core/headers"; import { azureHeadersPagedCustom } from "../../../actions/core/headers";
import { getToken } from "../../../actions/core/token"; import { respondError } from "../middleware/apiResponse";
import { consoleLogger } from "../../../actions/core/logger"; import { relayGet } from "../middleware/relayForwarding";
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/";
export default async function ApiProxy(req, res) { export default async function ApiProxy(req, res) {
const pageNumber = req.query.pageNumber; const pageNumber = req.query.pageNumber;
@@ -87,36 +80,33 @@ export default async function ApiProxy(req, res) {
}); });
} }
try { const queryUrl =
const token = await getToken(); "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=" + '<cookie pagenumber="' + pageNumber + '" />'
: "");
const queryUrl = return relayGet({
"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=" + queryUrl,
orderby + res,
" " + requestOptionsBuilder: (accessToken) =>
fieldSort + azureHeadersPagedCustom(accessToken, showNumberOfRecords),
"&$count=true" + transformData: (data) => {
(typeof pageNumber != "undefined" if (_.has(data, "@odata.nextLink") === true) {
? "&$skiptoken=" + '<cookie pagenumber="' + pageNumber + '" />' const dataStr = JSON.stringify(data["@odata.nextLink"]);
: ""); data["@odata.nextLink"] = dataStr.split("/v8.2/")[1];
}
const { data } = await axios.get( return data;
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), },
azureHeadersPagedCustom(token.access_token, showNumberOfRecords) errorResponse: {
);
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, {
status: 400, status: 400,
code: "BASIC_DNS_SEARCH_PAGED_FETCH_FAILED", code: "BASIC_DNS_SEARCH_PAGED_FETCH_FAILED",
message: "Failed to fetch paged basic DNS search results" message: "Failed to fetch paged basic DNS search results"
}); }
} });
} }
@@ -1,13 +1,6 @@
import axios from "axios";
import { azureHeadersNoOdata } from "../../../actions/core/headers"; import { azureHeadersNoOdata } from "../../../actions/core/headers";
import { consoleLogger } from "../../../actions/core/logger"; import { respondError } from "../middleware/apiResponse";
import { getToken } from "../../../actions/core/token"; import { relayGet } from "../middleware/relayForwarding";
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/";
export default async function ApiProxy(req, res) { export default async function ApiProxy(req, res) {
const appealTypeName = req.query.appealTypeName; const appealTypeName = req.query.appealTypeName;
@@ -44,29 +37,23 @@ export default async function ApiProxy(req, res) {
}); });
} }
try { const queryUrl =
const token = await getToken(); appealTypeName +
"?$filter=_" +
primaryIdAttribute +
"s_value eq " +
incidentID +
"&$count=true";
const queryUrl = return relayGet({
appealTypeName + queryUrl,
"?$filter=_" + res,
primaryIdAttribute + requestOptionsBuilder: (accessToken) =>
"s_value eq " + azureHeadersNoOdata(accessToken),
incidentID + errorResponse: {
"&$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, {
status: 400, status: 400,
code: "BASIC_PART_SAVED_DETAILS_FETCH_FAILED", code: "BASIC_PART_SAVED_DETAILS_FETCH_FAILED",
message: "Failed to fetch basic part-saved details" message: "Failed to fetch basic part-saved details"
}); }
} });
} }
+18 -29
View File
@@ -1,40 +1,29 @@
import axios from "axios";
import _ from "lodash"; import _ from "lodash";
import { azureHeaders } from "../../../actions/core/headers"; import { azureHeaders } from "../../../actions/core/headers";
import { consoleLogger } from "../../../actions/core/logger"; import { respondError } from "../middleware/apiResponse";
import { getToken } from "../../../actions/core/token"; import { relayGet } from "../middleware/relayForwarding";
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/";
export default async function ApiProxy(req, res) { export default async function ApiProxy(req, res) {
try { const queryUrl =
const token = await getToken(); "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 = return relayGet({
"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"; queryUrl,
res,
requestOptionsBuilder: (accessToken) => azureHeaders(accessToken),
transformData: (data) => {
let dataStr;
const { data } = await axios.get( _.has(data, "@odata.nextLink") === true &&
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), ((dataStr = JSON.stringify(data["@odata.nextLink"])),
azureHeaders(token.access_token) (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]));
);
let dataStr; return data;
},
_.has(data, "@odata.nextLink") === true && errorResponse: {
((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, {
status: 400, status: 400,
code: "DNS_LIST_FETCH_FAILED", code: "DNS_LIST_FETCH_FAILED",
message: "Failed to fetch DNS list" message: "Failed to fetch DNS list"
}); }
} });
} }
@@ -2141,11 +2141,8 @@ test("getbasicdnssearchpaged returns ORDER_BY_REQUIRED when orderby missing", as
const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", { const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async () => ({}),
hashAPIPath: () => "&hash=expected",
azureHeadersPagedCustom: () => ({}), azureHeadersPagedCustom: () => ({}),
axios: { get: async () => ({ data: { value: [] } }) },
consoleLogger: () => {},
_: { has: () => false } _: { 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", { const mod = loadModule("pages/api/endpoint/getbasicdnssearchpaged_api.js", {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async ({ res, errorResponse }) =>
hashAPIPath: () => "&hash=expected", respondErrorMock(res, errorResponse),
azureHeadersPagedCustom: () => ({}), azureHeadersPagedCustom: () => ({}),
axios: {
get: async () => {
throw new Error("relay failed");
}
},
consoleLogger: () => {},
_: { has: () => false } _: { 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", { const mod = loadModule("pages/api/endpoint/getappealid_api.js", {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async () => ({}),
hashAPIPath: () => "&hash=expected", azureHeaders: () => ({})
azureHeaders: () => ({}),
axios: { get: async () => ({ data: { value: [] } }) },
consoleLogger: () => {}
}); });
const req = { 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", { const mod = loadModule("pages/api/endpoint/getappealid_api.js", {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async ({ res, errorResponse }) =>
hashAPIPath: () => "&hash=expected", respondErrorMock(res, errorResponse),
azureHeaders: () => ({}), azureHeaders: () => ({})
axios: {
get: async () => {
throw new Error("relay failed");
}
},
consoleLogger: () => {}
}); });
const req = { 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", { const mod = loadModule("pages/api/endpoint/getdnslist_api.js", {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async ({ res, errorResponse }) =>
hashAPIPath: () => "&hash=expected", respondErrorMock(res, errorResponse),
azureHeaders: () => ({}), azureHeaders: () => ({}),
axios: {
get: async () => {
throw new Error("relay failed");
}
},
consoleLogger: () => {},
_: { has: () => false } _: { has: () => false }
}); });
@@ -3270,11 +3246,8 @@ test("getbasicpartsaveddetails returns APPEAL_TYPE_NAME_REQUIRED when appealType
{ {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async () => ({}),
hashAPIPath: () => "&hash=expected", azureHeadersNoOdata: () => ({})
azureHeadersNoOdata: () => ({}),
axios: { get: async () => ({ data: { value: [] } }) },
consoleLogger: () => {}
} }
); );
@@ -3300,15 +3273,9 @@ test("getbasicpartsaveddetails catch path returns BASIC_PART_SAVED_DETAILS_FETCH
{ {
respondError: respondErrorMock, respondError: respondErrorMock,
respondSuccess: respondSuccessMock, respondSuccess: respondSuccessMock,
getToken: async () => ({ access_token: "token" }), relayGet: async ({ res, errorResponse }) =>
hashAPIPath: () => "&hash=expected", respondErrorMock(res, errorResponse),
azureHeadersNoOdata: () => ({}), azureHeadersNoOdata: () => ({})
axios: {
get: async () => {
throw new Error("relay failed");
}
},
consoleLogger: () => {}
} }
); );