@@ -61,3 +61,7 @@ context/
|
||||
memory-bank/
|
||||
workflows/
|
||||
AI_CONTEXT.md
|
||||
memory-bank/activeContext.md
|
||||
memory-bank/change-log.md
|
||||
memory-bank/refactor-plan-actions-index.md
|
||||
memory-bank/progress.md
|
||||
|
||||
@@ -1,648 +0,0 @@
|
||||
# Active Context — PEDW FrontEnd
|
||||
|
||||
## Current development focus (from recent commits)
|
||||
|
||||
- API contract consistency rollout (TASK22102), starting with helper-based response normalization in selected email/admin handlers.
|
||||
- Search/case navigation correctness, especially breadcrumb and back-link behavior.
|
||||
- My Portal "view all" and DNS application path handling.
|
||||
- Welsh/English email behavior for specific notification templates.
|
||||
- PDF output formatting and hyperlink behavior.
|
||||
|
||||
## Recently changed areas (high signal)
|
||||
|
||||
- `components/breadcrumbs.js`
|
||||
- `components/case/summary.js`
|
||||
- `components/search/searchresults.js`
|
||||
- `components/search/addresssearchresults.js`
|
||||
- `components/myportal/viewall.js`
|
||||
- `pages/myportal/case/[ticketnumber].js`
|
||||
- `pages/api/email/notify.js`
|
||||
- `actions/index.js`
|
||||
- `actions/core/env.js`
|
||||
- `actions/core/logger.js`
|
||||
- `actions/core/hash.js`
|
||||
- `actions/core/token.js`
|
||||
- `actions/core/headers.js`
|
||||
- `actions/services/legacyActionsService.js`
|
||||
- `i18n.js`
|
||||
|
||||
## Active concerns
|
||||
|
||||
- Breadcrumb and route-state logic is complex and query-dependent (`va`, `adv`, `ads`, `key`), so regressions are easy.
|
||||
- Locale-specific route and label behavior is still a high-risk area due to rewrite + component logic coupling.
|
||||
- Sensitive logging remains present in auth/email/actions paths and should be reduced/redacted over time.
|
||||
- Hash validation on some file endpoints appears inconsistent (some checks active, some commented), requiring careful change discipline.
|
||||
|
||||
## Refactor status update (2026-03-12)
|
||||
|
||||
- Priority 1 refactor (Phase 1) is in place:
|
||||
- `actions/index.js` now acts as a compatibility barrel.
|
||||
- Core helper concerns were extracted into `actions/core/*`.
|
||||
- Existing API/file/domain wrappers were moved into `actions/services/legacyActionsService.js` with backward-compatible exports retained via the barrel.
|
||||
- No intentional behavior changes were introduced in this phase; focus was structural risk reduction.
|
||||
- Priority 1 refactor (Phase 2) is now in place:
|
||||
- Domain service grouping modules added under `actions/services/*`.
|
||||
- `actions/services/index.js` now provides grouped service barrel exports.
|
||||
- `actions/index.js` now re-exports from `./services` and `./core/*` while preserving existing public action names.
|
||||
- `legacyActionsService` remains as a compatibility backing module and should be slimmed in later increments as consumers move to direct service imports.
|
||||
|
||||
## Refactor status update (Priority 1 execution pass — 2026-03-12)
|
||||
|
||||
- Completed an additional Priority 1 consumer-migration pass focused on high-use portal/form helpers.
|
||||
- Updated these files to use focused service/core imports instead of broad `actions` barrel imports:
|
||||
- `components/case/representation/representationElements.js`
|
||||
- `components/elements/index.js`
|
||||
- `components/utils/index.js`
|
||||
- `lib/newappeal/loadNewAppealPage.js`
|
||||
- `lib/myportal/loadMyPortalAppealPage.js`
|
||||
- `components/myportal/topthree.js`
|
||||
- `components/myportal/topthree_reps.js`
|
||||
- `components/myportal/awaitingsubmissionfromblob.js`
|
||||
- Compatibility model remains in place via `actions/index.js`; migration is incremental and non-breaking by design.
|
||||
|
||||
## Refactor status update (Priority 1 execution pass — viewall/search/unsubscribe)
|
||||
|
||||
- Completed another focused import migration pass to reduce broad `actions` barrel coupling in user-facing high-use areas:
|
||||
- `components/myportal/viewall.js`
|
||||
- `components/search/addresssearchresults.js`
|
||||
- `pages/unsubscribe/[watchlistid].js`
|
||||
- `pages/unsubscribeall/[watchlistid].js`
|
||||
- These files now use explicit imports from `actions/services/*` and `actions/core/*` modules.
|
||||
- Remaining non-comment broad `actions` imports are now primarily in selected `pages/api/endpoint/*` and `pages/api/file/*` handlers.
|
||||
|
||||
## Refactor status update (Priority 1 execution pass — endpoint/file proxies)
|
||||
|
||||
- Completed focused import migration for selected endpoint and file proxy handlers:
|
||||
- `pages/api/endpoint/getmylpacases_api.js`
|
||||
- `pages/api/endpoint/getbasicdnssearchpaged_api.js`
|
||||
- `pages/api/endpoint/getsearchdocumentdetailspaged_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/createappealcompletemessageproxy_api.js`
|
||||
- Replaced broad `../../../actions` imports with focused `actions/core/*` and service-module imports.
|
||||
- Eliminated duplicated local hash helper in `getmylpacases_api.js` by reusing shared `actions/core/hash`.
|
||||
- Current broad-import scan indicates only comment-only legacy references remain.
|
||||
|
||||
## Delivery handover status (2026-03-13)
|
||||
|
||||
- Current import-migration wave has been checked in and completed through PR.
|
||||
- This branch is now treated as the completed migration baseline.
|
||||
- Next implementation chunk should start on a **new branch** and target decomposition of `actions/services/legacyActionsService.js`.
|
||||
- Immediate focus for next branch:
|
||||
1. extract low-risk reference/search wrappers into direct service/client modules,
|
||||
2. migrate portal/document internals in small parity-checked batches,
|
||||
3. slim compatibility layer once parity is proven.
|
||||
|
||||
## Phase 5 kickoff status (2026-03-13)
|
||||
|
||||
- Branch in progress: `TASK21997-phase5-legacyactions-split`.
|
||||
- First decomposition slice completed:
|
||||
- search wrappers extracted to `actions/services/searchDirectService.js`
|
||||
- reference-data wrappers extracted to `actions/services/referenceDataDirectService.js`
|
||||
- `searchService` and `referenceDataService` now route through these direct modules.
|
||||
- `legacyActionsService` remains in place for non-migrated domains (portal/document/account/case/admin/notify/integration) pending next slices.
|
||||
|
||||
## Phase 5 status update (2026-03-13 — document slice)
|
||||
|
||||
- Added `actions/services/documentDirectService.js` and migrated `documentService.js` to route through it.
|
||||
- `legacyActionsService` now remains for:
|
||||
- portal, account, case, admin, notify, integration domains.
|
||||
- Search/reference/document grouped services now point at dedicated direct modules.
|
||||
|
||||
## Phase 5 completion status (2026-03-13)
|
||||
|
||||
- Grouped services now route through focused direct modules for all domains:
|
||||
- search, reference-data, document, portal, account, case, admin, integration, notify.
|
||||
- `actions/services/legacyActionsService.js` has been removed.
|
||||
- Current architecture baseline:
|
||||
- `actions/index.js` -> compatibility barrel re-exporting `actions/services` + core modules
|
||||
- `actions/services/*Service.js` -> grouped public service boundaries
|
||||
- `actions/services/*DirectService.js` -> implementation modules per domain
|
||||
- Validation checkpoints completed:
|
||||
- zero `legacyActionsService` imports in `actions/services/*.js`
|
||||
- lint passes with warnings only
|
||||
- build succeeds
|
||||
|
||||
## Phase 6 hardening status (2026-03-13 — in progress)
|
||||
|
||||
- Working branch: `TASK21998-phase6-postphase5-hardening` (created from `SIPS-Development`).
|
||||
- Main hardening actions completed so far:
|
||||
- Added focused parity test harness:
|
||||
- `tests/phase6/service-parity.test.cjs`
|
||||
- verifies grouped service import/export parity and `actions/services/index.js` re-export stability.
|
||||
- Added shared error-path helper:
|
||||
- `actions/services/httpServiceUtils.js`
|
||||
- consolidates repeated `consoleLogger + error.response` and `ErrResponse` generation patterns.
|
||||
- Applied safe duplication reduction in:
|
||||
- `searchDirectService.js`
|
||||
- `referenceDataDirectService.js`
|
||||
- `caseDirectService.js`
|
||||
- `adminDirectService.js`
|
||||
- Logging cleanup for sensitive/noisy service paths (removed debug `console.log` usage) in:
|
||||
- `searchDirectService.js`
|
||||
- `documentDirectService.js`
|
||||
- `portalDirectService.js`
|
||||
- `caseDirectService.js`
|
||||
- `adminDirectService.js`
|
||||
|
||||
### Current validation evidence
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `npm run lint` -> warnings only
|
||||
- Targeted local smoke snapshots:
|
||||
- `GET /advancedsearch` and `GET /cy/advancedsearch` -> 200
|
||||
- `GET /case` and `GET /cy/case` -> 200
|
||||
- `GET /myportal` and `GET /cy/myportal` -> 307 redirect to `/auth/signin` (unauthenticated negative path)
|
||||
- `GET /api/file/getbloblistproxy?container=test&casefolderID=test` -> 400 (negative path)
|
||||
- `POST /api/email/notify` with empty payload -> 400 (negative path)
|
||||
|
||||
### Active caveat
|
||||
|
||||
- `GET /searchresults` and `GET /cy/searchresults` return 500 in current local dev run due pre-existing SSR serialization issue:
|
||||
- `initialState.search.searchString` is undefined in `getServerSideProps` payload.
|
||||
- captured in `/tmp/phase6-dev.log`.
|
||||
|
||||
## Phase 7 hardening status (2026-03-13)
|
||||
|
||||
- Working branch created from `origin/SIPS-Development`:
|
||||
- `TASK21988a-phase7-postphase6-hardening`
|
||||
- Pre-existing search results SSR serialization issue now addressed:
|
||||
- `pages/searchresults.js`
|
||||
- changed `setSearch(query.q)` to `setSearch(query?.q || "")` in `getServerSideProps`.
|
||||
- local smoke result: both `/searchresults` and `/cy/searchresults` return 200 in this run.
|
||||
- Behavioural test expansion delivered for remaining service domains:
|
||||
- `tests/phase7/service-behaviour.test.cjs`
|
||||
- covers document, portal, account, notify, integration.
|
||||
|
||||
### Phase 7 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `npm run lint` -> warnings only (existing react-hooks dependency warnings; no new lint errors)
|
||||
|
||||
### Phase 7 smoke/negative-path snapshot
|
||||
|
||||
- Dev server auto-bound to `localhost:3001` (3000 already in use).
|
||||
- `GET /searchresults`, `GET /cy/searchresults` -> 200
|
||||
- `GET /case`, `GET /cy/case` -> 200
|
||||
- `GET /myportal`, `GET /cy/myportal` -> 307 redirect to `/auth/signin`
|
||||
- `GET /api/file/getbloblistproxy?container=test&casefolderID=test` -> 400
|
||||
- `POST /api/email/notify` with `{}` -> 400
|
||||
|
||||
### Testing strategy decision (captured)
|
||||
|
||||
- Current phase includes **both**:
|
||||
1. migration/parity guard tests,
|
||||
2. focused behavioural unit tests for critical response/error contracts.
|
||||
- Broader behavioural coverage remains a follow-on expansion item after this focused baseline.
|
||||
|
||||
## Phase 8 hardening status (2026-03-13)
|
||||
|
||||
- Working branch created from `origin/SIPS-Development`:
|
||||
- `TASK22017-phase8-hardening-slice`
|
||||
- Sensitive API hardening slice completed across:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/endpoint/getportallogin_api.js`
|
||||
- Hash/integrity posture updates:
|
||||
- re-enabled hash validation on previously bypassed/commented file handlers
|
||||
- added standardized early 400 handling for missing/invalid hash and missing key params
|
||||
- preserved response-shape contracts and function signatures
|
||||
- Logging discipline updates:
|
||||
- removed noisy direct `console.log` usage in `createappealcompletemessage_api`
|
||||
- routed error paths through `consoleLogger` in updated sensitive flow
|
||||
- Required consumer parity updates applied:
|
||||
- `actions/services/documentDirectService.js`
|
||||
- now appends hash for `deleteblobcase` and `deleteblobrep` calls
|
||||
- `actions/services/portalDirectService.js`
|
||||
- now appends hash for `createappealcompletemessage_api` call using existing hash path contract
|
||||
|
||||
### Phase 8 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 8 manual HTTP snapshot
|
||||
|
||||
- Dev server on `http://localhost:3001`.
|
||||
- Negative-path checks for invalid/missing hash (and missing required params where relevant) return **400** across all 4 target handlers.
|
||||
- Valid-hash spot-check for `getportallogin_api` reached hash-validated path but still returned **400** due local upstream relay/CRM dependency behavior.
|
||||
|
||||
## Phase 9 hardening status (2026-03-13)
|
||||
|
||||
- Working branch created from `origin/SIPS-Development`:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Additional sensitive API hardening slice completed across:
|
||||
- `pages/api/file/createrepcompletemessage_api.js`
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- Hash/integrity posture updates:
|
||||
- re-enabled/enforced hash checks in handlers where hash validation was bypassed/commented
|
||||
- standardized early 400 handling for missing/invalid hash and missing key query/body values
|
||||
- preserved response-shape contracts and function signatures
|
||||
- Logging discipline updates:
|
||||
- removed noisy `console.log` traces from sensitive upload/rep-complete message paths
|
||||
- retained centralized error logging via `consoleLogger` where present
|
||||
- Required caller parity updates applied:
|
||||
- `actions/services/portalDirectService.js`
|
||||
- `sendRepCompleteMessage` now appends hash
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `uploadFiles`, `uploadRepFiles`, `uploadSingleFile` now append hash
|
||||
|
||||
### Phase 9 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 9 manual HTTP snapshot
|
||||
|
||||
- Negative-path checks for missing/invalid hash and invalid/missing key params on all 4 phase-9 handlers return **400** on local server (`localhost:3000`).
|
||||
- Valid-hash happy-path spot-check completed using `.env.local` hash key:
|
||||
- `POST /api/file/uploadsinglefile?hash=<valid>` -> **200**
|
||||
|
||||
## Phase 10 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Additional sensitive API hardening slice completed across:
|
||||
- `pages/api/file/getawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- Hash/integrity posture updates:
|
||||
- explicit early 400 guards for missing/empty required query inputs and hash
|
||||
- standardized early hash-mismatch 400 behaviour in all 4 handlers
|
||||
- preserved response-shape contracts and function signatures
|
||||
- Logging discipline updates:
|
||||
- removed old commented debug traces in touched handlers
|
||||
- Caller parity:
|
||||
- no additional service-layer updates required in this slice; existing direct services already append hash for these APIs.
|
||||
|
||||
### Phase 10 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 10 manual HTTP snapshot
|
||||
|
||||
- Missing-hash negative-path checks for all 4 handlers return **400** (`localhost:3000`).
|
||||
- Valid-hash spot-check result:
|
||||
- `getbloblist` returned **500** with valid hash in local environment (consistent with downstream/local dependency constraints; hash gate passed).
|
||||
|
||||
## Phase 11 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Additional sensitive API hardening slice completed across:
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- Hash/integrity posture updates:
|
||||
- explicit early 400 guards for missing/empty required query inputs and hash
|
||||
- standardized early hash-mismatch 400 behaviour in all touched handlers
|
||||
- preserved response-shape contracts and function signatures
|
||||
- Logging discipline updates:
|
||||
- removed old commented debug traces in touched handlers
|
||||
- Caller parity:
|
||||
- no additional service-layer updates required in this slice.
|
||||
|
||||
### Phase 11 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 11 manual HTTP snapshot
|
||||
|
||||
- Missing/invalid-hash negative-path checks for selected handlers return **400** (`localhost:3000`).
|
||||
- Valid-hash spot-check result:
|
||||
- `getbloblist` returned **500** with valid hash in local environment (consistent with downstream/local dependency constraints; hash gate passed).
|
||||
|
||||
## Phase 12 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Additional sensitive API hardening slice completed across:
|
||||
- `pages/api/file/downloadblob.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- Hash/integrity posture updates:
|
||||
- explicit early 400 guards for missing/empty required query inputs
|
||||
- standardized early hash-mismatch 400 behaviour in `downloadblob`
|
||||
- preserved response-shape contracts and function signatures
|
||||
|
||||
### Phase 12 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 12 manual HTTP snapshot
|
||||
|
||||
- Missing/invalid-input negative-path checks for selected handlers return **400** (`localhost:3000`).
|
||||
- Valid-hash spot-check result:
|
||||
- `downloadblob` returned **500** with valid hash in local environment (consistent with downstream/local dependency constraints; hash gate passed).
|
||||
|
||||
## Phase 13 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Additional sensitive API hardening slice completed across:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createcaseinvolvement_api.js`
|
||||
- `pages/api/file/createrepinvolvement_api.js`
|
||||
- Hash/integrity posture updates:
|
||||
- explicit early 400 guards for missing/empty required query/body inputs
|
||||
- standardized early hash-mismatch 400 behaviour in delete-blob handlers
|
||||
- preserved response-shape contracts and function signatures
|
||||
- Logging discipline updates:
|
||||
- removed noisy direct body/query logging from involvement handlers
|
||||
|
||||
### Phase 13 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> pass (7/7)
|
||||
- `npm run lint` -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
### Phase 13 manual HTTP snapshot
|
||||
|
||||
- Missing/invalid-input negative-path checks for selected handlers return **400** (`localhost:3000`).
|
||||
- Valid-hash spot-check result:
|
||||
- `deleteblobcase` returned **500** with valid hash in local environment (consistent with downstream/local dependency constraints; hash gate passed).
|
||||
|
||||
## Phase 14 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Addressed browser runtime error in authenticated new-appeal journey:
|
||||
- `TypeError` in `hashAPIPath` caused by client-side code attempting to parse server-only `HASHKEY`.
|
||||
- Implemented server-side hash signing bridge for browser callers:
|
||||
- new endpoint `pages/api/endpoint/gethash_api.js`
|
||||
- session-gated (`getSession`), allow-listed supported API prefixes, returns `{ hash }`.
|
||||
- Updated direct service callers to use signer endpoint instead of direct client hash computation:
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `actions/services/portalDirectService.js`
|
||||
- Maintained contract compatibility for target file APIs and existing response shapes.
|
||||
|
||||
### Phase 14 validation evidence snapshot
|
||||
|
||||
- phase6 parity/behaviour tests -> pass
|
||||
- phase7/8/9/10/11/12/13 behaviour tests -> pass
|
||||
- phase14 behaviour tests -> pass (4/4)
|
||||
- lint -> warnings only (pre-existing hook dependency warnings)
|
||||
|
||||
## Phase 14 closeout follow-up status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Browser-safe hash signing expanded for portal login flow:
|
||||
- `actions/services/accountDirectService.js`
|
||||
- `getPortalLogin` now requests signed hash from `/api/endpoint/gethash_api`
|
||||
- server fallback retained for non-browser contexts when `HASHKEY` is present
|
||||
- `pages/api/endpoint/gethash_api.js`
|
||||
- allow-list now includes `/api/endpoint/getportallogin_api`
|
||||
- Contract alignment + runtime fix updates:
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- removed hard requirement for `casefolderID` to match actual route contract (`container + hash`)
|
||||
- `components/elements/index.js`
|
||||
- restored missing `updateLinks` import required by Quill-related runtime path
|
||||
|
||||
### Follow-up validation evidence
|
||||
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- full previously-run phase6–phase14 suite remains passing
|
||||
- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps` set)
|
||||
|
||||
### Active risk posture after follow-up
|
||||
|
||||
- Signer endpoint scope remains controlled by strict allow-list (single new path added).
|
||||
- Response-shape contract stability preserved for touched handlers/services.
|
||||
- Remaining risk is primarily external dependency behavior on valid-hash happy paths in local environments (relay/storage), not hash-bypass behavior.
|
||||
|
||||
## Phase 15 hardening status (2026-03-13)
|
||||
|
||||
- Working branch created from `origin/SIPS-Development`:
|
||||
- `TASK22028-phase15-hardening-slice`
|
||||
- Delivered consistency-only hardening across 4 additional sensitive file handlers:
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- Hardening posture updates:
|
||||
- standardized explicit early 400 on missing hash where validation was already required
|
||||
- standardized early hash-mismatch return path style in `setupcontainer`
|
||||
- preserved response-shape/signature contracts (no intended contract changes)
|
||||
- Added focused phase-15 tests:
|
||||
- `tests/phase15/service-behaviour.test.cjs`
|
||||
- negative-path checks for missing/invalid hash across all selected handlers
|
||||
- required-input negative-path checks where applicable
|
||||
- one valid-hash mocked happy-path check (`setupcontainer` response shape)
|
||||
|
||||
### Phase 15 validation evidence snapshot
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> pass (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase15/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||
|
||||
### Phase 15 manual HTTP snapshot
|
||||
|
||||
- Dev server run on `http://localhost:3002`.
|
||||
- Missing/invalid hash negative-path checks for selected handlers -> **400**.
|
||||
- Missing required params where applicable -> **400**.
|
||||
- Valid-hash spot-check:
|
||||
- `GET /api/file/setupcontainer?ident=c1&hash=<valid>` -> **200**.
|
||||
|
||||
## Phase 16 hardening status (2026-03-13)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22028-phase15-hardening-slice`
|
||||
- Added a consistency-only file-proxy hardening slice across:
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- Changes focused on:
|
||||
- standardized required-input guards via `hasValue`
|
||||
- removed unused imports/dead constants from touched proxy handlers
|
||||
- preserved existing response contracts and status semantics
|
||||
- Added focused test pack:
|
||||
- `tests/phase16/service-behaviour.test.cjs`
|
||||
|
||||
### Phase 16 validation evidence snapshot
|
||||
|
||||
- phase6 parity + phase6–phase15 behavioural suites: pass
|
||||
- phase16 behavioural suite: pass (4/4)
|
||||
- lint: warnings only (pre-existing)
|
||||
|
||||
## Follow-up status (2026-03-13)
|
||||
|
||||
- Applied targeted signer allow-list fix for browser delete-awaiting-submission path.
|
||||
- Updated `pages/api/endpoint/gethash_api.js` allow-list with:
|
||||
- `/api/file/deleteblobcase`
|
||||
- Added focused behavioural test:
|
||||
- `tests/phase14/service-behaviour.test.cjs` now includes allow-list coverage for deleteblobcase path.
|
||||
|
||||
## Follow-up status (2026-03-13 — additional delete flows)
|
||||
|
||||
- Expanded signer allow-list for additional browser delete paths:
|
||||
- `/api/endpoint/deletemyrepresentations_api`
|
||||
- `/api/endpoint/deletewatchedcases_api`
|
||||
- `/api/file/deleteblobrep`
|
||||
- Migrated affected direct-service delete calls to signer-based hash retrieval:
|
||||
- `portalDirectService.deleteWatchedCases`
|
||||
- `portalDirectService.deleteMyRepresentations`
|
||||
- `documentDirectService.deleteAwaitingSubmissionsFromBlob`
|
||||
- `documentDirectService.deleteMyRepresentationsFromBlob`
|
||||
|
||||
## Follow-up status (2026-03-13 — myportal + i18n alignment)
|
||||
|
||||
- Updated myportal representation delete call sites to pass `caseRef`:
|
||||
- `components/myportal/topthree_reps.js`
|
||||
- `components/myportal/viewall.js`
|
||||
- Applied small view-all presentation cleanup and watched-case refresh filtering adjustment.
|
||||
- Added missing `myportal` namespace mapping in `i18n.js` for `/newappeal/[appealtypes]`.
|
||||
|
||||
## Phase 17 closeout status (2026-03-13)
|
||||
|
||||
- Working branch created from `origin/SIPS-Development`:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Completed remaining closeout hardening slice for sensitive file handlers:
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- `pages/api/file/createappealcompletemessageproxy_api.js`
|
||||
- Applied consistency-only updates:
|
||||
- standardized missing/invalid hash 400 negative paths in selected PDF handlers
|
||||
- standardized required-input 400 guards where route contract requires it
|
||||
- removed noisy direct logs from touched sensitive handlers
|
||||
- preserved existing response shapes/signatures
|
||||
- Added focused behavioural coverage:
|
||||
- `tests/phase17/service-behaviour.test.cjs` (6/6 passing)
|
||||
- includes missing/invalid hash negative-path checks and valid-input proxy happy-path contract check
|
||||
- Broader consistency scan note:
|
||||
- no additional broad hash/400 consistency gaps were identified in `pages/api/endpoint/**` in this pass.
|
||||
|
||||
## Phase 18 additional consolidation slices (2026-03-14)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Completed all three requested additional slices:
|
||||
1. endpoint logging/input hygiene
|
||||
2. file hash-guard flow consistency
|
||||
3. create/update input-guard + logging hygiene
|
||||
- Touched handlers:
|
||||
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||
- `pages/api/endpoint/getaccounts_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/createcase_api.js`
|
||||
- `pages/api/file/updatecase_api.js`
|
||||
- `pages/api/file/editRepJson.js`
|
||||
- Outcome:
|
||||
- consistent early 400 guard behavior for missing/invalid required inputs/hash in selected handlers
|
||||
- noisy direct logs removed in touched sensitive flows
|
||||
- no response-shape/signature contract changes intended
|
||||
- Added focused test pack:
|
||||
- `tests/phase18/service-behaviour.test.cjs` (8/8 pass)
|
||||
|
||||
## Phase 19 endpoint consistency slice (2026-03-14)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Applied a small consistency-only slice across:
|
||||
- `pages/api/endpoint/createwatchedcases_api.js`
|
||||
- `pages/api/endpoint/getappealtypes_api.js`
|
||||
- `pages/api/endpoint/getdnslist_api.js`
|
||||
- Scope:
|
||||
- standardized missing/malformed required `@odata.bind` handling in `createwatchedcases_api` with early 400
|
||||
- removed noisy query logging in appeal-types and dns-list endpoint handlers
|
||||
- preserved response shapes/signatures
|
||||
- Added focused test file:
|
||||
- `tests/phase19/service-behaviour.test.cjs` (5/5 pass)
|
||||
|
||||
## Phase 20 endpoint consistency slice (2026-03-14)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Applied a small consistency-only slice across:
|
||||
- `pages/api/endpoint/getbasicdnssearch_api.js`
|
||||
- `pages/api/endpoint/getbasicdnsurlsearch_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchdetailspaged_api.js`
|
||||
- Scope:
|
||||
- removed noisy query logging in `getbasicdnssearch_api`
|
||||
- added early 400 for missing/empty `searchString` in `getbasicdnsurlsearch_api`
|
||||
- added early 400 for missing required params in `getbasicsearchdetailspaged_api`
|
||||
- preserved response shapes/signatures
|
||||
- Added focused test file:
|
||||
- `tests/phase20/service-behaviour.test.cjs` (5/5 pass)
|
||||
|
||||
## Likely next steps
|
||||
|
||||
1. Stabilize and simplify breadcrumb/back-link decision logic with focused regression checks.
|
||||
2. Standardize hash-check enforcement patterns across file and endpoint APIs.
|
||||
3. Reduce verbose logging in sensitive flows (auth/email/file/account).
|
||||
4. Expand automated tests for navigation and representation eligibility edge-cases (currently minimal `tests/`).
|
||||
|
||||
See `memory-bank/refactor-backlog.md` for the prioritized refactor plan and sequencing.
|
||||
Priority 1 detailed execution plan is documented in `memory-bank/refactor-plan-actions-index.md`.
|
||||
|
||||
## Areas currently important for contributors
|
||||
|
||||
- `components/breadcrumbs.js`
|
||||
- `components/case/summary.js`
|
||||
- `pages/api/endpoint/**`
|
||||
- `pages/api/file/**`
|
||||
- `pages/api/auth/[...nextauth].js`
|
||||
- `pages/api/email/notify.js`
|
||||
- `next.config.js`, `i18n.js`, `locales/**`
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,858 +0,0 @@
|
||||
# Progress — PEDW FrontEnd
|
||||
|
||||
## Completed / evidenced milestones
|
||||
|
||||
- Bilingual routing structure is established with extensive Welsh rewrites and locale namespace mapping.
|
||||
- Next-auth + Prisma SQL Server persistence is integrated for user/session lifecycle.
|
||||
- Broad API surface exists for search, case, portal, admin, and file/document operations.
|
||||
- CRM/relay hash pattern is implemented across many endpoint handlers.
|
||||
- Recent shipped updates focused on:
|
||||
- search/case back-navigation and breadcrumb behavior,
|
||||
- DNS/view-all navigation logic,
|
||||
- preferred-language handling for notify template selection,
|
||||
- PDF formatting/hyperlink fixes,
|
||||
- upload validation character updates.
|
||||
|
||||
## Work in progress signals
|
||||
|
||||
- Ongoing iterative fixes in breadcrumb/case-summary flows suggest navigation consistency is still being tuned.
|
||||
- Mixed proxy/non-proxy endpoint implementations continue to evolve in parallel.
|
||||
- Priority 1 refactor plan execution started for `actions/index.js` split (Phase 1 foundation complete).
|
||||
|
||||
## Planned / partially implemented (inferred from code state)
|
||||
|
||||
- Partial migration or coexistence of deployment/runtime patterns (Next runtime plus custom server artifacts).
|
||||
- Continued refinement of document/email workflow behavior (template/language and formatting quality).
|
||||
- Prioritized structural refactor backlog documented in `memory-bank/refactor-backlog.md`.
|
||||
|
||||
## Known technical debt
|
||||
|
||||
- `actions/services/legacyActionsService.js` still holds broad domain wrappers after Phase 1 extraction (core helpers now separated).
|
||||
- Significant duplication of token/hash/relay logic across endpoint files.
|
||||
- Inconsistent security/hash guard enforcement in some file routes.
|
||||
- Sparse automated test coverage (`tests/` currently empty).
|
||||
- Verbose logging in sensitive flows (auth/email/actions/file) increases privacy risk.
|
||||
|
||||
## Latest update (2026-03-12)
|
||||
|
||||
- Branch `0000-update-actions-monolith` created from development branch for Priority 1 refactor implementation.
|
||||
- `actions/index.js` reduced to a compatibility barrel.
|
||||
- New core helper modules added under `actions/core/`:
|
||||
- `env.js`
|
||||
- `logger.js`
|
||||
- `hash.js`
|
||||
- `token.js`
|
||||
- `headers.js`
|
||||
- Existing action wrappers moved to `actions/services/legacyActionsService.js` and continue to be exported via the barrel for compatibility.
|
||||
- `actions/clients/` scaffolded for upcoming client extraction phase.
|
||||
- Phase 2 service grouping completed with dedicated service files and `actions/services/index.js` barrel.
|
||||
- Export parity check confirms grouped service modules cover all legacy exported action names.
|
||||
- Phase 3 migration started by moving selected high-churn consumers and representative endpoint handlers to direct `actions/services/*` and `actions/core/*` imports.
|
||||
- Phase 4 hardening baseline added with new guard helpers and redacted logging, adopted in selected notify/login/contact API handlers.
|
||||
- Additional Priority 1 consumer migration pass completed for high-use portal/form/representation helpers:
|
||||
- Updated direct service/core imports in:
|
||||
- `components/admin/tabs/documents.js`
|
||||
- `components/admin/tabs/storage.js`
|
||||
- `components/admin/utils/serverside.js`
|
||||
- `components/case/documents.js`
|
||||
- `components/case/representation/representationComplete.js`
|
||||
- `components/case/representation/representationElements.js`
|
||||
- `components/elements/index.js`
|
||||
- `components/myportal/awaitingsubmissionfromblob.js`
|
||||
- `components/myportal/topthree.js`
|
||||
- `components/myportal/topthree_reps.js`
|
||||
- `components/utils/index.js`
|
||||
- `lib/myportal/loadMyPortalAppealPage.js`
|
||||
- `lib/newappeal/loadNewAppealPage.js`
|
||||
- Additional Priority 1 consumer migration pass completed for view-all/search/unsubscribe flows:
|
||||
- Updated direct service/core imports in:
|
||||
- `components/myportal/viewall.js`
|
||||
- `components/search/addresssearchresults.js`
|
||||
- `pages/unsubscribe/[watchlistid].js`
|
||||
- `pages/unsubscribeall/[watchlistid].js`
|
||||
- Broad `actions` barrel import count reduced again; remaining non-comment usages are now concentrated in selected `pages/api/endpoint/*` and `pages/api/file/*` handlers.
|
||||
- Additional Priority 1 endpoint/file proxy migration chunk completed:
|
||||
- Updated imports in:
|
||||
- `pages/api/endpoint/getmylpacases_api.js`
|
||||
- `pages/api/endpoint/getbasicdnssearchpaged_api.js`
|
||||
- `pages/api/endpoint/getsearchdocumentdetailspaged_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/createappealcompletemessageproxy_api.js`
|
||||
- Migrated from broad `../../../actions` imports to focused `actions/core/*` + service imports.
|
||||
- Removed duplicated local hash helper in `getmylpacases_api.js` and reused `actions/core/hash`.
|
||||
- Broad `actions` import scan now reports only comment-only references (no active broad imports in `.js` files).
|
||||
- Lint check re-run outcome unchanged: `next lint` fails due legacy/unsupported ESLint options in repo tooling config (not introduced by this refactor pass).
|
||||
|
||||
## Handover update (2026-03-13)
|
||||
|
||||
- Priority 1 import-migration wave has been checked in and completed via pull request.
|
||||
- Recent migration commits captured in this wave:
|
||||
- `f0202bb`
|
||||
- `fa98535`
|
||||
- `bef8f25`
|
||||
- Active broad `.../actions` imports are now eliminated from live `.js` code paths.
|
||||
- Next refactor phase will begin on a **new branch** and focus on splitting `actions/services/legacyActionsService.js` into smaller direct implementation modules.
|
||||
- Recommended decomposition order for the new branch:
|
||||
1. reference/search extraction
|
||||
2. portal/document extraction
|
||||
3. compatibility layer slimming + dead wrapper removal
|
||||
|
||||
## Latest update (2026-03-13 — Phase 5 kickoff)
|
||||
|
||||
- New branch created from `SIPS-Development`: `TASK21997-phase5-legacyactions-split`.
|
||||
- Completed first Phase 5 decomposition slice from `legacyActionsService`:
|
||||
- Added `actions/services/searchDirectService.js`
|
||||
- Added `actions/services/referenceDataDirectService.js`
|
||||
- Updated `actions/services/searchService.js` to source from `searchDirectService`
|
||||
- Updated `actions/services/referenceDataService.js` to source from `referenceDataDirectService`
|
||||
- Compatibility approach preserved: public grouped service exports and function signatures remain unchanged for callers.
|
||||
- Validation status unchanged:
|
||||
- `npm run lint` still fails due pre-existing Next/ESLint option incompatibility in repo tooling configuration.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 5 document slice)
|
||||
|
||||
- Continued on branch `TASK21997-phase5-legacyactions-split`.
|
||||
- Completed second decomposition slice from `legacyActionsService`:
|
||||
- Added `actions/services/documentDirectService.js`
|
||||
- Updated `actions/services/documentService.js` to source from `documentDirectService`
|
||||
- Compatibility approach preserved:
|
||||
- grouped service export names/signatures unchanged for consumers.
|
||||
- Verification:
|
||||
- service import scan now shows remaining legacy-backed grouped modules are:
|
||||
- `portalService`, `accountService`, `caseService`, `adminService`, `notifyService`, `integrationService`
|
||||
- `npm run lint` passes with warnings only (no blocking errors).
|
||||
|
||||
## Latest update (2026-03-13 — Phase 5 completion)
|
||||
|
||||
- Continued decomposition and completed all remaining slices on `TASK21997-phase5-legacyactions-split`:
|
||||
- portal direct extraction
|
||||
- account + case direct extraction
|
||||
- admin + integration + notify direct extraction
|
||||
- legacy cleanup/removal
|
||||
- New direct modules now in place:
|
||||
- `portalDirectService.js`
|
||||
- `accountDirectService.js`
|
||||
- `caseDirectService.js`
|
||||
- `adminDirectService.js`
|
||||
- `integrationDirectService.js`
|
||||
- `notifyDirectService.js`
|
||||
- `actions/services/legacyActionsService.js` removed after import parity checks.
|
||||
- Validation evidence:
|
||||
- `actions/services` import scan: zero `./legacyActionsService` references
|
||||
- `npm run lint`: warnings only, no blocking errors
|
||||
- `npm run build`: successful production build
|
||||
|
||||
## Latest update (2026-03-13 — Phase 6 post-Phase-5 hardening, in progress)
|
||||
|
||||
- Branch created from `SIPS-Development`: `TASK21998-phase6-postphase5-hardening`.
|
||||
- Added focused parity checks for grouped/direct service stability and service index barrel stability:
|
||||
- `tests/phase6/service-parity.test.cjs`
|
||||
- validates grouped service import/export parity across:
|
||||
- search, referenceData, document, portal, account, case, admin, integration, notify
|
||||
- validates `actions/services/index.js` export list stability/order.
|
||||
- Added shared service error helper:
|
||||
- `actions/services/httpServiceUtils.js`
|
||||
- centralizes common `consoleLogger + return error.response` and `ErrResponse` shape patterns.
|
||||
- Applied low-risk duplication reduction in direct services (no signature/shape changes intended):
|
||||
- `searchDirectService.js`
|
||||
- `referenceDataDirectService.js`
|
||||
- `caseDirectService.js`
|
||||
- `adminDirectService.js`
|
||||
- Logging cleanup/redaction-oriented hardening (sensitive-flow noisy logs removed):
|
||||
- removed debug `console.log` statements from:
|
||||
- `searchDirectService.js`
|
||||
- `documentDirectService.js`
|
||||
- `portalDirectService.js`
|
||||
- `caseDirectService.js`
|
||||
- `adminDirectService.js`
|
||||
|
||||
### Validation snapshot (Phase 6)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `npm run lint` -> **warnings only** (same pre-existing warnings)
|
||||
|
||||
### Decision captured (Phase 6)
|
||||
|
||||
- Agreed approach: keep parity guard tests **and** add focused behavioural unit tests in the current refactor stream (instead of deferring all behavioural checks to long-term roadmap).
|
||||
- Implemented now:
|
||||
- `tests/phase6/service-behaviour.test.cjs`
|
||||
- mocked-axios behavioural checks for critical success/error contracts across search/reference/case/admin direct services.
|
||||
- Wider behavioural expansion remains on roadmap for additional domains/functions.
|
||||
|
||||
### Targeted smoke snapshot (local dev server)
|
||||
|
||||
- Search flow:
|
||||
- `GET /searchresults` (EN/CY) -> **500** in local env due existing serialization issue (`initialState.search.searchString` undefined in SSR payload), observed in `/tmp/phase6-dev.log`.
|
||||
- `GET /advancedsearch` and `GET /cy/advancedsearch` -> **200**
|
||||
- Case flow:
|
||||
- `GET /case` and `GET /cy/case` -> **200**
|
||||
- My Portal flow (negative-path):
|
||||
- `GET /myportal` and `GET /cy/myportal` -> **307** redirect to `/auth/signin` (expected unauthenticated behavior)
|
||||
- Document flow (negative-path):
|
||||
- `GET /api/file/getbloblistproxy?container=test&casefolderID=test` -> **400**
|
||||
- Notify flow (negative-path):
|
||||
- `POST /api/email/notify` with `{}` -> **400**
|
||||
|
||||
## Latest update (2026-03-13 — Phase 8 hardening slice)
|
||||
|
||||
- New branch created from `origin/SIPS-Development` with work item prefix:
|
||||
- `TASK22017-phase8-hardening-slice`
|
||||
- Hardened four sensitive API handlers with minimal reversible guards:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/endpoint/getportallogin_api.js`
|
||||
- Scope delivered:
|
||||
- re-enabled/enforced hash checks where bypassed/commented
|
||||
- standardized early 400 negative paths for missing/invalid required query inputs
|
||||
- reduced noisy sensitive-path logging and routed errors through `consoleLogger`
|
||||
- preserved function signatures and existing response-shape contracts
|
||||
- Consumer parity updates (required to preserve behaviour after hash enforcement):
|
||||
- `actions/services/documentDirectService.js`
|
||||
- append hash for delete-blob-case/delete-blob-rep calls
|
||||
- `actions/services/portalDirectService.js`
|
||||
- append hash for create-appeal-complete-message call (hash path preserved to existing API contract)
|
||||
- Added focused tests:
|
||||
- `tests/phase8/service-behaviour.test.cjs`
|
||||
- covers negative paths for all 4 handlers (missing/invalid hash and missing key params as applicable)
|
||||
- includes one happy-path check for `getportallogin_api` with valid hash via mocked dependencies
|
||||
|
||||
### Validation snapshot (Phase 8)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks dependency warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 8)
|
||||
|
||||
- Local dev server on `http://localhost:3001`.
|
||||
- Negative-path checks:
|
||||
- `GET /api/file/deleteblobcase?...` missing hash -> **400**
|
||||
- `GET /api/file/deleteblobcase?...&hash=bad` -> **400**
|
||||
- `GET /api/file/deleteblobrep?...` missing hash -> **400**
|
||||
- `GET /api/file/deleteblobrep?...&hash=bad` -> **400**
|
||||
- `GET /api/file/deleteblobrep?...` missing `repfile` -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?...` missing hash -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?...&hash=bad` -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?...` missing `tempcaseref` -> **400**
|
||||
- `GET /api/endpoint/getportallogin_api?...` missing hash -> **400**
|
||||
- `GET /api/endpoint/getportallogin_api?...&hash=bad` -> **400**
|
||||
- `GET /api/endpoint/getportallogin_api?...` missing `emailAddress` -> **400**
|
||||
- Valid-hash spot-check:
|
||||
- `GET /api/endpoint/getportallogin_api?...&hash=<valid>` reached handler with valid hash but returned **400** from upstream relay/CRM call in local environment (expected environmental dependency risk, not hash-guard bypass).
|
||||
|
||||
## Latest update (2026-03-13 — Phase 9 hardening slice)
|
||||
|
||||
- New branch created from `origin/SIPS-Development` with work item prefix:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Hardened four additional sensitive file handlers with minimal reversible changes:
|
||||
- `pages/api/file/createrepcompletemessage_api.js`
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- Scope delivered:
|
||||
- re-enabled/enforced hash validation where bypassed/commented
|
||||
- added/standardized early 400 negative paths for missing/invalid hash and missing key parameters
|
||||
- reduced noisy logging in sensitive file-upload/message paths
|
||||
- preserved handler signatures and external response-shape contracts
|
||||
- Consumer parity updates:
|
||||
- `actions/services/portalDirectService.js`
|
||||
- append hash for `createrepcompletemessage_api` call
|
||||
- `actions/services/documentDirectService.js`
|
||||
- append hash for `upload` and `uploadsinglefile` calls
|
||||
- Added focused tests:
|
||||
- `tests/phase9/service-behaviour.test.cjs`
|
||||
- covers negative paths across all 4 phase-9 handlers
|
||||
|
||||
### Validation snapshot (Phase 9)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 9)
|
||||
|
||||
- Negative-path checks on `localhost:3000`:
|
||||
- `GET /api/file/createrepcompletemessage_api?...` missing hash -> **400**
|
||||
- `GET /api/file/createrepcompletemessage_api?...&hash=bad` -> **400**
|
||||
- `POST /api/file/upload` missing hash -> **400**
|
||||
- `POST /api/file/upload?hash=bad` -> **400**
|
||||
- `POST /api/file/uploadsinglefile` missing hash -> **400**
|
||||
- `POST /api/file/uploadsinglefile?hash=bad` -> **400**
|
||||
- `GET /api/file/setupcontainer?ident=...` missing hash -> **400**
|
||||
- `GET /api/file/setupcontainer?ident=...&hash=bad` -> **400**
|
||||
- `GET /api/file/setupcontainer?hash=bad` (missing ident) -> **400**
|
||||
- Valid-hash happy-path spot-check:
|
||||
- Completed using `.env.local` runtime key: `POST /api/file/uploadsinglefile?hash=<valid>` -> **200**
|
||||
|
||||
### Rollback plan (Phase 9)
|
||||
|
||||
1. Revert the four hardened API handlers.
|
||||
2. Revert direct-service hash append changes in `documentDirectService` and `portalDirectService`.
|
||||
3. Remove `tests/phase9/service-behaviour.test.cjs` if full slice rollback required.
|
||||
4. Re-run phase6/7/8/9 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 10 hardening slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Hardened four additional sensitive file handlers with minimal reversible changes:
|
||||
- `pages/api/file/getawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- Scope delivered:
|
||||
- added explicit early 400 handling for missing/empty required inputs (`container`, `casefolderID` where applicable, `hash`)
|
||||
- standardized hash-mismatch negative-path checks to early return 400
|
||||
- removed legacy/noisy commented debug blocks from touched handlers
|
||||
- preserved existing handler signatures and response-shape contracts
|
||||
- Caller parity impact:
|
||||
- no new caller changes required in this slice because direct-service consumers already append hash for these APIs.
|
||||
- Added focused tests:
|
||||
- `tests/phase10/service-behaviour.test.cjs`
|
||||
- covers negative paths for all 4 selected phase-10 handlers
|
||||
- includes one valid-hash contract-preserving happy-path check (`getbloblist` via mocked dependencies)
|
||||
|
||||
### Validation snapshot (Phase 10)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 10)
|
||||
|
||||
- Negative-path checks on `localhost:3000`:
|
||||
- `GET /api/file/getawaitingsubmissionfromblob?container=test` (missing hash) -> **400**
|
||||
- `GET /api/file/getprogressobjblob?container=test&casefolderID=case-1` (missing hash) -> **400**
|
||||
- `GET /api/file/getbloblist?container=test&casefolderID=case-1` (missing hash) -> **400**
|
||||
- `GET /api/file/getrepsblob?container=test&casefolderID=case-1` (missing hash) -> **400**
|
||||
- Valid-hash happy-path spot-check:
|
||||
- `GET /api/file/getbloblist?container=test&casefolderID=case-1&hash=<valid>` -> **500**
|
||||
- expected due local storage/upstream dependency constraints, while confirming hash gate is no longer the failing check.
|
||||
|
||||
### Rollback plan (Phase 10)
|
||||
|
||||
1. Revert the four hardened API handlers.
|
||||
2. Remove `tests/phase10/service-behaviour.test.cjs` if full phase-10 rollback required.
|
||||
3. Re-run phase6/7/8/9/10 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 11 hardening slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Hardened three additional sensitive file handlers with minimal reversible changes:
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- Scope delivered:
|
||||
- added explicit early 400 handling for missing/empty required query inputs (`container`, `casefolderID`, `blobname`, `hash` as applicable)
|
||||
- standardized hash-mismatch negative-path checks to early return 400
|
||||
- removed legacy/noisy commented debug traces in touched handlers
|
||||
- preserved existing handler signatures and response-shape contracts
|
||||
- Caller parity impact:
|
||||
- no new caller changes required in this slice; direct-service consumers already pass expected query/hash data.
|
||||
- Added focused tests:
|
||||
- `tests/phase11/service-behaviour.test.cjs`
|
||||
- covers negative paths for selected phase-11 handlers
|
||||
- includes one valid-hash contract-preserving happy-path check (`getbloblist` via mocked dependencies)
|
||||
|
||||
### Validation snapshot (Phase 11)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 11)
|
||||
|
||||
- Negative-path checks on `localhost:3000`:
|
||||
- `GET /api/file/getbloblist?container=test&casefolderID=case-1` (missing hash) -> **400**
|
||||
- `GET /api/file/deleteblob?container=test&casefolderID=case-1&blobname=file.pdf` (missing hash) -> **400**
|
||||
- `GET /api/file/deleteawaitingsubmissionfromblob?container=test&casefolderID=case-1&blobname=file.pdf` (missing hash) -> **400**
|
||||
- `GET /api/file/deleteblob?container=test&casefolderID=case-1&hash=bad` (missing blobname/invalid hash) -> **400**
|
||||
- Valid-hash happy-path spot-check:
|
||||
- `GET /api/file/getbloblist?container=test&casefolderID=case-1&hash=<valid>` -> **500**
|
||||
- expected due local storage/upstream dependency constraints, while confirming hash gate is no longer the failing check.
|
||||
|
||||
### Rollback plan (Phase 11)
|
||||
|
||||
1. Revert the three hardened API handlers.
|
||||
2. Remove `tests/phase11/service-behaviour.test.cjs` if full phase-11 rollback required.
|
||||
3. Re-run phase6/7/8/9/10/11 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 12 hardening slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Hardened four additional sensitive handlers with minimal reversible changes:
|
||||
- `pages/api/file/downloadblob.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- Scope delivered:
|
||||
- added explicit early 400 handling for missing/empty required query inputs (`container`, `casefolderID`, `blobname`, `hash` as applicable)
|
||||
- standardized hash-mismatch negative-path checks for `downloadblob`
|
||||
- preserved existing function signatures and response-shape contracts
|
||||
- Added focused tests:
|
||||
- `tests/phase12/service-behaviour.test.cjs`
|
||||
- covers negative paths for selected phase-12 handlers
|
||||
- includes one valid-hash contract-preserving happy-path check (`downloadblob` via mocked dependencies)
|
||||
|
||||
### Validation snapshot (Phase 12)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 12)
|
||||
|
||||
- Negative-path checks on `localhost:3000`:
|
||||
- `GET /api/file/downloadblob?container=test&casefolderID=case-1&blobname=file.pdf` (missing hash) -> **400**
|
||||
- `GET /api/file/downloadblob?container=test&casefolderID=case-1&blobname=file.pdf&hash=bad` -> **400**
|
||||
- `GET /api/file/getbloblistproxy?casefolderID=case-1` (missing container) -> **400**
|
||||
- `GET /api/file/getrepsblobproxy` (missing container) -> **400**
|
||||
- `GET /api/file/getawaitingsubmissionfromblobproxy` (missing container) -> **400**
|
||||
- Valid-hash happy-path spot-check:
|
||||
- `GET /api/file/downloadblob?container=test&casefolderID=case-1&blobname=file.pdf&hash=<valid>` -> **500**
|
||||
- expected due local storage/upstream dependency constraints, while confirming hash gate is no longer the failing check.
|
||||
|
||||
### Rollback plan (Phase 12)
|
||||
|
||||
1. Revert the four hardened API handlers.
|
||||
2. Remove `tests/phase12/service-behaviour.test.cjs` if full phase-12 rollback required.
|
||||
3. Re-run phase6/7/8/9/10/11/12 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 13 hardening slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Hardened four additional sensitive handlers with minimal reversible changes:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createcaseinvolvement_api.js`
|
||||
- `pages/api/file/createrepinvolvement_api.js`
|
||||
- Scope delivered:
|
||||
- added explicit early 400 handling for missing/empty required query/body inputs (`hash`, `contactid`, `incidentid` as applicable)
|
||||
- standardized hash-mismatch negative-path checks for delete-blob handlers
|
||||
- removed noisy body/query logging in involvement handlers
|
||||
- preserved existing function signatures and response-shape contracts
|
||||
- Added focused tests:
|
||||
- `tests/phase13/service-behaviour.test.cjs`
|
||||
- covers negative paths for selected phase-13 handlers
|
||||
- includes valid-hash/valid-body contract-preserving happy-path checks via mocked dependencies
|
||||
|
||||
### Validation snapshot (Phase 13)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `npm run lint` -> **warnings only** (pre-existing react-hooks warnings; no new lint errors)
|
||||
|
||||
### Manual HTTP checks (Phase 13)
|
||||
|
||||
- Negative-path checks on `localhost:3000`:
|
||||
- `GET /api/file/deleteblobcase?container=test&casefolderID=case-1` (missing hash) -> **400**
|
||||
- `GET /api/file/deleteblobcase?container=test&casefolderID=case-1&hash=bad` -> **400**
|
||||
- `GET /api/file/deleteblobrep?container=test&casefolderID=case-1&repfile=r.pdf` (missing hash) -> **400**
|
||||
- `POST /api/file/createcaseinvolvement_api` with `{}` -> **400**
|
||||
- `POST /api/file/createrepinvolvement_api` with `{}` -> **400**
|
||||
- Valid-hash happy-path spot-check:
|
||||
- `GET /api/file/deleteblobcase?container=test&casefolderID=case-1&hash=<valid>` -> **500**
|
||||
- expected due local storage/upstream dependency constraints, while confirming hash gate is no longer the failing check.
|
||||
|
||||
### Rollback plan (Phase 13)
|
||||
|
||||
1. Revert the four hardened API handlers.
|
||||
2. Remove `tests/phase13/service-behaviour.test.cjs` if full phase-13 rollback required.
|
||||
3. Re-run phase6/7/8/9/10/11/12/13 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 14 client-hash signing fix)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Fixed authenticated user-journey runtime error during new appeal flow where client-side hashing tried to use server-only `HASHKEY`.
|
||||
- Added authenticated server-side hash signer endpoint:
|
||||
- `pages/api/endpoint/gethash_api.js`
|
||||
- requires session (`getSession`), allow-lists supported API paths, returns `{ hash }`.
|
||||
- Updated direct services to request hash from server signer for browser calls, with server-side fallback only when `HASHKEY` exists:
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `actions/services/portalDirectService.js`
|
||||
- Preserved existing API contracts and response shapes in file handlers.
|
||||
- Added focused tests:
|
||||
- `tests/phase14/service-behaviour.test.cjs`
|
||||
- covers signer endpoint negative paths + authenticated happy path.
|
||||
|
||||
### Validation snapshot (Phase 14)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `npm run lint` -> **warnings only** (pre-existing hook dependency warnings)
|
||||
|
||||
### Rollback plan (Phase 14)
|
||||
|
||||
1. Revert `pages/api/endpoint/gethash_api.js`.
|
||||
2. Revert direct-service hash signer usage in `documentDirectService.js` and `portalDirectService.js`.
|
||||
3. Remove `tests/phase14/service-behaviour.test.cjs` if rolling back full phase-14 slice.
|
||||
4. Re-run phase6–phase14 tests + lint.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 14 closeout follow-up)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22019-phase-9-hardening`
|
||||
- Completed follow-up hardening and contract-alignment after runtime verification:
|
||||
- `actions/services/accountDirectService.js`
|
||||
- `getPortalLogin` now uses authenticated signer endpoint (`/api/endpoint/gethash_api`) for browser-safe hash generation
|
||||
- retains server-only fallback to local `hashAPIPath` when `HASHKEY` is available
|
||||
- `pages/api/endpoint/gethash_api.js`
|
||||
- allow-list expanded for `/api/endpoint/getportallogin_api`
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- removed enforced `casefolderID` guard and argument pass-through to align with route contract (`/api/file/getrepsblob?container=...`)
|
||||
- `components/elements/index.js`
|
||||
- added missing `updateLinks` import from `components/utils` to resolve Quill runtime error
|
||||
- Tests updated:
|
||||
- `tests/phase7/service-behaviour.test.cjs`
|
||||
- updated `getPortalLogin` expectations for signer endpoint behavior
|
||||
- `tests/phase14/service-behaviour.test.cjs`
|
||||
- added allow-list coverage for signer path `/api/endpoint/getportallogin_api`
|
||||
|
||||
### Validation snapshot (Phase 14 follow-up)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing hook dependency warnings)
|
||||
|
||||
### Risks + mitigations (follow-up)
|
||||
|
||||
- Risk: signer endpoint over-expansion could broaden hash issuance.
|
||||
- Mitigation: strict allow-list retained; only required `getportallogin_api` path added.
|
||||
- Risk: tightened/changed guards could break existing caller contracts.
|
||||
- Mitigation: `getrepsblob` guard aligned back to actual route contract; response shape unchanged.
|
||||
- Risk: UI runtime dependency regressions during hardening verification.
|
||||
- Mitigation: missing `updateLinks` import restored with minimal diff.
|
||||
|
||||
### Rollback plan (follow-up)
|
||||
|
||||
1. Revert signer-path migration commit `6094874` (or files: `accountDirectService.js`, `gethash_api.js`, `tests/phase7`, `tests/phase14`).
|
||||
2. Revert contract/import alignment commit `b8fa514` (or files: `getrepsblob.js`, `components/elements/index.js`).
|
||||
3. Re-run phase6–phase14 tests and lint to confirm rollback parity.
|
||||
|
||||
### Rollback plan (Phase 8)
|
||||
|
||||
1. Revert the four hardened API handlers.
|
||||
2. Revert direct-service hash append changes in `documentDirectService` and `portalDirectService`.
|
||||
3. Remove `tests/phase8/service-behaviour.test.cjs` if full slice rollback required.
|
||||
4. Re-run phase6/7 baseline tests + lint after rollback.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 15 hardening slice)
|
||||
|
||||
- New branch created from `origin/SIPS-Development` with confirmed work item prefix:
|
||||
- `TASK22028-phase15-hardening-slice`
|
||||
- Delivered a small consistency-only hardening slice across 4 additional sensitive file handlers:
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- Scope delivered:
|
||||
- standardized explicit early 400 handling for missing hash in handlers where hash validation was present but missing-hash guard was inconsistent
|
||||
- standardized guard style in `setupcontainer` to early-return on hash mismatch (same status/contract)
|
||||
- standardized missing required hash handling in `createappealcompletemessage_api`
|
||||
- preserved response shapes and signatures (no contract changes)
|
||||
- Added focused phase-15 tests:
|
||||
- `tests/phase15/service-behaviour.test.cjs`
|
||||
- negative-path coverage for missing hash across all 4 handlers
|
||||
- additional required-input checks where applicable
|
||||
- one valid-hash happy-path contract check (`setupcontainer` -> 200 with existing response shape)
|
||||
|
||||
### Validation snapshot (Phase 15)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase15/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||
|
||||
## Latest update (2026-03-14 — Phase 19 endpoint consistency slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Delivered a small additional consistency-only endpoint slice across:
|
||||
- `pages/api/endpoint/createwatchedcases_api.js`
|
||||
- `pages/api/endpoint/getappealtypes_api.js`
|
||||
- `pages/api/endpoint/getdnslist_api.js`
|
||||
- Scope delivered:
|
||||
- added required early 400 handling for malformed/missing `@odata.bind` values in `createwatchedcases_api`
|
||||
- removed noisy query logging in `getappealtypes_api` and `getdnslist_api`
|
||||
- preserved response-shape/signature contracts
|
||||
- Added focused tests:
|
||||
- `tests/phase19/service-behaviour.test.cjs` (5/5 passing)
|
||||
|
||||
### Validation snapshot (Phase 19)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (12/12)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (9/9)
|
||||
- `node tests/phase15/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase16/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase17/service-behaviour.test.cjs` -> **pass** (6/6)
|
||||
- `node tests/phase18/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase19/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||
|
||||
## Latest update (2026-03-14 — Phase 20 endpoint consistency slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22057-phase17-hardening-closeout`
|
||||
- Delivered a small additional consistency-only endpoint slice across:
|
||||
- `pages/api/endpoint/getbasicdnssearch_api.js`
|
||||
- `pages/api/endpoint/getbasicdnsurlsearch_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchdetailspaged_api.js`
|
||||
- Scope delivered:
|
||||
- removed noisy query logging in `getbasicdnssearch_api`
|
||||
- standardized missing/empty `searchString` handling in `getbasicdnsurlsearch_api` with early 400
|
||||
- standardized missing required input handling in `getbasicsearchdetailspaged_api` (`appealTypeName`, `primaryIdAttribute`, `incidentID`) with early 400
|
||||
- preserved response-shape/signature contracts
|
||||
- Added focused tests:
|
||||
- `tests/phase20/service-behaviour.test.cjs` (5/5 passing)
|
||||
|
||||
### Validation snapshot (Phase 20)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (12/12)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (9/9)
|
||||
- `node tests/phase15/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase16/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase17/service-behaviour.test.cjs` -> **pass** (6/6)
|
||||
- `node tests/phase18/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase19/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase20/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `npm run lint` -> **warnings only** (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||
|
||||
### Manual HTTP snapshot (Phase 15)
|
||||
|
||||
- Dev server run on `http://localhost:3002`.
|
||||
- Negative-path checks:
|
||||
- `POST /api/file/upload` missing hash -> **400**
|
||||
- `POST /api/file/upload?hash=wrong` -> **400**
|
||||
- `POST /api/file/uploadsinglefile` missing hash -> **400**
|
||||
- `POST /api/file/uploadsinglefile?hash=wrong` -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?container=c1&tempcaseref=t1` missing hash -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?container=c1&tempcaseref=t1&hash=wrong` -> **400**
|
||||
- `GET /api/file/createappealcompletemessage_api?container=c1` missing required params/hash -> **400**
|
||||
- `GET /api/file/setupcontainer?ident=c1` missing hash -> **400**
|
||||
- `GET /api/file/setupcontainer?ident=c1&hash=wrong` -> **400**
|
||||
- `GET /api/file/setupcontainer?hash=<valid>` missing required `ident` -> **400**
|
||||
- Valid-hash spot-check:
|
||||
- `GET /api/file/setupcontainer?ident=c1&hash=<valid>` -> **200**
|
||||
|
||||
### Rollback plan (Phase 15)
|
||||
|
||||
1. Revert these files:
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- `tests/phase15/service-behaviour.test.cjs`
|
||||
2. Re-run phase6–phase15 tests and lint to confirm parity.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 16 file-proxy consistency slice)
|
||||
|
||||
- Continued on branch:
|
||||
- `TASK22028-phase15-hardening-slice`
|
||||
- Delivered a small consistency-only hardening slice across 3 sensitive file proxy handlers:
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- Scope delivered:
|
||||
- removed unused imports/constants in touched handlers to reduce noisy/unsafe dead code
|
||||
- standardized required input checks using shared local `hasValue` pattern
|
||||
- preserved response shape/signature/status behavior contracts
|
||||
- Added focused tests:
|
||||
- `tests/phase16/service-behaviour.test.cjs`
|
||||
- negative-path checks for missing required container/casefolder inputs
|
||||
- one valid-input happy-path contract check for `getbloblistproxy` (200 + passthrough shape)
|
||||
|
||||
### Validation snapshot (Phase 16)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> **pass** (7/7)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase15/service-behaviour.test.cjs` -> **pass** (5/5)
|
||||
- `node tests/phase16/service-behaviour.test.cjs` -> **pass** (4/4)
|
||||
- `npm run lint` -> **warnings only** (pre-existing `react-hooks/exhaustive-deps` warnings)
|
||||
|
||||
### Manual HTTP checks (Phase 16)
|
||||
|
||||
- Dev server on `http://localhost:3002`.
|
||||
- Negative-path checks:
|
||||
- `/api/file/getbloblistproxy?casefolderID=case-1` -> **400**
|
||||
- `/api/file/getbloblistproxy?container=c1` -> **400**
|
||||
- `/api/file/getrepsblobproxy` -> **400**
|
||||
- `/api/file/getawaitingsubmissionfromblobproxy` -> **400**
|
||||
- Feasible valid-input spot-check:
|
||||
- `/api/file/getbloblistproxy?container=c1&casefolderID=case-1` -> **400** (expected downstream/local dependency behavior; input guard passed)
|
||||
|
||||
### Rollback plan (Phase 16)
|
||||
|
||||
1. Revert commit `c196edb` or files:
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- `tests/phase16/service-behaviour.test.cjs`
|
||||
2. Re-run phase6–phase16 tests and lint.
|
||||
|
||||
## Latest update (2026-03-13 — signer allow-list follow-up)
|
||||
|
||||
- Branch: `TASK22028-phase15-hardening-slice`
|
||||
- Fix applied:
|
||||
- added `/api/file/deleteblobcase` to signer endpoint allow-list in `pages/api/endpoint/gethash_api.js`
|
||||
- Test update:
|
||||
- extended `tests/phase14/service-behaviour.test.cjs` with deleteblobcase allow-list coverage
|
||||
|
||||
### Validation snapshot (follow-up)
|
||||
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (6/6)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `npm run lint` -> **warnings only** (pre-existing)
|
||||
|
||||
## Latest update (2026-03-13 — signer delete-flow follow-up)
|
||||
|
||||
- Branch: `TASK22028-phase15-hardening-slice`
|
||||
- Extended signer allow-list and browser signer usage for additional delete flows:
|
||||
- `deleteWatchedCases`
|
||||
- `deleteMyRepresentations`
|
||||
- `deleteAwaitingSubmissionsFromBlob`
|
||||
- `deleteMyRepresentationsFromBlob`
|
||||
|
||||
### Validation snapshot (delete-flow follow-up)
|
||||
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (12/12)
|
||||
- `node tests/phase14/service-behaviour.test.cjs` -> **pass** (9/9)
|
||||
- `npm run lint` -> **warnings only** (pre-existing)
|
||||
|
||||
## Latest update (2026-03-13 — myportal + i18n follow-up)
|
||||
|
||||
- Branch: `TASK22028-phase15-hardening-slice`
|
||||
- Delivered follow-up fixes:
|
||||
- myportal representation delete calls now use `caseRef` in top-three and view-all flows
|
||||
- missing `myportal` namespace added for `/newappeal/[appealtypes]` in `i18n.js`
|
||||
|
||||
### Validation snapshot (myportal + i18n follow-up)
|
||||
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (12/12)
|
||||
- `npm run lint` -> **warnings only** (pre-existing)
|
||||
|
||||
## Outstanding risks / gaps
|
||||
|
||||
- Navigation regressions across EN/CY + route query combinations.
|
||||
- Drift between rewrite config and component-level locale path logic.
|
||||
- Deployment ambiguity due to multiple CI/CD artifacts (Azure pipeline, Jenkins, Docker) with unclear active source of truth.
|
||||
- Security posture variability where hash checks are bypassed/commented in selected handlers.
|
||||
|
||||
## Latest update (2026-03-13 — Phase 7 post-Phase-6 hardening)
|
||||
|
||||
- New branch created from `origin/SIPS-Development` with required work item prefix:
|
||||
- `TASK21988a-phase7-postphase6-hardening`
|
||||
- Fixed known pre-existing SSR serialization issue on search results route:
|
||||
- `pages/searchresults.js`
|
||||
- updated SSR dispatch fallback from `setSearch(query.q)` to `setSearch(query?.q || "")`
|
||||
- result: `/searchresults` and `/cy/searchresults` now return 200 in local smoke checks (no 500 observed in this run).
|
||||
- Added expanded behavioural coverage for remaining direct-service domains:
|
||||
- new test file: `tests/phase7/service-behaviour.test.cjs`
|
||||
- includes document, portal, account, notify, integration behavioural checks
|
||||
- includes negative-path assertions for relay/hash/token-sensitive behaviors where feasible:
|
||||
- hashed URL append checks (`hashAPIPath`)
|
||||
- error handling contracts (undefined/error string/rethrow depending on existing function contract)
|
||||
|
||||
### Validation snapshot (Phase 7)
|
||||
|
||||
- `node tests/phase6/service-parity.test.cjs` -> **pass**
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> **pass** (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> **pass** (10/10)
|
||||
- `npm run lint` -> **warnings only** (pre-existing hook dependency warnings; no new lint errors)
|
||||
|
||||
### Targeted smoke snapshot (local dev server)
|
||||
|
||||
- Note: dev server auto-started on `http://localhost:3001` because port `3000` was in use.
|
||||
- Search flow:
|
||||
- `GET /searchresults` and `GET /cy/searchresults` -> **200**
|
||||
- Case flow:
|
||||
- `GET /case` and `GET /cy/case` -> **200**
|
||||
- My Portal flow (negative-path):
|
||||
- `GET /myportal` and `GET /cy/myportal` -> **307** redirect to `/auth/signin`
|
||||
- Document flow (negative-path):
|
||||
- `GET /api/file/getbloblistproxy?container=test&casefolderID=test` -> **400**
|
||||
- Notify flow (negative-path):
|
||||
- `POST /api/email/notify` with `{}` -> **400**
|
||||
@@ -1,991 +0,0 @@
|
||||
# Refactor Plan — Priority 1 (`actions/index.js` split)
|
||||
|
||||
Last updated: 2026-03-13
|
||||
|
||||
## Handover note (2026-03-13)
|
||||
|
||||
- Import-migration wave from `actions` barrel to focused modules has been checked in and completed via PR.
|
||||
- Next chunk will be delivered from a **new branch** and should start at legacy decomposition.
|
||||
|
||||
### Next branch kickoff scope (Phase 5)
|
||||
|
||||
Primary objective: split `actions/services/legacyActionsService.js` into smaller direct implementations while preserving function signatures.
|
||||
|
||||
Recommended first slice:
|
||||
|
||||
1. Extract reference/search internals from `legacyActionsService.js` into dedicated modules.
|
||||
2. Keep `actions/services/*` exports stable and route through new direct implementations.
|
||||
3. Add focused parity checks per extracted function group (inputs, headers, hash behavior, return shapes).
|
||||
|
||||
Then continue with:
|
||||
|
||||
4. Portal/document extraction in small batches.
|
||||
5. Remove dead wrappers from `legacyActionsService.js` once call paths are fully migrated.
|
||||
|
||||
### Phase 5 slice roadmap (completed)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Search + reference direct extraction
|
||||
- `searchDirectService.js`, `referenceDataDirectService.js`
|
||||
- `searchService.js` / `referenceDataService.js` re-pointed
|
||||
2. `[x]` Document direct extraction
|
||||
- create `documentDirectService.js`
|
||||
- re-point `documentService.js`
|
||||
- parity checks: blob paths/hash behavior/return shapes
|
||||
3. `[x]` Portal direct extraction
|
||||
- create `portalDirectService.js`
|
||||
- re-point `portalService.js`
|
||||
- parity checks: watched-case and completion-message flows
|
||||
4. `[x]` Account + case direct extraction
|
||||
- create `accountDirectService.js`, `caseDirectService.js`
|
||||
- re-point grouped services
|
||||
5. `[x]` Notify + integration + admin direct extraction
|
||||
- create `notifyDirectService.js`, `integrationDirectService.js`, `adminDirectService.js`
|
||||
6. `[x]` Legacy slim-down pass
|
||||
- remove dead wrappers from `legacyActionsService.js`
|
||||
- keep only temporary compatibility exports still required
|
||||
7. `[x]` Final cleanup + validation pass
|
||||
- grep checks for remaining `./legacyActionsService` imports
|
||||
- lint/manual smoke checks
|
||||
|
||||
### Phase 5 completion snapshot (2026-03-13)
|
||||
|
||||
- All grouped service modules now route through focused direct modules under `actions/services/*DirectService.js`.
|
||||
- `actions/services/legacyActionsService.js` has been removed.
|
||||
- Verification completed:
|
||||
- `actions/services` scan returns zero `./legacyActionsService` imports.
|
||||
- `npm run lint` completes with warnings only.
|
||||
- `npm run build` completes successfully.
|
||||
|
||||
### Next phase kickoff recommendation
|
||||
|
||||
Focus on post-split hardening and cleanup:
|
||||
|
||||
1. Add parity-focused tests for extracted direct services and `actions/services/index.js` export stability.
|
||||
2. Consolidate repeated axios/error-handling patterns into shared clients/utilities where safe.
|
||||
3. Reduce verbose debug logging in direct services (especially account/case/document paths) with redaction discipline.
|
||||
4. Run targeted manual smoke matrix for search/case/myportal/document/notify with EN/CY parity checks.
|
||||
|
||||
### Phase 6 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Add parity-focused tests for extracted services and barrel stability
|
||||
- `tests/phase6/service-parity.test.cjs`
|
||||
- validates grouped/direct service export parity
|
||||
- validates `actions/services/index.js` export stability
|
||||
2. `[x]` Reduce repeated axios/error-handling patterns safely
|
||||
- added shared helper: `actions/services/httpServiceUtils.js`
|
||||
- adopted in selected direct modules without signature/return-shape changes
|
||||
3. `[x]` Logging cleanup in sensitive direct-service flows
|
||||
- removed noisy debug logs from search/document/portal/case/admin direct services
|
||||
4. `[x]` Add focused behavioural contract tests in current phase
|
||||
- `tests/phase6/service-behaviour.test.cjs`
|
||||
- success/error contract checks with mocked axios/logger (8/8 passing)
|
||||
5. `[x]` Run targeted validation/smoke checks
|
||||
- parity test: pass
|
||||
- behavioural test: pass
|
||||
- manual smoke matrix executed (search/case/myportal/document/notify, EN/CY + negative paths)
|
||||
|
||||
Notes:
|
||||
|
||||
- Known pre-existing caveat during smoke checks:
|
||||
- `/searchresults` and `/cy/searchresults` return 500 in local dev due SSR serialization issue (`initialState.search.searchString` undefined).
|
||||
- treated as existing issue, not introduced by this refactor phase.
|
||||
|
||||
### Future requirements / next-phase backlog (post-Phase-6)
|
||||
|
||||
1. Investigate and fix `/searchresults` SSR serialization issue (EN/CY parity).
|
||||
2. Expand behavioural service tests to remaining domains:
|
||||
- document
|
||||
- portal
|
||||
- account
|
||||
- notify
|
||||
- integration
|
||||
3. Add focused negative-path tests for token/hash/relay-sensitive handlers.
|
||||
4. Continue small-batch hardening with reversible commits and parity checks per batch.
|
||||
5. Prepare PR evidence bundle for each increment:
|
||||
- lint + test commands
|
||||
- behavioural/parity test outputs
|
||||
- manual EN/CY + negative-path smoke matrix
|
||||
|
||||
### Phase 7 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Create new branch from `origin/SIPS-Development` with required work item prefix
|
||||
- branch: `TASK21988a-phase7-postphase6-hardening`
|
||||
2. `[x]` Fix pre-existing `/searchresults` SSR serialization issue with EN/CY parity
|
||||
- updated `pages/searchresults.js`:
|
||||
- `setSearch(query.q)` -> `setSearch(query?.q || "")`
|
||||
3. `[x]` Expand behavioural tests to remaining service domains
|
||||
- added `tests/phase7/service-behaviour.test.cjs`
|
||||
- coverage includes: document, portal, account, notify, integration
|
||||
4. `[x]` Add feasible negative-path tests for token/hash/relay-sensitive flows
|
||||
- validated hashed path composition (e.g., `hashAPIPath` usage)
|
||||
- validated existing function error contracts (undefined/error string/rethrow)
|
||||
5. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `npm run lint` -> warnings only (no new errors)
|
||||
6. `[x]` Execute targeted manual smoke matrix (EN/CY + negative paths)
|
||||
- local server on `localhost:3001` (3000 occupied)
|
||||
- `/searchresults`, `/cy/searchresults` -> 200
|
||||
- `/case`, `/cy/case` -> 200
|
||||
- `/myportal`, `/cy/myportal` -> 307 -> `/auth/signin`
|
||||
- `/api/file/getbloblistproxy?container=test&casefolderID=test` -> 400
|
||||
- `POST /api/email/notify` with `{}` -> 400
|
||||
|
||||
### Phase 7 rollback notes
|
||||
|
||||
- Revert `pages/searchresults.js` fallback change if search behaviour regresses unexpectedly.
|
||||
- Remove `tests/phase7/service-behaviour.test.cjs` if test scope needs to be rolled back.
|
||||
- Reset branch to pre-Phase-7 commit if full rollback required.
|
||||
|
||||
### Phase 8 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Create new branch from `origin/SIPS-Development` with required work item prefix
|
||||
- branch: `TASK22017-phase8-hardening-slice`
|
||||
2. `[x]` Re-enable/enforce hash validation in 4 sensitive target handlers
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/endpoint/getportallogin_api.js`
|
||||
3. `[x]` Standardize negative-path behavior for invalid/missing hash and required keys
|
||||
- added early 400 guards for missing required query params where applicable
|
||||
- preserved response-shape contracts/signatures
|
||||
4. `[x]` Improve logging discipline in sensitive flow
|
||||
- removed noisy direct console output in `createappealcompletemessage_api`
|
||||
- routed updated error path logging via `consoleLogger`
|
||||
5. `[x]` Keep caller behavior compatible after hash re-enforcement
|
||||
- `actions/services/documentDirectService.js` now appends hash for:
|
||||
- `deleteblobcase`
|
||||
- `deleteblobrep`
|
||||
- `actions/services/portalDirectService.js` now appends hash for:
|
||||
- `createappealcompletemessage_api` (existing hash-path contract retained)
|
||||
6. `[x]` Add focused Phase 8 tests
|
||||
- added `tests/phase8/service-behaviour.test.cjs`
|
||||
- includes negative-path coverage for all 4 target handlers
|
||||
- includes one valid-hash happy-path contract check (`getportallogin_api`) via mocks
|
||||
7. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (pre-existing react-hooks dependency warnings)
|
||||
8. `[x]` Execute manual HTTP negative-path matrix + feasible happy-path spot-check
|
||||
- invalid/missing hash across 4 target handlers -> 400
|
||||
- missing required params where tested -> 400
|
||||
- valid-hash `getportallogin_api` spot-check returned 400 in local env due upstream relay/CRM dependency
|
||||
|
||||
### Phase 8 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/endpoint/getportallogin_api.js`
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `actions/services/portalDirectService.js`
|
||||
- `tests/phase8/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 9 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Create new branch from `origin/SIPS-Development` with required work item prefix
|
||||
- branch: `TASK22019-phase-9-hardening`
|
||||
2. `[x]` Re-enable/enforce hash validation in 4 additional sensitive file handlers
|
||||
- `pages/api/file/createrepcompletemessage_api.js`
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
3. `[x]` Standardize negative-path behavior for invalid/missing hash and key params
|
||||
- added early 400 handling for missing required query/body values where applicable
|
||||
4. `[x]` Improve logging discipline in sensitive paths
|
||||
- removed noisy direct console logging in touched handlers
|
||||
5. `[x]` Keep caller behavior compatible after hash re-enforcement
|
||||
- `actions/services/portalDirectService.js`
|
||||
- `sendRepCompleteMessage` now appends hash
|
||||
- `actions/services/documentDirectService.js`
|
||||
- upload-related calls now append hash
|
||||
6. `[x]` Add focused Phase 9 tests
|
||||
- added `tests/phase9/service-behaviour.test.cjs`
|
||||
- negative-path tests for all 4 selected handlers
|
||||
7. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (no new lint errors)
|
||||
8. `[x]` Execute manual HTTP negative-path matrix
|
||||
- missing/invalid hash checks across selected handlers -> 400
|
||||
- missing key params where tested -> 400
|
||||
9. `[x]` Valid-hash HTTP happy-path spot-check
|
||||
- `POST /api/file/uploadsinglefile?hash=<valid>` -> 200 (hash generated from `.env.local` key)
|
||||
|
||||
### Phase 9 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/createrepcompletemessage_api.js`
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- `actions/services/portalDirectService.js`
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `tests/phase9/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7/phase8/phase9 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 10 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Continue connected hardening slices on same branch
|
||||
- branch: `TASK22019-phase-9-hardening`
|
||||
2. `[x]` Re-enable/standardize hash + param guard handling in 4 additional file retrieval handlers
|
||||
- `pages/api/file/getawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
3. `[x]` Standardize negative-path behavior
|
||||
- added explicit early 400 for missing required query/hash values
|
||||
- standardized early 400 for hash mismatch
|
||||
4. `[x]` Improve logging discipline in touched handlers
|
||||
- removed old commented debug traces
|
||||
5. `[x]` Add focused Phase 10 tests
|
||||
- added `tests/phase10/service-behaviour.test.cjs`
|
||||
- includes negative-path tests for all 4 handlers
|
||||
6. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `npm run lint` -> warnings only (no new lint errors)
|
||||
7. `[x]` Execute manual HTTP negative-path matrix + valid-hash spot-check
|
||||
- missing hash across selected handlers -> 400
|
||||
- valid hash `getbloblist` spot-check -> 500 (downstream/local dependency)
|
||||
|
||||
### Phase 10 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/getawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- `tests/phase10/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7/phase8/phase9/phase10 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 11 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Continue connected hardening slices on same branch
|
||||
- branch: `TASK22019-phase-9-hardening`
|
||||
2. `[x]` Re-enable/standardize hash + param guard handling in additional sensitive handlers
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
3. `[x]` Standardize negative-path behavior
|
||||
- added explicit early 400 for missing required query/hash values
|
||||
- standardized early 400 for hash mismatch
|
||||
4. `[x]` Improve logging discipline in touched handlers
|
||||
- removed old commented debug traces
|
||||
5. `[x]` Add focused Phase 11 tests
|
||||
- added `tests/phase11/service-behaviour.test.cjs`
|
||||
- includes negative-path tests for selected handlers
|
||||
6. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `npm run lint` -> warnings only (no new lint errors)
|
||||
7. `[x]` Execute manual HTTP negative-path matrix + valid-hash spot-check
|
||||
- missing/invalid hash across selected handlers -> 400
|
||||
- valid hash `getbloblist` spot-check -> 500 (downstream/local dependency)
|
||||
|
||||
### Phase 11 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- `tests/phase11/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7/phase8/phase9/phase10/phase11 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 12 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Continue connected hardening slices on same branch
|
||||
- branch: `TASK22019-phase-9-hardening`
|
||||
2. `[x]` Re-enable/standardize hash + param guard handling in additional sensitive handlers
|
||||
- `pages/api/file/downloadblob.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
3. `[x]` Standardize negative-path behavior
|
||||
- added explicit early 400 for missing required query/hash values
|
||||
- standardized early 400 for hash mismatch in `downloadblob`
|
||||
4. `[x]` Add focused Phase 12 tests
|
||||
- added `tests/phase12/service-behaviour.test.cjs`
|
||||
- includes negative-path tests for selected handlers
|
||||
5. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `npm run lint` -> warnings only (no new lint errors)
|
||||
6. `[x]` Execute manual HTTP negative-path matrix + valid-hash spot-check
|
||||
- missing/invalid inputs across selected handlers -> 400
|
||||
- valid hash `downloadblob` spot-check -> 500 (downstream/local dependency)
|
||||
|
||||
### Phase 12 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/downloadblob.js`
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- `tests/phase12/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7/phase8/phase9/phase10/phase11/phase12 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 13 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Continue connected hardening slices on same branch
|
||||
- branch: `TASK22019-phase-9-hardening`
|
||||
2. `[x]` Re-enable/standardize hash + param guard handling in additional sensitive handlers
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createcaseinvolvement_api.js`
|
||||
- `pages/api/file/createrepinvolvement_api.js`
|
||||
3. `[x]` Standardize negative-path behavior
|
||||
- added explicit early 400 for missing required query/hash/body values
|
||||
- standardized early 400 for hash mismatch in delete handlers
|
||||
4. `[x]` Improve logging discipline in touched handlers
|
||||
- removed noisy body/query logging in involvement handlers
|
||||
5. `[x]` Add focused Phase 13 tests
|
||||
- added `tests/phase13/service-behaviour.test.cjs`
|
||||
- includes negative-path tests and valid-input mocked happy paths
|
||||
6. `[x]` Execute validation bundle
|
||||
- `node tests/phase6/service-parity.test.cjs` -> pass
|
||||
- `node tests/phase6/service-behaviour.test.cjs` -> pass (8/8)
|
||||
- `node tests/phase7/service-behaviour.test.cjs` -> pass (10/10)
|
||||
- `node tests/phase8/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase9/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase10/service-behaviour.test.cjs` -> pass (5/5)
|
||||
- `node tests/phase11/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase12/service-behaviour.test.cjs` -> pass (4/4)
|
||||
- `node tests/phase13/service-behaviour.test.cjs` -> pass (7/7)
|
||||
- `npm run lint` -> warnings only (no new lint errors)
|
||||
7. `[x]` Execute manual HTTP negative-path matrix + valid-hash spot-check
|
||||
- missing/invalid inputs across selected handlers -> 400
|
||||
- valid hash `deleteblobcase` spot-check -> 500 (downstream/local dependency)
|
||||
|
||||
### Phase 13 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/deleteblobcase.js`
|
||||
- `pages/api/file/deleteblobrep.js`
|
||||
- `pages/api/file/createcaseinvolvement_api.js`
|
||||
- `pages/api/file/createrepinvolvement_api.js`
|
||||
- `tests/phase13/service-behaviour.test.cjs`
|
||||
- Re-run phase6/phase7/phase8/phase9/phase10/phase11/phase12/phase13 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 14 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Diagnose authenticated browser journey runtime error
|
||||
- traced to client-side `hashAPIPath` usage reading server-only `HASHKEY`
|
||||
2. `[x]` Implement minimal reversible signing bridge
|
||||
- added `pages/api/endpoint/gethash_api.js`
|
||||
- session-gated with allow-listed API path prefixes
|
||||
3. `[x]` Keep client/service contracts stable
|
||||
- updated `documentDirectService` and `portalDirectService` to request hash from signer endpoint
|
||||
- kept server-side fallback hashing only when env key exists
|
||||
4. `[x]` Add focused phase-14 tests
|
||||
- added `tests/phase14/service-behaviour.test.cjs`
|
||||
- covers 400/401 negative paths and authenticated happy path
|
||||
5. `[x]` Execute validation bundle
|
||||
- phase6–phase14 test set all pass
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Phase 14 rollback notes
|
||||
|
||||
- Revert these files to rollback the fix slice:
|
||||
- `pages/api/endpoint/gethash_api.js`
|
||||
- `actions/services/documentDirectService.js`
|
||||
- `actions/services/portalDirectService.js`
|
||||
- `tests/phase14/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase14 tests + lint after rollback.
|
||||
|
||||
### Phase 14 closeout follow-up snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Expand signer usage to remaining browser-sensitive hashed call in account domain
|
||||
- `actions/services/accountDirectService.js`
|
||||
- `getPortalLogin` now requests hash from authenticated signer endpoint
|
||||
2. `[x]` Extend signer allow-list minimally for required route
|
||||
- `pages/api/endpoint/gethash_api.js`
|
||||
- added `/api/endpoint/getportallogin_api` only
|
||||
3. `[x]` Preserve and verify behavioural contract coverage
|
||||
- `tests/phase7/service-behaviour.test.cjs` updated for signer flow
|
||||
- `tests/phase14/service-behaviour.test.cjs` extended with allow-list path test
|
||||
4. `[x]` Apply runtime-discovered contract alignment fixes (minimal/reversible)
|
||||
- `pages/api/file/getrepsblob.js`
|
||||
- removed unnecessary `casefolderID` requirement to match endpoint contract
|
||||
- `components/elements/index.js`
|
||||
- restored missing `updateLinks` import for Quill path
|
||||
5. `[x]` Re-run required validation
|
||||
- phase6 parity + behaviour -> pass
|
||||
- phase7/8/9/10/11/12/13/14 behaviour -> pass
|
||||
- lint -> warnings only (pre-existing)
|
||||
6. `[x]` Push branch for review continuity
|
||||
- `TASK22019-phase-9-hardening` pushed to origin
|
||||
|
||||
### Phase 14 closeout follow-up rollback notes
|
||||
|
||||
- Revert `6094874` to remove portal-login signer migration and tests.
|
||||
- Revert `b8fa514` to remove getrepsblob contract-alignment + Quill import fix.
|
||||
- Re-run phase6–phase14 tests and lint after rollback.
|
||||
|
||||
### Phase 15 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Create new branch from `origin/SIPS-Development` with required work item prefix
|
||||
- branch: `TASK22028-phase15-hardening-slice`
|
||||
2. `[x]` Apply minimal consistency-only hardening in 4 additional sensitive handlers
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
3. `[x]` Standardize negative-path behavior
|
||||
- explicit early 400 for missing/invalid hash in selected handlers
|
||||
- explicit missing required-input checks where route contract requires them
|
||||
- preserved response-shape contracts and function signatures
|
||||
4. `[x]` Add focused Phase 15 tests
|
||||
- added `tests/phase15/service-behaviour.test.cjs`
|
||||
- includes negative-path tests for missing/invalid hash + required-input checks
|
||||
- includes one valid-hash mocked happy-path response-shape check
|
||||
5. `[x]` Execute required validation bundle
|
||||
- phase6 parity + phase6–phase14 behaviour tests -> pass
|
||||
- phase15 behaviour tests -> pass
|
||||
- lint -> warnings only (pre-existing)
|
||||
6. `[x]` Execute targeted manual checks
|
||||
- missing/invalid hash across selected handlers -> 400
|
||||
- missing required params where applicable -> 400
|
||||
- valid-hash `setupcontainer` spot-check -> 200
|
||||
|
||||
### Phase 15 rollback notes
|
||||
|
||||
- Revert these files to rollback the full hardening slice:
|
||||
- `pages/api/file/upload.js`
|
||||
- `pages/api/file/uploadsinglefile.js`
|
||||
- `pages/api/file/createappealcompletemessage_api.js`
|
||||
- `pages/api/file/setupcontainer.js`
|
||||
- `tests/phase15/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase15 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 16 completion snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Continue on current branch with separate commit for additional small slice
|
||||
- branch: `TASK22028-phase15-hardening-slice`
|
||||
2. `[x]` Apply minimal consistency hardening in 3 file proxy handlers
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
3. `[x]` Standardize required-input guard pattern
|
||||
- added local `hasValue` checks for required params
|
||||
- removed unused/dead imports/constants in touched files
|
||||
4. `[x]` Add focused Phase 16 tests
|
||||
- added `tests/phase16/service-behaviour.test.cjs`
|
||||
- missing-input negative-path checks + one valid-input contract check
|
||||
5. `[x]` Execute validation bundle
|
||||
- phase6 parity + phase6–phase16 behaviour tests pass
|
||||
- lint warnings only (pre-existing)
|
||||
6. `[x]` Execute manual checks
|
||||
- missing required-input proxy calls -> 400
|
||||
|
||||
### Phase 16 rollback notes
|
||||
|
||||
- Revert commit `c196edb` (or files):
|
||||
- `pages/api/file/getbloblistproxy.js`
|
||||
- `pages/api/file/getrepsblobproxy.js`
|
||||
- `pages/api/file/getawaitingsubmissionfromblobproxy.js`
|
||||
- `tests/phase16/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase16 baseline tests and lint.
|
||||
|
||||
### Follow-up completion snapshot (2026-03-13)
|
||||
|
||||
1. `[x]` Address browser signer failure for delete-awaiting-submission path
|
||||
- added signer allow-list entry: `/api/file/deleteblobcase`
|
||||
2. `[x]` Add focused behavioural test coverage
|
||||
- extended `tests/phase14/service-behaviour.test.cjs`
|
||||
3. `[x]` Validate
|
||||
- phase14 behaviour tests pass (6/6)
|
||||
- phase7 behaviour tests pass (10/10)
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Follow-up rollback notes
|
||||
|
||||
- Revert commit `49dd990` to undo allow-list/test follow-up.
|
||||
|
||||
### Follow-up completion snapshot (2026-03-13 — additional delete flows)
|
||||
|
||||
1. `[x]` Expand signer allow-list for required browser delete paths
|
||||
2. `[x]` Move remaining delete service calls to signer-based hash retrieval
|
||||
3. `[x]` Extend focused service/signer tests
|
||||
4. `[x]` Validate
|
||||
- phase7 behaviour tests pass (12/12)
|
||||
- phase14 behaviour tests pass (9/9)
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Follow-up rollback notes (additional delete flows)
|
||||
|
||||
- Revert the follow-up commit for this slice to restore prior hashing behavior.
|
||||
|
||||
### Follow-up completion snapshot (2026-03-13 — myportal + i18n alignment)
|
||||
|
||||
1. `[x]` Align myportal delete-representation calls to use `caseRef`
|
||||
2. `[x]` Add missing i18n namespace mapping for `/newappeal/[appealtypes]`
|
||||
3. `[x]` Validate
|
||||
- phase7 behaviour tests pass (12/12)
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Follow-up rollback notes (myportal + i18n alignment)
|
||||
|
||||
- Revert commit `d32d7d0` to restore prior myportal/i18n state.
|
||||
|
||||
### Phase 17 closeout snapshot (2026-03-13)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Create new branch from `origin/SIPS-Development` with required work item prefix
|
||||
- branch: `TASK22057-phase17-hardening-closeout`
|
||||
2. `[x]` Complete remaining consistency-only hardening targets in `pages/api/file/**`
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- `pages/api/file/createappealcompletemessageproxy_api.js`
|
||||
3. `[x]` Standardize negative-path behavior and logging discipline
|
||||
- explicit early 400 guards for missing/invalid hash where applicable
|
||||
- explicit early 400 guards for missing required params where applicable
|
||||
- noisy direct logs removed from touched handlers
|
||||
- response-shape/signature contracts preserved
|
||||
4. `[x]` Add focused Phase 17 tests
|
||||
- added `tests/phase17/service-behaviour.test.cjs`
|
||||
- includes missing/invalid hash negative-path checks
|
||||
- includes missing required-param checks
|
||||
- includes valid-input happy-path check for proxy contract
|
||||
5. `[x]` Execute required validation bundle
|
||||
- phase6 parity + phase6–phase17 behaviour tests -> pass
|
||||
- lint -> warnings only (pre-existing)
|
||||
6. `[x]` Consistency scan for remaining `pages/api/**`
|
||||
- no additional broad hash/400 consistency gaps identified in `pages/api/endpoint/**` during this pass
|
||||
|
||||
### Phase 17 rollback notes
|
||||
|
||||
- Revert these files to rollback the full closeout slice:
|
||||
- `pages/api/file/generatepdf.js`
|
||||
- `pages/api/file/generateappealpdf.js`
|
||||
- `pages/api/file/createappealcompletemessageproxy_api.js`
|
||||
- `tests/phase17/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase17 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 18 additional consolidation slices snapshot (2026-03-14)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Confirm requested scope expansion to implement all 3 additional slices
|
||||
2. `[x]` Apply endpoint logging/input hygiene slice
|
||||
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||
- `pages/api/endpoint/getaccounts_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||
3. `[x]` Apply file hash-guard flow consistency slice
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
4. `[x]` Apply create/update input-guard + logging hygiene slice
|
||||
- `pages/api/file/createcase_api.js`
|
||||
- `pages/api/file/updatecase_api.js`
|
||||
- `pages/api/file/editRepJson.js`
|
||||
5. `[x]` Add focused phase-18 behavioural tests
|
||||
- added `tests/phase18/service-behaviour.test.cjs`
|
||||
- negative-path coverage for required inputs/hash
|
||||
- contract-preserving happy-path checks on selected handlers
|
||||
6. `[x]` Execute validation bundle
|
||||
- phase6 parity + phase6–phase18 behavioural tests all pass
|
||||
- lint warnings only (pre-existing `react-hooks/exhaustive-deps` set)
|
||||
|
||||
### Phase 18 rollback notes
|
||||
|
||||
- Revert files in this slice:
|
||||
- `pages/api/endpoint/deletewatchedcases_api.js`
|
||||
- `pages/api/endpoint/getaccounts_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchpaged_api.js`
|
||||
- `pages/api/endpoint/getappealpdfdocuments_api.js`
|
||||
- `pages/api/file/deleteblob.js`
|
||||
- `pages/api/file/deleteawaitingsubmissionfromblob.js`
|
||||
- `pages/api/file/getprogressobjblob.js`
|
||||
- `pages/api/file/getbloblist.js`
|
||||
- `pages/api/file/createcase_api.js`
|
||||
- `pages/api/file/updatecase_api.js`
|
||||
- `pages/api/file/editRepJson.js`
|
||||
- `tests/phase18/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase18 baseline tests and lint after rollback to confirm parity.
|
||||
|
||||
### Phase 19 endpoint consistency slice snapshot (2026-03-14)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Apply small consistency-only endpoint hardening in selected handlers
|
||||
- `pages/api/endpoint/createwatchedcases_api.js`
|
||||
- `pages/api/endpoint/getappealtypes_api.js`
|
||||
- `pages/api/endpoint/getdnslist_api.js`
|
||||
2. `[x]` Standardize negative-path required input handling
|
||||
- early 400 on missing/malformed required `@odata.bind` values in `createwatchedcases_api`
|
||||
3. `[x]` Remove noisy sensitive-path logging
|
||||
- removed direct query logging in appeal-types and dns-list handlers
|
||||
4. `[x]` Add focused tests
|
||||
- `tests/phase19/service-behaviour.test.cjs` (5/5)
|
||||
5. `[x]` Execute validation bundle
|
||||
- phase6 parity + phase6–phase19 behavioural suites pass
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Phase 19 rollback notes
|
||||
|
||||
- Revert files in this slice:
|
||||
- `pages/api/endpoint/createwatchedcases_api.js`
|
||||
- `pages/api/endpoint/getappealtypes_api.js`
|
||||
- `pages/api/endpoint/getdnslist_api.js`
|
||||
- `tests/phase19/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase19 baseline tests and lint after rollback.
|
||||
|
||||
### Phase 20 endpoint consistency slice snapshot (2026-03-14)
|
||||
|
||||
Status key: `[x] done`, `[ ] pending`
|
||||
|
||||
1. `[x]` Apply small consistency-only endpoint hardening in selected handlers
|
||||
- `pages/api/endpoint/getbasicdnssearch_api.js`
|
||||
- `pages/api/endpoint/getbasicdnsurlsearch_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchdetailspaged_api.js`
|
||||
2. `[x]` Standardize negative-path required input handling
|
||||
- early 400 for missing/empty `searchString` in `getbasicdnsurlsearch_api`
|
||||
- early 400 for missing required params in `getbasicsearchdetailspaged_api`
|
||||
3. `[x]` Remove noisy sensitive-path logging
|
||||
- removed direct query logging in `getbasicdnssearch_api`
|
||||
4. `[x]` Add focused tests
|
||||
- `tests/phase20/service-behaviour.test.cjs` (5/5)
|
||||
5. `[x]` Execute validation bundle
|
||||
- phase6 parity + phase6–phase20 behavioural suites pass
|
||||
- lint warnings only (pre-existing)
|
||||
|
||||
### Phase 20 rollback notes
|
||||
|
||||
- Revert files in this slice:
|
||||
- `pages/api/endpoint/getbasicdnssearch_api.js`
|
||||
- `pages/api/endpoint/getbasicdnsurlsearch_api.js`
|
||||
- `pages/api/endpoint/getbasicsearchdetailspaged_api.js`
|
||||
- `tests/phase20/service-behaviour.test.cjs`
|
||||
- Re-run phase6–phase20 baseline tests and lint after rollback.
|
||||
|
||||
## Safe execution mode for migration chunks (required)
|
||||
|
||||
To reduce terminal hangs during bulk migration work, run refactor chunks in **safe stepwise mode** instead of long chained commands.
|
||||
|
||||
Required command pattern:
|
||||
|
||||
1. Read/inspect target files.
|
||||
2. Apply edits only.
|
||||
3. Verify with targeted grep for remaining broad imports.
|
||||
4. Check `git status`.
|
||||
5. Stage files.
|
||||
6. Commit.
|
||||
7. Re-run verification and then continue to the next chunk.
|
||||
|
||||
Rules:
|
||||
|
||||
- Do **not** combine edit + verify + add + commit + log in one long chained command.
|
||||
- Keep each terminal call short and single-purpose.
|
||||
- If a command is interrupted, re-check `git status` and resume from the next incomplete step.
|
||||
- Preserve smallest viable diff and behavior parity.
|
||||
|
||||
## Implementation status snapshot (2026-03-12)
|
||||
|
||||
- Phase 1 completed:
|
||||
- Extracted core helpers into `actions/core/{env,logger,hash,token,headers}.js`.
|
||||
- Moved wrapper functions into `actions/services/legacyActionsService.js`.
|
||||
- Reduced `actions/index.js` to compatibility barrel re-exports.
|
||||
- Added `actions/clients/README.md` scaffold for upcoming client extraction.
|
||||
- Validation status:
|
||||
- `npm run lint` currently fails at framework/tooling option level (legacy ESLint options), not due to this refactor logic.
|
||||
- Manual test matrix is required next (search/case/myportal/auth/notify/file).
|
||||
|
||||
## Implementation status snapshot (Phase 2 update — 2026-03-12)
|
||||
|
||||
- Phase 2 completed:
|
||||
- Added grouped service modules under `actions/services/`:
|
||||
- `searchService.js`
|
||||
- `caseService.js`
|
||||
- `accountService.js`
|
||||
- `portalService.js`
|
||||
- `documentService.js`
|
||||
- `referenceDataService.js`
|
||||
- `notifyService.js`
|
||||
- `adminService.js`
|
||||
- `integrationService.js`
|
||||
- Added `actions/services/index.js` barrel.
|
||||
- Updated `actions/index.js` to export from `./services` and `./core/*`.
|
||||
- Verified export parity: all `legacyActionsService` exports are represented by grouped service modules.
|
||||
- Current compatibility model:
|
||||
- Consumers can continue importing from `../actions` with unchanged function names/signatures.
|
||||
- `legacyActionsService` remains an internal compatibility implementation module until Phase 3 consumer migration.
|
||||
|
||||
## Implementation status snapshot (Phase 3 update — 2026-03-12)
|
||||
|
||||
- Phase 3 started with targeted consumer migration to focused imports.
|
||||
- Updated high-churn consumers:
|
||||
- `components/case/summary.js`
|
||||
- `createWatchedCases`, `deleteWatchedCases`, `getWatchedCasesProxy`, `setCaseInvolvment` -> `actions/services/portalService`
|
||||
- `getLinkedCases` -> `actions/services/searchService`
|
||||
- `getPortalModuleDetailsProxy` -> `actions/services/caseService`
|
||||
- `pages/api/email/notify.js`
|
||||
- `consoleLogger` -> `actions/core/logger`
|
||||
- `getPreferredLanguage` -> `actions/services/accountService`
|
||||
- Updated selected endpoint handlers to core imports:
|
||||
- `pages/api/endpoint/getwatchedcases_api.js`
|
||||
- `pages/api/endpoint/getadvancedsearchpaged_api.js`
|
||||
- `pages/api/endpoint/getpreferredlanguage_api.js`
|
||||
- `pages/api/endpoint/createcrmtask_api.js`
|
||||
- Compatibility remains preserved via `actions/index.js` barrel while migration proceeds incrementally.
|
||||
|
||||
## Implementation status snapshot (Phase 4 update — 2026-03-12)
|
||||
|
||||
- Phase 4 started with targeted hardening helpers and sensitive-path adoption.
|
||||
- Added `actions/core/guards.js` with:
|
||||
- `isNonEmptyString`
|
||||
- `sanitizeString`
|
||||
- `escapeODataString`
|
||||
- Expanded `actions/core/logger.js` with redaction support:
|
||||
- `redactSensitive`
|
||||
- masking for likely email/token/secret patterns in logged content
|
||||
- Updated `actions/index.js` compatibility barrel to export `./core/guards`.
|
||||
- Applied hardening in selected sensitive/high-churn handlers:
|
||||
- `pages/api/email/notify.js`
|
||||
- email input sanitization + required-field guard
|
||||
- redacted logging for outbound payload diagnostics
|
||||
- `pages/api/endpoint/getpreferredlanguage_api.js`
|
||||
- sanitized/validated `emailAddress`
|
||||
- OData string escaping for query construction
|
||||
- redacted query logging
|
||||
- `pages/api/endpoint/createcrmtask_api.js`
|
||||
- sanitized/validated request inputs for subject/email/body
|
||||
- centralized structured error logging via `consoleLogger`
|
||||
|
||||
## Implementation status snapshot (Priority 1 execution pass — 2026-03-12)
|
||||
|
||||
- Completed another targeted consumer migration pass from broad `actions` barrel imports to focused service/core imports in these files:
|
||||
- `components/admin/tabs/documents.js`
|
||||
- `components/admin/tabs/storage.js`
|
||||
- `components/admin/utils/serverside.js`
|
||||
- `components/case/documents.js`
|
||||
- `components/case/representation/representationComplete.js`
|
||||
- `components/case/representation/representationElements.js`
|
||||
- `components/elements/index.js`
|
||||
- `components/myportal/awaitingsubmissionfromblob.js`
|
||||
- `components/myportal/topthree.js`
|
||||
- `components/myportal/topthree_reps.js`
|
||||
- `components/utils/index.js`
|
||||
- `lib/myportal/loadMyPortalAppealPage.js`
|
||||
- `lib/newappeal/loadNewAppealPage.js`
|
||||
- Kept compatibility behavior via existing `actions/index.js` barrel.
|
||||
- Validation note: `npm run lint` remains blocked by repository ESLint/Next option incompatibility (pre-existing tooling configuration).
|
||||
|
||||
## Goal
|
||||
|
||||
Reduce coupling and regression risk by splitting `actions/index.js` into focused modules while preserving existing behavior and call signatures during migration.
|
||||
|
||||
## Why this is first
|
||||
|
||||
`actions/index.js` is currently a high-risk hotspot: API wrappers, relay/hash helpers, token acquisition, logging, file helpers, and notification helpers are mixed in one module. This increases blast radius for every change.
|
||||
|
||||
## Scope (planned)
|
||||
|
||||
- In scope:
|
||||
- Module extraction and internal architecture cleanup.
|
||||
- Backward-compatible export strategy.
|
||||
- Migration plan for consumers under `components/**`, `pages/**`, and `pages/api/**`.
|
||||
- Out of scope (for this refactor phase):
|
||||
- Functional changes to business logic.
|
||||
- Contract changes to API handlers.
|
||||
- Dependency swaps.
|
||||
|
||||
## Proposed target structure
|
||||
|
||||
```text
|
||||
actions/
|
||||
index.js # compatibility barrel (temporary)
|
||||
core/
|
||||
env.js # BASE_URL/API_ROOT/relay roots
|
||||
logger.js # consoleLogger/conLog with redaction helpers
|
||||
hash.js # hashAPIPath/hashString/dehashString
|
||||
token.js # getToken + token request config
|
||||
headers.js # azureHeaders* helpers
|
||||
clients/
|
||||
relayClient.js # signed relay calls + shared axios behavior
|
||||
endpointClient.js # endpoint route wrappers
|
||||
fileClient.js # file route wrappers
|
||||
notifyClient.js # email/notify wrappers
|
||||
services/
|
||||
caseService.js
|
||||
searchService.js
|
||||
portalService.js
|
||||
accountService.js
|
||||
documentService.js
|
||||
```
|
||||
|
||||
Notes:
|
||||
|
||||
- Keep `actions/index.js` as a façade initially to avoid breaking imports.
|
||||
- Move internals first, then update call sites incrementally.
|
||||
|
||||
## Migration strategy (phased)
|
||||
|
||||
### Phase 1 — Safe extraction with no consumer changes
|
||||
|
||||
1. Create new modules under `actions/core/**` and `actions/clients/**`.
|
||||
2. Move utility functions (`hash`, `headers`, `token`, `logger`, env helpers).
|
||||
3. Re-export all existing functions from `actions/index.js` unchanged.
|
||||
4. Verify parity with lint + smoke checks.
|
||||
|
||||
### Phase 2 — Service grouping
|
||||
|
||||
1. Group route wrapper functions into service modules by domain (case/search/portal/account/document).
|
||||
2. Keep identical function names/signatures.
|
||||
3. Add thin unit tests for pure helpers first (`hash`, `env`, route builder helpers).
|
||||
|
||||
### Phase 3 — Consumer migration
|
||||
|
||||
1. Replace broad imports from `actions/index.js` with targeted imports from new modules.
|
||||
2. Migrate high-churn areas first:
|
||||
- `components/breadcrumbs.js`
|
||||
- `components/case/summary.js`
|
||||
- `pages/api/email/notify.js`
|
||||
- selected `pages/api/endpoint/**` handlers
|
||||
3. Keep index barrel until migration completion.
|
||||
|
||||
### Phase 4 — Harden + slim
|
||||
|
||||
1. Introduce typed/validated request helper boundaries (JS doc + runtime guard checks).
|
||||
2. Remove dead exports and duplicate wrappers.
|
||||
3. Finalize redacted logging policy in sensitive flows.
|
||||
|
||||
## Backward compatibility rules
|
||||
|
||||
- Do not change public function names/signatures during Phases 1–2.
|
||||
- Keep return shapes identical unless explicitly planned and validated.
|
||||
- Preserve existing hash and relay behavior contract.
|
||||
- Preserve EN/CY behavior where helper functions influence locale-sensitive flows.
|
||||
|
||||
## Validation plan
|
||||
|
||||
- `npm run lint`
|
||||
- Manual smoke paths:
|
||||
- search -> results -> case summary -> back nav
|
||||
- myportal viewall -> case summary -> breadcrumb return path
|
||||
- auth email sign-in flow
|
||||
- notify new case reference language selection path
|
||||
- file/document fetch paths touched by moved wrappers
|
||||
- Negative-path checks:
|
||||
- invalid hash or malformed query for sensitive handlers
|
||||
- token acquisition failures and relay timeout handling
|
||||
|
||||
## Regression tests and coverage plan (to add during implementation)
|
||||
|
||||
### Test tooling recommendation
|
||||
|
||||
Current repo has no active automated test runner configured in `package.json`. For this refactor, introduce a minimal unit test setup (recommended: Jest) focused on pure logic first.
|
||||
|
||||
Suggested scripts:
|
||||
|
||||
- `test`: run all unit tests
|
||||
- `test:watch`: local watch mode
|
||||
- `test:coverage`: coverage output for CI and PR evidence
|
||||
|
||||
### Minimum tests for Priority 1
|
||||
|
||||
1. `actions/core/hash.js`
|
||||
- deterministic hash for known input
|
||||
- query string handling (`?hash=` vs `&hash=`)
|
||||
- malformed/edge inputs
|
||||
2. `actions/core/headers.js`
|
||||
- expected OData/auth headers produced for each helper
|
||||
3. `actions/core/token.js`
|
||||
- token request config generation
|
||||
- success and error mapping behavior (mock axios)
|
||||
4. `actions/core/env.js`
|
||||
- server vs browser base URL resolution
|
||||
5. compatibility barrel (`actions/index.js`)
|
||||
- exports parity test to ensure existing function names remain available during migration
|
||||
|
||||
### Integration-style safeguards (mocked external calls)
|
||||
|
||||
- Relay client request assembly test:
|
||||
- signed URL includes hash
|
||||
- token is attached
|
||||
- timeout/error behavior is consistent
|
||||
- Notify client wrapper test:
|
||||
- language/template routing remains unchanged for PEDW-NEW-CASEREF path
|
||||
|
||||
### Coverage targets for this refactor
|
||||
|
||||
- `actions/core/**`: >= 90% lines/functions
|
||||
- `actions/clients/**`: >= 80% lines/functions
|
||||
- Global coverage gate for this phase: >= 70% (new tests only; avoid blocking unrelated legacy code)
|
||||
|
||||
### Regression gate in PR
|
||||
|
||||
Required evidence for each phase:
|
||||
|
||||
1. `npm run lint`
|
||||
2. `npm run test`
|
||||
3. `npm run test:coverage` (attach summary)
|
||||
4. Manual smoke matrix from this plan (search/case/myportal/auth/notify/file)
|
||||
|
||||
## Risks and mitigations
|
||||
|
||||
- Risk: import breakage due to broad existing usage.
|
||||
- Mitigation: keep compatibility barrel and migrate in small batches.
|
||||
- Risk: hidden behavior differences from refactor-only moves.
|
||||
- Mitigation: freeze signatures + add helper tests + run route smoke matrix each phase.
|
||||
- Risk: sensitive logging leakage while touching shared helpers.
|
||||
- Mitigation: centralize logger early and enforce redaction helper.
|
||||
|
||||
## Definition of done (for this priority)
|
||||
|
||||
1. `actions/index.js` reduced to compatibility exports only (minimal logic).
|
||||
2. Core helper modules and service modules exist and are used by migrated consumers.
|
||||
3. Lint and manual validation matrix pass.
|
||||
4. Memory docs updated (`activeContext`, `progress`, `change-log`).
|
||||
@@ -11,40 +11,51 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var token = await getToken();
|
||||
var queryUrl = "contacts";
|
||||
var data = JSON.stringify(req.body);
|
||||
|
||||
var config = {
|
||||
method: "post",
|
||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
headers: {
|
||||
"OData-MaxVersion": "4.0",
|
||||
"OData-Version": "4.0",
|
||||
"Accept": "application/json",
|
||||
"Prefer": 'odata.include-annotations="*",return=representation',
|
||||
"Authorization": "Bearer " + token.access_token,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data: data
|
||||
};
|
||||
|
||||
return axios(config)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
if (!req.body || Object.keys(req.body).length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ACCOUNT_PAYLOAD_REQUIRED",
|
||||
message: "Account payload is required"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl = "contacts";
|
||||
const data = JSON.stringify(req.body);
|
||||
|
||||
const config = {
|
||||
method: "post",
|
||||
url: WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
headers: {
|
||||
"OData-MaxVersion": "4.0",
|
||||
"OData-Version": "4.0",
|
||||
Accept: "application/json",
|
||||
Prefer: 'odata.include-annotations="*",return=representation',
|
||||
Authorization: "Bearer " + token.access_token,
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
data
|
||||
};
|
||||
|
||||
const response = await axios(config);
|
||||
return respondSuccess(res, response.data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ACCOUNT_CREATE_FAILED",
|
||||
message: "Failed to create account"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,49 +16,45 @@
|
||||
* description: hello world
|
||||
*/
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
const emailAddress = req.query.emailAddress;
|
||||
|
||||
if (typeof emailAddress === "undefined" || emailAddress.length === 0) {
|
||||
return res.status(400).json();
|
||||
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1, contactid";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
);
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1, contactid";
|
||||
|
||||
// var queryUrl = "contacts/?$count=true&$select=emailaddress1, contactid";
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||
? 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);
|
||||
})
|
||||
: res.status(400).json();
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ACCOUNTS_FETCH_FAILED",
|
||||
message: "Failed to fetch accounts"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,149 +22,149 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var searchString = req.query.searchstring;
|
||||
var token = await getToken();
|
||||
const encodedSearchString = req.query.searchstring;
|
||||
|
||||
searchString = _.isEmpty(searchString)
|
||||
? searchString
|
||||
: JSON.parse(decodeURI(searchString));
|
||||
if (
|
||||
typeof encodedSearchString !== "string" ||
|
||||
encodedSearchString.trim().length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "SEARCH_STRING_REQUIRED",
|
||||
message: "searchstring is required"
|
||||
});
|
||||
}
|
||||
|
||||
var queryString = "";
|
||||
let searchString;
|
||||
try {
|
||||
searchString = JSON.parse(decodeURI(encodedSearchString));
|
||||
} catch (error) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_SEARCH_STRING",
|
||||
message: "searchstring must be valid JSON"
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!searchString ||
|
||||
typeof searchString !== "object" ||
|
||||
Object.keys(searchString).length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "SEARCH_STRING_REQUIRED",
|
||||
message: "searchstring is required"
|
||||
});
|
||||
}
|
||||
|
||||
const toEscapedString = (value) => String(value).replace(/\'/g, "''");
|
||||
let queryString = "";
|
||||
|
||||
queryString +=
|
||||
_.has(searchString, "q") && searchString.q != null
|
||||
searchString.q != null
|
||||
? "(contains(title, '" +
|
||||
searchString.q.replace(/\'/g, "''") +
|
||||
toEscapedString(searchString.q) +
|
||||
"') or contains(ticketnumber,'" +
|
||||
searchString.q.replace(/\'/g, "''") +
|
||||
toEscapedString(searchString.q) +
|
||||
"')) and"
|
||||
: "";
|
||||
|
||||
queryString +=
|
||||
_.has(searchString, "lpaRef") && searchString.lpaRef != null
|
||||
searchString.lpaRef != null
|
||||
? "(contains(pinswg_lpareference, '" +
|
||||
searchString.lpaRef.replace(/\'/g, "''") +
|
||||
toEscapedString(searchString.lpaRef) +
|
||||
"')) and"
|
||||
: "";
|
||||
|
||||
queryString +=
|
||||
(_.has(searchString, "lpa") || _.has(searchString, "LPA")) &&
|
||||
(searchString.lpa != null || searchString.LPA != null) &&
|
||||
searchString.lpa != null
|
||||
? " _pinswg_associatedlpa_value eq " + searchString.lpa + " and"
|
||||
: "";
|
||||
|
||||
queryString +=
|
||||
_.has(searchString, "apt") && searchString.apt != null
|
||||
searchString.apt != null
|
||||
? " pinswg_appealcasetype eq " + searchString.apt + " and"
|
||||
: "";
|
||||
|
||||
queryString +=
|
||||
_.has(searchString, "statuscode") && searchString.statuscode != null
|
||||
searchString.statuscode != null
|
||||
? " statuscode eq " + searchString.statuscode + " and"
|
||||
: "";
|
||||
|
||||
//console.log(queryString);
|
||||
|
||||
var queryUrl =
|
||||
const 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=createdon desc&$count=true";
|
||||
|
||||
//console.log(queryString, "\n", queryUrl);
|
||||
try {
|
||||
const token = await getToken();
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
var apiResponse = _.isEmpty(searchString)
|
||||
? res.status(400).json()
|
||||
: axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.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 (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) {
|
||||
const dataStr = JSON.stringify(data["@odata.nextLink"]);
|
||||
data["@odata.nextLink"] = dataStr.split("/v8.2/")[1];
|
||||
}
|
||||
|
||||
if (_.has(searchString, "projecttype")) {
|
||||
await updateValueArray(data, token);
|
||||
if (searchString.projecttype != null) {
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
|
||||
async function updateValueArray(data, token) {
|
||||
for (let i = 0; i < data.value.length; i++) {
|
||||
const item = data.value[i];
|
||||
try {
|
||||
const projectTypeQuery =
|
||||
"pinswg_sipses?$filter=_pinswg_sipscase_value eq " +
|
||||
item.incidentid +
|
||||
" and _pinswg_projecttype_value eq " +
|
||||
searchString.projecttype +
|
||||
"&$select=_pinswg_projecttype_value";
|
||||
|
||||
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)
|
||||
);
|
||||
const response = await axios.get(
|
||||
WEBAPI_URL +
|
||||
projectTypeQuery +
|
||||
hashAPIPath(projectTypeQuery),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
// Assuming the response contains the additional data you want to add
|
||||
// console.log(response.data.value[0]);
|
||||
console.log(response.data.value);
|
||||
if (
|
||||
response.data.value.length > 0 &&
|
||||
response.data.value[0]._pinswg_projecttype_value != null
|
||||
) {
|
||||
Object.assign(item, response.data.value[0]);
|
||||
}
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
data.value = data.value.filter(
|
||||
(item) => item._pinswg_projecttype_value != null
|
||||
);
|
||||
|
||||
data.value = data.value.filter(
|
||||
(item) => item._pinswg_projecttype_value != null
|
||||
);
|
||||
data["@odata.count"] = data.value.length;
|
||||
}
|
||||
|
||||
data["@odata.count"] = data.value.length;
|
||||
|
||||
// console.log(newDataObj);
|
||||
}
|
||||
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
});
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ADVANCED_SEARCH_FETCH_FAILED",
|
||||
message: "Failed to fetch advanced search results"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,39 +11,53 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var loggedInUserId = req.query.loggedInUserId;
|
||||
var token = await getToken();
|
||||
const loggedInUserId = req.query.loggedInUserId;
|
||||
|
||||
var queryUrl =
|
||||
"incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " +
|
||||
loggedInUserId +
|
||||
" and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true";
|
||||
if (
|
||||
typeof loggedInUserId !== "string" ||
|
||||
loggedInUserId.trim().length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "LOGGED_IN_USER_ID_REQUIRED",
|
||||
message: "loggedInUserId is required"
|
||||
});
|
||||
}
|
||||
|
||||
//console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl));
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"incidents?$select=pinswg_environmentalstatementlocation,modifiedon,description,numberofchildincidents,_accountid_value,_customerid_value,_pinswg_associatedlpa_value,_ownerid_value,pinswg_appealcasetype,statuscode,ticketnumber,title, _primarycontactid_value,pinswg_lpareference,pinswg_appellantlastname,pinswg_appellantfirstname,pinswg_appellantagent,pinswg_agentfirstname,pinswg_agentlastname,pinswg_agentcompanyname&$expand=primarycontactid($select=fullname)&$filter=_customerid_value eq " +
|
||||
loggedInUserId +
|
||||
" and pinswg_appealcasetype ne null&$orderby=createdon desc&$count=true";
|
||||
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
data.value.forEach(function (element) {
|
||||
element.pinswg_title = element.title;
|
||||
});
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
);
|
||||
|
||||
data.value.forEach(function (element) {
|
||||
element.pinswg_title = element.title;
|
||||
});
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "AWAITING_SUBMISSION_PROXY_FETCH_FAILED",
|
||||
message: "Failed to fetch awaiting submission proxy details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,65 +16,61 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var searchString = req.query.searchString;
|
||||
var token = await getToken();
|
||||
const searchString = req.query.searchString;
|
||||
|
||||
searchString = searchString.replace(/\'/g, "''");
|
||||
if (typeof searchString !== "string" || searchString.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "SEARCH_STRING_REQUIRED",
|
||||
message: "searchString is required"
|
||||
});
|
||||
}
|
||||
|
||||
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=(contains(title, '" +
|
||||
searchString +
|
||||
"') or contains(ticketnumber, '" +
|
||||
searchString +
|
||||
"') or contains(pinswg_lpareference, '" +
|
||||
searchString +
|
||||
"')) and pinswg_appealcasetype ne null " +
|
||||
(process.env.SHOWSIPS !== "true"
|
||||
? "and pinswg_appealcasetype ne 846040002 "
|
||||
: "") +
|
||||
"and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true";
|
||||
try {
|
||||
const token = await getToken();
|
||||
const escapedSearchString = searchString.replace(/\'/g, "''");
|
||||
|
||||
console.log(
|
||||
"\n==========================================\n",
|
||||
"\nBasic search ",
|
||||
"\nSearch String: " + searchString,
|
||||
"\n\nQuery url: " + queryUrl,
|
||||
"\n\nRelay link: " + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
"\n==========================================\n"
|
||||
);
|
||||
const 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=(contains(title, '" +
|
||||
escapedSearchString +
|
||||
"') or contains(ticketnumber, '" +
|
||||
escapedSearchString +
|
||||
"') or contains(pinswg_lpareference, '" +
|
||||
escapedSearchString +
|
||||
"')) and pinswg_appealcasetype ne null " +
|
||||
(process.env.SHOWSIPS !== "true"
|
||||
? "and pinswg_appealcasetype ne 846040002 "
|
||||
: "") +
|
||||
"and pinswg_publishtoweb eq true&$orderby=createdon desc&$count=true";
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: searchString.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
var dataStr;
|
||||
_.has(data, "@odata.nextLink") == true &&
|
||||
((dataStr = JSON.stringify(data["@odata.nextLink"])),
|
||||
(data["@odata.nextLink"] = dataStr.split("/v8.2/")[1]));
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
})
|
||||
: res.status(400).json();
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
return apiResponse;
|
||||
if (Object.prototype.hasOwnProperty.call(data, "@odata.nextLink")) {
|
||||
const 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: "BASIC_SEARCH_FETCH_FAILED",
|
||||
message: "Failed to fetch basic search results"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,48 +16,47 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var searchString = req.query.id;
|
||||
var token = await getToken();
|
||||
const caseId = req.query.id;
|
||||
|
||||
var queryUrl = `tasks?$select=_regardingobjectid_value,subject,description,createdon,statuscode,scheduledend,scheduledstart&$count=true&$filter=_regardingobjectid_value eq ${searchString} and contains(subject, 'Banner') and statuscode ne 5&$orderby=createdon desc`;
|
||||
if (typeof caseId !== "string" || caseId.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CASE_ID_REQUIRED",
|
||||
message: "id is required"
|
||||
});
|
||||
}
|
||||
|
||||
console.log(
|
||||
"\n==========================================\n",
|
||||
"\nCase messages ",
|
||||
"\nSearch String: " + searchString,
|
||||
"\n\nQuery url: " + queryUrl,
|
||||
"\n\nRelay link: " + WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
"\n==========================================\n"
|
||||
);
|
||||
try {
|
||||
const token = await getToken();
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: searchString.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
})
|
||||
: res.status(400).json();
|
||||
const queryUrl =
|
||||
"tasks?$select=_regardingobjectid_value,subject,description,createdon,statuscode,scheduledend,scheduledstart&$count=true&$filter=_regardingobjectid_value eq " +
|
||||
caseId +
|
||||
" and contains(subject, 'Banner') and statuscode ne 5&$orderby=createdon desc";
|
||||
|
||||
return apiResponse;
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CASE_MESSAGE_FETCH_FAILED",
|
||||
message: "Failed to fetch case messages"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,42 +16,45 @@
|
||||
* description: hello world
|
||||
*/
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
var token = await getToken();
|
||||
const emailAddress = req.query.emailAddress;
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1, contactid";
|
||||
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||
? 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);
|
||||
})
|
||||
: res.status(400).json();
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1, contactid";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
);
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ACCOUNT_CHECK_FAILED",
|
||||
message: "Failed to check email account"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,51 +22,56 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
var pwd = req.query.pwd;
|
||||
var token = await getToken();
|
||||
const emailAddress = req.query.emailAddress;
|
||||
const pwd = req.query.pwd;
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"' and pinswg_custom_password eq '" +
|
||||
pwd +
|
||||
"'&$count=true&$select=emailaddress1,contactid,pinswg_custom_password,yomifullname,firstname,lastname";
|
||||
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
//console.log(_.isEmpty(req.query));
|
||||
if (typeof pwd !== "string" || pwd.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PASSWORD_REQUIRED",
|
||||
message: "pwd is required"
|
||||
});
|
||||
}
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" &&
|
||||
emailAddress.length > 0 &&
|
||||
typeof pwd != "undefined" &&
|
||||
pwd.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
console.log(data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
})
|
||||
: res.status(400).json();
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"' and pinswg_custom_password eq '" +
|
||||
pwd +
|
||||
"'&$count=true&$select=emailaddress1,contactid,pinswg_custom_password,yomifullname,firstname,lastname";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "LOGIN_FETCH_FAILED",
|
||||
message: "Failed to fetch login details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,35 +18,50 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var loggedInUserId = req.query.loggedInUserId;
|
||||
var token = await getToken();
|
||||
const loggedInUserId = req.query.loggedInUserId;
|
||||
|
||||
var queryUrl =
|
||||
"pinswg_representationses?$filter= _pinswg_contact_value eq " +
|
||||
loggedInUserId +
|
||||
"&$count=true&$orderby=createdon desc";
|
||||
if (
|
||||
typeof loggedInUserId !== "string" ||
|
||||
loggedInUserId.trim().length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "LOGGED_IN_USER_ID_REQUIRED",
|
||||
message: "loggedInUserId is required"
|
||||
});
|
||||
}
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const token = await getToken();
|
||||
|
||||
const queryUrl =
|
||||
"pinswg_representationses?$filter= _pinswg_contact_value eq " +
|
||||
loggedInUserId +
|
||||
"&$count=true&$orderby=createdon desc";
|
||||
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
);
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "MY_REPRESENTATIONS_PROXY_FETCH_FAILED",
|
||||
message: "Failed to fetch my representations proxy details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,42 +18,45 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var contactid = req.query.contactid;
|
||||
var token = await getToken();
|
||||
const contactid = req.query.contactid;
|
||||
|
||||
var queryUrl =
|
||||
"contacts(" +
|
||||
contactid +
|
||||
")?$select=firstname, lastname, emailaddress1, telephone1, company, address1_line1,address1_line2,address1_city, address1_county,address1_postalcode,pinswg_typeofinvolvement,pinswg_contact_associatedlpa,pinswg_preferredlanguage&$count=true";
|
||||
if (typeof contactid !== "string" || contactid.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CONTACT_ID_REQUIRED",
|
||||
message: "contactid is required"
|
||||
});
|
||||
}
|
||||
|
||||
//console.log(WEBAPI_URL + queryUrl + hashAPIPath(queryUrl));
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts(" +
|
||||
contactid +
|
||||
")?$select=firstname, lastname, emailaddress1, telephone1, company, address1_line1,address1_line2,address1_city, address1_county,address1_postalcode,pinswg_typeofinvolvement,pinswg_contact_associatedlpa,pinswg_preferredlanguage&$count=true";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
);
|
||||
|
||||
var apiResponse =
|
||||
contactid.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(consoleLogger(err));
|
||||
res.status(400).json(err);
|
||||
})
|
||||
: res.status(400).json();
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PERSONAL_ACCOUNT_FETCH_FAILED",
|
||||
message: "Failed to fetch personal account"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,50 +17,65 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
var checkHash = req.query.hash;
|
||||
const emailAddress = req.query.emailAddress;
|
||||
const checkHash = req.query.hash;
|
||||
|
||||
if (typeof emailAddress == "undefined" || emailAddress.length === 0) {
|
||||
return res.status(400).json();
|
||||
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
var checkquerypath =
|
||||
if (typeof checkHash !== "string" || checkHash.length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "HASH_REQUIRED",
|
||||
message: "hash is required"
|
||||
});
|
||||
}
|
||||
|
||||
const checkquerypath =
|
||||
"/api/endpoint/getportallogin_api?emailAddress=" + emailAddress;
|
||||
|
||||
if (hashAPIPath(checkquerypath) != "&hash=" + checkHash) {
|
||||
return res.status(400).json();
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INVALID_HASH",
|
||||
message: "Invalid hash"
|
||||
});
|
||||
}
|
||||
|
||||
var token = await getToken();
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"' and statuscode eq 1&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname";
|
||||
|
||||
var apiResponse = axios
|
||||
.get(
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"' and statuscode eq 1&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
});
|
||||
);
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PORTAL_LOGIN_FETCH_FAILED",
|
||||
message: "Failed to fetch portal login"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,45 +17,45 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeadersPaged } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = req.query.emailAddress;
|
||||
var token = await getToken();
|
||||
const emailAddress = req.query.emailAddress;
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname";
|
||||
if (typeof emailAddress !== "string" || emailAddress.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
//console.log(req.query);
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
emailAddress +
|
||||
"'&$count=true&$select=emailaddress1,contactid,yomifullname,firstname,lastname";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
);
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||
? axios
|
||||
.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeadersPaged(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
//console.log(data);
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
})
|
||||
: res.status(400).json();
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PORTAL_LOGIN_PROXY_FETCH_FAILED",
|
||||
message: "Failed to fetch portal login proxy details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,54 +16,51 @@
|
||||
* description: hello world
|
||||
*/
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import _ from "lodash";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import {
|
||||
escapeODataString,
|
||||
isNonEmptyString,
|
||||
sanitizeString
|
||||
} from "../../../actions/core/guards";
|
||||
import { consoleLogger, redactSensitive } from "../../../actions/core/logger";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var emailAddress = sanitizeString(req.query.emailAddress);
|
||||
var token = await getToken();
|
||||
const emailAddress = sanitizeString(req.query.emailAddress);
|
||||
|
||||
if (!isNonEmptyString(emailAddress)) {
|
||||
return res.status(400).json({ error: "emailAddress is required" });
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "EMAIL_ADDRESS_REQUIRED",
|
||||
message: "emailAddress is required"
|
||||
});
|
||||
}
|
||||
|
||||
var queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
escapeODataString(emailAddress) +
|
||||
"'&$count=true&$select=pinswg_preferredlanguage,contactid";
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"contacts?$filter=emailaddress1 eq '" +
|
||||
escapeODataString(emailAddress) +
|
||||
"'&$count=true&$select=pinswg_preferredlanguage,contactid";
|
||||
|
||||
console.log(redactSensitive(queryUrl));
|
||||
// var queryUrl = "contacts/?$count=true&$select=emailaddress1, contactid";
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
);
|
||||
|
||||
var apiResponse = _.isEmpty(req.query)
|
||||
? res.status(400).json()
|
||||
: typeof emailAddress != "undefined" && emailAddress.length > 0
|
||||
? 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);
|
||||
})
|
||||
: res.status(400).json();
|
||||
|
||||
return apiResponse;
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PREFERRED_LANGUAGE_FETCH_FAILED",
|
||||
message: "Failed to fetch preferred language"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,35 +18,47 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var incidentID = req.query.incidentID;
|
||||
var token = await getToken();
|
||||
const incidentID = req.query.incidentID;
|
||||
|
||||
var queryUrl =
|
||||
"pinswg_representationses?$filter= _pinswg_case_value eq " +
|
||||
incidentID +
|
||||
"&$count=true&$orderby=createdon desc";
|
||||
if (typeof incidentID !== "string" || incidentID.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "INCIDENT_ID_REQUIRED",
|
||||
message: "incidentID is required"
|
||||
});
|
||||
}
|
||||
|
||||
return axios
|
||||
.get(
|
||||
try {
|
||||
const token = await getToken();
|
||||
|
||||
const queryUrl =
|
||||
"pinswg_representationses?$filter= _pinswg_case_value eq " +
|
||||
incidentID +
|
||||
"&$count=true&$orderby=createdon desc";
|
||||
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
);
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "REPRESENTATIONS_PROXY_FETCH_FAILED",
|
||||
message: "Failed to fetch representations proxy details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,69 +18,75 @@
|
||||
*/
|
||||
|
||||
import axios from "axios";
|
||||
import CryptoJS from "crypto-js";
|
||||
import { azureHeaders } from "../../../actions/core/headers";
|
||||
import { consoleLogger } from "../../../actions/core/logger";
|
||||
import { getToken } from "../../../actions/core/token";
|
||||
import { hashAPIPath } from "../../../actions/core/hash";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
const BASE_URL = process.env.API_ROOT || `http://localhost:${port}`;
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var loggedInUserId = req.query.loggedInUserId;
|
||||
var token = await getToken();
|
||||
const loggedInUserId = req.query.loggedInUserId;
|
||||
|
||||
// var queryUrl =
|
||||
// "pinswg_watchlists?$filter= _pinswg_contact_value eq " +
|
||||
// loggedInUserId +
|
||||
// "&$select=pinswg_emailnotifications,modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)";
|
||||
if (
|
||||
typeof loggedInUserId !== "string" ||
|
||||
loggedInUserId.trim().length === 0
|
||||
) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "LOGGED_IN_USER_ID_REQUIRED",
|
||||
message: "loggedInUserId is required"
|
||||
});
|
||||
}
|
||||
|
||||
var queryUrl =
|
||||
"pinswg_watchlists?$filter= _pinswg_contact_value eq " +
|
||||
loggedInUserId +
|
||||
"&$select=modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)";
|
||||
try {
|
||||
const token = await getToken();
|
||||
const queryUrl =
|
||||
"pinswg_watchlists?$filter= _pinswg_contact_value eq " +
|
||||
loggedInUserId +
|
||||
"&$select=modifiedon,pinswg_appealcasetype,pinswg_watchlistid,_pinswg_watchedcase_value,statuscode,pinswg_representationsubmitted,pinswg_representationtype&$count=true&$orderby=createdon desc&$expand=pinswg_WatchedCase($select=pinswg_AssociatedLPA)";
|
||||
|
||||
return axios
|
||||
.get(
|
||||
const { data } = await axios.get(
|
||||
WEBAPI_URL + queryUrl + hashAPIPath(queryUrl),
|
||||
azureHeaders(token.access_token)
|
||||
)
|
||||
.then(({ data }) => {
|
||||
data.value.forEach(function (element) {
|
||||
element.ticketnumber = element.pinswg_WatchedCase.ticketnumber;
|
||||
element.pinswg_title =
|
||||
element[
|
||||
"_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
|
||||
];
|
||||
);
|
||||
|
||||
data.value.forEach(function (element) {
|
||||
element.ticketnumber = element.pinswg_WatchedCase.ticketnumber;
|
||||
element.pinswg_title =
|
||||
element[
|
||||
"_pinswg_watchedcase_value@OData.Community.Display.V1.FormattedValue"
|
||||
];
|
||||
|
||||
element[
|
||||
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||
] =
|
||||
element.pinswg_WatchedCase[
|
||||
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||
] =
|
||||
element.pinswg_WatchedCase[
|
||||
"_pinswg_associatedlpa_value@OData.Community.Display.V1.FormattedValue"
|
||||
];
|
||||
element._pinswg_associatedlpa_value =
|
||||
element.pinswg_WatchedCase._pinswg_associatedlpa_value;
|
||||
element[
|
||||
];
|
||||
element._pinswg_associatedlpa_value =
|
||||
element.pinswg_WatchedCase._pinswg_associatedlpa_value;
|
||||
element[
|
||||
"_ownerid_value@OData.Community.Display.V1.FormattedValue"
|
||||
] =
|
||||
element.pinswg_WatchedCase[
|
||||
"_ownerid_value@OData.Community.Display.V1.FormattedValue"
|
||||
] =
|
||||
element.pinswg_WatchedCase[
|
||||
"_ownerid_value@OData.Community.Display.V1.FormattedValue"
|
||||
];
|
||||
element._ownerid_value =
|
||||
element.pinswg_WatchedCase._ownerid_value;
|
||||
];
|
||||
element._ownerid_value = element.pinswg_WatchedCase._ownerid_value;
|
||||
|
||||
delete element.pinswg_WatchedCase;
|
||||
});
|
||||
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
delete element.pinswg_WatchedCase;
|
||||
});
|
||||
|
||||
return respondSuccess(res, data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "WATCHED_CASES_PROXY_FETCH_FAILED",
|
||||
message: "Failed to fetch watched cases proxy details"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,43 +11,61 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var contactId = req.query.contactId;
|
||||
var token = await getToken();
|
||||
var data = JSON.stringify(req.body);
|
||||
var queryUrl = "contacts(" + contactId + ")";
|
||||
const contactId = req.query.contactId;
|
||||
|
||||
var config = {
|
||||
method: "patch",
|
||||
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
|
||||
};
|
||||
|
||||
console.log(config);
|
||||
|
||||
return axios(config)
|
||||
.then(({ data }) => {
|
||||
res.status(200).json(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
consoleLogger(error);
|
||||
res.status(400).json(error);
|
||||
if (typeof contactId !== "string" || contactId.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CONTACT_ID_REQUIRED",
|
||||
message: "contactId is required"
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.body || Object.keys(req.body).length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ACCOUNT_UPDATE_PAYLOAD_REQUIRED",
|
||||
message: "Account update payload is required"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await getToken();
|
||||
const data = JSON.stringify(req.body);
|
||||
const queryUrl = "contacts(" + contactId + ")";
|
||||
|
||||
const config = {
|
||||
method: "patch",
|
||||
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
|
||||
};
|
||||
|
||||
const response = await axios(config);
|
||||
return respondSuccess(res, response.data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "ACCOUNT_UPDATE_FAILED",
|
||||
message: "Failed to update account"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,41 +11,61 @@
|
||||
*/
|
||||
|
||||
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";
|
||||
import { respondError, respondSuccess } from "../middleware/apiResponse";
|
||||
|
||||
const WEBAPI_URL =
|
||||
process.env.RELAY_ROOT ||
|
||||
"https://dev-pedw-ns.servicebus.windows.net/dev-pedw-hc/";
|
||||
|
||||
export default async function ApiProxy(req, res) {
|
||||
var contactId = req.query.contactId;
|
||||
var token = await getToken();
|
||||
var data = JSON.stringify(req.body);
|
||||
var queryUrl = "contacts(" + contactId + ")";
|
||||
const contactId = req.query.contactId;
|
||||
|
||||
var config = {
|
||||
method: "patch",
|
||||
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(error);
|
||||
res.status(400).json(error);
|
||||
if (typeof contactId !== "string" || contactId.trim().length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "CONTACT_ID_REQUIRED",
|
||||
message: "contactId is required"
|
||||
});
|
||||
}
|
||||
|
||||
if (!req.body || Object.keys(req.body).length === 0) {
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PASSWORD_UPDATE_PAYLOAD_REQUIRED",
|
||||
message: "Password update payload is required"
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const token = await getToken();
|
||||
const data = JSON.stringify(req.body);
|
||||
const queryUrl = "contacts(" + contactId + ")";
|
||||
|
||||
const config = {
|
||||
method: "patch",
|
||||
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
|
||||
};
|
||||
|
||||
const response = await axios(config);
|
||||
return respondSuccess(res, response.data);
|
||||
} catch (error) {
|
||||
consoleLogger(error);
|
||||
return respondError(res, {
|
||||
status: 400,
|
||||
code: "PASSWORD_UPDATE_FAILED",
|
||||
message: "Failed to update password"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
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 run = async () => {
|
||||
await runHelperTests();
|
||||
await runHandlerTests();
|
||||
await runEmailHandlerTests();
|
||||
await runEndpointHandlerTests();
|
||||
console.log("Phase 21 combined suite passed.");
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,992 @@
|
||||
const assert = require("assert");
|
||||
const {
|
||||
loadModule,
|
||||
createRes,
|
||||
respondSuccessMock,
|
||||
respondErrorMock
|
||||
} = require("./_shared.cjs");
|
||||
|
||||
const tests = [];
|
||||
const test = (name, fn) => tests.push({ name, fn });
|
||||
|
||||
test("getaccounts returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getaccounts_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "EMAIL_ADDRESS_REQUIRED");
|
||||
});
|
||||
|
||||
test("getaccounts catch path returns ACCOUNTS_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getaccounts_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "ACCOUNTS_FETCH_FAILED");
|
||||
});
|
||||
|
||||
test("getemailaccountcheck returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "EMAIL_ADDRESS_REQUIRED");
|
||||
});
|
||||
|
||||
test("getemailaccountcheck catch path returns EMAIL_ACCOUNT_CHECK_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"EMAIL_ACCOUNT_CHECK_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getpreferredlanguage returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
sanitizeString: () => "",
|
||||
isNonEmptyString: () => false,
|
||||
escapeODataString: (value) => value,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "EMAIL_ADDRESS_REQUIRED");
|
||||
});
|
||||
|
||||
test("getpreferredlanguage catch path returns PREFERRED_LANGUAGE_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
sanitizeString: (value) => value,
|
||||
isNonEmptyString: () => true,
|
||||
escapeODataString: (value) => value,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"PREFERRED_LANGUAGE_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getpreferredlanguage success returns existing data payload contract", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getpreferredlanguage_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
sanitizeString: (value) => value,
|
||||
isNonEmptyString: () => true,
|
||||
escapeODataString: (value) => value,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => ({
|
||||
data: {
|
||||
value: [
|
||||
{
|
||||
contactid: "c1",
|
||||
pinswg_preferredlanguage: 807570001
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
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",
|
||||
pinswg_preferredlanguage: 807570001
|
||||
}
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
test("getlogin returns EMAIL_ADDRESS_REQUIRED when emailAddress missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getlogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { pwd: "abc123" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "EMAIL_ADDRESS_REQUIRED");
|
||||
});
|
||||
|
||||
test("getlogin returns PASSWORD_REQUIRED when pwd missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getlogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "PASSWORD_REQUIRED");
|
||||
});
|
||||
|
||||
test("getlogin catch path returns LOGIN_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getlogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local", pwd: "abc123" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "LOGIN_FETCH_FAILED");
|
||||
});
|
||||
|
||||
test("getportallogin returns HASH_REQUIRED when hash missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportallogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: (input) =>
|
||||
input && input.startsWith("/api/endpoint/getportallogin_api")
|
||||
? "&hash=expected"
|
||||
: "&hash=relay",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "HASH_REQUIRED");
|
||||
});
|
||||
|
||||
test("getportallogin returns INVALID_HASH when hash mismatch", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportallogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: (input) =>
|
||||
input && input.startsWith("/api/endpoint/getportallogin_api")
|
||||
? "&hash=expected"
|
||||
: "&hash=relay",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local", hash: "wrong" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_HASH");
|
||||
});
|
||||
|
||||
test("getportallogin catch path returns PORTAL_LOGIN_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportallogin_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: (input) =>
|
||||
input && input.startsWith("/api/endpoint/getportallogin_api")
|
||||
? "&hash=expected"
|
||||
: "&hash=relay",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: { emailAddress: "user@test.local", hash: "expected" }
|
||||
};
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"PORTAL_LOGIN_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getportalloginproxy catch path returns PORTAL_LOGIN_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getportalloginproxy_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"PORTAL_LOGIN_PROXY_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getpersonalaccount returns CONTACT_ID_REQUIRED when contactid missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getpersonalaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { contactid: "c1" } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "CONTACT_ID_REQUIRED");
|
||||
});
|
||||
|
||||
test("getpersonalaccount catch path returns PERSONAL_ACCOUNT_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getpersonalaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { contactid: "c1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"PERSONAL_ACCOUNT_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getaccounts success returns existing data payload contract", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getaccounts_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => ({ data: { value: [{ contactid: "c1" }] } })
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
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("getemailaccountcheck success returns existing data payload contract", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getemailaccountcheck_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => ({
|
||||
data: { value: [{ emailaddress1: "u@test" }] }
|
||||
})
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { emailAddress: "user@test.local" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 200);
|
||||
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
|
||||
value: [{ emailaddress1: "u@test" }]
|
||||
});
|
||||
});
|
||||
|
||||
test("getwatchedcasesproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getwatchedcasesproxy_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"LOGGED_IN_USER_ID_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getwatchedcasesproxy catch path returns WATCHED_CASES_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getwatchedcasesproxy_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { loggedInUserId: "c1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"WATCHED_CASES_PROXY_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getmyrepresentationsproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getmyrepresentationsproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"LOGGED_IN_USER_ID_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getmyrepresentationsproxy catch path returns MY_REPRESENTATIONS_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getmyrepresentationsproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: { loggedInUserId: "c1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"MY_REPRESENTATIONS_PROXY_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getrepresentationsproxy returns INCIDENT_ID_REQUIRED when incidentID missing", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getrepresentationsproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INCIDENT_ID_REQUIRED");
|
||||
});
|
||||
|
||||
test("getrepresentationsproxy catch path returns REPRESENTATIONS_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getrepresentationsproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: { incidentID: "i1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"REPRESENTATIONS_PROXY_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getawaitingsubmissionproxy returns LOGGED_IN_USER_ID_REQUIRED when loggedInUserId missing", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getawaitingsubmissionproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"LOGGED_IN_USER_ID_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getawaitingsubmissionproxy catch path returns AWAITING_SUBMISSION_PROXY_FETCH_FAILED", async () => {
|
||||
const mod = loadModule(
|
||||
"pages/api/endpoint/getawaitingsubmissionproxy_api.js",
|
||||
{
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeaders: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
}
|
||||
);
|
||||
|
||||
const req = { query: { loggedInUserId: "c1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"AWAITING_SUBMISSION_PROXY_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("createaccount returns ACCOUNT_PAYLOAD_REQUIRED when body missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/createaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => ({ data: { contactid: "c1" } }),
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {}, body: undefined };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"ACCOUNT_PAYLOAD_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("createaccount catch path returns ACCOUNT_CREATE_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/createaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => {
|
||||
throw new Error("relay failed");
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {}, body: { firstname: "A" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "ACCOUNT_CREATE_FAILED");
|
||||
});
|
||||
|
||||
test("updateaccount returns CONTACT_ID_REQUIRED when contactId missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updateaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => ({ data: { contactid: "c1" } }),
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {}, body: { firstname: "A" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "CONTACT_ID_REQUIRED");
|
||||
});
|
||||
|
||||
test("updateaccount returns ACCOUNT_UPDATE_PAYLOAD_REQUIRED when body missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updateaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => ({ data: { contactid: "c1" } }),
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { contactId: "c1" }, body: undefined };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"ACCOUNT_UPDATE_PAYLOAD_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("updateaccount catch path returns ACCOUNT_UPDATE_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updateaccount_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => {
|
||||
throw new Error("relay failed");
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { contactId: "c1" }, body: { firstname: "A" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "ACCOUNT_UPDATE_FAILED");
|
||||
});
|
||||
|
||||
test("updatepassword returns CONTACT_ID_REQUIRED when contactId missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updatepassword_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => ({ data: { contactid: "c1" } }),
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {}, body: { pinswg_password: "abc" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "CONTACT_ID_REQUIRED");
|
||||
});
|
||||
|
||||
test("updatepassword returns PASSWORD_UPDATE_PAYLOAD_REQUIRED when body missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updatepassword_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => ({ data: { contactid: "c1" } }),
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { contactId: "c1" }, body: undefined };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"PASSWORD_UPDATE_PAYLOAD_REQUIRED"
|
||||
);
|
||||
});
|
||||
|
||||
test("updatepassword catch path returns PASSWORD_UPDATE_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/updatepassword_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
axios: async () => {
|
||||
throw new Error("relay failed");
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: { contactId: "c1" },
|
||||
body: { pinswg_password: "abc" }
|
||||
};
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "PASSWORD_UPDATE_FAILED");
|
||||
});
|
||||
|
||||
test("getbasicsearch returns SEARCH_STRING_REQUIRED when searchString missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getbasicsearch_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED");
|
||||
});
|
||||
|
||||
test("getbasicsearch catch path returns BASIC_SEARCH_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getbasicsearch_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { searchString: "CAS" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"BASIC_SEARCH_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getcasemessage returns CASE_ID_REQUIRED when id missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getcasemessage_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "CASE_ID_REQUIRED");
|
||||
});
|
||||
|
||||
test("getcasemessage catch path returns CASE_MESSAGE_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getcasemessage_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { id: "i1" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"CASE_MESSAGE_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
test("getadvancedsearch returns SEARCH_STRING_REQUIRED when searchstring missing", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getadvancedsearch_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: {} };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "SEARCH_STRING_REQUIRED");
|
||||
});
|
||||
|
||||
test("getadvancedsearch returns INVALID_SEARCH_STRING when searchstring invalid JSON", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getadvancedsearch_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: { get: async () => ({ data: { value: [] } }) },
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = { query: { searchstring: "not-json" } };
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(res.state.jsonBody.error.code, "INVALID_SEARCH_STRING");
|
||||
});
|
||||
|
||||
test("getadvancedsearch catch path returns ADVANCED_SEARCH_FETCH_FAILED", async () => {
|
||||
const mod = loadModule("pages/api/endpoint/getadvancedsearch_api.js", {
|
||||
respondError: respondErrorMock,
|
||||
respondSuccess: respondSuccessMock,
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=expected",
|
||||
azureHeadersPaged: () => ({}),
|
||||
axios: {
|
||||
get: async () => {
|
||||
throw new Error("relay failed");
|
||||
}
|
||||
},
|
||||
consoleLogger: () => {}
|
||||
});
|
||||
|
||||
const req = {
|
||||
query: {
|
||||
searchstring: encodeURI(
|
||||
JSON.stringify({ q: "cas", lpa: "l1", apt: "1" })
|
||||
)
|
||||
}
|
||||
};
|
||||
const res = createRes();
|
||||
await mod.default(req, res);
|
||||
|
||||
assert.strictEqual(res.state.statusCode, 400);
|
||||
assert.strictEqual(
|
||||
res.state.jsonBody.error.code,
|
||||
"ADVANCED_SEARCH_FETCH_FAILED"
|
||||
);
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
for (const currentTest of tests) {
|
||||
await currentTest.fn();
|
||||
passed += 1;
|
||||
}
|
||||
console.log(
|
||||
`Phase 21 endpoint-handler contract tests passed (${passed}/${tests.length}).`
|
||||
);
|
||||
};
|
||||
|
||||
module.exports = run;
|
||||
|
||||
if (require.main === module) {
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user