+18
-5
@@ -1384,14 +1384,27 @@ export const getRepsBlobs = async (containerName) => {
|
||||
listOptions
|
||||
)) {
|
||||
const blobClient = containerClient.getBlobClient(blob.name);
|
||||
//console.log("getreps blob:", blob);
|
||||
blob.name.split("/")[2].indexOf("_rep.json") > 0 &&
|
||||
blob.name.split("/")[2].indexOf("undefined") < 0 &&
|
||||
|
||||
// Filter out soft-deleted/stale tag entries and malformed names.
|
||||
const namePart = blob.name.split("/")[2] ?? "";
|
||||
if (
|
||||
namePart.indexOf("_rep.json") <= 0 ||
|
||||
namePart.indexOf("undefined") >= 0
|
||||
)
|
||||
continue;
|
||||
|
||||
try {
|
||||
const properties = await blobClient.getProperties();
|
||||
|
||||
blobObj.push({
|
||||
"name": blob.name.split("/")[2],
|
||||
"name": namePart,
|
||||
"path": blob.name,
|
||||
"size": blobClient.getProperties().contentLength
|
||||
"size": properties.contentLength
|
||||
});
|
||||
} catch (error) {
|
||||
if (error?.statusCode === 404) continue;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log("blobObjwwwww:", blobObj);
|
||||
|
||||
@@ -130,7 +130,6 @@ const MyPortal = (props) => {
|
||||
{!isLPA && (
|
||||
<MakeNewAppeal docsOffline={docsOffline} />
|
||||
)}
|
||||
|
||||
{/* {props.awaitingSubmission.awaitingSubmission[
|
||||
"@odata.count"
|
||||
] > 0 && (
|
||||
@@ -147,9 +146,8 @@ const MyPortal = (props) => {
|
||||
"@odata.count"
|
||||
]) &&
|
||||
props.awaitingSubmission
|
||||
.awaitingSubmissionFromBlob[
|
||||
"@odata.count"
|
||||
] > 0 && (
|
||||
.awaitingSubmissionFromBlob.case.length >
|
||||
0 && (
|
||||
<AwaitingSubmissionFromBlob
|
||||
containerID={props.containerID}
|
||||
awaitingSubmissionFromBlob={
|
||||
@@ -189,7 +187,6 @@ const MyPortal = (props) => {
|
||||
isLPA={isLPA}
|
||||
/>
|
||||
)}
|
||||
|
||||
{props.myRepresentations.mySubmittedReps
|
||||
.mySubmittedReps.length > 0 && (
|
||||
<MySubmittedReps
|
||||
|
||||
@@ -129,3 +129,599 @@ Follow-ups:
|
||||
|
||||
- Continue with remaining non-standard API outlier(s), notably `pages/api/file/generateappealpdfcopy.js`.
|
||||
- Keep future slices logged in this file at commit-time now that memory-bank is versioned.
|
||||
|
||||
---
|
||||
|
||||
### CL-004: TASK22224 file + static endpoint contract hardening bundle (phase21)
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/{downloadblob,generateappealpdfcopy}.js`, `pages/api/endpoint/{getsipsmedia_api,getappealtypesfornewappeal_api}.js`, `tests/phase21/{file-handler-contract,endpoint-handler-contract}.test.cjs`
|
||||
type: change
|
||||
rationale: Deliver the agreed larger bounded slice for remaining non-standard file/static handlers, improving negative-path consistency while preserving current success payload behavior.
|
||||
impact: Standardized error envelopes/codes for download and generated PDF copy flows, method guard parity for static endpoints, and expanded phase21 contract coverage for both file and endpoint handlers.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `downloadblob.js`:
|
||||
- added explicit catch-path response via `respondError` with `DOWNLOAD_BLOB_FAILED`
|
||||
- kept success behavior intact (attachment header + raw file body)
|
||||
- removed dead internal helper (`streamToBuffer`) and tightened local declarations
|
||||
- `generateappealpdfcopy.js`:
|
||||
- removed unused imports/noisy console warnings
|
||||
- standardized required-input and negative-path contracts:
|
||||
- `INCIDENT_ID_REQUIRED` (400)
|
||||
- `CASE_NOT_FOUND` (404)
|
||||
- `FORM_COLLECTION_NOT_FOUND` (400)
|
||||
- `APPEAL_PDF_COPY_GENERATION_FAILED` (400)
|
||||
- preserved success output contract (PDF content headers + buffer body)
|
||||
- `getsipsmedia_api.js` and `getappealtypesfornewappeal_api.js`:
|
||||
- added method guard for non-GET requests using `METHOD_NOT_ALLOWED` (405)
|
||||
- preserved existing GET success payloads
|
||||
- Expanded phase21 tests:
|
||||
- `file-handler-contract.test.cjs`: added coverage for download failure + full generated PDF copy contract/negative paths
|
||||
- `endpoint-handler-contract.test.cjs`: added method guard tests for both static endpoints
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 17/17
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors)
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- If desired, next slice can target remaining file-route parity candidates outside this bundle, but this closes the planned TASK22224 scope.
|
||||
|
||||
---
|
||||
|
||||
### CL-005: TASK22224 downloadblob hotfix closure (path normalization + hash compatibility)
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/downloadblob.js`
|
||||
type: change
|
||||
rationale: Close post-merge runtime regressions reported on live links where download URLs alternated between filename-only/full-path blob names and mixed encoded/raw hash input variants.
|
||||
impact: Restored reliable blob downloads without relaxing hash security guarantees (still HMAC validated), and preserved existing caller compatibility across legacy/new URL encodings.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- Hotfix 1 (`f09f3b7`): normalized blob path resolution
|
||||
- accepts both forms of `blobname` input:
|
||||
- filename only (legacy)
|
||||
- full prefixed path (already includes `casefolderID/...`)
|
||||
- prevents double-prefix lookup failures
|
||||
- sets attachment filename from final path segment only
|
||||
- Hotfix 2 (`bd3bf68`): hash compatibility validation
|
||||
- validates against a bounded set of canonical query-path variants (raw/encoded combinations for `casefolderID` and `blobname`)
|
||||
- fixes `INVALID_HASH` false negatives for legitimate caller-generated links
|
||||
- keeps strict HMAC requirement in place (no unauthenticated bypass)
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (17/17)
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 17/17
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
- User confirmation: "downloadblob now works"
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Next recommended slice on this branch: complete file-route guard parity for `deleteblob.js`, `deleteblobcase.js`, and `deleteblobrep.js` by aligning hash validation canonicalization and explicit `respondError` contracts (`MISSING_REQUIRED_QUERY`, `INVALID_HASH`, operation-specific `*_FAILED`).
|
||||
- Extend `tests/phase21/file-handler-contract.test.cjs` for the above routes with mixed encoded/raw hash cases to lock compatibility.
|
||||
|
||||
---
|
||||
|
||||
### CL-006: TASK22224 file delete-route guard parity slice
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/{deleteblob,deleteblobcase,deleteblobrep}.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Execute the next planned slice to align hash/canonicalization behavior and negative-path contracts across high-risk file delete routes, matching the compatibility posture established for `downloadblob`.
|
||||
impact: Reduces false `INVALID_HASH` failures for legitimate encoded/raw caller variants while preserving strict hash enforcement and improving resilience via explicit catch-path contracts.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `deleteblob.js`
|
||||
- added bounded hash candidate validation for encoded/raw combinations of `casefolderID` and `blobname`
|
||||
- normalized delete path handling for both filename-only and already-prefixed blob paths
|
||||
- added explicit catch-path contract: `DELETE_BLOB_FAILED`
|
||||
- `deleteblobcase.js`
|
||||
- added hash candidate validation for raw/encoded `casefolderID`
|
||||
- added explicit catch-path contract: `DELETE_BLOB_CASE_FAILED`
|
||||
- `deleteblobrep.js`
|
||||
- added hash candidate validation for encoded/raw `casefolderID` + `repfile`
|
||||
- added explicit catch-path contract: `DELETE_BLOB_REP_FAILED`
|
||||
- Phase21 tests expanded (`file-handler-contract.test.cjs`):
|
||||
- encoded hash-variant acceptance cases for all three delete routes
|
||||
- explicit dependency-failure contract assertions for all three delete routes
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (23/23)
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 23/23
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional next slice: apply same bounded hash-canonicalization parity to remaining high-sensitivity file routes where mixed encoded/raw callers may exist (`getbloblist`, `getprogressobjblob`) and add regression cases to phase21.
|
||||
|
||||
---
|
||||
|
||||
### CL-007: TASK22224 getrepsblob stability hotfix after delete representation flow
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `actions/azurestorage.js` (`getRepsBlobs`), `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Resolve reported runtime 400 (`GET_REPS_BLOB_FAILED`) after delete representation actions, caused by stale soft-deleted blob tag hits during representation blob enumeration.
|
||||
impact: Prevents transient/stale Azure tag index entries from breaking representation retrieval, improving reliability of post-delete refresh without relaxing route security contracts.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- Hardened `getRepsBlobs(containerName)` in `actions/azurestorage.js`:
|
||||
- fixed async misuse (`blobClient.getProperties().contentLength` without await)
|
||||
- added existence/property guard with explicit `await blobClient.getProperties()`
|
||||
- skips 404s (soft-deleted/stale tag index results) instead of throwing
|
||||
- preserves behavior for non-404 failures (rethrow for proper error visibility)
|
||||
- kept existing `_rep.json`/`undefined` name filtering intact
|
||||
- Added phase21 contract coverage for `getrepsblob` route:
|
||||
- success payload contract test
|
||||
- dependency failure contract test (`GET_REPS_BLOB_FAILED`)
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (25/25)
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 25/25
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional: add the same stale-tag existence guard pattern to any remaining Azure tag-list readers that still consume `findBlobsByTags` results without property existence verification.
|
||||
|
||||
---
|
||||
|
||||
### CL-008: TASK22224 awaiting-submission route resilience parity hardening
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/getawaitingsubmissionfromblob.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Add explicit catch-path contract parity for awaiting-submission blob retrieval route so unexpected dependency failures return consistent, actionable error envelopes.
|
||||
impact: Improves reliability/diagnostics for post-delete case refresh and aligns file-route error handling style without changing success payload contract or hash verification behavior.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- Refactored `getawaitingsubmissionfromblob` handler to structured `try/catch` flow.
|
||||
- Preserved existing guard behavior:
|
||||
- `MISSING_REQUIRED_QUERY` for missing container/hash
|
||||
- `INVALID_HASH` for signature mismatch
|
||||
- Added explicit dependency failure contract:
|
||||
- `GET_AWAITING_SUBMISSION_BLOB_FAILED` (400)
|
||||
- message: `Failed to retrieve awaiting submission blobs`
|
||||
- Added phase21 coverage for this route:
|
||||
- success payload pass-through contract
|
||||
- dependency failure contract assertion
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (27/27)
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 27/27
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional parity sweep: apply the same explicit catch-path contract pattern to remaining file routes that still rely on implicit promise-chain errors.
|
||||
|
||||
---
|
||||
|
||||
### CL-009: TASK22224 proxy-route resilience and encoding parity bundle
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/{getbloblistproxy,getrepsblobproxy,getawaitingsubmissionfromblobproxy,createappealcompletemessageproxy_api}.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Continue larger-slice hardening by aligning proxy handlers with explicit async error handling and safer encoded upstream query forwarding for hash-based downstream calls.
|
||||
impact: Improves proxy reliability and compatibility for encoded query values while preserving existing proxy error contracts and response behavior.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `getbloblistproxy.js`
|
||||
- converted `.then/.catch` chain to explicit `try/catch`
|
||||
- encoded forwarded `container` and `casefolderID` query values
|
||||
- preserved error contract: `GET_BLOB_LIST_PROXY_FAILED`
|
||||
- `getrepsblobproxy.js`
|
||||
- converted `.then/.catch` chain to explicit `try/catch`
|
||||
- encoded forwarded `container`
|
||||
- preserved error contract: `GET_REPS_BLOB_PROXY_FAILED`
|
||||
- `getawaitingsubmissionfromblobproxy.js`
|
||||
- converted `.then/.catch` chain to explicit `try/catch`
|
||||
- preserved error contract: `GET_AWAITING_SUBMISSION_PROXY_FAILED`
|
||||
- `createappealcompletemessageproxy_api.js`
|
||||
- converted `.then/.catch` chain to explicit `try/catch`
|
||||
- encoded forwarded `container` and `tempcaseref`
|
||||
- preserved error contract: `CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED`
|
||||
- Phase21 tests expanded for proxy paths:
|
||||
- getbloblistproxy success + dependency failure
|
||||
- getrepsblobproxy success
|
||||
- getawaitingsubmissionfromblobproxy dependency failure
|
||||
- createappealcompletemessageproxy dependency failure
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/file-handler-contract.test.cjs` -> pass (46/46)
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 46/46
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 149/149
|
||||
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
### CL-011: TASK22224 aggressive non-file bundle (email/admin/endpoint parity)
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/email/{getmailinglist,getcaseref,notify}.js`, `pages/api/admin/{getnewappeals_api,getlatestdocuments_api}.js`, `pages/api/endpoint/getportallogin_api.js`, `tests/phase21/endpoint-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Execute requested aggressive bundling for remaining non-file modernization/parity candidates: remove legacy promise chains and improve hash compatibility on login endpoint while preserving existing contracts.
|
||||
impact: Improves consistency and resilience across email/admin/endpoint routes with no contract regressions; adds encoded hash-variant compatibility for portal login hash checks.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/email/getmailinglist.js`
|
||||
- converted axios `.then/.catch` to `try/catch`
|
||||
- preserved flattening behavior and error contract `MAILING_LIST_FETCH_FAILED`
|
||||
- `pages/api/email/getcaseref.js`
|
||||
- converted axios `.then/.catch` to `try/catch`
|
||||
- preserved flattening behavior and error contract `CASE_REF_FETCH_FAILED`
|
||||
- `pages/api/email/notify.js`
|
||||
- converted notify client `.then/.catch` to `try/catch`
|
||||
- preserved success payload and error contract `EMAIL_NOTIFY_FAILED`
|
||||
- `pages/api/admin/getnewappeals_api.js`
|
||||
- removed unused `CryptoJS` import
|
||||
- converted axios `.then/.catch` to `try/catch`
|
||||
- preserved `@odata.nextLink` normalization and error contract `ADMIN_NEW_APPEALS_FETCH_FAILED`
|
||||
- `pages/api/admin/getlatestdocuments_api.js`
|
||||
- converted axios `.then/.catch` to `try/catch`
|
||||
- preserved flatten/enrich behavior and error contract `ADMIN_LATEST_DOCS_FETCH_FAILED`
|
||||
- `pages/api/endpoint/getportallogin_api.js`
|
||||
- retained required query/hash guards
|
||||
- expanded hash validation to accept raw + encoded `emailAddress` query-path candidates
|
||||
- preserved error contract `PORTAL_LOGIN_FETCH_FAILED`
|
||||
- phase21 endpoint tests expanded:
|
||||
- `getportallogin` encoded hash variant success path
|
||||
- `getnewappeals_api` catch contract
|
||||
- `getlatestdocuments_api` catch contract
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 49/49
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Remaining major modernization candidate is `pages/api/file/generateappealpdf.js` (+ optional `pages/api/file/generatepdf.js`) if we continue final closure slices.
|
||||
|
||||
---
|
||||
|
||||
### CL-012: TASK22224 generatepdf/generateappealpdf async hardening slice
|
||||
|
||||
date: 2026-03-23
|
||||
author: Cline
|
||||
scope: `pages/api/file/{generateappealpdf,generatepdf}.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Execute next requested slice to modernize remaining file PDF-generation handlers still using promise chains, while preserving existing hash/response behavior.
|
||||
impact: Improves maintainability and error-path consistency for PDF generation routes; phase21 coverage now includes explicit failure contracts for both handlers.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- converted mixed promise-chain flow to `async/await` + `try/catch`
|
||||
- preserved existing guard contracts: `MISSING_REQUIRED_QUERY`, `INVALID_HASH`
|
||||
- preserved generation failure contract: `GENERATE_APPEAL_PDF_FAILED`
|
||||
- replaced JSX render call with `React.createElement(...)` compatibility form used by test loader
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- converted create/upload promise-chain to `async/await` + `try/catch`
|
||||
- preserved existing guard contracts: `HASH_REQUIRED`, `INVALID_HASH`
|
||||
- preserved generation failure contract: `GENERATE_PDF_FAILED`
|
||||
- replaced JSX render call with `React.createElement(...)` compatibility form used by test loader
|
||||
- phase21 file tests expanded:
|
||||
- `generatepdf` catch-path contract (`GENERATE_PDF_FAILED`)
|
||||
- `generateappealpdf` catch-path contract (`GENERATE_APPEAL_PDF_FAILED`)
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 51/51
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Remaining optional cleanup in these handlers is dead import/unused local pruning (non-behavioral) if we want a final low-risk tidy pass.
|
||||
|
||||
---
|
||||
|
||||
### CL-013: TASK22224 completion-message route parity closure slice
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/file/createappealcompletemessage_api.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Continue next requested slice by closing the final promise-chain parity outlier in file completion-message flow and strengthening phase21 contract coverage.
|
||||
impact: Improves maintainability and async error hygiene while preserving route behavior and existing error contracts.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- replaced inline `.catch(...)` on fire-and-forget `updateAccount(...)` with explicit async IIFE + `try/catch` and `void` invocation
|
||||
- preserved non-blocking behavior and logging semantics for account-update failure path
|
||||
- preserved primary route contracts and success payload (`{ status: "success" }`)
|
||||
- phase21 file contract tests expanded:
|
||||
- success path for encoded hash candidate on `createappealcompletemessage_api`
|
||||
- dependency-failure contract assertion for `CREATE_APPEAL_COMPLETE_MESSAGE_FAILED`
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional final low-risk tidy sweep: remove dead imports/unused locals in legacy file handlers now that contract hardening stream is functionally complete.
|
||||
|
||||
---
|
||||
|
||||
### CL-014: TASK22224 pdf render compatibility tidy slice
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/file/{generatepdf,generateappealpdf}.js`, `tests/phase21/file-handler-contract.test.cjs`
|
||||
type: change
|
||||
rationale: Apply a low-risk compatibility tidy so PDF render invocation remains stable across runtime and contract-test VM contexts.
|
||||
impact: Keeps functional behavior unchanged while reducing test/runtime mismatch risk in render path setup.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- switched render call input from `React.createElement(MyDocument, ...)` to direct `MyDocument(...)` invocation in `ReactPDF.renderToStream(...)`
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- switched render call input from `React.createElement(MyDocument, ...)` to direct `MyDocument(...)` invocation in `ReactPDF.renderToStream(...)`
|
||||
- `tests/phase21/file-handler-contract.test.cjs`
|
||||
- added `Buffer` injection for `generatepdf` catch-path test harness to align VM context expectations
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional: dead import cleanup (`Document/Page/Text/View/StyleSheet/PDFViewer`, `middleware`, `nextConnect`, `fs`, etc.) can be done in a dedicated non-behavioral hygiene PR.
|
||||
|
||||
---
|
||||
|
||||
### CL-015: TASK22224 pdf handler dead-code hygiene slice
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/file/{generatepdf,generateappealpdf}.js`
|
||||
type: change
|
||||
rationale: Execute the requested next low-risk slice by removing dead imports and unused locals in recently hardened PDF handlers.
|
||||
impact: Non-behavioral maintainability cleanup; reduces lint noise and future edit risk while preserving existing contracts.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- pruned unused Azure storage imports, leaving only `createRepPDFBlob`
|
||||
- pruned unused `@react-pdf/renderer` named imports
|
||||
- removed unused imports (`middleware`, `nextConnect`, `fs`)
|
||||
- removed unused locals (`casefolderID`, `representationType`, `repRaiser`, `localeSelect`, `repCapacity`, `repType`)
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- pruned unused Azure storage imports to only required functions
|
||||
- pruned unused `@react-pdf/renderer` named imports
|
||||
- removed unused imports (`middleware`, `nextConnect`, `fs`, `path`, unused pdf templates)
|
||||
- removed unused local (`caseRef`)
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional: run full repo lint in a separate pass for broader non-slice hygiene now that targeted contract suite is stable.
|
||||
|
||||
---
|
||||
|
||||
### CL-016: TASK22224 documents download contract slice
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/documents/download/[id].js`, `tests/phase21/{documents-handler-contract,api-contract-slice1}.test.cjs`
|
||||
type: change
|
||||
rationale: Execute next aggressive slice by standardizing document download guard behavior and bringing the route under phase21 contract coverage.
|
||||
impact: Improves reliability on invalid input and relay-failure paths while preserving existing user-visible fallback behavior (`/filenotavailable`) for download failures.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/documents/download/[id].js`
|
||||
- added explicit required-query guard for `id` and `hash`
|
||||
- unified fallback redirect path via constant (`/filenotavailable`)
|
||||
- preserved streaming download behavior and retry flow
|
||||
- added `tests/phase21/documents-handler-contract.test.cjs` covering:
|
||||
- missing query -> redirect contract
|
||||
- success -> attachment/content-type headers + stream pipe contract
|
||||
- relay failure -> redirect contract
|
||||
- updated combined runner (`tests/phase21/api-contract-slice1.test.cjs`) to include documents handler contract suite
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
- documents-handler: 3/3
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional future hardening: migrate documents route onto shared `respondError/respondSuccess` envelope if product requirements allow replacing redirect-style fallback.
|
||||
|
||||
---
|
||||
|
||||
### CL-017: TASK22224 endpoint legacy-comment hygiene slice
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/endpoint/{createwatchedcases_api,getadvancedsearchpaged_api}.js`
|
||||
type: change
|
||||
rationale: Complete second requested slice with low-risk maintainability cleanup by removing large obsolete commented legacy handler blocks.
|
||||
impact: Non-behavioral cleanup only; improves readability and reduces maintenance noise with no runtime contract changes.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `createwatchedcases_api.js`
|
||||
- removed obsolete commented promise-chain implementation block
|
||||
- `getadvancedsearchpaged_api.js`
|
||||
- removed obsolete commented legacy implementation block retained below active handler
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
- documents-handler: 3/3
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional further hygiene pass can target remaining oversized commented historical sections in non-sensitive handlers.
|
||||
|
||||
---
|
||||
|
||||
### CL-018: TASK22224 nextauth notify micro-refactor
|
||||
|
||||
date: 2026-03-24
|
||||
author: Cline
|
||||
scope: `pages/api/auth/[...nextauth].js`
|
||||
type: change
|
||||
rationale: Execute the explicitly approved auth micro-slice by replacing inline promise `.catch(...)` with explicit `try/catch` while preserving existing auth behavior.
|
||||
impact: Auth-sensitive non-functional refactor only; keeps current sign-in flow, template/locale routing, and error-handling semantics unchanged.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- `pages/api/auth/[...nextauth].js`
|
||||
- replaced:
|
||||
- `await notifyClient.sendEmail(...).catch((error) => consoleLogger(error))`
|
||||
- with explicit:
|
||||
- `try { await notifyClient.sendEmail(...) } catch (error) { consoleLogger(error) }`
|
||||
- preserved behavior contracts:
|
||||
- Notify failures are still logged and do not throw through auth handler
|
||||
- no changes to callback URL construction, locale/template selection, NextAuth options, session/cookies/pages config
|
||||
|
||||
Validation:
|
||||
|
||||
- `node tests/phase21/api-contract-slice1.test.cjs` -> pass
|
||||
- helper: 4/4
|
||||
- file-handler: 53/53
|
||||
- email-handler: 12/12
|
||||
- endpoint-handler: 152/152
|
||||
- documents-handler: 3/3
|
||||
|
||||
Follow-ups:
|
||||
|
||||
- Optional future auth hygiene (separate guarded slice): replace verbose auth `console.log` diagnostics with structured logger usage once production logging requirements are confirmed.
|
||||
|
||||
@@ -32,6 +32,28 @@ Last updated: 2026-03-12
|
||||
- **Target outcome:** Shared guard middleware for hash/auth/input validation + consistent negative-path responses.
|
||||
- **Initial scope:** enforce a common pre-handler contract on high-risk routes first.
|
||||
|
||||
### Priority 4 — Current slice status (2026-03-23)
|
||||
|
||||
- Completed on `TASK22224-file-endpoint-contract-bundle`:
|
||||
- `pages/api/file/downloadblob.js` hardening + hotfix closure
|
||||
- path normalization compatibility (filename-only + prefixed path)
|
||||
- encoded/raw hash validation compatibility guard
|
||||
- Confirmed outcome:
|
||||
- user-reported download failures resolved
|
||||
- phase21 file + combined contract suites passing
|
||||
|
||||
### Priority 4 — Next recommended slice on this branch
|
||||
|
||||
- Target routes:
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- Scope:
|
||||
- align hash canonicalization strategy with `downloadblob.js`
|
||||
- enforce explicit `respondError` contracts (`MISSING_REQUIRED_QUERY`, `INVALID_HASH`, route-specific `*_FAILED`)
|
||||
- preserve existing success payload shapes
|
||||
- extend `tests/phase21/file-handler-contract.test.cjs` with mixed encoded/raw hash variants and negative paths
|
||||
|
||||
## Priority 5 — Establish minimum automated regression baseline
|
||||
|
||||
- **Problem:** Limited automated tests for high-risk logic.
|
||||
|
||||
@@ -43,18 +43,18 @@ const encryptDocReference = (documentRef) => {
|
||||
};
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var pageNumber = req.query.pageNumber || 1;
|
||||
var token = await getToken();
|
||||
const pageNumber = req.query.pageNumber || 1;
|
||||
const token = await getToken();
|
||||
|
||||
var orderby = req.query.orderby || "createdon";
|
||||
var fieldSort = req.query.fieldSort || "desc";
|
||||
var showNumberOfRecords = req.query.showNumberOfRecords || 10;
|
||||
var documentType = req.query.documentType || "all";
|
||||
var documentOrigin = req.query.documentOrigin || "all";
|
||||
var numberOfWeeks = req.query.numberWeeks || 1;
|
||||
const orderby = req.query.orderby || "createdon";
|
||||
const fieldSort = req.query.fieldSort || "desc";
|
||||
const showNumberOfRecords = req.query.showNumberOfRecords || 10;
|
||||
const documentType = req.query.documentType || "all";
|
||||
const documentOrigin = req.query.documentOrigin || "all";
|
||||
const numberOfWeeks = req.query.numberWeeks || 1;
|
||||
|
||||
var docTypeQueryString = "";
|
||||
var docOriginQueryString = "";
|
||||
let docTypeQueryString = "";
|
||||
let docOriginQueryString = "";
|
||||
|
||||
if (documentType != "all") {
|
||||
if (documentType.indexOf(",") >= 0) {
|
||||
@@ -108,7 +108,7 @@ export default async function ApiProxy(req, res) {
|
||||
return resultDate.toISOString().replace(/\.000Z$/, "Z"); // format as 'YYYY-MM-DDT00:00:00Z'
|
||||
}
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"pinswg_documents?$select=pinswg_name,createdon,pinswg_publishtoweb,_pinswg_documentids_value,pinswg_isharedocumentlocations,pinswg_isharedocumentreference,pinswg_uploadurl,pinswg_uploadstatus,pinswg_origin&$filter=createdon ge " +
|
||||
daysAgoISO(numberOfWeeks) +
|
||||
docTypeQueryString +
|
||||
@@ -131,40 +131,37 @@ export default async function ApiProxy(req, res) {
|
||||
"\n==========================================\n"
|
||||
);
|
||||
|
||||
var apiResponse = axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPagedCustom(token.access_token, showNumberOfRecords)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
let flattened = data.value.map((r) => ({
|
||||
...r,
|
||||
ticketnumber: r.pinswg_DocumentIds?.ticketnumber || null,
|
||||
publishtoweb: r.pinswg_DocumentIds?.pinswg_publishtoweb || null
|
||||
}));
|
||||
);
|
||||
|
||||
data.value = flattened;
|
||||
data.value.forEach(function (element) {
|
||||
element.pinswg_hashlink = encryptDocReference(
|
||||
element.pinswg_isharedocumentreference
|
||||
);
|
||||
});
|
||||
const flattened = data.value.map((r) => ({
|
||||
...r,
|
||||
ticketnumber: r.pinswg_DocumentIds?.ticketnumber || null,
|
||||
publishtoweb: r.pinswg_DocumentIds?.pinswg_publishtoweb || null
|
||||
}));
|
||||
|
||||
var dataStr;
|
||||
|
||||
_.has(data, "@odata.nextLink") == true &&
|
||||
((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,
|
||||
code: "ADMIN_LATEST_DOCS_FETCH_FAILED",
|
||||
message: "Failed to fetch latest documents"
|
||||
});
|
||||
data.value = flattened;
|
||||
data.value.forEach(function (element) {
|
||||
element.pinswg_hashlink = encryptDocReference(
|
||||
element.pinswg_isharedocumentreference
|
||||
);
|
||||
});
|
||||
|
||||
return apiResponse;
|
||||
let dataStr;
|
||||
|
||||
_.has(data, "@odata.nextLink") == true &&
|
||||
((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,
|
||||
code: "ADMIN_LATEST_DOCS_FETCH_FAILED",
|
||||
message: "Failed to fetch latest documents"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPagedCustom } from "../../../actions/core/headers";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
@@ -29,15 +28,14 @@ const WEBAPI_URL =
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var searchString = req.query.searchString;
|
||||
var pageNumber = req.query.pageNumber || 1;
|
||||
var token = await getToken();
|
||||
const pageNumber = req.query.pageNumber || 1;
|
||||
const token = await getToken();
|
||||
|
||||
var orderby = req.query.orderby || "createdon";
|
||||
var fieldSort = req.query.fieldSort || "desc";
|
||||
var showNumberOfRecords = req.query.showNumberOfRecords || 10;
|
||||
const orderby = req.query.orderby || "createdon";
|
||||
const fieldSort = req.query.fieldSort || "desc";
|
||||
const showNumberOfRecords = req.query.showNumberOfRecords || 10;
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"incidents?$select=caseorigincode,pinswg_publishtoweb,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$orderby=" +
|
||||
orderby +
|
||||
" " +
|
||||
@@ -55,26 +53,23 @@ export default async function ApiProxy(req, res) {
|
||||
"\n==========================================\n"
|
||||
);
|
||||
|
||||
var apiResponse = axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPagedCustom(token.access_token, showNumberOfRecords)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
var dataStr;
|
||||
_.has(data, "@odata.nextLink") == true &&
|
||||
((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,
|
||||
code: "ADMIN_NEW_APPEALS_FETCH_FAILED",
|
||||
message: "Failed to fetch new appeals"
|
||||
});
|
||||
});
|
||||
);
|
||||
|
||||
return apiResponse;
|
||||
let dataStr;
|
||||
_.has(data, "@odata.nextLink") == true &&
|
||||
((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,
|
||||
code: "ADMIN_NEW_APPEALS_FETCH_FAILED",
|
||||
message: "Failed to fetch new appeals"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,8 +139,8 @@ const authOptions = (req, res) => {
|
||||
process.env.NOTIFY_API_KEY
|
||||
);
|
||||
|
||||
await notifyClient
|
||||
.sendEmail(
|
||||
try {
|
||||
await notifyClient.sendEmail(
|
||||
effectiveLocale === "cy"
|
||||
? templateIdcy
|
||||
: templateId,
|
||||
@@ -149,8 +149,10 @@ const authOptions = (req, res) => {
|
||||
personalisation,
|
||||
reference
|
||||
}
|
||||
)
|
||||
.catch((error) => consoleLogger(error));
|
||||
);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
}
|
||||
}
|
||||
})
|
||||
],
|
||||
|
||||
@@ -116,6 +116,8 @@ const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
const FILE_NOT_AVAILABLE_PATH = "/filenotavailable";
|
||||
|
||||
// Retry utility with logging
|
||||
async function retry(fn, retries = 3, delay = 1000) {
|
||||
for (let attempt = 1; attempt <= retries; attempt++) {
|
||||
@@ -133,6 +135,18 @@ async function retry(fn, retries = 3, delay = 1000) {
|
||||
const ApiProxy = async (req, res) => {
|
||||
const docRef = req.query;
|
||||
|
||||
if (
|
||||
typeof docRef?.id !== "string" ||
|
||||
docRef.id.length === 0 ||
|
||||
typeof docRef?.hash !== "string" ||
|
||||
docRef.hash.length === 0
|
||||
) {
|
||||
if (!res.headersSent) {
|
||||
res.redirect(FILE_NOT_AVAILABLE_PATH);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await getToken();
|
||||
const startTime = Date.now();
|
||||
@@ -186,11 +200,11 @@ const ApiProxy = async (req, res) => {
|
||||
|
||||
response.data.on("error", (err) => {
|
||||
consoleLogger(err);
|
||||
if (!res.headersSent) res.redirect("/filenotavailable");
|
||||
if (!res.headersSent) res.redirect(FILE_NOT_AVAILABLE_PATH);
|
||||
});
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
if (!res.headersSent) res.redirect("/filenotavailable");
|
||||
if (!res.headersSent) res.redirect(FILE_NOT_AVAILABLE_PATH);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -22,27 +22,25 @@ function flattenWatchlistEntry(entry) {
|
||||
}
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"pinswg_watchlists?$count=true&$filter=pinswg_emailnotifications ne null&$expand=pinswg_Contact($select=contactid,emailaddress1)&$select=pinswg_emailnotifications,_pinswg_watchedcase_value";
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
const flattenedResults = data.value.map(flattenWatchlistEntry);
|
||||
);
|
||||
const flattenedResults = data.value.map(flattenWatchlistEntry);
|
||||
|
||||
return respondSuccess(res, flattenedResults);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CASE_REF_FETCH_FAILED",
|
||||
message: "Failed to fetch case references"
|
||||
});
|
||||
return respondSuccess(res, flattenedResults);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CASE_REF_FETCH_FAILED",
|
||||
message: "Failed to fetch case references"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,27 +22,25 @@ function flattenWatchlistEntry(entry) {
|
||||
}
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"pinswg_watchlists?$count=true&$filter=pinswg_emailnotifications ne null&$expand=pinswg_Contact($select=contactid,emailaddress1)&$select=pinswg_emailnotifications,_pinswg_watchedcase_value";
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
const flattenedResults = data.value.map(flattenWatchlistEntry);
|
||||
);
|
||||
const flattenedResults = data.value.map(flattenWatchlistEntry);
|
||||
|
||||
return respondSuccess(res, flattenedResults);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MAILING_LIST_FETCH_FAILED",
|
||||
message: "Failed to fetch mailing list"
|
||||
});
|
||||
return respondSuccess(res, flattenedResults);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MAILING_LIST_FETCH_FAILED",
|
||||
message: "Failed to fetch mailing list"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+14
-15
@@ -31,7 +31,7 @@ import { getPreferredLanguage } from "../../../actions/services/accountService";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var data = req.body;
|
||||
const data = req.body;
|
||||
const emailAddress = sanitizeString(data?.emailAddress);
|
||||
|
||||
if (!isNonEmptyString(emailAddress)) {
|
||||
@@ -58,7 +58,7 @@ export default async function ApiProxy(req, res) {
|
||||
}
|
||||
}
|
||||
|
||||
var NotifyClient = require("notifications-node-client").NotifyClient;
|
||||
const NotifyClient = require("notifications-node-client").NotifyClient;
|
||||
|
||||
const notifyClient = new NotifyClient(process.env.NOTIFY_API_KEY);
|
||||
//const emailReplyToId = process.env.EMAIL_REPLY_TO_ID;
|
||||
@@ -68,20 +68,19 @@ export default async function ApiProxy(req, res) {
|
||||
payload: redactSensitive(data)
|
||||
});
|
||||
|
||||
notifyClient
|
||||
.sendEmail(data.templateId, data.emailAddress, {
|
||||
try {
|
||||
await notifyClient.sendEmail(data.templateId, data.emailAddress, {
|
||||
personalisation: data.personalisation,
|
||||
reference: data.reference
|
||||
})
|
||||
.then(() => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_NOTIFY_FAILED",
|
||||
message: "Failed to send notify email"
|
||||
});
|
||||
});
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_NOTIFY_FAILED",
|
||||
message: "Failed to send notify email"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,35 +20,6 @@ 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 token = await getToken();
|
||||
// var data = JSON.stringify(req.body);
|
||||
// var queryUrl = "pinswg_watchlists";
|
||||
|
||||
// console.log(data);
|
||||
// var config = {
|
||||
// method: "put",
|
||||
// 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: data,
|
||||
// };
|
||||
|
||||
// return axios(config)
|
||||
// .then(({ data }) => {
|
||||
// res.status(200).json(data);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// consoleLogger(Object.assign(err, data));
|
||||
// res.status(400).json(error);
|
||||
// });
|
||||
// }
|
||||
const recordExists = async (incidentId, contactId, token) => {
|
||||
const filter = `$filter=pinswg_WatchedCase/incidentid eq ${incidentId} and pinswg_Contact/contactid eq ${contactId}`;
|
||||
const queryUrl = `pinswg_watchlists?${filter}`;
|
||||
|
||||
@@ -258,161 +258,3 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// export default async function ApiProxy(req, res) {
|
||||
// var searchString = req.query.searchstring;
|
||||
// var pageNumber = req.query.pageNumber;
|
||||
// var token = await getToken();
|
||||
|
||||
// var orderby = req.query.orderby;
|
||||
// var fieldSort = req.query.fieldSort;
|
||||
// var showNumberOfRecords = req.query.showNumberOfRecords;
|
||||
|
||||
// searchString = _.isEmpty(searchString)
|
||||
// ? searchString
|
||||
// : JSON.parse(decodeURI(searchString));
|
||||
|
||||
// var queryString = "";
|
||||
|
||||
// queryString +=
|
||||
// _.has(searchString, "q") && searchString.q != null
|
||||
// ? "(contains(title, '" +
|
||||
// searchString.q.replace(/\'/g, "''") +
|
||||
// "') or contains(ticketnumber,'" +
|
||||
// searchString.q.replace(/\'/g, "''") +
|
||||
// "')) and"
|
||||
// : "";
|
||||
|
||||
// queryString +=
|
||||
// (_.has(searchString, "lpa") || _.has(searchString, "LPA")) &&
|
||||
// searchString.lpa != null
|
||||
// ? " _pinswg_associatedlpa_value eq " + searchString.lpa + " and"
|
||||
// : "";
|
||||
|
||||
// queryString +=
|
||||
// _.has(searchString, "apt") && searchString.apt != null
|
||||
// ? " pinswg_appealcasetype eq " + searchString.apt + " and"
|
||||
// : "";
|
||||
|
||||
// queryString +=
|
||||
// _.has(searchString, "statuscode") && searchString.statuscode != null
|
||||
// ? " statuscode eq " + searchString.statuscode + " and"
|
||||
// : "";
|
||||
|
||||
// var queryUrl =
|
||||
// "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,pinswg_appellantagent,pinswg_appellantfirstname,pinswg_appellantlastname&$expand=primarycontactid($select=fullname)&$filter=" +
|
||||
// queryString +
|
||||
// " pinswg_appealcasetype ne null and pinswg_publishtoweb eq true&$orderby=" +
|
||||
// orderby +
|
||||
// " " +
|
||||
// fieldSort +
|
||||
// "&$count=true";
|
||||
|
||||
// console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl));
|
||||
|
||||
// var apiResponse = _.isEmpty(searchString)
|
||||
// ? res.status(400).json()
|
||||
// : axios
|
||||
// .get(
|
||||
// WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
// azureHeadersPagedCustom(
|
||||
// token.access_token,
|
||||
// showNumberOfRecords
|
||||
// )
|
||||
// )
|
||||
// .then(async ({ data }) => {
|
||||
// var dataStr;
|
||||
// _.has(data, "@odata.nextLink") == true &&
|
||||
// ((dataStr = JSON.stringify(data["@odata.nextLink"])),
|
||||
// (data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]));
|
||||
|
||||
// if (_.has(searchString, "projecttype")) {
|
||||
// await updateValueArray(data, token);
|
||||
|
||||
// async function updateValueArray(data, token) {
|
||||
// let hasNextPage = true;
|
||||
// let nextPageUrl = data["@odata.nextLink"];
|
||||
|
||||
// // Loop through all pages
|
||||
// while (hasNextPage) {
|
||||
// // Iterate through current data and process it
|
||||
// for (let i = 0; i < data.value.length; i++) {
|
||||
// const item = data.value[i];
|
||||
|
||||
// try {
|
||||
// // Axios call using the `incidentid` to fetch additional data
|
||||
// const response = await axios.get(
|
||||
// WEBAPI_URL +
|
||||
// "pinswg_sipses?$filter=_pinswg_sipscase_value eq " +
|
||||
// item.incidentid +
|
||||
// " and _pinswg_projecttype_value eq " +
|
||||
// searchString.projecttype +
|
||||
// "&$select=_pinswg_projecttype_value" +
|
||||
// hashAPIPath(
|
||||
// "pinswg_sipses?$filter=_pinswg_sipscase_value eq " +
|
||||
// item.incidentid +
|
||||
// " and _pinswg_projecttype_value eq " +
|
||||
// searchString.projecttype +
|
||||
// "&$select=_pinswg_projecttype_value"
|
||||
// ),
|
||||
// azureHeadersPaged(token.access_token)
|
||||
// );
|
||||
|
||||
// if (
|
||||
// response.data.value.length > 0 &&
|
||||
// response.data.value[0]
|
||||
// ._pinswg_projecttype_value != null
|
||||
// ) {
|
||||
// Object.assign(
|
||||
// item,
|
||||
// response.data.value[0]
|
||||
// ); // Update item with new data
|
||||
// } else {
|
||||
// console.log(
|
||||
// `No project type found for incident ID ${item.incidentid}`
|
||||
// );
|
||||
// }
|
||||
// } catch (error) {
|
||||
// console.error(
|
||||
// `Error fetching data for incident ID ${item.incidentid}:`,
|
||||
// error
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
// // If there's a next page, fetch it
|
||||
// if (nextPageUrl) {
|
||||
// const nextPageData = await axios.get(
|
||||
// WEBAPI_URL +
|
||||
// nextPageUrl +
|
||||
// hashAPIPath(nextPageUrl),
|
||||
// azureHeadersPaged(token.access_token)
|
||||
// );
|
||||
// data.value = [
|
||||
// ...data.value,
|
||||
// ...nextPageData.data.value,
|
||||
// ];
|
||||
// nextPageUrl =
|
||||
// nextPageData.data["@odata.nextLink"];
|
||||
// } else {
|
||||
// hasNextPage = false;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// data.value = data.value.filter(
|
||||
// (item) => item._pinswg_projecttype_value != null
|
||||
// );
|
||||
|
||||
// data["@odata.count"] = data.value.length;
|
||||
// }
|
||||
|
||||
// res.status(200).json(data);
|
||||
// })
|
||||
// .catch((error) => {
|
||||
// consoleLogger(error);
|
||||
// res.status(400).json(error);
|
||||
// });
|
||||
|
||||
// return apiResponse;
|
||||
// }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { respondSuccess } from "../middleware/apiResponse";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
/**
|
||||
* @swagger
|
||||
@@ -13,6 +13,14 @@ import { respondSuccess } from "../middleware/apiResponse";
|
||||
*/
|
||||
|
||||
const ApiResponse = (req, res) => {
|
||||
if (req.method && req.method !== "GET") {
|
||||
return respondError(res, {
|
||||
status: 405,
|
||||
code: "METHOD_NOT_ALLOWED",
|
||||
message: "Only GET is supported"
|
||||
});
|
||||
}
|
||||
|
||||
return respondSuccess(res, {
|
||||
"value": [
|
||||
{
|
||||
|
||||
@@ -49,8 +49,16 @@ export default async function ApiProxy(req, res) {
|
||||
|
||||
const checkquerypath =
|
||||
"/api/endpoint/getportallogin_api?emailAddress=" + emailAddress;
|
||||
const encodedCheckquerypath =
|
||||
"/api/endpoint/getportallogin_api?emailAddress=" +
|
||||
encodeURIComponent(emailAddress);
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const expectedHashCandidates = [
|
||||
hashAPIPath(checkquerypath),
|
||||
hashAPIPath(encodedCheckquerypath)
|
||||
];
|
||||
|
||||
if (!expectedHashCandidates.includes("&hash=" + checkHash)) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
import { respondSuccess } from "../middleware/apiResponse";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
// export default async function ApiProxy(req, res) {
|
||||
// var caseid = req.query.caseid;
|
||||
// var token = await getToken();
|
||||
export default function ApiProxy(req, res) {
|
||||
if (req.method && req.method !== "GET") {
|
||||
return respondError(res, {
|
||||
status: 405,
|
||||
code: "METHOD_NOT_ALLOWED",
|
||||
message: "Only GET is supported"
|
||||
});
|
||||
}
|
||||
|
||||
// var queryUrl =
|
||||
// "pinswg_sipsevents?$filter=_pinswg_sipseventsid_value eq " +
|
||||
// caseid +
|
||||
// "&$count=true";
|
||||
|
||||
// return 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);
|
||||
// });
|
||||
// }
|
||||
|
||||
export default function ApiProx(req, res) {
|
||||
const data = {
|
||||
"@odata.count": 4,
|
||||
"value": [
|
||||
|
||||
@@ -18,10 +18,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var tempCaseRef = req.query.tempcaseref;
|
||||
var typeofinvolvement = req.query.inv;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const tempCaseRef = req.query.tempcaseref;
|
||||
const typeofinvolvement = req.query.inv;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -38,13 +38,22 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/createappealcompletemessage_api?container=" +
|
||||
containerName +
|
||||
"&tempcaseref=" +
|
||||
tempCaseRef;
|
||||
containerName +
|
||||
"&tempcaseref=" +
|
||||
tempCaseRef,
|
||||
"/api/file/createappealcompletemessage_api?container=" +
|
||||
encodeURIComponent(containerName) +
|
||||
"&tempcaseref=" +
|
||||
encodeURIComponent(tempCaseRef)
|
||||
];
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -52,83 +61,87 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const blobProgress = await getProgressBlobs(
|
||||
containerName,
|
||||
tempCaseRef
|
||||
).then((data) => {
|
||||
return downloadProgressFile(containerName, data.path, tempCaseRef);
|
||||
});
|
||||
try {
|
||||
const progressBlob = await getProgressBlobs(containerName, tempCaseRef);
|
||||
const blobProgress = await downloadProgressFile(
|
||||
containerName,
|
||||
progressBlob.path,
|
||||
tempCaseRef
|
||||
);
|
||||
|
||||
const caseObj = await downloadProgressFile(
|
||||
containerName,
|
||||
tempCaseRef + "/" + tempCaseRef + "_case.json",
|
||||
tempCaseRef
|
||||
);
|
||||
const caseObj = await downloadProgressFile(
|
||||
containerName,
|
||||
tempCaseRef + "/" + tempCaseRef + "_case.json",
|
||||
tempCaseRef
|
||||
);
|
||||
|
||||
Object.assign(blobProgress, {
|
||||
"appealComplete": tempCaseRef
|
||||
});
|
||||
Object.assign(blobProgress, {
|
||||
appealComplete: tempCaseRef
|
||||
});
|
||||
|
||||
delete blobProgress["pinswg_name"];
|
||||
delete blobProgress.pinswg_name;
|
||||
|
||||
await createBlob(JSON.stringify(blobProgress), containerName, tempCaseRef)
|
||||
.then(() => {
|
||||
//update case.json with lpa ref and description
|
||||
await createBlob(
|
||||
JSON.stringify(blobProgress),
|
||||
containerName,
|
||||
tempCaseRef
|
||||
);
|
||||
|
||||
let combinedAddress =
|
||||
blobProgress.pinswg_siteaddressline1 +
|
||||
(!_.isEmpty(blobProgress.pinswg_siteaddressline2)
|
||||
? ", " + blobProgress.pinswg_siteaddressline2
|
||||
: "") +
|
||||
(!_.isEmpty(blobProgress.pinswg_siteaddresstown)
|
||||
? ", " + blobProgress.pinswg_siteaddresstown
|
||||
: "");
|
||||
let combinedAddress =
|
||||
blobProgress.pinswg_siteaddressline1 +
|
||||
(!_.isEmpty(blobProgress.pinswg_siteaddressline2)
|
||||
? ", " + blobProgress.pinswg_siteaddressline2
|
||||
: "") +
|
||||
(!_.isEmpty(blobProgress.pinswg_siteaddresstown)
|
||||
? ", " + blobProgress.pinswg_siteaddresstown
|
||||
: "");
|
||||
|
||||
combinedAddress =
|
||||
combinedAddress +
|
||||
", " +
|
||||
blobProgress.pinswg_siteaddresscounty +
|
||||
", " +
|
||||
blobProgress.pinswg_siteaddresspostcode;
|
||||
combinedAddress =
|
||||
combinedAddress +
|
||||
", " +
|
||||
blobProgress.pinswg_siteaddresscounty +
|
||||
", " +
|
||||
blobProgress.pinswg_siteaddresspostcode;
|
||||
|
||||
Object.assign(caseObj, {
|
||||
"pinswg_lpareference":
|
||||
blobProgress.pinswg_lpaapplicationreference,
|
||||
"description": blobProgress.pinswg_developmentdescription,
|
||||
"pinswg_caseaddress": combinedAddress
|
||||
});
|
||||
Object.assign(caseObj, {
|
||||
pinswg_lpareference: blobProgress.pinswg_lpaapplicationreference,
|
||||
description: blobProgress.pinswg_developmentdescription,
|
||||
pinswg_caseaddress: combinedAddress
|
||||
});
|
||||
|
||||
//send update case.json and then create queue message
|
||||
getCaseBlob(containerName, tempCaseRef, caseObj).then(() => {
|
||||
let contactId = caseObj["customerid_contact@odata.bind"];
|
||||
contactId = contactId.match(/\(([^)]+)\)/);
|
||||
await getCaseBlob(containerName, tempCaseRef, caseObj);
|
||||
|
||||
if (typeofinvolvement != 846040000) {
|
||||
updateAccount(
|
||||
let contactId = caseObj["customerid_contact@odata.bind"];
|
||||
contactId = contactId.match(/\(([^)]+)\)/);
|
||||
|
||||
if (typeofinvolvement != 846040000) {
|
||||
void (async () => {
|
||||
try {
|
||||
await updateAccount(
|
||||
contactId[1],
|
||||
{
|
||||
"pinswg_typeofinvolvement": 846040001
|
||||
pinswg_typeofinvolvement: 846040001
|
||||
},
|
||||
true
|
||||
).catch((error) => {
|
||||
consoleLogger(error);
|
||||
});
|
||||
);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
createCaseCompleteMessage(containerName, tempCaseRef);
|
||||
return respondSuccess(res, {
|
||||
status: "success"
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_APPEAL_COMPLETE_MESSAGE_FAILED",
|
||||
message: "Failed to create appeal complete message"
|
||||
});
|
||||
await createCaseCompleteMessage(containerName, tempCaseRef);
|
||||
return respondSuccess(res, {
|
||||
status: "success"
|
||||
});
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_APPEAL_COMPLETE_MESSAGE_FAILED",
|
||||
message: "Failed to create appeal complete message"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -17,8 +17,8 @@ const hasValue = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0;
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var tempCaseRef = req.query.tempcaseref;
|
||||
const containerName = req.query.container;
|
||||
const tempCaseRef = req.query.tempcaseref;
|
||||
|
||||
if (!hasValue(containerName) || !hasValue(tempCaseRef)) {
|
||||
return respondError(res, {
|
||||
@@ -28,30 +28,28 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"/api/file/createappealcompletemessage_api?container=" +
|
||||
containerName +
|
||||
encodeURIComponent(containerName) +
|
||||
"&tempcaseref=" +
|
||||
tempCaseRef;
|
||||
encodeURIComponent(tempCaseRef);
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED",
|
||||
message: "Failed to create appeal complete message"
|
||||
});
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_APPEAL_COMPLETE_MESSAGE_PROXY_FAILED",
|
||||
message: "Failed to create appeal complete message"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
@@ -36,20 +35,20 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
var contactid = req.body.contactid;
|
||||
var queryUrl =
|
||||
const token = await getToken();
|
||||
const contactid = req.body.contactid;
|
||||
const queryUrl =
|
||||
"incidents(" +
|
||||
req.body.incidentid +
|
||||
")/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 + ")"
|
||||
};
|
||||
|
||||
var config = {
|
||||
const config = {
|
||||
method: "post",
|
||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
headers: {
|
||||
@@ -64,18 +63,20 @@ export default async function ApiProxy(req, res) {
|
||||
data: data
|
||||
};
|
||||
|
||||
return axios(config)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
return error.status == 412
|
||||
? respondSuccess(res, { "record": "exists" })
|
||||
: (consoleLogger(Object.assign(error, data)),
|
||||
respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_CASE_INVOLVEMENT_FAILED",
|
||||
message: "Failed to create case involvement"
|
||||
}));
|
||||
try {
|
||||
const { data: responseData } = await axios(config);
|
||||
return respondSuccess(res, responseData);
|
||||
} catch (error) {
|
||||
const errorStatus = error?.status || error?.response?.status;
|
||||
if (errorStatus == 412) {
|
||||
return respondSuccess(res, { record: "exists" });
|
||||
}
|
||||
|
||||
consoleLogger(Object.assign(error, data));
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_CASE_INVOLVEMENT_FAILED",
|
||||
message: "Failed to create case involvement"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var tempCaseRef = req.query.tempcaseref;
|
||||
var filename = req.query.repid;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const tempCaseRef = req.query.tempcaseref;
|
||||
const filename = req.query.repid;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -35,15 +35,26 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/createrepcompletemessage_api?container=" +
|
||||
containerName +
|
||||
"&tempcaseref=" +
|
||||
tempCaseRef +
|
||||
"&repid=" +
|
||||
filename;
|
||||
containerName +
|
||||
"&tempcaseref=" +
|
||||
tempCaseRef +
|
||||
"&repid=" +
|
||||
filename,
|
||||
"/api/file/createrepcompletemessage_api?container=" +
|
||||
encodeURIComponent(containerName) +
|
||||
"&tempcaseref=" +
|
||||
encodeURIComponent(tempCaseRef) +
|
||||
"&repid=" +
|
||||
encodeURIComponent(filename)
|
||||
];
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -51,18 +62,21 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await createRepCompleteMessage(containerName, tempCaseRef, filename)
|
||||
.then((data) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_REP_COMPLETE_MESSAGE_FAILED",
|
||||
message: "Failed to create representation complete message"
|
||||
});
|
||||
try {
|
||||
const data = await createRepCompleteMessage(
|
||||
containerName,
|
||||
tempCaseRef,
|
||||
filename
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_REP_COMPLETE_MESSAGE_FAILED",
|
||||
message: "Failed to create representation complete message"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
@@ -36,22 +35,22 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
var contactid = req.body.contactid;
|
||||
var queryUrl =
|
||||
const token = await getToken();
|
||||
const contactid = req.body.contactid;
|
||||
const queryUrl =
|
||||
"incidents(" +
|
||||
req.body.incidentid +
|
||||
")/pinswg_incident_contact_case_involvement/$ref";
|
||||
|
||||
var crmUrl = "https://" + process.env.CRMURL;
|
||||
var crmVersion = process.env.CRMURL_VERSION;
|
||||
const crmUrl = "https://" + process.env.CRMURL;
|
||||
const crmVersion = process.env.CRMURL_VERSION;
|
||||
|
||||
var data = {
|
||||
const data = {
|
||||
"@odata.id":
|
||||
crmUrl + "/api/data/" + crmVersion + "/contacts(" + contactid + ")"
|
||||
};
|
||||
|
||||
var config = {
|
||||
const config = {
|
||||
method: "post",
|
||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
headers: {
|
||||
@@ -66,18 +65,20 @@ export default async function ApiProxy(req, res) {
|
||||
data: data
|
||||
};
|
||||
|
||||
return axios(config)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
return error.status == 412
|
||||
? respondSuccess(res, { "record": "exists" })
|
||||
: (consoleLogger(Object.assign(error, data)),
|
||||
respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_REP_INVOLVEMENT_FAILED",
|
||||
message: "Failed to create representation involvement"
|
||||
}));
|
||||
try {
|
||||
const { data: responseData } = await axios(config);
|
||||
return respondSuccess(res, responseData);
|
||||
} catch (error) {
|
||||
const errorStatus = error?.status || error?.response?.status;
|
||||
if (errorStatus == 412) {
|
||||
return respondSuccess(res, { record: "exists" });
|
||||
}
|
||||
|
||||
consoleLogger(Object.assign(error, data));
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CREATE_REP_INVOLVEMENT_FAILED",
|
||||
message: "Failed to create representation involvement"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,10 +9,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
var blobName = req.query.blobname;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const blobName = req.query.blobname;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -31,15 +31,43 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID +
|
||||
"&blobname=" +
|
||||
blobName;
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
const blobNameTrimmed = blobName.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/deleteawaitingsubmissionfromblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/deleteawaitingsubmissionfromblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed),
|
||||
|
||||
// legacy compatibility with callers that hash against deleteblob route
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -47,11 +75,22 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||
(data) => {
|
||||
return respondSuccess(res, { data: data });
|
||||
}
|
||||
);
|
||||
try {
|
||||
const normalizedBlobName = blobNameTrimmed.startsWith(
|
||||
casefolderIDTrimmed + "/"
|
||||
)
|
||||
? blobNameTrimmed
|
||||
: casefolderIDTrimmed + "/files/" + blobNameTrimmed;
|
||||
|
||||
const data = await deleteBlob(containerName, normalizedBlobName);
|
||||
return respondSuccess(res, { data: data });
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "DELETE_AWAITING_SUBMISSION_BLOB_FAILED",
|
||||
message: "Unable to delete awaiting submission blob"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -9,10 +9,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
var blobName = req.query.blobname;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const blobName = req.query.blobname;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -31,15 +31,41 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderID) +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobName);
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
const blobNameTrimmed = blobName.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed),
|
||||
"/api/file/deleteblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -47,11 +73,22 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlob(containerName, casefolderID + "/files/" + blobName).then(
|
||||
(data) => {
|
||||
return respondSuccess(res, { data: data });
|
||||
}
|
||||
);
|
||||
try {
|
||||
const normalizedBlobName = blobNameTrimmed.startsWith(
|
||||
casefolderIDTrimmed + "/"
|
||||
)
|
||||
? blobNameTrimmed
|
||||
: casefolderIDTrimmed + "/files/" + blobNameTrimmed;
|
||||
|
||||
const data = await deleteBlob(containerName, normalizedBlobName);
|
||||
return respondSuccess(res, { data: data });
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "DELETE_BLOB_FAILED",
|
||||
message: "Unable to delete blob"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -9,9 +9,9 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -28,13 +28,24 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/deleteblobcase?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID;
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/deleteblobcase?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed,
|
||||
"/api/file/deleteblobcase?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -42,9 +53,16 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await deleteBlobCase(containerName, casefolderID).then((data) => {
|
||||
try {
|
||||
const data = await deleteBlobCase(containerName, casefolderIDTrimmed);
|
||||
return respondSuccess(res, { data: data });
|
||||
});
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "DELETE_BLOB_CASE_FAILED",
|
||||
message: "Unable to delete blob case"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -9,10 +9,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
var repfile = req.query.repfile;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const repfile = req.query.repfile;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -31,15 +31,41 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/deleteblobrep?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID +
|
||||
"&repfile=" +
|
||||
repfile;
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
const repfileTrimmed = repfile.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/deleteblobrep?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&repfile=" +
|
||||
repfileTrimmed,
|
||||
"/api/file/deleteblobrep?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&repfile=" +
|
||||
repfileTrimmed,
|
||||
"/api/file/deleteblobrep?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed +
|
||||
"&repfile=" +
|
||||
encodeURIComponent(repfileTrimmed),
|
||||
"/api/file/deleteblobrep?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed) +
|
||||
"&repfile=" +
|
||||
encodeURIComponent(repfileTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -47,11 +73,17 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
casefolderID = casefolderID + "/" + repfile;
|
||||
|
||||
await deleteBlobRep(containerName, casefolderID).then((data) => {
|
||||
try {
|
||||
const normalizedRepPath = casefolderIDTrimmed + "/" + repfileTrimmed;
|
||||
const data = await deleteBlobRep(containerName, normalizedRepPath);
|
||||
return respondSuccess(res, { data: data });
|
||||
});
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "DELETE_BLOB_REP_FAILED",
|
||||
message: "Unable to delete blob representation"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -10,10 +10,10 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
var blobName = req.query.blobname;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const blobName = req.query.blobname;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -32,15 +32,39 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
const blobNameTrimmed = blobName.trim();
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/downloadblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID +
|
||||
"&blobname=" +
|
||||
blobName.trim();
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/downloadblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderID) +
|
||||
"&blobname=" +
|
||||
blobNameTrimmed,
|
||||
"/api/file/downloadblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed),
|
||||
"/api/file/downloadblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderID) +
|
||||
"&blobname=" +
|
||||
encodeURIComponent(blobNameTrimmed)
|
||||
];
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -48,34 +72,36 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const bloblocation =
|
||||
casefolderID + (blobName.indexOf(".json") > 0 ? "/" : "/files/");
|
||||
try {
|
||||
const decodedBlobName = decodeURI(blobName);
|
||||
const normalizedBlobName = decodedBlobName.startsWith(
|
||||
casefolderID + "/"
|
||||
)
|
||||
? decodedBlobName
|
||||
: casefolderID +
|
||||
(decodedBlobName.indexOf(".json") > 0 ? "/" : "/files/") +
|
||||
decodedBlobName;
|
||||
|
||||
const downloaded = await downloadFile(
|
||||
containerName,
|
||||
bloblocation + decodeURI(blobName)
|
||||
);
|
||||
const downloaded = await downloadFile(
|
||||
containerName,
|
||||
normalizedBlobName
|
||||
);
|
||||
const responseFileName = decodedBlobName.split("/").pop();
|
||||
|
||||
res.setHeader(
|
||||
"content-disposition",
|
||||
"attachment; filename=" + decodeURI(blobName)
|
||||
);
|
||||
return res.status(200).send(downloaded);
|
||||
res.setHeader(
|
||||
"content-disposition",
|
||||
"attachment; filename=" + responseFileName
|
||||
);
|
||||
return res.status(200).send(downloaded);
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "DOWNLOAD_BLOB_FAILED",
|
||||
message: "Unable to download blob"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function streamToBuffer(readableStream) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
readableStream.on("data", (data) => {
|
||||
chunks.push(data instanceof Buffer ? data : Buffer.from(data));
|
||||
});
|
||||
readableStream.on("end", () => {
|
||||
resolve(Buffer.concat(chunks));
|
||||
});
|
||||
readableStream.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: false
|
||||
|
||||
@@ -73,12 +73,6 @@
|
||||
*/
|
||||
|
||||
import {
|
||||
createContainer,
|
||||
getContainers,
|
||||
getBlobs,
|
||||
createRepBlob,
|
||||
uploadFile,
|
||||
uploadPDFAppealFiles,
|
||||
downloadProgressFile,
|
||||
getProgressBlobs,
|
||||
getTempCaseBlob,
|
||||
@@ -86,23 +80,9 @@ import {
|
||||
} from "../../../actions/azurestorage";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
|
||||
import ReactPDF, {
|
||||
Document,
|
||||
Page,
|
||||
Text,
|
||||
View,
|
||||
StyleSheet,
|
||||
PDFViewer
|
||||
} from "@react-pdf/renderer";
|
||||
import middleware from "../middleware/middleware";
|
||||
import nextConnect from "next-connect";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import ReactPDF from "@react-pdf/renderer";
|
||||
import { getPickLists } from "../../../actions/services/referenceDataService";
|
||||
import { planningappeals78_pdf } from "../../../components/pdftemplates/planningappeals78_pdf";
|
||||
import { finalComments_pdf } from "../../../components/pdftemplates/finalComments_pdf";
|
||||
import { statement_pdf } from "../../../components/pdftemplates/statement_pdf";
|
||||
import { writtenStatement_pdf } from "../../../components/pdftemplates/writtenStatement_pdf";
|
||||
import { other_pdf } from "../../../components/pdftemplates/other_pdf";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
@@ -112,16 +92,15 @@ import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
// ApiProxy.post(async (req, res) => {
|
||||
|
||||
export default async function handler(req, res) {
|
||||
var checkHash = req.query.hash;
|
||||
var appealType = req.query.appealType;
|
||||
var isDownload = req.query?.download === "true";
|
||||
const checkHash = req.query.hash;
|
||||
const appealType = req.query.appealType;
|
||||
const isDownload = req.query?.download === "true";
|
||||
|
||||
let appealBodyObj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
var containerID = appealBodyObj.containerID;
|
||||
var casefolderID = appealBodyObj.casefolderID;
|
||||
var caseRef = appealBodyObj.caseRef;
|
||||
var checkquerypath = "/api/file/generateappealpdf";
|
||||
const containerID = appealBodyObj.containerID;
|
||||
const casefolderID = appealBodyObj.casefolderID;
|
||||
let checkquerypath = "/api/file/generateappealpdf";
|
||||
|
||||
if (
|
||||
typeof checkHash === "undefined" ||
|
||||
@@ -184,92 +163,97 @@ export default async function handler(req, res) {
|
||||
}
|
||||
};
|
||||
|
||||
const tempCaseBlob = await getTempCaseBlob(containerID, casefolderID);
|
||||
const blobProgress = await getProgressBlobs(containerID, casefolderID).then(
|
||||
(data) => {
|
||||
//console.log("Progress blob path:", data);
|
||||
return downloadProgressFile(containerID, data.path, casefolderID);
|
||||
}
|
||||
);
|
||||
try {
|
||||
const tempCaseBlob = await getTempCaseBlob(containerID, casefolderID);
|
||||
const progressBlob = await getProgressBlobs(containerID, casefolderID);
|
||||
const blobProgress = await downloadProgressFile(
|
||||
containerID,
|
||||
progressBlob.path,
|
||||
casefolderID
|
||||
);
|
||||
|
||||
const uniqueArray = appealBodyObj.filesList.filter((obj, index, self) => {
|
||||
// Check if the name has already been encountered
|
||||
return index === self.findIndex((t) => t.name === obj.name);
|
||||
});
|
||||
|
||||
Object.assign(blobProgress, {
|
||||
"filesList": uniqueArray,
|
||||
"caseObj": tempCaseBlob
|
||||
});
|
||||
|
||||
const pickListData = await getPickLists(appealType);
|
||||
|
||||
// ReactPDF.render(
|
||||
// <MyDocument docProps={blobProgress} pickListData={pickListData} />,
|
||||
// process.cwd() + `/tmp/${blobProgress.pinswg_name}.pdf`
|
||||
// );
|
||||
|
||||
// ReactPDF.renderToStream();
|
||||
|
||||
const streamToBuffer = async (readableStream) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
readableStream.on("data", (chunk) => {
|
||||
chunks.push(
|
||||
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
||||
);
|
||||
});
|
||||
readableStream.on("end", () => resolve(Buffer.concat(chunks)));
|
||||
readableStream.on("error", reject);
|
||||
});
|
||||
};
|
||||
|
||||
const renderedPDF = await ReactPDF.renderToStream(
|
||||
<MyDocument docProps={blobProgress} pickListData={pickListData} />
|
||||
);
|
||||
|
||||
const repDate = new Date();
|
||||
|
||||
let day = repDate.getDate();
|
||||
let month = repDate.getMonth() + 1;
|
||||
let year = repDate.getFullYear();
|
||||
|
||||
const pdfFileName =
|
||||
year +
|
||||
"-" +
|
||||
("0" + month).slice(-2) +
|
||||
"-" +
|
||||
("0" + day).slice(-2) +
|
||||
"_-_Appeal_Form";
|
||||
|
||||
const renderedPDFBuffer = await streamToBuffer(renderedPDF);
|
||||
|
||||
await createAppealPDFBlob(
|
||||
renderedPDFBuffer,
|
||||
containerID,
|
||||
blobProgress.pinswg_name || blobProgress.caseObj.ticketnumber
|
||||
)
|
||||
.then((data) => {
|
||||
if (isDownload) {
|
||||
const filename = `${pdfFileName}.pdf`;
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="${filename}"`
|
||||
);
|
||||
return res.status(200).send(renderedPDFBuffer);
|
||||
const uniqueArray = appealBodyObj.filesList.filter(
|
||||
(obj, index, self) => {
|
||||
return index === self.findIndex((t) => t.name === obj.name);
|
||||
}
|
||||
return respondSuccess(res, {
|
||||
status: "success",
|
||||
data: data,
|
||||
path: `/files/${pdfFileName}.pdf`
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GENERATE_APPEAL_PDF_FAILED",
|
||||
message: "Failed to generate appeal PDF"
|
||||
});
|
||||
);
|
||||
|
||||
Object.assign(blobProgress, {
|
||||
"filesList": uniqueArray,
|
||||
"caseObj": tempCaseBlob
|
||||
});
|
||||
|
||||
const pickListData = await getPickLists(appealType);
|
||||
|
||||
// ReactPDF.render(
|
||||
// <MyDocument docProps={blobProgress} pickListData={pickListData} />,
|
||||
// process.cwd() + `/tmp/${blobProgress.pinswg_name}.pdf`
|
||||
// );
|
||||
|
||||
// ReactPDF.renderToStream();
|
||||
|
||||
const streamToBuffer = async (readableStream) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const chunks = [];
|
||||
readableStream.on("data", (chunk) => {
|
||||
chunks.push(
|
||||
Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
|
||||
);
|
||||
});
|
||||
readableStream.on("end", () => resolve(Buffer.concat(chunks)));
|
||||
readableStream.on("error", reject);
|
||||
});
|
||||
};
|
||||
|
||||
const renderedPDF = await ReactPDF.renderToStream(
|
||||
MyDocument({
|
||||
docProps: blobProgress,
|
||||
pickListData: pickListData
|
||||
})
|
||||
);
|
||||
|
||||
const repDate = new Date();
|
||||
|
||||
let day = repDate.getDate();
|
||||
let month = repDate.getMonth() + 1;
|
||||
let year = repDate.getFullYear();
|
||||
|
||||
const pdfFileName =
|
||||
year +
|
||||
"-" +
|
||||
("0" + month).slice(-2) +
|
||||
"-" +
|
||||
("0" + day).slice(-2) +
|
||||
"_-_Appeal_Form";
|
||||
|
||||
const renderedPDFBuffer = await streamToBuffer(renderedPDF);
|
||||
|
||||
const data = await createAppealPDFBlob(
|
||||
renderedPDFBuffer,
|
||||
containerID,
|
||||
blobProgress.pinswg_name || blobProgress.caseObj.ticketnumber
|
||||
);
|
||||
|
||||
if (isDownload) {
|
||||
const filename = `${pdfFileName}.pdf`;
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="${filename}"`
|
||||
);
|
||||
return res.status(200).send(renderedPDFBuffer);
|
||||
}
|
||||
|
||||
return respondSuccess(res, {
|
||||
status: "success",
|
||||
data: data,
|
||||
path: `/files/${pdfFileName}.pdf`
|
||||
});
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GENERATE_APPEAL_PDF_FAILED",
|
||||
message: "Failed to generate appeal PDF"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,45 +1,17 @@
|
||||
import {
|
||||
createContainer,
|
||||
getContainers,
|
||||
getBlobs,
|
||||
createRepBlob,
|
||||
uploadFile,
|
||||
uploadPDFAppealFiles,
|
||||
downloadProgressFile,
|
||||
getProgressBlobs,
|
||||
getTempCaseBlob,
|
||||
createAppealPDFBlob
|
||||
} from "../../../actions/azurestorage";
|
||||
import {
|
||||
getCaseByID,
|
||||
getPortalModuleDetails,
|
||||
getAppealPDFDocs
|
||||
} from "../../../actions/services/caseService";
|
||||
import { getFormCollectionByID } from "../../../components/utils";
|
||||
import ReactPDF, {
|
||||
Document,
|
||||
Page,
|
||||
Text,
|
||||
View,
|
||||
StyleSheet,
|
||||
PDFViewer,
|
||||
pdf
|
||||
} from "@react-pdf/renderer";
|
||||
|
||||
import middleware from "../middleware/middleware";
|
||||
import nextConnect from "next-connect";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { pdf } from "@react-pdf/renderer";
|
||||
import { getPickLists } from "../../../actions/services/referenceDataService";
|
||||
import { planningappeals78_pdf } from "../../../components/pdftemplates/planningappeals78_pdf";
|
||||
import { finalComments_pdf } from "../../../components/pdftemplates/finalComments_pdf";
|
||||
import { statement_pdf } from "../../../components/pdftemplates/statement_pdf";
|
||||
import { writtenStatement_pdf } from "../../../components/pdftemplates/writtenStatement_pdf";
|
||||
import { other_pdf } from "../../../components/pdftemplates/other_pdf";
|
||||
import { respondError } from "../middleware/apiResponse";
|
||||
|
||||
const getDetails = (resultsObj, detailsType) => {
|
||||
const getDetails = (resultsObj) => {
|
||||
if (!Array.isArray(resultsObj)) {
|
||||
console.warn("getDetails: resultsObj is not an array");
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
@@ -51,7 +23,6 @@ const getDetails = (resultsObj, detailsType) => {
|
||||
|
||||
const form = getFormCollectionByID(appealType);
|
||||
if (!form) {
|
||||
console.warn("No form found for appeal type:", appealType);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -64,54 +35,74 @@ const getDetails = (resultsObj, detailsType) => {
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const incidentid = req.body?.docProps;
|
||||
if (!incidentid) return res.status(400).send("Missing incident ID");
|
||||
|
||||
const caseObj = await getCaseByID(incidentid);
|
||||
if (!Array.isArray(caseObj) || caseObj.length === 0)
|
||||
return res.status(404).send("Case not found");
|
||||
|
||||
const caseDetailsObj = await getDetails(caseObj, "myCases");
|
||||
const fileList = (await getAppealPDFDocs(incidentid)) || [];
|
||||
|
||||
// Inject into the first item in `value` array
|
||||
|
||||
if (caseDetailsObj[0]?.value?.[0]) {
|
||||
caseDetailsObj[0].value[0].pinswg_developmentdescription =
|
||||
caseObj[0].description;
|
||||
caseDetailsObj[0].value[0].caseObj = caseObj[0];
|
||||
caseDetailsObj[0].value[0].filesList = fileList;
|
||||
if (!incidentid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INCIDENT_ID_REQUIRED",
|
||||
message: "Incident id is required"
|
||||
});
|
||||
}
|
||||
|
||||
const whichForm = getFormCollectionByID(caseObj[0].pinswg_appealcasetype);
|
||||
const pickListData = await getPickLists(whichForm.UrlName);
|
||||
try {
|
||||
const caseObj = await getCaseByID(incidentid);
|
||||
if (!Array.isArray(caseObj) || caseObj.length === 0) {
|
||||
return respondError(res, {
|
||||
status: 404,
|
||||
code: "CASE_NOT_FOUND",
|
||||
message: "Case not found"
|
||||
});
|
||||
}
|
||||
|
||||
const appealTypeMap = {
|
||||
846040000: planningappeals78_pdf,
|
||||
846040004: planningappeals78_pdf
|
||||
};
|
||||
const caseDetailsObj = await getDetails(caseObj);
|
||||
const fileList = (await getAppealPDFDocs(incidentid)) || [];
|
||||
|
||||
//console.log(caseDetailsObj[0].value[0]);
|
||||
if (caseDetailsObj[0]?.value?.[0]) {
|
||||
caseDetailsObj[0].value[0].pinswg_developmentdescription =
|
||||
caseObj[0].description;
|
||||
caseDetailsObj[0].value[0].caseObj = caseObj[0];
|
||||
caseDetailsObj[0].value[0].filesList = fileList;
|
||||
}
|
||||
|
||||
const MyDocument = (values) => {
|
||||
const type = caseObj[0].pinswg_appealcasetype;
|
||||
const renderDocument = appealTypeMap[type] || other_pdf;
|
||||
return renderDocument(values);
|
||||
};
|
||||
const whichForm = getFormCollectionByID(
|
||||
caseObj[0].pinswg_appealcasetype
|
||||
);
|
||||
if (!whichForm?.UrlName) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "FORM_COLLECTION_NOT_FOUND",
|
||||
message: "Unable to resolve form collection"
|
||||
});
|
||||
}
|
||||
const pickListData = await getPickLists(whichForm.UrlName);
|
||||
|
||||
const pdfComponent = MyDocument({
|
||||
docProps: caseDetailsObj[0].value[0],
|
||||
pickListData
|
||||
});
|
||||
const appealTypeMap = {
|
||||
846040000: planningappeals78_pdf,
|
||||
846040004: planningappeals78_pdf
|
||||
};
|
||||
|
||||
const pdfBuffer = await pdf(pdfComponent).toBuffer();
|
||||
const renderDocument =
|
||||
appealTypeMap[caseObj[0].pinswg_appealcasetype] || other_pdf;
|
||||
const pdfComponent = renderDocument({
|
||||
docProps: caseDetailsObj?.[0]?.value?.[0] || {},
|
||||
pickListData
|
||||
});
|
||||
|
||||
const repDate = new Date();
|
||||
const formattedDate = repDate.toISOString().split("T")[0];
|
||||
const pdfBuffer = await pdf(pdfComponent).toBuffer();
|
||||
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename=${formattedDate}_-_Appeal_Form.pdf`
|
||||
);
|
||||
res.send(pdfBuffer);
|
||||
const repDate = new Date();
|
||||
const formattedDate = repDate.toISOString().split("T")[0];
|
||||
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename=${formattedDate}_-_Appeal_Form.pdf`
|
||||
);
|
||||
return res.send(pdfBuffer);
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "APPEAL_PDF_COPY_GENERATION_FAILED",
|
||||
message: "Unable to generate appeal PDF copy"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+39
-122
@@ -72,28 +72,10 @@
|
||||
* description: Success
|
||||
*/
|
||||
|
||||
import {
|
||||
createContainer,
|
||||
getContainers,
|
||||
getBlobs,
|
||||
createRepBlob,
|
||||
uploadFile,
|
||||
uploadPDFRepFiles,
|
||||
createRepPDFBlob
|
||||
} from "../../../actions/azurestorage";
|
||||
import { createRepPDFBlob } from "../../../actions/azurestorage";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
|
||||
import ReactPDF, {
|
||||
Document,
|
||||
Page,
|
||||
Text,
|
||||
View,
|
||||
StyleSheet,
|
||||
PDFViewer
|
||||
} from "@react-pdf/renderer";
|
||||
import middleware from "../middleware/middleware";
|
||||
import nextConnect from "next-connect";
|
||||
import fs from "fs";
|
||||
import ReactPDF from "@react-pdf/renderer";
|
||||
import { EnforcementQuestionnaire_pdf } from "../../../components/pdftemplates/enforcement_pdf";
|
||||
import { lpaQuestionnaire_pdf } from "../../../components/pdftemplates/lpaQuestionnaire_pdf";
|
||||
import { finalComments_pdf } from "../../../components/pdftemplates/finalComments_pdf";
|
||||
@@ -109,7 +91,7 @@ import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
const shouldDownload = req.query.download === "true";
|
||||
var checkHash = req.query.hash;
|
||||
const checkHash = req.query.hash;
|
||||
let reqBodyobj =
|
||||
typeof req.body === "string" ? JSON.parse(req.body) : req.body;
|
||||
|
||||
@@ -169,15 +151,11 @@ export default async function handler(req, res) {
|
||||
// "\n//////////////////////\n"
|
||||
// );
|
||||
|
||||
var containerID = reqBodyobj.containerID;
|
||||
var casefolderID = reqBodyobj.casefolderID;
|
||||
var caseRef = reqBodyobj.ticketnumber || reqBodyobj.caseRef;
|
||||
var representationType = reqBodyobj.representationType;
|
||||
var repRaiser = reqBodyobj.lastname;
|
||||
var localeSelect = reqBodyobj.locale;
|
||||
const containerID = reqBodyobj.containerID;
|
||||
const caseRef = reqBodyobj.ticketnumber || reqBodyobj.caseRef;
|
||||
//console.log("there are files:", Object.keys(req.files).length);
|
||||
|
||||
var checkquerypath = "/api/file/generatepdf";
|
||||
let checkquerypath = "/api/file/generatepdf";
|
||||
|
||||
if (shouldDownload) {
|
||||
checkquerypath += "?download=true";
|
||||
@@ -247,67 +225,6 @@ export default async function handler(req, res) {
|
||||
}
|
||||
};
|
||||
|
||||
let repCapacity = "";
|
||||
switch (reqBodyobj.representationCapacity) {
|
||||
case "Appellant":
|
||||
repCapacity = "APP";
|
||||
break;
|
||||
case "Agent":
|
||||
repCapacity = "AGENT";
|
||||
break;
|
||||
case "Interested Party/Person":
|
||||
repCapacity = "IP";
|
||||
break;
|
||||
case "Land Owner":
|
||||
repCapacity = "LO";
|
||||
break;
|
||||
case "lpa":
|
||||
case "LPA":
|
||||
repCapacity = "LPA";
|
||||
break;
|
||||
|
||||
default:
|
||||
// code block
|
||||
}
|
||||
|
||||
let repType = "";
|
||||
switch (reqBodyobj.representationType) {
|
||||
case "Statement":
|
||||
repType = "Statement";
|
||||
break;
|
||||
case "Statement of common ground":
|
||||
repType = "SCG";
|
||||
break;
|
||||
case "Written statement":
|
||||
repType = "WS";
|
||||
break;
|
||||
case "Written statement of evidence":
|
||||
repType = "WSE";
|
||||
break;
|
||||
case "Questionnaire":
|
||||
repType = "Questionnaire";
|
||||
break;
|
||||
case "Final comments":
|
||||
repType = "Comments";
|
||||
break;
|
||||
case "Local Impact Report":
|
||||
repType = "Impact";
|
||||
break;
|
||||
case "Consultation Response":
|
||||
repType = "Consultation_Response";
|
||||
break;
|
||||
case "Marine Impact Report":
|
||||
repType = "Marine_Impact";
|
||||
break;
|
||||
|
||||
case "Other":
|
||||
repType = "OTHER";
|
||||
break;
|
||||
|
||||
default:
|
||||
// code block
|
||||
}
|
||||
|
||||
//console.log(
|
||||
// "///////////////////////////////////\n file created: \n" +
|
||||
// process.cwd() +
|
||||
@@ -336,40 +253,40 @@ export default async function handler(req, res) {
|
||||
});
|
||||
};
|
||||
|
||||
const renderedPDFStream = await ReactPDF.renderToStream(
|
||||
<MyDocument docProps={reqBodyobj} />
|
||||
);
|
||||
const renderedPDFBuffer = await streamToBuffer(renderedPDFStream);
|
||||
try {
|
||||
const renderedPDFStream = await ReactPDF.renderToStream(
|
||||
MyDocument({ docProps: reqBodyobj })
|
||||
);
|
||||
const renderedPDFBuffer = await streamToBuffer(renderedPDFStream);
|
||||
|
||||
await createRepPDFBlob(
|
||||
renderedPDFBuffer,
|
||||
containerID,
|
||||
caseRef,
|
||||
reqBodyobj.repfile_name
|
||||
)
|
||||
.then((data) => {
|
||||
if (shouldDownload) {
|
||||
const filename = `${reqBodyobj.repfile_name || "questionnaire"}.pdf`;
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="${filename}"`
|
||||
);
|
||||
return res.status(200).send(renderedPDFBuffer);
|
||||
}
|
||||
const data = await createRepPDFBlob(
|
||||
renderedPDFBuffer,
|
||||
containerID,
|
||||
caseRef,
|
||||
reqBodyobj.repfile_name
|
||||
);
|
||||
|
||||
return respondSuccess(res, {
|
||||
status: "success",
|
||||
data: data,
|
||||
path: `${caseRef}/${reqBodyobj.repfile_name}/files/${reqBodyobj.repfile_name}.pdf`
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GENERATE_PDF_FAILED",
|
||||
message: "Failed to generate PDF"
|
||||
});
|
||||
if (shouldDownload) {
|
||||
const filename = `${reqBodyobj.repfile_name || "questionnaire"}.pdf`;
|
||||
res.setHeader("Content-Type", "application/pdf");
|
||||
res.setHeader(
|
||||
"Content-Disposition",
|
||||
`attachment; filename="${filename}"`
|
||||
);
|
||||
return res.status(200).send(renderedPDFBuffer);
|
||||
}
|
||||
|
||||
return respondSuccess(res, {
|
||||
status: "success",
|
||||
data: data,
|
||||
path: `${caseRef}/${reqBodyobj.repfile_name}/files/${reqBodyobj.repfile_name}.pdf`
|
||||
});
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GENERATE_PDF_FAILED",
|
||||
message: "Failed to generate PDF"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import {
|
||||
downloadAllProgressFiles,
|
||||
getAllProgressBlobs
|
||||
} from "../../../actions/azurestorage";
|
||||
import _ from "lodash";
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
@@ -12,8 +11,8 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -28,10 +27,17 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/getawaitingsubmissionfromblob?container=" + containerName;
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/getawaitingsubmissionfromblob?container=" + containerName,
|
||||
"/api/file/getawaitingsubmissionfromblob?container=" +
|
||||
encodeURIComponent(containerName)
|
||||
];
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -39,13 +45,20 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const blobObj = await getAllProgressBlobs(containerName)
|
||||
.then((data) => {
|
||||
return downloadAllProgressFiles(containerName, data);
|
||||
})
|
||||
.then((data) => {
|
||||
return respondSuccess(res, data);
|
||||
try {
|
||||
const progressBlobs = await getAllProgressBlobs(containerName);
|
||||
const data = await downloadAllProgressFiles(
|
||||
containerName,
|
||||
progressBlobs
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_AWAITING_SUBMISSION_BLOB_FAILED",
|
||||
message: "Failed to retrieve awaiting submission blobs"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -11,7 +11,7 @@ const hasValue = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0;
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var containerName = req.query.container;
|
||||
const containerName = req.query.container;
|
||||
|
||||
if (!hasValue(containerName)) {
|
||||
return respondError(res, {
|
||||
@@ -21,25 +21,23 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"/api/file/getawaitingsubmissionfromblob?container=" + containerName;
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_AWAITING_SUBMISSION_PROXY_FAILED",
|
||||
message: "Failed to fetch awaiting submission blob"
|
||||
});
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_AWAITING_SUBMISSION_PROXY_FAILED",
|
||||
message: "Failed to fetch awaiting submission blob"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,10 +40,9 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -60,13 +59,24 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/getbloblist?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID;
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/getbloblist?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed,
|
||||
"/api/file/getbloblist?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -74,16 +84,24 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const data =
|
||||
casefolderID.split("/").length > 1
|
||||
? await getRepsFilesBlobs(
|
||||
containerName,
|
||||
casefolderID.split("/")[0],
|
||||
casefolderID.split("/")[1]
|
||||
)
|
||||
: await getBlobs(containerName, casefolderID);
|
||||
try {
|
||||
const data =
|
||||
casefolderIDTrimmed.split("/").length > 1
|
||||
? await getRepsFilesBlobs(
|
||||
containerName,
|
||||
casefolderIDTrimmed.split("/")[0],
|
||||
casefolderIDTrimmed.split("/")[1]
|
||||
)
|
||||
: await getBlobs(containerName, casefolderIDTrimmed);
|
||||
|
||||
return respondSuccess(res, { "value": [data] });
|
||||
return respondSuccess(res, { "value": [data] });
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_BLOB_LIST_FAILED",
|
||||
message: "Failed to retrieve blob list"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -11,8 +11,8 @@ const hasValue = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0;
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
|
||||
if (!hasValue(containerName) || !hasValue(casefolderID)) {
|
||||
return respondError(res, {
|
||||
@@ -22,28 +22,26 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
const queryUrl =
|
||||
"/api/file/getbloblist?container=" +
|
||||
containerName +
|
||||
encodeURIComponent(containerName) +
|
||||
"&casefolderID=" +
|
||||
casefolderID;
|
||||
encodeURIComponent(casefolderID);
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_BLOB_LIST_PROXY_FAILED",
|
||||
message: "Failed to fetch blob list"
|
||||
});
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_BLOB_LIST_PROXY_FAILED",
|
||||
message: "Failed to fetch blob list"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,9 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const casefolderID = req.query.casefolderID;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -32,13 +31,24 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
"/api/file/getprogressobjblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderID;
|
||||
const casefolderIDTrimmed = casefolderID.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/getprogressobjblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
casefolderIDTrimmed,
|
||||
"/api/file/getprogressobjblob?container=" +
|
||||
containerName +
|
||||
"&casefolderID=" +
|
||||
encodeURIComponent(casefolderIDTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -46,13 +56,24 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await getProgressBlobs(containerName, casefolderID)
|
||||
.then((data) => {
|
||||
return downloadProgressFile(containerName, data.path, casefolderID);
|
||||
})
|
||||
.then((data) => {
|
||||
return respondSuccess(res, data);
|
||||
try {
|
||||
const progressBlob = await getProgressBlobs(
|
||||
containerName,
|
||||
casefolderIDTrimmed
|
||||
);
|
||||
const data = await downloadProgressFile(
|
||||
containerName,
|
||||
progressBlob.path,
|
||||
casefolderIDTrimmed
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_PROGRESS_OBJ_BLOB_FAILED",
|
||||
message: "Failed to retrieve progress blob"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -33,7 +33,6 @@ import {
|
||||
downloadAllRepsFiles,
|
||||
getRepsBlobs
|
||||
} from "../../../actions/azurestorage";
|
||||
import _ from "lodash";
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
@@ -44,10 +43,8 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.container;
|
||||
var casefolderID = req.query.casefolderID;
|
||||
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.container;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -62,9 +59,16 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath = "/api/file/getrepsblob?container=" + containerName;
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/getrepsblob?container=" + containerName,
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(containerName)
|
||||
];
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -72,23 +76,19 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
const blobObj = await getRepsBlobs(containerName)
|
||||
.then(async (data) => {
|
||||
return data;
|
||||
})
|
||||
.then(async (data) => {
|
||||
let result = await downloadAllRepsFiles(containerName, data);
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
return respondSuccess(res, result);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_FAILED",
|
||||
message: "Failed to retrieve representation blobs"
|
||||
});
|
||||
try {
|
||||
const repsBlobs = await getRepsBlobs(containerName);
|
||||
const result = await downloadAllRepsFiles(containerName, repsBlobs);
|
||||
res.setHeader("Cache-Control", "no-store");
|
||||
return respondSuccess(res, result);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_FAILED",
|
||||
message: "Failed to retrieve representation blobs"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default ApiProxy;
|
||||
|
||||
@@ -11,7 +11,7 @@ const hasValue = (value) =>
|
||||
typeof value === "string" && value.trim().length > 0;
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var containerName = req.query.container;
|
||||
const containerName = req.query.container;
|
||||
|
||||
if (!hasValue(containerName)) {
|
||||
return respondError(res, {
|
||||
@@ -21,24 +21,23 @@ export default async function ApiProxy(req, res) {
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
const token = await getToken();
|
||||
|
||||
var queryUrl = "/api/file/getrepsblob?container=" + containerName;
|
||||
const queryUrl =
|
||||
"/api/file/getrepsblob?container=" + encodeURIComponent(containerName);
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const { data } = await axios.get(
|
||||
BASE_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_PROXY_FAILED",
|
||||
message: "Failed to fetch representation blobs"
|
||||
});
|
||||
);
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "GET_REPS_BLOB_PROXY_FAILED",
|
||||
message: "Failed to fetch representation blobs"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import {
|
||||
createContainer,
|
||||
createContainerSas,
|
||||
getContainers,
|
||||
getBlobs,
|
||||
uploadFile
|
||||
} from "../../../actions/azurestorage";
|
||||
import { createContainer } from "../../../actions/azurestorage";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
@@ -16,8 +10,8 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.get(async (req, res) => {
|
||||
var containerName = req.query.ident;
|
||||
var checkHash = req.query.hash;
|
||||
const containerName = req.query.ident;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (
|
||||
typeof containerName === "undefined" ||
|
||||
@@ -32,9 +26,19 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath = "/api/file/setupcontainer?ident=" + containerName;
|
||||
const containerNameTrimmed = containerName.trim();
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/setupcontainer?ident=" + containerNameTrimmed,
|
||||
"/api/file/setupcontainer?ident=" +
|
||||
encodeURIComponent(containerNameTrimmed)
|
||||
];
|
||||
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "&hash=" + checkHash
|
||||
);
|
||||
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -42,18 +46,17 @@ ApiProxy.get(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
await createContainer(containerName)
|
||||
.then((data) => {
|
||||
return respondSuccess(res, { data: "success", output: data });
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "SETUP_CONTAINER_FAILED",
|
||||
message: "Failed to setup container"
|
||||
});
|
||||
try {
|
||||
const data = await createContainer(containerNameTrimmed);
|
||||
return respondSuccess(res, { data: "success", output: data });
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "SETUP_CONTAINER_FAILED",
|
||||
message: "Failed to setup container"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
@@ -10,12 +9,12 @@ const WEBAPI_URL =
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var data = JSON.stringify(req.body);
|
||||
var appealObj = req.query.appealObj;
|
||||
var incidentID = req.query.incident;
|
||||
const data = JSON.stringify(req.body);
|
||||
const appealObj = req.query.appealObj;
|
||||
const incidentID = req.query.incident;
|
||||
|
||||
var updateFormCollection = req.query.updateFormCollection;
|
||||
var queryUrl = updateFormCollection + "(" + appealObj + ")";
|
||||
const updateFormCollection = req.query.updateFormCollection;
|
||||
const queryUrl = updateFormCollection + "(" + appealObj + ")";
|
||||
|
||||
if (
|
||||
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",
|
||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
headers: {
|
||||
@@ -53,16 +52,15 @@ export default async function ApiProxy(req, res) {
|
||||
|
||||
//console.log("update case:", data);
|
||||
|
||||
return axios(config)
|
||||
.then(({ data }) => {
|
||||
return respondSuccess(res, data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "UPDATE_CASE_FAILED",
|
||||
message: "Failed to update case"
|
||||
});
|
||||
try {
|
||||
const { data: responseData } = await axios(config);
|
||||
return respondSuccess(res, responseData);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "UPDATE_CASE_FAILED",
|
||||
message: "Failed to update case"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+18
-21
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
createBlob,
|
||||
createRepBlob,
|
||||
uploadFile
|
||||
} from "../../../actions/azurestorage";
|
||||
import { createBlob, createRepBlob } from "../../../actions/azurestorage";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
@@ -13,7 +9,7 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.post(async (req, res) => {
|
||||
var checkHash = req.query.hash;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (typeof checkHash === "undefined" || checkHash.length === 0) {
|
||||
return respondError(res, {
|
||||
@@ -23,9 +19,12 @@ ApiProxy.post(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath = "/api/file/upload";
|
||||
const hashCandidatePaths = ["/api/file/upload", "/api/file/upload?"];
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "?hash=" + checkHash
|
||||
);
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "?hash=" + checkHash) {
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -51,21 +50,19 @@ ApiProxy.post(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
repOrAppeal
|
||||
try {
|
||||
const data = await (repOrAppeal
|
||||
? createRepBlob(appealData, containerID, casefolderID)
|
||||
: createBlob(appealData, containerID, casefolderID)
|
||||
)
|
||||
.then((data) => {
|
||||
return respondSuccess(res, { data });
|
||||
})
|
||||
.catch(() => {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "UPLOAD_FAILED",
|
||||
message: "Failed to upload blob"
|
||||
});
|
||||
: createBlob(appealData, containerID, casefolderID));
|
||||
|
||||
return respondSuccess(res, { data });
|
||||
} catch {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "UPLOAD_FAILED",
|
||||
message: "Failed to upload blob"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export const config = {
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {
|
||||
createBlob,
|
||||
createRepBlob,
|
||||
uploadSingleFile
|
||||
} from "../../../actions/azurestorage";
|
||||
import { uploadSingleFile } from "../../../actions/azurestorage";
|
||||
|
||||
import nextConnect from "next-connect";
|
||||
import middleware from "../middleware/middleware";
|
||||
@@ -37,7 +33,7 @@ const ApiProxy = nextConnect();
|
||||
ApiProxy.use(middleware);
|
||||
|
||||
ApiProxy.post(async (req, res) => {
|
||||
var checkHash = req.query.hash;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (typeof checkHash === "undefined" || checkHash.length === 0) {
|
||||
return respondError(res, {
|
||||
@@ -47,9 +43,15 @@ ApiProxy.post(async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath = "/api/file/uploadsinglefile";
|
||||
const hashCandidatePaths = [
|
||||
"/api/file/uploadsinglefile",
|
||||
"/api/file/uploadsinglefile?"
|
||||
];
|
||||
const isHashValid = hashCandidatePaths.some(
|
||||
(candidatePath) => hashAPIPath(candidatePath) == "?hash=" + checkHash
|
||||
);
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "?hash=" + checkHash) {
|
||||
if (!isHashValid) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
@@ -87,8 +89,8 @@ ApiProxy.post(async (req, res) => {
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
];
|
||||
|
||||
var allowedFilesFormData = {};
|
||||
var invalidFiles = []; // To store the names of invalid files
|
||||
const allowedFilesFormData = {};
|
||||
const invalidFiles = []; // To store the names of invalid files
|
||||
|
||||
for (const [fileName, fileDetails] of Object.entries(uploadedFiles)) {
|
||||
const file = fileDetails[0];
|
||||
|
||||
@@ -2,12 +2,14 @@ const runHelperTests = require("./api-response-helper.test.cjs");
|
||||
const runHandlerTests = require("./file-handler-contract.test.cjs");
|
||||
const runEmailHandlerTests = require("./email-handler-contract.test.cjs");
|
||||
const runEndpointHandlerTests = require("./endpoint-handler-contract.test.cjs");
|
||||
const runDocumentsHandlerTests = require("./documents-handler-contract.test.cjs");
|
||||
|
||||
const run = async () => {
|
||||
await runHelperTests();
|
||||
await runHandlerTests();
|
||||
await runEmailHandlerTests();
|
||||
await runEndpointHandlerTests();
|
||||
await runDocumentsHandlerTests();
|
||||
console.log("Phase 21 combined suite passed.");
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,134 @@
|
||||
const assert = require("assert");
|
||||
const { Readable } = require("stream");
|
||||
const { loadModule } = require("./_shared.cjs");
|
||||
|
||||
const tests = [];
|
||||
const test = (name, fn) => tests.push({ name, fn });
|
||||
|
||||
const createRedirectRes = () => {
|
||||
const state = {
|
||||
redirectedTo: null,
|
||||
headers: {},
|
||||
piped: false,
|
||||
headersSent: false
|
||||
};
|
||||
|
||||
return {
|
||||
state,
|
||||
get headersSent() {
|
||||
return state.headersSent;
|
||||
},
|
||||
setHeader(key, value) {
|
||||
state.headers[key.toLowerCase()] = value;
|
||||
},
|
||||
redirect(path) {
|
||||
state.redirectedTo = path;
|
||||
state.headersSent = true;
|
||||
return path;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
test("documents download redirects when id/hash query is missing", async () => {
|
||||
const mod = loadModule("pages/api/documents/download/[id].js", {
|
||||
getToken: async () => ({ access_token: "tok" }),
|
||||
consoleLogger: () => {},
|
||||
axios: { get: async () => ({}) }
|
||||
});
|
||||
|
||||
const req = { query: { id: "DOC-1" } };
|
||||
const res = createRedirectRes();
|
||||
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.redirectedTo, "/filenotavailable");
|
||||
});
|
||||
|
||||
test("documents download success sets attachment headers and pipes stream", async () => {
|
||||
const stream = new Readable({
|
||||
read() {}
|
||||
});
|
||||
|
||||
const mod = loadModule("pages/api/documents/download/[id].js", {
|
||||
setTimeout: (fn) => {
|
||||
fn();
|
||||
return 0;
|
||||
},
|
||||
getToken: async () => ({ access_token: "tok" }),
|
||||
consoleLogger: () => {},
|
||||
axios: {
|
||||
get: async () => ({
|
||||
headers: {
|
||||
"content-disposition": "attachment; filename=test-file.pdf"
|
||||
},
|
||||
data: stream
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
const req = { query: { id: "DOC-1", hash: "h1" } };
|
||||
const res = createRedirectRes();
|
||||
|
||||
stream.pipe = (target) => {
|
||||
target.state.piped = true;
|
||||
target.state.headersSent = true;
|
||||
return target;
|
||||
};
|
||||
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(
|
||||
res.state.headers["content-disposition"],
|
||||
"attachment; filename=test-file.pdf"
|
||||
);
|
||||
assert.strictEqual(
|
||||
res.state.headers["content-type"],
|
||||
"application/octet-stream"
|
||||
);
|
||||
assert.strictEqual(res.state.piped, true);
|
||||
assert.strictEqual(res.state.redirectedTo, null);
|
||||
});
|
||||
|
||||
test("documents download redirects when relay request fails", async () => {
|
||||
const mod = loadModule("pages/api/documents/download/[id].js", {
|
||||
setTimeout: (fn) => {
|
||||
fn();
|
||||
return 0;
|
||||
},
|
||||
getToken: async () => ({ access_token: "tok" }),
|
||||
consoleLogger: () => {},
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const req = { query: { id: "DOC-1", hash: "h1" } };
|
||||
const res = createRedirectRes();
|
||||
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.redirectedTo, "/filenotavailable");
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
for (const currentTest of tests) {
|
||||
await currentTest.fn();
|
||||
passed += 1;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`Phase 21 documents-handler contract tests passed (${passed}/${tests.length}).`
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = run;
|
||||
|
||||
if (require.main === module) {
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
@@ -323,6 +323,93 @@ test("getportallogin catch path returns PORTAL_LOGIN_FETCH_FAILED", async () =>
|
||||
);
|
||||
});
|
||||
|
||||
test("getportallogin accepts encoded email hash variant", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportallogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: (input) =>
|
||||
input && input.includes("emailAddress=user%2Btest%40local")
|
||||
? "&hash=expected"
|
||||
: "&hash=other",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => ({ data: { value: [{ contactid: "c1" }] } })
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: { emailAddress: "user+test@local", hash: "expected" }
|
||||
};
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||
value: [{ contactid: "c1" }]
|
||||
});
|
||||
});
|
||||
|
||||
test("getnewappeals catch path returns ADMIN_NEW_APPEALS_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/admin/getnewappeals_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPagedCustom: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("admin new appeals failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {},
|
||||
_: { has: () => false }
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"ADMIN_NEW_APPEALS_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getlatestdocuments catch path returns ADMIN_LATEST_DOCS_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/admin/getlatestdocuments_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPagedCustom: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("latest docs failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {},
|
||||
process: { env: { HASHKEY: "abc" } },
|
||||
CryptoJS: {
|
||||
HmacSHA256: () => ({ toString: () => "hash" }),
|
||||
enc: { Hex: { parse: () => "parsed" } }
|
||||
},
|
||||
_: { has: () => false }
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"ADMIN_LATEST_DOCS_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getportalloginproxy catch path returns PORTAL_LOGIN_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportalloginproxy_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
@@ -3688,6 +3775,37 @@ test("getsipsmedia returns success contract", async () => {
|
||||
assert.strictEqual(res.state.jsonBody["@odata.count"], 4);
|
||||
});
|
||||
|
||||
test("getsipsmedia returns METHOD_NOT_ALLOWED for non-GET methods", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getsipsmedia_api.js", {
|
||||
respondSuccess: respondSuccessMock,
|
||||
respondError: respondErrorMock
|
||||
});
|
||||
|
||||
const req = { method: "POST", query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 405);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "METHOD_NOT_ALLOWED");
|
||||
});
|
||||
|
||||
test("getappealtypesfornewappeal returns METHOD_NOT_ALLOWED for non-GET methods", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getappealtypesfornewappeal_api.js",
|
||||
{
|
||||
respondSuccess: respondSuccessMock,
|
||||
respondError: respondErrorMock
|
||||
}
|
||||
);
|
||||
|
||||
const req = { method: "POST", query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 405);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "METHOD_NOT_ALLOWED");
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
for (const currentTest of tests) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user