From ba7d94d83d49bc50608fd99e910c70300f51bd52 Mon Sep 17 00:00:00 2001 From: robbond Date: Wed, 25 Mar 2026 09:43:40 +0000 Subject: [PATCH] refactor(actions): include residual axios-risk slices in branch --- actions/services/portalDirectService.js | 11 +- actions/services/searchDirectService.js | 321 ++++++++++-------------- context/architecture.md | 55 ++++ memory-bank/change-log.md | 66 +++++ 4 files changed, 251 insertions(+), 202 deletions(-) diff --git a/actions/services/portalDirectService.js b/actions/services/portalDirectService.js index 6b36dbd4..a041bf5a 100644 --- a/actions/services/portalDirectService.js +++ b/actions/services/portalDirectService.js @@ -1,4 +1,3 @@ -import axios from "axios"; import { BASE_URL } from "../core/env"; import { consoleLogger } from "../core/logger"; import { buildHashedQueryUrl } from "../clients/relayClient"; @@ -129,7 +128,7 @@ export const deleteMyRepresentations = (myRepresentationsID) => { return buildHashedQueryUrl(queryUrl) .then((signedUrl) => - axios({ + requestJson({ method: "delete", url: signedUrl, headers: { @@ -142,9 +141,6 @@ export const deleteMyRepresentations = (myRepresentationsID) => { } }) ) - .then((res) => { - return res.data; - }) .catch((error) => { consoleLogger(error); }); @@ -170,14 +166,11 @@ export const deleteWatchedCases = async (watchedCaseID) => { return buildHashedQueryUrl(queryUrl) .then((signedUrl) => - axios({ + requestJson({ method: "delete", url: signedUrl }) ) - .then((res) => { - return res.data; - }) .catch((error) => { consoleLogger(error); }); diff --git a/actions/services/searchDirectService.js b/actions/services/searchDirectService.js index 0037ea98..15742157 100644 --- a/actions/services/searchDirectService.js +++ b/actions/services/searchDirectService.js @@ -1,35 +1,25 @@ -import axios from "axios"; import { BASE_URL } from "../core/env"; import { consoleLogger } from "../core/logger"; +import { getJson } from "../clients/endpointClient"; import { logAndReturnResponse, logAndReturnEmptyValueErrorResponse } from "./httpServiceUtils"; export const getBasicSearch = (searchString) => { - return axios - .get( - BASE_URL + - "/api/endpoint/getbasicsearch_api?searchString=" + - searchString - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + return getJson( + BASE_URL + + "/api/endpoint/getbasicsearch_api?searchString=" + + searchString + ).catch(logAndReturnResponse); }; export const getBasicDNSURLSearch = (searchString) => { - return axios - .get( - BASE_URL + - "/api/endpoint/getbasicdnsurlsearch_api?searchString=" + - searchString - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + return getJson( + BASE_URL + + "/api/endpoint/getbasicdnsurlsearch_api?searchString=" + + searchString + ).catch(logAndReturnResponse); }; export const getAddressSearchPaged = ( @@ -39,23 +29,18 @@ export const getAddressSearchPaged = ( fieldSort, showNumberOfRecords ) => { - return axios - .get( - "/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" + - searchString + - "&pageNumber=" + - pageNumber + - "&orderby=" + - orderBy + - "&fieldSort=" + - fieldSort + - "&showNumberOfRecords=" + - showNumberOfRecords - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + return getJson( + "/api/endpoint/getbasicsearch_by_address_paged_api?searchString=" + + searchString + + "&pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + ).catch(logAndReturnResponse); }; export const getBasicSearchPaged = ( @@ -65,46 +50,36 @@ export const getBasicSearchPaged = ( fieldSort, showNumberOfRecords ) => { - return axios - .get( - "/api/endpoint/getbasicsearchpaged_api?searchString=" + - searchString + - "&pageNumber=" + - pageNumber + - "&orderby=" + - orderBy + - "&fieldSort=" + - fieldSort + - "&showNumberOfRecords=" + - showNumberOfRecords - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + return getJson( + "/api/endpoint/getbasicsearchpaged_api?searchString=" + + searchString + + "&pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + ).catch(logAndReturnResponse); }; export const getDNSCoords = () => { - return axios - .get(BASE_URL + "/api/endpoint/getdnscoords_api") - .then((res) => { - return res.data; - }) - .catch(logAndReturnEmptyValueErrorResponse); + return getJson(BASE_URL + "/api/endpoint/getdnscoords_api").catch( + logAndReturnEmptyValueErrorResponse + ); }; export const getDNSList = (searchString) => { - return axios - .get(BASE_URL + "/api/endpoint/getdnslist_api") - .then((res) => res.data) - .catch(logAndReturnResponse); + return getJson(BASE_URL + "/api/endpoint/getdnslist_api").catch( + logAndReturnResponse + ); }; export const getBasicDNSSearch = (searchString) => { - return axios - .get(BASE_URL + "/api/endpoint/getbasicdnssearch_api") - .then((res) => res.data) - .catch(logAndReturnResponse); + return getJson(BASE_URL + "/api/endpoint/getbasicdnssearch_api").catch( + logAndReturnResponse + ); }; export const getBasicDNSSearchPaged = ( @@ -113,30 +88,24 @@ export const getBasicDNSSearchPaged = ( fieldSort, showNumberOfRecords ) => { - return axios - .get( - "/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" + - pageNumber + - "&orderby=" + - orderBy + - "&fieldSort=" + - fieldSort + - "&showNumberOfRecords=" + - showNumberOfRecords - ) - .then((res) => res.data) - .catch(logAndReturnResponse); + return getJson( + "/api/endpoint/getbasicdnssearchpaged_api?pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + ).catch(logAndReturnResponse); }; export const getAdvancedSearch = (searchString) => { - return axios - .get( - BASE_URL + - "/api/endpoint/getadvancedsearch_api?searchstring=" + - JSON.stringify(searchString) - ) - .then((res) => res.data) - .catch(logAndReturnEmptyValueErrorResponse); + return getJson( + BASE_URL + + "/api/endpoint/getadvancedsearch_api?searchstring=" + + JSON.stringify(searchString) + ).catch(logAndReturnEmptyValueErrorResponse); }; export const getAdvancedSearchPaged = ( @@ -146,21 +115,18 @@ export const getAdvancedSearchPaged = ( fieldSort, showNumberOfRecords ) => { - return axios - .get( - "/api/endpoint/getadvancedsearchpaged_api?searchstring=" + - JSON.stringify(searchString) + - "&pageNumber=" + - pageNumber + - "&orderby=" + - orderBy + - "&fieldSort=" + - fieldSort + - "&showNumberOfRecords=" + - showNumberOfRecords - ) - .then((res) => res.data) - .catch(logAndReturnEmptyValueErrorResponse); + return getJson( + "/api/endpoint/getadvancedsearchpaged_api?searchstring=" + + JSON.stringify(searchString) + + "&pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + ).catch(logAndReturnEmptyValueErrorResponse); }; export const getBasicSearchDetails = async ( @@ -169,20 +135,17 @@ export const getBasicSearchDetails = async ( primaryIdAttribute, incidentID ) => { - return axios - .get( - BASE_URL + - "/api/endpoint/getbasicsearchdetails_api?appealTypeName=" + - appealTypeName + - "&primaryIdAttribute=" + - primaryIdAttribute + - "&incidentID=" + - incidentID - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - }); + return getJson( + BASE_URL + + "/api/endpoint/getbasicsearchdetails_api?appealTypeName=" + + appealTypeName + + "&primaryIdAttribute=" + + primaryIdAttribute + + "&incidentID=" + + incidentID + ).catch((error) => { + consoleLogger(error); + }); }; export const getBasicPartSavedDetails = ( @@ -191,20 +154,17 @@ export const getBasicPartSavedDetails = ( primaryIdAttribute, incidentID ) => { - return axios - .get( - BASE_URL + - "/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" + - appealTypeName + - "&primaryIdAttribute=" + - primaryIdAttribute + - "&incidentID=" + - incidentID - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - }); + return getJson( + BASE_URL + + "/api/endpoint/getbasicpartsaveddetails_api?appealTypeName=" + + appealTypeName + + "&primaryIdAttribute=" + + primaryIdAttribute + + "&incidentID=" + + incidentID + ).catch((error) => { + consoleLogger(error); + }); }; export const getBasicSearchDetailsPaged = async ( @@ -231,8 +191,8 @@ export const getBasicSearchDetailsPaged = async ( )}`; try { - const res = await axios.get(url); - return res.data.value || []; + const data = await getJson(url); + return data.value || []; } catch (error) { consoleLogger(error); return []; @@ -240,27 +200,20 @@ export const getBasicSearchDetailsPaged = async ( }; export const getSearchDocumentDetails = (incidentID) => { - return axios - .get( - "/api/endpoint/getsearchdocumentdetails_api?incidentid=" + - incidentID - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - throw error; - }); + return getJson( + "/api/endpoint/getsearchdocumentdetails_api?incidentid=" + incidentID + ).catch((error) => { + consoleLogger(error); + throw error; + }); }; export const getSearchDocumentTypes = (incidentID) => { - return axios - .get( - "/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - }); + return getJson( + "/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID + ).catch((error) => { + consoleLogger(error); + }); }; export const getSearchDocumentDetailsPaged = ( @@ -271,39 +224,30 @@ export const getSearchDocumentDetailsPaged = ( showNumberOfRecords, documentType ) => { - return axios - .get( - "/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" + - incidentID + - "&pageNumber=" + - pageNumber + - "&orderby=" + - orderBy + - "&fieldSort=" + - fieldSort + - "&showNumberOfRecords=" + - showNumberOfRecords + - "&documentType=" + - documentType - ) - .then((res) => res.data) - .catch((error) => { - consoleLogger(error); - }); + return getJson( + "/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" + + incidentID + + "&pageNumber=" + + pageNumber + + "&orderby=" + + orderBy + + "&fieldSort=" + + fieldSort + + "&showNumberOfRecords=" + + showNumberOfRecords + + "&documentType=" + + documentType + ).catch((error) => { + consoleLogger(error); + }); }; export const getLinkedCases = (parentIncidentid) => { - return axios - .get( - "/api/endpoint/getlinkedcases_api?parentincidentid=" + - parentIncidentid - ) - .then((res) => { - return res.data; - }) - .catch((error) => { - consoleLogger(error); - }); + return getJson( + "/api/endpoint/getlinkedcases_api?parentincidentid=" + parentIncidentid + ).catch((error) => { + consoleLogger(error); + }); }; export const getAddressSearch = async (searchString) => { @@ -313,14 +257,5 @@ export const getAddressSearch = async (searchString) => { var queryUrl = BASE_URL + "/api/endpoint/getbasicsearch_by_address_api?" + str; - var config = { - method: "get", - url: queryUrl - }; - - return axios(config) - .then((res) => { - return res.data; - }) - .catch(logAndReturnEmptyValueErrorResponse); + return getJson(queryUrl).catch(logAndReturnEmptyValueErrorResponse); }; diff --git a/context/architecture.md b/context/architecture.md index 8fc7a59c..151886f2 100644 --- a/context/architecture.md +++ b/context/architecture.md @@ -75,3 +75,58 @@ Guidance: - Do not treat these files as active runtime architecture unless explicitly reactivated. - If reactivation is proposed, document rationale and rollout/rollback in `memory-bank/change-log.md` and `context/runbook.md`. + +## Current State Assessment and Prioritised Next Steps (2026-03-25) + +### Assessment summary + +The platform has moved into a stronger operational and architectural posture through sustained bounded refactor slices and contract hardening. + +Strengths: + +1. **Governance maturity is high** + - Guardrails are explicit for auth/session integrity, CSP/security headers, Prisma source-of-truth, relay hash integrity, and EN/CY parity. +2. **API reliability posture has improved materially** + - Endpoint contract hardening and phase21 contract test expansion have reduced inconsistency in negative-path handling. +3. **Relay operations are significantly more robust** + - Shared relay forwarding now includes bounded retry/timeout policy, structured redacted lifecycle logging, and documented rollout/rollback controls. +4. **Façade decomposition is delivering low-risk progress** + - `actions` layer migration to shared clients (`relayClient`, `endpointClient`) is reducing duplicated request boilerplate and lowering drift risk. + +Primary residual risks/gaps: + +1. **Remaining direct-service inconsistency** + - Some direct services still contain legacy axios/request patterns and bespoke signed-request blocks. +2. **Coverage concentration** + - Contract tests are strong in targeted slices, but end-to-end/high-value journey coverage in sensitive flows remains comparatively sparse. +3. **Logging hygiene variance** + - Structured redaction exists in relay paths, but broader codebase logging still has uneven consistency. +4. **i18n parity assurance remains process-heavy** + - EN/CY parity relies heavily on manual discipline rather than automated parity checks. + +### Prioritised next steps + +1. **Complete direct-service consistency sweep (low risk, high maintainability)** + - Prioritise `actions/services/searchDirectService.js` for `getJson`/`requestJson` adoption in bounded slices. + - Preserve existing error-return behavior contracts per function. +2. **Consolidate signed-request patterns (medium risk, high security clarity)** + - Introduce a focused signed-request helper for hash-based/signed delete/get pathways currently repeated in service modules. + - Keep existing hash/header semantics unchanged while reducing duplication. +3. **Add high-value regression automation (high value)** + - Add focused automated checks for: + - auth callback/redirect safety + - one signed-delete negative path + - one upload/document authorization negative path + - one EN/CY route parity check +4. **Perform targeted logging hardening in sensitive paths** + - Continue replacing direct/verbose logging in `auth`, `file`, `email`, and account-sensitive endpoint paths with redacted structured logging patterns. +5. **Introduce EN/CY parity CI checks** + - Add automated checks for route rewrite parity and locale key alignment to reduce drift and manual burden. +6. **Continue endpoint sprawl reduction** + - Keep collapsing duplicated proxy/request patterns behind shared helpers in bounded route clusters while preserving public response contracts. + +### Recommended execution sequence + +- **Sequence A (immediate):** Step 1 + Step 3 (fastest risk reduction per effort) +- **Sequence B (next):** Step 2 + Step 4 (security/logging consistency consolidation) +- **Sequence C (after):** Step 5 + Step 6 (institutionalise parity and reduce long-tail maintenance cost) diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index db6f52d6..339c8e13 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -1892,3 +1892,69 @@ Validation: Follow-ups: - Optional next bounded slice: assess whether signed-delete helpers in `portalDirectService` should remain explicit axios calls (for clarity on headers/hash semantics) or move to a dedicated signed-request client helper. + +--- + +### CL-053: Architecture documentation update — current-state assessment + prioritised roadmap + +date: 2026-03-25 +author: Cline +scope: `context/architecture.md` +type: change +rationale: User requested the architectural assessment be documented in-project so current state, residual risks, and recommended sequencing are explicitly captured for team planning. +impact: Improves documentation clarity and planning alignment; no runtime behavior changes. +status: completed + +Summary: + +- Added a new section to `context/architecture.md`: + - **Current State Assessment and Prioritised Next Steps (2026-03-25)** +- Documented: + - strengths (governance maturity, API/relay hardening progress, façade decomposition progress) + - residual risks/gaps (remaining direct-service inconsistency, coverage concentration, logging variance, i18n parity automation gap) + - prioritised next steps with rationale + - recommended execution sequencing (immediate/next/after) + +Validation: + +- Manual coherence check against existing guardrails/runbook/integration-map and memory-bank context. + +Follow-ups: + +- Optional: convert the prioritised steps into a sprint-ready plan (tasks, acceptance criteria, validation matrix, rollback notes). + +--- + +### CL-054: Branch correction — include residual duplication/axios risk slices (search + signed-delete portal flows) + +date: 2026-03-25 +author: Cline +scope: `actions/services/{portalDirectService,searchDirectService}.js`, `context/architecture.md` +type: change +rationale: User requested the previously identified primary remaining technical risk slices be explicitly included on this branch, specifically residual axios/duplication in search direct service and signed-delete portal helper flows. +impact: Further reduces direct-service axios boilerplate/duplication and aligns additional high-use service paths to shared endpoint client patterns while preserving existing behavior contracts. +status: completed + +Summary: + +- Implemented the requested risk slices on branch: + 1. **Portal signed-delete flow parity** (`portalDirectService`) + - migrated signed delete helpers from direct `axios({...}).then(res.data)` to `requestJson({...})`: + - `deleteMyRepresentations` + - `deleteWatchedCases` + - removed now-unused `axios` import from module + - preserved hash-signing flow and custom headers semantics + 2. **Search direct service axios reduction** (`searchDirectService`) + - migrated service GET helpers from direct `axios.get(...).then(res.data)` and `axios(config)` to `getJson(...)` + - preserved existing catch semantics (`logAndReturnResponse`, `logAndReturnEmptyValueErrorResponse`, and explicit throw path in `getSearchDocumentDetails`) + - removed direct `axios` import from module +- Documentation alignment: + - retained architecture assessment section in `context/architecture.md` that calls out these residual-risk slices and prioritisation. + +Validation: + +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) + +Follow-ups: + +- Optional next bounded slice: assess whether a dedicated signed-request client helper should encapsulate hash + headers + method conventions to prevent future drift in remaining signed flows.