From 98e159d52313b09f71f4fc56d8236e2a393de552 Mon Sep 17 00:00:00 2001 From: robbond Date: Mon, 23 Mar 2026 11:55:23 +0000 Subject: [PATCH] TASK22211: normalize case creation and media endpoint contracts --- pages/api/endpoint/createcase_api.js | 136 ++++++++++++------ .../api/endpoint/getappealpdfdocuments_api.js | 68 +++++---- pages/api/endpoint/getsipsmedia_api.js | 13 +- .../endpoint-handler-contract.test.cjs | 128 +++++++++++++++++ 4 files changed, 257 insertions(+), 88 deletions(-) diff --git a/pages/api/endpoint/createcase_api.js b/pages/api/endpoint/createcase_api.js index fcee4bf6..469b9e3c 100644 --- a/pages/api/endpoint/createcase_api.js +++ b/pages/api/endpoint/createcase_api.js @@ -42,65 +42,109 @@ */ import axios from "axios"; -import CryptoJS from "crypto-js"; -import _ from "lodash"; +import { consoleLogger } from "../../../actions/core/logger"; import { getToken } from "../../../actions/core/token"; import { getCaseBlob } from "../../../actions/azurestorage"; 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 createCaseBody = req.body; - var contactid = req.query.contactid; - var appealTypeId = req.query.appealTypeId; - var containerName = req.query.containername; - var lpaID = req.query.lpaID; - var queryUrl = "incidents"; - var token = await getToken(); + const createCaseBody = req.body; + const contactid = req.query.contactid; + const appealTypeId = req.query.appealTypeId; + const containerName = req.query.containername; + const lpaID = req.query.lpaID; + const queryUrl = "incidents"; - var data = { - "title": "insertion test case", - "caseorigincode": 3, - "servicestage": 1, - "customerid_contact@odata.bind": "/contacts(" + contactid + ")", - "pinswg_appealcasetype": appealTypeId, - "pinswg_AssociatedLPA@odata.bind": "/accounts(" + lpaID + ")" - }; - var newData = Object.assign(data, createCaseBody); + if (!createCaseBody || typeof createCaseBody !== "object") { + return respondError(res, { + status: 400, + code: "CASE_PAYLOAD_REQUIRED", + message: "Case payload is required" + }); + } + if (typeof contactid !== "string" || contactid.trim().length === 0) { + return respondError(res, { + status: 400, + code: "CONTACT_ID_REQUIRED", + message: "contactid is required" + }); + } + if (typeof appealTypeId !== "string" || appealTypeId.trim().length === 0) { + return respondError(res, { + status: 400, + code: "APPEAL_TYPE_ID_REQUIRED", + message: "appealTypeId is required" + }); + } + if ( + typeof containerName !== "string" || + containerName.trim().length === 0 + ) { + return respondError(res, { + status: 400, + code: "CONTAINER_NAME_REQUIRED", + message: "containername is required" + }); + } + if (typeof lpaID !== "string" || lpaID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "LPA_ID_REQUIRED", + message: "lpaID is required" + }); + } - delete newData.lpaTypes; - delete newData.appealTypes; + try { + const token = await getToken(); - console.log("/////Create Case:\n", newData, "\n//////////////"); + const data = { + "title": "insertion test case", + "caseorigincode": 3, + "servicestage": 1, + "customerid_contact@odata.bind": "/contacts(" + contactid + ")", + "pinswg_appealcasetype": appealTypeId, + "pinswg_AssociatedLPA@odata.bind": "/accounts(" + lpaID + ")" + }; + const newData = Object.assign(data, createCaseBody); - var config = { - method: "post", - url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), - headers: { - "OData-MaxVersion": "4.0", - "OData-Version": "4.0", - "Accept": "application/json", - "Prefer": 'odata.include-annotations="*",return=representation', - "Authorization": "Bearer " + token.access_token, - "Content-Type": "application/json" - }, - data: JSON.stringify(newData) - }; - var apiResponse = _.isEmpty(req.query) - ? res.status(400).json() - : axios(config) - .then(({ data }) => { - getCaseBlob(containerName, data.ticketnumber, createCaseBody); + delete newData.lpaTypes; + delete newData.appealTypes; - res.status(200).json(data); - }) - .catch((error) => { - //consoleLogger(error); - res.status(400).json(error); - }); + console.log("/////Create Case:\n", newData, "\n//////////////"); - return apiResponse; + const config = { + method: "post", + url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), + headers: { + "OData-MaxVersion": "4.0", + "OData-Version": "4.0", + "Accept": "application/json", + "Prefer": 'odata.include-annotations="*",return=representation', + "Authorization": "Bearer " + token.access_token, + "Content-Type": "application/json" + }, + data: JSON.stringify(newData) + }; + + const { data: responseData } = await axios(config); + await getCaseBlob( + containerName, + responseData.ticketnumber, + createCaseBody + ); + + return respondSuccess(res, responseData); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "CASE_CREATE_FAILED", + message: "Failed to create case" + }); + } } diff --git a/pages/api/endpoint/getappealpdfdocuments_api.js b/pages/api/endpoint/getappealpdfdocuments_api.js index 7579ed32..6ba1809d 100644 --- a/pages/api/endpoint/getappealpdfdocuments_api.js +++ b/pages/api/endpoint/getappealpdfdocuments_api.js @@ -17,11 +17,11 @@ */ 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 || @@ -57,45 +57,51 @@ const groupArray = (arr) => { }; export default async function ApiProxy(req, res) { - var incidentID = req.query.incidentid; + const incidentID = req.query.incidentid; - if (typeof incidentID === "undefined" || incidentID.length === 0) { - return res.status(400).json(); + if (typeof incidentID !== "string" || incidentID.trim().length === 0) { + return respondError(res, { + status: 400, + code: "INCIDENT_ID_REQUIRED", + message: "incidentid is required" + }); } - var token = await getToken(); + try { + const token = await getToken(); - var 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 axios - .get( + const { data } = await axios.get( WEBAPI_URL + queryUrl + hashAPIPath(queryUrl), azureHeaders(token.access_token) - ) - .then(({ data }) => { - // //delete data["pinswg_documentid"]; - // data.value.forEach(function (element) { - // delete element["pinswg_documentid"]; - // }); + ); + // //delete data["pinswg_documentid"]; + // data.value.forEach(function (element) { + // delete element["pinswg_documentid"]; + // }); - // var dataArr = groupArray(data.value); + // 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])); + // // 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; - }); - - res.status(200).json(data); - }) - .catch((error) => { - consoleLogger(error); - res.status(400).json(error); + data.value.forEach((item) => { + item.name = item.pinswg_name; }); + + return respondSuccess(res, data); + } catch (error) { + consoleLogger(error); + return respondError(res, { + status: 400, + code: "APPEAL_PDF_DOCUMENTS_FETCH_FAILED", + message: "Failed to fetch appeal PDF documents" + }); + } } diff --git a/pages/api/endpoint/getsipsmedia_api.js b/pages/api/endpoint/getsipsmedia_api.js index eb031fd7..1ef461b4 100644 --- a/pages/api/endpoint/getsipsmedia_api.js +++ b/pages/api/endpoint/getsipsmedia_api.js @@ -1,13 +1,4 @@ -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"; - -const WEBAPI_URL = - process.env.RELAY_ROOT || - "https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/"; +import { respondSuccess } from "../middleware/apiResponse"; // export default async function ApiProxy(req, res) { // var caseid = req.query.caseid; @@ -134,5 +125,5 @@ export default function ApiProx(req, res) { } ] }; - return res.status(200).json(data); + return respondSuccess(res, data); } diff --git a/tests/phase21/endpoint-handler-contract.test.cjs b/tests/phase21/endpoint-handler-contract.test.cjs index 97a1f55f..443d3737 100644 --- a/tests/phase21/endpoint-handler-contract.test.cjs +++ b/tests/phase21/endpoint-handler-contract.test.cjs @@ -3119,6 +3119,134 @@ test("getdnscoords catch path returns DNS_COORDS_FETCH_FAILED", async () => { ); }); +test("getappealpdfdocuments returns INCIDENT_ID_REQUIRED when incidentid missing", async () => { + const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_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, "INCIDENT_ID_REQUIRED"); +}); + +test("getappealpdfdocuments catch path returns APPEAL_PDF_DOCUMENTS_FETCH_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_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: { incidentid: "i1" } }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual( + res.state.jsonBody.error.code, + "APPEAL_PDF_DOCUMENTS_FETCH_FAILED" + ); +}); + +test("createcase returns CASE_PAYLOAD_REQUIRED when body missing", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: { ticketnumber: "CAS-1" } }), + consoleLogger: () => {} + }); + + const req = { query: {}, body: undefined }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_PAYLOAD_REQUIRED"); +}); + +test("createcase returns CONTACT_ID_REQUIRED when contactid missing", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => ({ data: { ticketnumber: "CAS-1" } }), + consoleLogger: () => {} + }); + + const req = { + query: { appealTypeId: "a1", containername: "c1", lpaID: "l1" }, + body: { title: "x" } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CONTACT_ID_REQUIRED"); +}); + +test("createcase catch path returns CASE_CREATE_FAILED", async () => { + const mod = loadModule("pages/api/endpoint/createcase_api.js", { + respondError: respondErrorMock, + respondSuccess: respondSuccessMock, + getToken: async () => ({ access_token: "token" }), + getCaseBlob: async () => {}, + hashAPIPath: () => "&hash=expected", + axios: async () => { + throw new Error("create failed"); + }, + consoleLogger: () => {} + }); + + const req = { + query: { + contactid: "c1", + appealTypeId: "a1", + containername: "container", + lpaID: "l1" + }, + body: { title: "x" } + }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 400); + assert.strictEqual(res.state.jsonBody.error.code, "CASE_CREATE_FAILED"); +}); + +test("getsipsmedia returns success contract", async () => { + const mod = loadModule("pages/api/endpoint/getsipsmedia_api.js", { + respondSuccess: respondSuccessMock + }); + + const req = { query: {} }; + const res = createRes(); + await mod.default(req, res); + + assert.strictEqual(res.state.statusCode, 200); + assert.strictEqual(res.state.jsonBody["@odata.count"], 4); +}); + const run = async () => { let passed = 0; for (const currentTest of tests) {