TASK22057: phase18 additional consolidation slices
This commit is contained in:
@@ -568,6 +568,33 @@
|
|||||||
- Broader consistency scan note:
|
- Broader consistency scan note:
|
||||||
- no additional broad hash/400 consistency gaps were identified in `pages/api/endpoint/**` in this pass.
|
- no additional broad hash/400 consistency gaps were identified in `pages/api/endpoint/**` in this pass.
|
||||||
|
|
||||||
|
## Phase 18 additional consolidation slices (2026-03-14)
|
||||||
|
|
||||||
|
- Continued on branch:
|
||||||
|
- `TASK22057-phase17-hardening-closeout`
|
||||||
|
- Completed all three requested additional slices:
|
||||||
|
1. endpoint logging/input hygiene
|
||||||
|
2. file hash-guard flow consistency
|
||||||
|
3. create/update input-guard + logging hygiene
|
||||||
|
- Touched handlers:
|
||||||
|
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||||
|
- `pages/api/endpoint/getaccounts_api.js`
|
||||||
|
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||||
|
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||||
|
- `pages/api/file/deleteblob.js`
|
||||||
|
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||||
|
- `pages/api/file/getprogressobjblob.js`
|
||||||
|
- `pages/api/file/getbloblist.js`
|
||||||
|
- `pages/api/file/createcase_api.js`
|
||||||
|
- `pages/api/file/updatecase_api.js`
|
||||||
|
- `pages/api/file/editRepJson.js`
|
||||||
|
- Outcome:
|
||||||
|
- consistent early 400 guard behavior for missing/invalid required inputs/hash in selected handlers
|
||||||
|
- noisy direct logs removed in touched sensitive flows
|
||||||
|
- no response-shape/signature contract changes intended
|
||||||
|
- Added focused test pack:
|
||||||
|
- `tests/phase18/service-behaviour.test.cjs` (8/8 pass)
|
||||||
|
|
||||||
## Likely next steps
|
## Likely next steps
|
||||||
|
|
||||||
1. Stabilize and simplify breadcrumb/back-link decision logic with focused regression checks.
|
1. Stabilize and simplify breadcrumb/back-link decision logic with focused regression checks.
|
||||||
|
|||||||
@@ -1156,3 +1156,61 @@ Validation:
|
|||||||
Follow-ups:
|
Follow-ups:
|
||||||
|
|
||||||
- If desired, run additional integrated HTTP/manual checks in an environment with full relay/storage dependencies for end-to-end happy-path confirmation beyond mocked behaviour tests.
|
- If desired, run additional integrated HTTP/manual checks in an environment with full relay/storage dependencies for end-to-end happy-path confirmation beyond mocked behaviour tests.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### CL-032: Phase 18 additional consolidation slices — endpoint/file consistency and guard hygiene
|
||||||
|
|
||||||
|
date: 2026-03-14
|
||||||
|
author: Cline
|
||||||
|
scope: `pages/api/endpoint/{deletewatchedcases_api,getaccounts_api,getbasicsearchpaged_api,getappealpdfdocuments_api}.js`, `pages/api/file/{deleteblob,deleteawaitingsubmissionfromblob,getprogressobjblob,getbloblist,createcase_api,updatecase_api,editRepJson}.js`, `tests/phase18/service-behaviour.test.cjs`, `memory-bank/*`
|
||||||
|
type: change
|
||||||
|
rationale: Complete the user-requested three additional small, reversible hardening slices (logging hygiene, hash-guard flow consistency, and required-input guard consistency) without broad refactor or contract drift.
|
||||||
|
impact: Improves consistency of negative-path behavior and reduces noisy logging in selected sensitive handlers while preserving response/signature contracts.
|
||||||
|
status: completed
|
||||||
|
|
||||||
|
Summary:
|
||||||
|
|
||||||
|
- Implemented all three requested additional slices:
|
||||||
|
1. endpoint logging/input hygiene
|
||||||
|
2. file hash-guard flow consistency
|
||||||
|
3. create/update input-guard + logging hygiene
|
||||||
|
- Endpoint updates:
|
||||||
|
- `deletewatchedcases_api`: added required `watchedCaseID` early 400 guard and removed direct URL logging.
|
||||||
|
- `getaccounts_api`: added required `emailAddress` early 400 guard and removed direct query logging.
|
||||||
|
- `getbasicsearchpaged_api`: added required `searchString` early 400 guard and removed noisy query logging.
|
||||||
|
- `getappealpdfdocuments_api`: added required `incidentid` early 400 guard and removed direct URL logging.
|
||||||
|
- File handler updates:
|
||||||
|
- standardized hash-check flow by removing redundant post-validation branches in:
|
||||||
|
- `deleteblob`
|
||||||
|
- `deleteawaitingsubmissionfromblob`
|
||||||
|
- `getprogressobjblob`
|
||||||
|
- `getbloblist`
|
||||||
|
- added required-input early 400 guards in:
|
||||||
|
- `createcase_api` (contact/appealType/container/lpa)
|
||||||
|
- `updatecase_api` (collection/appealObj/incident/body)
|
||||||
|
- removed noisy tag logging in `editRepJson`.
|
||||||
|
- Added focused test pack:
|
||||||
|
- `tests/phase18/service-behaviour.test.cjs` with negative-path and contract-preserving happy-path checks (8/8 pass).
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||||
|
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||||
|
- `node tests/phase7/service-behaviour.test.cjs` -> pass (12/12)
|
||||||
|
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||||
|
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||||
|
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||||
|
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||||
|
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||||
|
- `node tests/phase13/service-behaviour.test.cjs` -> pass (7/7)
|
||||||
|
- `node tests/phase14/service-behaviour.test.cjs` -> pass (9/9)
|
||||||
|
- `node tests/phase15/service-behaviour.test.cjs` -> pass (5/5)
|
||||||
|
- `node tests/phase16/service-behaviour.test.cjs` -> pass (4/4)
|
||||||
|
- `node tests/phase17/service-behaviour.test.cjs` -> pass (6/6)
|
||||||
|
- `node tests/phase18/service-behaviour.test.cjs` -> pass (8/8)
|
||||||
|
- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||||
|
|
||||||
|
Follow-ups:
|
||||||
|
|
||||||
|
- Optional: execute integrated manual HTTP matrix for phase-18 touched handlers in an environment with full relay/storage dependencies for non-mocked happy-path verification.
|
||||||
|
|||||||
@@ -612,6 +612,50 @@ Status key: `[x] done`, `[ ] pending`
|
|||||||
- `tests/phase17/service-behaviour.test.cjs`
|
- `tests/phase17/service-behaviour.test.cjs`
|
||||||
- Re-run phase6–phase17 baseline tests and lint after rollback to confirm parity.
|
- Re-run phase6–phase17 baseline tests and lint after rollback to confirm parity.
|
||||||
|
|
||||||
|
### Phase 18 additional consolidation slices snapshot (2026-03-14)
|
||||||
|
|
||||||
|
Status key: `[x] done`, `[ ] pending`
|
||||||
|
|
||||||
|
1. `[x]` Confirm requested scope expansion to implement all 3 additional slices
|
||||||
|
2. `[x]` Apply endpoint logging/input hygiene slice
|
||||||
|
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||||
|
- `pages/api/endpoint/getaccounts_api.js`
|
||||||
|
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||||
|
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||||
|
3. `[x]` Apply file hash-guard flow consistency slice
|
||||||
|
- `pages/api/file/deleteblob.js`
|
||||||
|
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||||
|
- `pages/api/file/getprogressobjblob.js`
|
||||||
|
- `pages/api/file/getbloblist.js`
|
||||||
|
4. `[x]` Apply create/update input-guard + logging hygiene slice
|
||||||
|
- `pages/api/file/createcase_api.js`
|
||||||
|
- `pages/api/file/updatecase_api.js`
|
||||||
|
- `pages/api/file/editRepJson.js`
|
||||||
|
5. `[x]` Add focused phase-18 behavioural tests
|
||||||
|
- added `tests/phase18/service-behaviour.test.cjs`
|
||||||
|
- negative-path coverage for required inputs/hash
|
||||||
|
- contract-preserving happy-path checks on selected handlers
|
||||||
|
6. `[x]` Execute validation bundle
|
||||||
|
- phase6 parity + phase6–phase18 behavioural tests all pass
|
||||||
|
- lint warnings only (pre-existing `react-hooks/exhaustive-deps` set)
|
||||||
|
|
||||||
|
### Phase 18 rollback notes
|
||||||
|
|
||||||
|
- Revert files in this slice:
|
||||||
|
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||||
|
- `pages/api/endpoint/getaccounts_api.js`
|
||||||
|
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||||
|
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||||
|
- `pages/api/file/deleteblob.js`
|
||||||
|
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||||
|
- `pages/api/file/getprogressobjblob.js`
|
||||||
|
- `pages/api/file/getbloblist.js`
|
||||||
|
- `pages/api/file/createcase_api.js`
|
||||||
|
- `pages/api/file/updatecase_api.js`
|
||||||
|
- `pages/api/file/editRepJson.js`
|
||||||
|
- `tests/phase18/service-behaviour.test.cjs`
|
||||||
|
- Re-run phase6–phase18 baseline tests and lint after rollback to confirm parity.
|
||||||
|
|
||||||
## Safe execution mode for migration chunks (required)
|
## Safe execution mode for migration chunks (required)
|
||||||
|
|
||||||
To reduce terminal hangs during bulk migration work, run refactor chunks in **safe stepwise mode** instead of long chained commands.
|
To reduce terminal hangs during bulk migration work, run refactor chunks in **safe stepwise mode** instead of long chained commands.
|
||||||
|
|||||||
@@ -35,11 +35,14 @@ const hashAPIPath = (queryPath) => {
|
|||||||
|
|
||||||
export default async function ApiProxy(req, res) {
|
export default async function ApiProxy(req, res) {
|
||||||
var watchedCaseID = req.query.watchedCaseID;
|
var watchedCaseID = req.query.watchedCaseID;
|
||||||
|
|
||||||
|
if (typeof watchedCaseID === "undefined" || watchedCaseID.length === 0) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
var queryUrl = "pinswg_watchlists(" + watchedCaseID + ")";
|
var queryUrl = "pinswg_watchlists(" + watchedCaseID + ")";
|
||||||
|
|
||||||
console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl));
|
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
method: "delete",
|
method: "delete",
|
||||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
|
|||||||
@@ -39,6 +39,11 @@ const hashAPIPath = (queryPath) => {
|
|||||||
|
|
||||||
export default async function ApiProxy(req, res) {
|
export default async function ApiProxy(req, res) {
|
||||||
var emailAddress = req.query.emailAddress;
|
var emailAddress = req.query.emailAddress;
|
||||||
|
|
||||||
|
if (typeof emailAddress === "undefined" || emailAddress.length === 0) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
|
|
||||||
var queryUrl =
|
var queryUrl =
|
||||||
@@ -46,25 +51,24 @@ export default async function ApiProxy(req, res) {
|
|||||||
emailAddress +
|
emailAddress +
|
||||||
"'&$count=true&$select=emailaddress1, contactid";
|
"'&$count=true&$select=emailaddress1, contactid";
|
||||||
|
|
||||||
console.log(queryUrl);
|
|
||||||
// var queryUrl = "contacts/?$count=true&$select=emailaddress1, contactid";
|
// var queryUrl = "contacts/?$count=true&$select=emailaddress1, contactid";
|
||||||
|
|
||||||
var apiResponse = _.isEmpty(req.query)
|
var apiResponse = _.isEmpty(req.query)
|
||||||
? res.status(400).json()
|
? res.status(400).json()
|
||||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||||
? axios
|
? axios
|
||||||
.get(
|
.get(
|
||||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
azureHeaders(token.access_token)
|
azureHeaders(token.access_token)
|
||||||
)
|
)
|
||||||
.then(({ data }) => {
|
.then(({ data }) => {
|
||||||
res.status(200).json(data);
|
res.status(200).json(data);
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
res.status(400).json(error);
|
res.status(400).json(error);
|
||||||
})
|
})
|
||||||
: res.status(400).json();
|
: res.status(400).json();
|
||||||
|
|
||||||
return apiResponse;
|
return apiResponse;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ const groupArray = (arr) => {
|
|||||||
obj[
|
obj[
|
||||||
"pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue"
|
"pinswg_isharedocumentlocations@OData.Community.Display.V1.FormattedValue"
|
||||||
], // Change 'name' to 'userName'
|
], // Change 'name' to 'userName'
|
||||||
count: count, // Add 'count' as 'occurrences'
|
count: count // Add 'count' as 'occurrences'
|
||||||
};
|
};
|
||||||
|
|
||||||
result.push(transformedObj);
|
result.push(transformedObj);
|
||||||
@@ -71,6 +71,11 @@ const groupArray = (arr) => {
|
|||||||
|
|
||||||
export default async function ApiProxy(req, res) {
|
export default async function ApiProxy(req, res) {
|
||||||
var incidentID = req.query.incidentid;
|
var incidentID = req.query.incidentid;
|
||||||
|
|
||||||
|
if (typeof incidentID === "undefined" || incidentID.length === 0) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
|
|
||||||
var queryUrl =
|
var queryUrl =
|
||||||
@@ -78,8 +83,6 @@ export default async function ApiProxy(req, res) {
|
|||||||
incidentID +
|
incidentID +
|
||||||
" and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations";
|
" and not(contains(pinswg_name,'_Appeal_Form.pdf'))&$select=pinswg_name,pinswg_isharedocumentlocations";
|
||||||
|
|
||||||
console.log("docu: ", WEBAPI_URL + queryUrl + hashAPIPath(queryUrl));
|
|
||||||
|
|
||||||
return axios
|
return axios
|
||||||
.get(
|
.get(
|
||||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ const WEBAPI_URL =
|
|||||||
export default async function ApiProxy(req, res) {
|
export default async function ApiProxy(req, res) {
|
||||||
var searchString = req.query.searchString;
|
var searchString = req.query.searchString;
|
||||||
var pageNumber = req.query.pageNumber;
|
var pageNumber = req.query.pageNumber;
|
||||||
|
|
||||||
|
if (typeof searchString === "undefined" || searchString.length === 0) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
|
|
||||||
var orderby = req.query.orderby;
|
var orderby = req.query.orderby;
|
||||||
@@ -84,8 +89,6 @@ export default async function ApiProxy(req, res) {
|
|||||||
? "&$skiptoken=" + '<cookie pagenumber="' + pageNumber + '" />'
|
? "&$skiptoken=" + '<cookie pagenumber="' + pageNumber + '" />'
|
||||||
: "");
|
: "");
|
||||||
|
|
||||||
console.log("basic search paged:", queryUrl);
|
|
||||||
|
|
||||||
var apiResponse =
|
var apiResponse =
|
||||||
searchString.length > 0
|
searchString.length > 0
|
||||||
? axios
|
? axios
|
||||||
|
|||||||
@@ -36,6 +36,19 @@ export default async function ApiProxy(req, res) {
|
|||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
var tempCaseRef = getTempCaseRef();
|
var tempCaseRef = getTempCaseRef();
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof contactid === "undefined" ||
|
||||||
|
contactid.length === 0 ||
|
||||||
|
typeof appealTypeId === "undefined" ||
|
||||||
|
appealTypeId.length === 0 ||
|
||||||
|
typeof containerName === "undefined" ||
|
||||||
|
containerName.length === 0 ||
|
||||||
|
typeof lpaID === "undefined" ||
|
||||||
|
lpaID.length === 0
|
||||||
|
) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
"title": tempCaseRef,
|
"title": tempCaseRef,
|
||||||
"ticketnumber": tempCaseRef,
|
"ticketnumber": tempCaseRef,
|
||||||
@@ -53,8 +66,6 @@ export default async function ApiProxy(req, res) {
|
|||||||
delete newData.lpaTypes;
|
delete newData.lpaTypes;
|
||||||
delete newData.appealTypes;
|
delete newData.appealTypes;
|
||||||
|
|
||||||
console.log("/////Create Case:\n", newData, "\n//////////////");
|
|
||||||
|
|
||||||
var newAppealObj = {
|
var newAppealObj = {
|
||||||
"pinswg_name": tempCaseRef,
|
"pinswg_name": tempCaseRef,
|
||||||
"pinswg_appealcasetype": appealTypeId,
|
"pinswg_appealcasetype": appealTypeId,
|
||||||
|
|||||||
@@ -38,14 +38,11 @@ ApiProxy.get(async (req, res) => {
|
|||||||
return res.status(400).json();
|
return res.status(400).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||||
await deleteBlob(
|
(data) => {
|
||||||
containerName,
|
|
||||||
casefolderID + "/files/" + blobName
|
|
||||||
).then((data) => {
|
|
||||||
return res.status(200).json({ data: data });
|
return res.status(200).json({ data: data });
|
||||||
});
|
}
|
||||||
}
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -38,14 +38,11 @@ ApiProxy.get(async (req, res) => {
|
|||||||
return res.status(400).json();
|
return res.status(400).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||||
await deleteBlob(
|
(data) => {
|
||||||
containerName,
|
|
||||||
casefolderID + "/files/" + blobName
|
|
||||||
).then((data) => {
|
|
||||||
return res.status(200).json({ data: data });
|
return res.status(200).json({ data: data });
|
||||||
});
|
}
|
||||||
}
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ export default async function handler(req, res) {
|
|||||||
const tags = {
|
const tags = {
|
||||||
containerid: json.containerID,
|
containerid: json.containerID,
|
||||||
caseID: json.ticketnumber,
|
caseID: json.ticketnumber,
|
||||||
blobType: "Representation",
|
blobType: "Representation"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Convert back to string
|
// Convert back to string
|
||||||
@@ -53,11 +53,10 @@ export default async function handler(req, res) {
|
|||||||
updatedContent,
|
updatedContent,
|
||||||
Buffer.byteLength(updatedContent),
|
Buffer.byteLength(updatedContent),
|
||||||
{
|
{
|
||||||
blobHTTPHeaders: { blobContentType: "application/json" },
|
blobHTTPHeaders: { blobContentType: "application/json" }
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("the tags:", tags);
|
|
||||||
const withTags = await blockBlobClient.setTags(tags);
|
const withTags = await blockBlobClient.setTags(tags);
|
||||||
const withMeta = await blockBlobClient.setMetadata(tags);
|
const withMeta = await blockBlobClient.setMetadata(tags);
|
||||||
|
|
||||||
|
|||||||
@@ -65,19 +65,17 @@ ApiProxy.get(async (req, res) => {
|
|||||||
return res.status(400).json();
|
return res.status(400).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
casefolderID.split("/").length > 1
|
||||||
casefolderID.split("/").length > 1
|
? await getRepsFilesBlobs(
|
||||||
? await getRepsFilesBlobs(
|
containerName,
|
||||||
containerName,
|
casefolderID.split("/")[0],
|
||||||
casefolderID.split("/")[0],
|
casefolderID.split("/")[1]
|
||||||
casefolderID.split("/")[1]
|
).then((data) => {
|
||||||
).then((data) => {
|
return res.status(200).json({ "value": [data] });
|
||||||
return res.status(200).json({ "value": [data] });
|
})
|
||||||
})
|
: await getBlobs(containerName, casefolderID).then((data) => {
|
||||||
: await getBlobs(containerName, casefolderID).then((data) => {
|
return res.status(200).json({ "value": [data] });
|
||||||
return res.status(200).json({ "value": [data] });
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -37,19 +37,13 @@ ApiProxy.get(async (req, res) => {
|
|||||||
return res.status(400).json();
|
return res.status(400).json();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hashAPIPath(checkquerypath) == "&hash=" + checkHash) {
|
await getProgressBlobs(containerName, casefolderID)
|
||||||
const blobObj = await getProgressBlobs(containerName, casefolderID)
|
.then((data) => {
|
||||||
.then((data) => {
|
return downloadProgressFile(containerName, data.path, casefolderID);
|
||||||
return downloadProgressFile(
|
})
|
||||||
containerName,
|
.then((data) => {
|
||||||
data.path,
|
return res.status(200).json(data);
|
||||||
casefolderID
|
});
|
||||||
);
|
|
||||||
})
|
|
||||||
.then((data) => {
|
|
||||||
return res.status(200).json(data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
|
|||||||
@@ -29,6 +29,19 @@ export default async function ApiProxy(req, res) {
|
|||||||
var updateFormCollection = req.query.updateFormCollection;
|
var updateFormCollection = req.query.updateFormCollection;
|
||||||
var queryUrl = updateFormCollection + "(" + appealObj + ")";
|
var queryUrl = updateFormCollection + "(" + appealObj + ")";
|
||||||
|
|
||||||
|
if (
|
||||||
|
typeof updateFormCollection === "undefined" ||
|
||||||
|
updateFormCollection.length === 0 ||
|
||||||
|
typeof appealObj === "undefined" ||
|
||||||
|
appealObj.length === 0 ||
|
||||||
|
typeof incidentID === "undefined" ||
|
||||||
|
incidentID.length === 0 ||
|
||||||
|
typeof req.body === "undefined" ||
|
||||||
|
req.body === null
|
||||||
|
) {
|
||||||
|
return res.status(400).json();
|
||||||
|
}
|
||||||
|
|
||||||
var token = await getToken();
|
var token = await getToken();
|
||||||
|
|
||||||
var config = {
|
var config = {
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
const fs = require("fs");
|
||||||
|
const path = require("path");
|
||||||
|
const vm = require("vm");
|
||||||
|
const assert = require("assert");
|
||||||
|
|
||||||
|
const rootDir = path.resolve(__dirname, "..", "..");
|
||||||
|
|
||||||
|
const loadModule = (relativePath, injected = {}) => {
|
||||||
|
const filePath = path.join(rootDir, relativePath);
|
||||||
|
let source = fs.readFileSync(filePath, "utf8");
|
||||||
|
|
||||||
|
source = source.replace(/import[\s\S]*?from\s+"[^"]+";\n?/g, "");
|
||||||
|
source = source.replace(
|
||||||
|
/export default async function\s+(\w+)\s*\(/,
|
||||||
|
"async function $1("
|
||||||
|
);
|
||||||
|
source = source.replace(/export const\s+/g, "const ");
|
||||||
|
source = source.replace(
|
||||||
|
/export default\s+(\w+);/g,
|
||||||
|
"module.exports.default = $1;"
|
||||||
|
);
|
||||||
|
|
||||||
|
source +=
|
||||||
|
'\nif (typeof ApiProxy !== "undefined" && !module.exports.default) module.exports.default = ApiProxy;\n';
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
module: { exports: {} },
|
||||||
|
exports: {},
|
||||||
|
require,
|
||||||
|
process,
|
||||||
|
console: {
|
||||||
|
log: () => {},
|
||||||
|
info: () => {},
|
||||||
|
warn: () => {},
|
||||||
|
error: () => {}
|
||||||
|
},
|
||||||
|
...injected
|
||||||
|
};
|
||||||
|
|
||||||
|
vm.runInNewContext(source, context, { filename: filePath });
|
||||||
|
return context.module.exports;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createNextConnectMock = () => {
|
||||||
|
const router = {
|
||||||
|
handler: null,
|
||||||
|
use: () => {},
|
||||||
|
get(fn) {
|
||||||
|
this.handler = fn;
|
||||||
|
},
|
||||||
|
post(fn) {
|
||||||
|
this.handler = fn;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return () => router;
|
||||||
|
};
|
||||||
|
|
||||||
|
const createRes = () => {
|
||||||
|
const state = {
|
||||||
|
statusCode: null,
|
||||||
|
jsonBody: undefined
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
status(code) {
|
||||||
|
state.statusCode = code;
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
json(payload) {
|
||||||
|
state.jsonBody = payload;
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const tests = [];
|
||||||
|
const test = (name, fn) => tests.push({ name, fn });
|
||||||
|
|
||||||
|
test("deletewatchedcases_api rejects missing watchedCaseID with 400", async () => {
|
||||||
|
let axiosCalls = 0;
|
||||||
|
const mod = loadModule("pages/api/endpoint/deletewatchedcases_api.js", {
|
||||||
|
axios: async () => {
|
||||||
|
axiosCalls += 1;
|
||||||
|
return { data: {} };
|
||||||
|
},
|
||||||
|
getToken: async () => ({ access_token: "token" })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: {} };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(axiosCalls, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getaccounts_api rejects missing emailAddress with 400", async () => {
|
||||||
|
const mod = loadModule("pages/api/endpoint/getaccounts_api.js", {
|
||||||
|
_: { isEmpty: () => true },
|
||||||
|
axios: { get: async () => ({ data: { value: [] } }) },
|
||||||
|
getToken: async () => ({ access_token: "token" }),
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
azureHeaders: () => ({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: {} };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getbasicsearchpaged_api rejects missing searchString with 400", async () => {
|
||||||
|
const mod = loadModule("pages/api/endpoint/getbasicsearchpaged_api.js", {
|
||||||
|
axios: { get: async () => ({ data: { value: [] } }) },
|
||||||
|
getToken: async () => ({ access_token: "token" }),
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
azureHeadersPagedCustom: () => ({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: { pageNumber: "1" } };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getappealpdfdocuments_api rejects missing incidentid with 400", async () => {
|
||||||
|
const mod = loadModule("pages/api/endpoint/getappealpdfdocuments_api.js", {
|
||||||
|
axios: { get: async () => ({ data: { value: [] } }) },
|
||||||
|
getToken: async () => ({ access_token: "token" }),
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
azureHeaders: () => ({})
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: {} };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("createcase_api rejects missing required query params with 400", async () => {
|
||||||
|
const blobCalls = [];
|
||||||
|
const mod = loadModule("pages/api/file/createcase_api.js", {
|
||||||
|
_: { isEmpty: () => true },
|
||||||
|
getToken: async () => ({ access_token: "token" }),
|
||||||
|
getTempCaseRef: () => "TMP-1",
|
||||||
|
uuidv4: () => "uuid-1",
|
||||||
|
getCaseBlob: (...args) => blobCalls.push(args),
|
||||||
|
createBlob: (...args) => blobCalls.push(args)
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = { query: {}, body: {} };
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
assert.strictEqual(blobCalls.length, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("updatecase_api rejects missing required inputs with 400", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/updatecase_api.js", {
|
||||||
|
getToken: async () => ({ access_token: "token" }),
|
||||||
|
axios: async () => ({ data: { ok: true } })
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: { incident: "inc-1", appealObj: "appeal-1" },
|
||||||
|
body: { title: "x" }
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 400);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("deleteblob valid hash still returns 200", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/deleteblob.js", {
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
deleteBlob: async () => true,
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: {
|
||||||
|
container: "c1",
|
||||||
|
casefolderID: "case-1",
|
||||||
|
blobname: "f.pdf",
|
||||||
|
hash: "expected"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getprogressobjblob valid hash still returns 200", async () => {
|
||||||
|
const mod = loadModule("pages/api/file/getprogressobjblob.js", {
|
||||||
|
hashAPIPath: () => "&hash=expected",
|
||||||
|
getProgressBlobs: async () => ({ path: "p1" }),
|
||||||
|
downloadProgressFile: async () => ({ filesList: [] }),
|
||||||
|
nextConnect: createNextConnectMock(),
|
||||||
|
middleware: () => {}
|
||||||
|
});
|
||||||
|
|
||||||
|
const req = {
|
||||||
|
query: {
|
||||||
|
container: "c1",
|
||||||
|
casefolderID: "case-1",
|
||||||
|
hash: "expected"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const res = createRes();
|
||||||
|
await mod.default.handler(req, res);
|
||||||
|
|
||||||
|
assert.strictEqual(res.state.statusCode, 200);
|
||||||
|
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||||
|
filesList: []
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const run = async () => {
|
||||||
|
let passed = 0;
|
||||||
|
|
||||||
|
for (const currentTest of tests) {
|
||||||
|
await currentTest.fn();
|
||||||
|
passed += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`Phase 18 behavioural tests passed (${passed}/${tests.length}).`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
run().catch((error) => {
|
||||||
|
console.error(error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user