From 9af541ac6babae181ab70da76845a3caf33aa22c Mon Sep 17 00:00:00 2001 From: robbond Date: Mon, 23 Mar 2026 11:12:19 +0000 Subject: [PATCH] TASK22211: normalize myportal retrieval endpoint contracts --- .../api/endpoint/getawaitingsubmission_api.js | 55 +++-- pages/api/endpoint/getmycases_api.js | 55 +++-- .../api/endpoint/getmyrepresentations_api.js | 47 +++-- pages/api/endpoint/getwatchedcases_api.js | 99 ++++----- .../endpoint-handler-contract.test.cjs | 189 ++++++++++++++++++ 5 files changed, 339 insertions(+), 106 deletions(-) diff --git a/pages/api/endpoint/getawaitingsubmission_api.js b/pages/api/endpoint/getawaitingsubmission_api.js index 05339feb..5ea8be59 100644 --- a/pages/api/endpoint/getawaitingsubmission_api.js +++ b/pages/api/endpoint/getawaitingsubmission_api.js @@ -18,39 +18,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; 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/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var 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"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); - return axios - .get( + 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 { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + data.value.forEach(function (element) { + element.pinswg_title = element.title; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + 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 d510c6f7..5420fde2 100644 --- a/pages/api/endpoint/getmycases_api.js +++ b/pages/api/endpoint/getmycases_api.js @@ -18,43 +18,54 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; 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/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var 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"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - // Removed servicestage but not sure why..... - //" and servicestage eq 0 and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true"; + try { + const token = await getToken(); - //console.log("///////////\nPortal query: ", queryUrl, "<<< { - data.value.forEach(function (element) { - element.pinswg_title = element.title; - }); - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + data.value.forEach(function (element) { + element.pinswg_title = element.title; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + 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 0fc8d7a8..9ad04a89 100644 --- a/pages/api/endpoint/getmyrepresentations_api.js +++ b/pages/api/endpoint/getmyrepresentations_api.js @@ -18,37 +18,50 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; 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/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var queryUrl = - "pinswg_representationses?$filter= _pinswg_contact_value eq " + - loggedInUserId + - "&$count=true&$orderby=createdon desc"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl)); + try { + const token = await getToken(); - return axios - .get( + 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) - ) - .then(({ data }) => { - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + ); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "MY_REPRESENTATIONS_FETCH_FAILED", + message: "Failed to fetch my representations" }); + } } diff --git a/pages/api/endpoint/getwatchedcases_api.js b/pages/api/endpoint/getwatchedcases_api.js index 84f3f272..f5db25a6 100644 --- a/pages/api/endpoint/getwatchedcases_api.js +++ b/pages/api/endpoint/getwatchedcases_api.js @@ -18,71 +18,76 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; 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/"; export default async function ApiProxy(req, res) { - var loggedInUserId = req.query.loggedInUserId; - var token = await getToken(); + const loggedInUserId = req.query.loggedInUserId; - var 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)"; + if ( + typeof loggedInUserId !== "string" || + loggedInUserId.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "LOGGED_IN_USER_ID_REQUIRED", + message: "loggedInUserId is required" + }); + } - //console.log(queryUrl); - return axios - .get( + 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 { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ 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_WatchedCase?.[ - "_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue" - ]; - element._pinswg_associatedlpa_value = - element.pinswg_WatchedCase?._pinswg_associatedlpa_value; - element[ + ]; + element._pinswg_associatedlpa_value = + element.pinswg_WatchedCase?._pinswg_associatedlpa_value; + element[ + "_ownerid_value@OData.Community.Display.V1.FormattedValue" + ] = + element.pinswg_WatchedCase?.[ "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ] = - element.pinswg_WatchedCase?.[ - "_ownerid_value@OData.Community.Display.V1.FormattedValue" - ]; - element._ownerid_value = - element.pinswg_WatchedCase?._ownerid_value; + ]; + element._ownerid_value = element.pinswg_WatchedCase?._ownerid_value; - delete element.pinswg_WatchedCase; - }); - - // const uniqueItems = Array.from( - // new Map( - // data.value.map((item) => [item.ticketnumber, item]) - // ).values() - // ); - - // data = { "@odata.count": uniqueItems.length, "value": uniqueItems }; - - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + delete element.pinswg_WatchedCase; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + 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 4ef9aae9..1676961f 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -1333,6 +1333,195 @@ test("getsearchdocumentTypes success returns grouped data payload contract", asy ]); }); +test("getmycases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getmycases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getmycases catch path returns MY_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getmycases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "MY_CASES_FETCH_FAILED"); +}); + +test("getmyrepresentations returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getmyrepresentations catch path returns MY_REPRESENTATIONS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getmyrepresentations_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "MY_REPRESENTATIONS_FETCH_FAILED" + ); +}); + +test("getwatchedcases returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getwatchedcases catch path returns WATCHED_CASES_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getwatchedcases_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "WATCHED_CASES_FETCH_FAILED" + ); +}); + +test("getawaitingsubmission returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => { + const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { get: async () => ({ data: { value: [] } }) }, + consoleLogger: () => {} + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "LOGGED_IN_USER_ID_REQUIRED" + ); +}); + +test("getawaitingsubmission catch path returns AWAITING_SUBMISSION_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getawaitingsubmission_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + hashAPIPath: () => "&hash=expected", + azureHeaders: () => ({}), + axios: { + get: async () => { + throw new Error("relay failed"); + } + }, + consoleLogger: () => {} + }); + + const req = { query: { loggedInUserId: "c1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "AWAITING_SUBMISSION_FETCH_FAILED" + ); +}); + const run = async () => { let passed = 0; for (const currentTest of tests) {