TASK22224: modernize involvement and updatecase handlers
This commit is contained in:
@@ -379,3 +379,49 @@ Validation:
|
|||||||
Follow-ups:
|
Follow-ups:
|
||||||
|
|
||||||
- Optional next big slice: bring remaining proxy/message routes using raw axios promise chains (`createcaseinvolvement_api.js`, `createrepinvolvement_api.js`, `updatecase_api.js`) onto the same async/await + explicit contract pattern.
|
- Optional next big slice: bring remaining proxy/message routes using raw axios promise chains (`createcaseinvolvement_api.js`, `createrepinvolvement_api.js`, `updatecase_api.js`) onto the same async/await + explicit contract pattern.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CL-010: TASK22224 involvement/update route async contract hardening bundle
|
||||||
|
|
||||||
|
date: 2026-03-23
|
||||||
|
author: Cline
|
||||||
|
scope: `pages/api/file/{createcaseinvolvement_api,createrepinvolvement_api,updatecase_api}.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||||
|
type: change
|
||||||
|
rationale: Execute the next requested slice by modernizing remaining relay-backed involvement/update handlers that still used axios promise chains and legacy dead imports.
|
||||||
|
impact: Improves reliability/readability and preserves existing response contracts, including 412 "record exists" semantics for involvement creation flows.
|
||||||
|
status: completed
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
- `createcaseinvolvement_api.js`
|
||||||
|
- removed unused `CryptoJS` import
|
||||||
|
- refactored axios `.then/.catch` to explicit `try/catch`
|
||||||
|
- preserved conflict behavior: status 412 -> success `{ record: "exists" }`
|
||||||
|
- preserved failure contract: `CREATE_CASE_INVOLVEMENT_FAILED`
|
||||||
|
- `createrepinvolvement_api.js`
|
||||||
|
- removed unused `CryptoJS` import
|
||||||
|
- refactored axios `.then/.catch` to explicit `try/catch`
|
||||||
|
- preserved conflict behavior: status 412 -> success `{ record: "exists" }`
|
||||||
|
- preserved failure contract: `CREATE_REP_INVOLVEMENT_FAILED`
|
||||||
|
- `updatecase_api.js`
|
||||||
|
- removed unused `CryptoJS` import
|
||||||
|
- refactored axios `.then/.catch` to explicit `try/catch`
|
||||||
|
- preserved failure contract: `UPDATE_CASE_FAILED`
|
||||||
|
- Phase21 tests expanded:
|
||||||
|
- createcaseinvolvement 412 conflict success contract
|
||||||
|
- createrepinvolvement dependency failure contract
|
||||||
|
- updatecase dependency failure contract
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (49/49)
|
||||||
|
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||||
|
- helper: 4/4
|
||||||
|
- file-handler: 49/49
|
||||||
|
- email-handler: 12/12
|
||||||
|
- endpoint-handler: 149/149
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- Optional: apply equivalent modernization to any remaining relay-backed handlers outside `pages/api/file/` that still use raw axios promise chains and have no explicit phase21 contract assertions.
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import CryptoJS from "crypto-js";
|
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
import { getToken } from "../../../actions/core/token";
|
import { getToken } from "../../../actions/core/token";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
@@ -36,20 +35,20 @@ export default async function ApiProxy(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
const token = await getToken();
|
||||||
var contactid = req.body.contactid;
|
const contactid = req.body.contactid;
|
||||||
var queryUrl =
|
const queryUrl =
|
||||||
"incidents(" +
|
"incidents(" +
|
||||||
req.body.incidentid +
|
req.body.incidentid +
|
||||||
")/pinswg_incident_contact_case_involvement/$ref";
|
")/pinswg_incident_contact_case_involvement/$ref";
|
||||||
|
|
||||||
var crmUrl = "https://" + process.env.CRMURL;
|
const crmUrl = "https://" + process.env.CRMURL;
|
||||||
|
|
||||||
var data = {
|
const data = {
|
||||||
"@odata.id": crmUrl + "/api/data/v8.2/contacts(" + contactid + ")"
|
"@odata.id": crmUrl + "/api/data/v8.2/contacts(" + contactid + ")"
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = {
|
const config = {
|
||||||
method: "post",
|
method: "post",
|
||||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -64,18 +63,20 @@ export default async function ApiProxy(req, res) {
|
|||||||
data: data
|
data: data
|
||||||
};
|
};
|
||||||
|
|
||||||
return axios(config)
|
try {
|
||||||
.then(({ data }) => {
|
const { data: responseData } = await axios(config);
|
||||||
return respondSuccess(res, data);
|
return respondSuccess(res, responseData);
|
||||||
})
|
} catch (error) {
|
||||||
.catch((error) => {
|
const errorStatus = error?.status || error?.response?.status;
|
||||||
return error.status == 412
|
if (errorStatus == 412) {
|
||||||
? respondSuccess(res, { "record": "exists" })
|
return respondSuccess(res, { record: "exists" });
|
||||||
: (consoleLogger(Object.assign(error, data)),
|
}
|
||||||
respondError(res, {
|
|
||||||
status: 400,
|
consoleLogger(Object.assign(error, data));
|
||||||
code: "CREATE_CASE_INVOLVEMENT_FAILED",
|
return respondError(res, {
|
||||||
message: "Failed to create case involvement"
|
status: 400,
|
||||||
}));
|
code: "CREATE_CASE_INVOLVEMENT_FAILED",
|
||||||
|
message: "Failed to create case involvement"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import CryptoJS from "crypto-js";
|
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
import { getToken } from "../../../actions/core/token";
|
import { getToken } from "../../../actions/core/token";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
@@ -36,22 +35,22 @@ export default async function ApiProxy(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
const token = await getToken();
|
||||||
var contactid = req.body.contactid;
|
const contactid = req.body.contactid;
|
||||||
var queryUrl =
|
const queryUrl =
|
||||||
"incidents(" +
|
"incidents(" +
|
||||||
req.body.incidentid +
|
req.body.incidentid +
|
||||||
")/pinswg_incident_contact_case_involvement/$ref";
|
")/pinswg_incident_contact_case_involvement/$ref";
|
||||||
|
|
||||||
var crmUrl = "https://" + process.env.CRMURL;
|
const crmUrl = "https://" + process.env.CRMURL;
|
||||||
var crmVersion = process.env.CRMURL_VERSION;
|
const crmVersion = process.env.CRMURL_VERSION;
|
||||||
|
|
||||||
var data = {
|
const data = {
|
||||||
"@odata.id":
|
"@odata.id":
|
||||||
crmUrl + "/api/data/" + crmVersion + "/contacts(" + contactid + ")"
|
crmUrl + "/api/data/" + crmVersion + "/contacts(" + contactid + ")"
|
||||||
};
|
};
|
||||||
|
|
||||||
var config = {
|
const config = {
|
||||||
method: "post",
|
method: "post",
|
||||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -66,18 +65,20 @@ export default async function ApiProxy(req, res) {
|
|||||||
data: data
|
data: data
|
||||||
};
|
};
|
||||||
|
|
||||||
return axios(config)
|
try {
|
||||||
.then(({ data }) => {
|
const { data: responseData } = await axios(config);
|
||||||
return respondSuccess(res, data);
|
return respondSuccess(res, responseData);
|
||||||
})
|
} catch (error) {
|
||||||
.catch((error) => {
|
const errorStatus = error?.status || error?.response?.status;
|
||||||
return error.status == 412
|
if (errorStatus == 412) {
|
||||||
? respondSuccess(res, { "record": "exists" })
|
return respondSuccess(res, { record: "exists" });
|
||||||
: (consoleLogger(Object.assign(error, data)),
|
}
|
||||||
respondError(res, {
|
|
||||||
status: 400,
|
consoleLogger(Object.assign(error, data));
|
||||||
code: "CREATE_REP_INVOLVEMENT_FAILED",
|
return respondError(res, {
|
||||||
message: "Failed to create representation involvement"
|
status: 400,
|
||||||
}));
|
code: "CREATE_REP_INVOLVEMENT_FAILED",
|
||||||
|
message: "Failed to create representation involvement"
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import CryptoJS from "crypto-js";
|
|
||||||
import { consoleLogger } from "../../../actions/core/logger";
|
import { consoleLogger } from "../../../actions/core/logger";
|
||||||
import { getToken } from "../../../actions/core/token";
|
import { getToken } from "../../../actions/core/token";
|
||||||
import { hashAPIPath } from "../../../actions/core/hash";
|
import { hashAPIPath } from "../../../actions/core/hash";
|
||||||
@@ -10,12 +9,12 @@ const WEBAPI_URL =
|
|||||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||||
|
|
||||||
export default async function ApiProxy(req, res) {
|
export default async function ApiProxy(req, res) {
|
||||||
var data = JSON.stringify(req.body);
|
const data = JSON.stringify(req.body);
|
||||||
var appealObj = req.query.appealObj;
|
const appealObj = req.query.appealObj;
|
||||||
var incidentID = req.query.incident;
|
const incidentID = req.query.incident;
|
||||||
|
|
||||||
var updateFormCollection = req.query.updateFormCollection;
|
const updateFormCollection = req.query.updateFormCollection;
|
||||||
var queryUrl = updateFormCollection + "(" + appealObj + ")";
|
const queryUrl = updateFormCollection + "(" + appealObj + ")";
|
||||||
|
|
||||||
if (
|
if (
|
||||||
typeof updateFormCollection === "undefined" ||
|
typeof updateFormCollection === "undefined" ||
|
||||||
@@ -35,9 +34,9 @@ export default async function ApiProxy(req, res) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
const token = await getToken();
|
||||||
|
|
||||||
var config = {
|
const config = {
|
||||||
method: "patch",
|
method: "patch",
|
||||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
headers: {
|
headers: {
|
||||||
@@ -53,16 +52,15 @@ export default async function ApiProxy(req, res) {
|
|||||||
|
|
||||||
//console.log("update case:", data);
|
//console.log("update case:", data);
|
||||||
|
|
||||||
return axios(config)
|
try {
|
||||||
.then(({ data }) => {
|
const { data: responseData } = await axios(config);
|
||||||
return respondSuccess(res, data);
|
return respondSuccess(res, responseData);
|
||||||
})
|
} catch (error) {
|
||||||
.catch((error) => {
|
consoleLogger(error);
|
||||||
consoleLogger(error);
|
return respondError(res, {
|
||||||
return respondError(res, {
|
status: 400,
|
||||||
status: 400,
|
code: "UPDATE_CASE_FAILED",
|
||||||
code: "UPDATE_CASE_FAILED",
|
message: "Failed to update case"
|
||||||
message: "Failed to update case"
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -694,6 +694,95 @@ test("createappealcompletemessageproxy dependency failure returns CREATE_APPEAL_
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("createcaseinvolvement returns record exists on 412 conflict", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/createcaseinvolvement_api.js", {
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getToken: async () => ({ access_token: "tok" }),
|
||||||
|
hashAPIPath: () => "?hash=abc",
|
||||||
|
consoleLogger: () => {},
|
||||||
|
process: {
|
||||||
|
env: { CRMURL: "crm.example", RELAY_ROOT: "https://relay/" }
|
||||||
|
},
|
||||||
|
axios: async () => {
|
||||||
|
const error = new Error("exists");
|
||||||
|
error.status = 412;
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
body: { contactid: "c1", incidentid: "i1" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||||
|
record: "exists"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("createrepinvolvement dependency failure returns CREATE_REP_INVOLVEMENT_FAILED", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/createrepinvolvement_api.js", {
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getToken: async () => ({ access_token: "tok" }),
|
||||||
|
hashAPIPath: () => "?hash=abc",
|
||||||
|
consoleLogger: () => {},
|
||||||
|
process: {
|
||||||
|
env: {
|
||||||
|
CRMURL: "crm.example",
|
||||||
|
CRMURL_VERSION: "v9.2",
|
||||||
|
RELAY_ROOT: "https://relay/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
axios: async () => {
|
||||||
|
throw new Error("rep involvement failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
body: { contactid: "c1", incidentid: "i1" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(
|
||||||
|
res.state.jsonBody.error.code,
|
||||||
|
"CREATE_REP_INVOLVEMENT_FAILED"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("updatecase dependency failure returns UPDATE_CASE_FAILED", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/updatecase_api.js", {
|
||||||
|
respondError: respondErrorMock,
|
||||||
|
respondSuccess: respondSuccessMock,
|
||||||
|
getToken: async () => ({ access_token: "tok" }),
|
||||||
|
hashAPIPath: () => "?hash=abc",
|
||||||
|
consoleLogger: () => {},
|
||||||
|
process: { env: { RELAY_ROOT: "https://relay/" } },
|
||||||
|
axios: async () => {
|
||||||
|
throw new Error("update failed");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: {
|
||||||
|
updateFormCollection: "pinswg_forms",
|
||||||
|
appealObj: "obj1",
|
||||||
|
incident: "inc1"
|
||||||
|
},
|
||||||
|
body: { field: "value" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(res.state.jsonBody.error.code, "UPDATE_CASE_FAILED");
|
||||||
|
});
|
||||||
|
|
||||||
test("getawaitingsubmissionfromblob handler success returns payload", async () => {
|
test("getawaitingsubmissionfromblob handler success returns payload", async () => {
|
||||||
const mod = loadModule("pages/api/file/getawaitingsubmissionfromblob.js", {
|
const mod = loadModule("pages/api/file/getawaitingsubmissionfromblob.js", {
|
||||||
nextConnect: createNextConnectMock(),
|
nextConnect: createNextConnectMock(),
|
||||||
|
|||||||
Reference in New Issue
Block a user