diff --git a/actions/clients/README.md b/actions/clients/README.md index 9d2ccbc3..6534a3ea 100644 --- a/actions/clients/README.md +++ b/actions/clients/README.md @@ -1,6 +1,13 @@ # Actions Clients -This folder is reserved for extracted client wrappers from `actions/index.js` as part of the Priority 1 refactor plan. +This folder contains extracted client wrappers from `actions/index.js` as part of the Priority 1 refactor plan. -Phase 1 delivered core helper extraction and compatibility barrel support. -Client-level extraction (`relayClient`, `endpointClient`, `fileClient`, `notifyClient`) is planned for the next increment. +Current extracted clients: + +- `relayClient` (shared hash-signing helper used by account/portal/document direct services) +- `endpointClient` (shared JSON request helpers for GET and generic axios config requests) + +Notes: + +- Core helper extraction and compatibility barrel support remain in place. +- Additional client extraction (`fileClient`, `notifyClient`) can be layered in incrementally without changing public exports from `actions/index.js`. diff --git a/actions/clients/endpointClient.js b/actions/clients/endpointClient.js new file mode 100644 index 00000000..2e5ff950 --- /dev/null +++ b/actions/clients/endpointClient.js @@ -0,0 +1,11 @@ +import axios from "axios"; + +export const getJson = async (url, config) => { + const response = await axios.get(url, config); + return response.data; +}; + +export const requestJson = async (config) => { + const response = await axios(config); + return response.data; +}; diff --git a/actions/clients/index.js b/actions/clients/index.js new file mode 100644 index 00000000..3676d8e2 --- /dev/null +++ b/actions/clients/index.js @@ -0,0 +1,2 @@ +export * from "./relayClient"; +export * from "./endpointClient"; diff --git a/actions/clients/relayClient.js b/actions/clients/relayClient.js new file mode 100644 index 00000000..3876b7b9 --- /dev/null +++ b/actions/clients/relayClient.js @@ -0,0 +1,22 @@ +import axios from "axios"; +import { hashAPIPath } from "../core/hash"; + +export const buildHashedQueryUrl = async (queryUrl) => { + try { + const signRes = await axios.get( + "/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) + ); + + if (!signRes?.data?.hash) { + throw new Error("Hash signature unavailable"); + } + + return queryUrl + signRes.data.hash; + } catch (error) { + if (typeof window === "undefined" && process.env.HASHKEY) { + return queryUrl + hashAPIPath(queryUrl); + } + + throw error; + } +}; diff --git a/actions/index.js b/actions/index.js index 4849f4a1..b7c500c3 100644 --- a/actions/index.js +++ b/actions/index.js @@ -5,4 +5,5 @@ export * from "./core/token"; export * from "./core/headers"; export * from "./core/guards"; +export * from "./clients"; export * from "./services"; diff --git a/actions/services/accountDirectService.js b/actions/services/accountDirectService.js index 5a236450..4a52f652 100644 --- a/actions/services/accountDirectService.js +++ b/actions/services/accountDirectService.js @@ -1,27 +1,7 @@ import axios from "axios"; import { BASE_URL } from "../core/env"; import { consoleLogger } from "../core/logger"; -import { hashAPIPath } from "../core/hash"; - -const buildHashedQueryUrl = async (queryUrl) => { - try { - const signRes = await axios.get( - "/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) - ); - - if (!signRes?.data?.hash) { - throw new Error("Hash signature unavailable"); - } - - return queryUrl + signRes.data.hash; - } catch (error) { - if (typeof window === "undefined" && process.env.HASHKEY) { - return queryUrl + hashAPIPath(queryUrl); - } - - throw error; - } -}; +import { buildHashedQueryUrl } from "../clients/relayClient"; export const getPersonalAccount = (contactid) => { return axios diff --git a/actions/services/adminDirectService.js b/actions/services/adminDirectService.js index 94b14af8..93deb2ef 100644 --- a/actions/services/adminDirectService.js +++ b/actions/services/adminDirectService.js @@ -1,11 +1,10 @@ -import axios from "axios"; import { BASE_URL } from "../core/env"; import { logAndReturnResponse } from "./httpServiceUtils"; +import { getJson } from "../clients/endpointClient"; export const getNewAppeals = async (searchString) => { try { - const res = await axios.get(BASE_URL + "/api/admin/getnewappeals_api"); - return res.data; + return await getJson(BASE_URL + "/api/admin/getnewappeals_api"); } catch (error) { return logAndReturnResponse(error); } @@ -18,8 +17,8 @@ export const getNewAppealsPage = async ( fieldSort, showNumberOfRecords ) => { - return axios - .get( + try { + return await getJson( "/api/admin/getnewappeals_api?searchString=" + searchString + "&pageNumber=" + @@ -30,11 +29,10 @@ export const getNewAppealsPage = async ( fieldSort + "&showNumberOfRecords=" + showNumberOfRecords - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + ); + } catch (error) { + return logAndReturnResponse(error); + } }; export const getNewDocumentsPaged = async ( @@ -46,8 +44,8 @@ export const getNewDocumentsPaged = async ( documentOrigin, selectedWeeks ) => { - return axios - .get( + try { + return await getJson( "/api/admin/getlatestdocuments_api?pageNumber=" + pageNumber + "&orderby=" + @@ -62,9 +60,8 @@ export const getNewDocumentsPaged = async ( selectedWeeks + "&documentOrigin=" + documentOrigin - ) - .then((res) => { - return res.data; - }) - .catch(logAndReturnResponse); + ); + } catch (error) { + return logAndReturnResponse(error); + } }; diff --git a/actions/services/documentDirectService.js b/actions/services/documentDirectService.js index 567ebc7b..f187a2cf 100644 --- a/actions/services/documentDirectService.js +++ b/actions/services/documentDirectService.js @@ -2,26 +2,7 @@ import axios from "axios"; import { BASE_URL } from "../core/env"; import { consoleLogger } from "../core/logger"; import { hashAPIPath } from "../core/hash"; - -const buildHashedQueryUrl = async (queryUrl) => { - try { - const signRes = await axios.get( - "/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) - ); - - if (!signRes?.data?.hash) { - throw new Error("Hash signature unavailable"); - } - - return queryUrl + signRes.data.hash; - } catch (error) { - if (typeof window === "undefined" && process.env.HASHKEY) { - return queryUrl + hashAPIPath(queryUrl); - } - - throw error; - } -}; +import { buildHashedQueryUrl } from "../clients/relayClient"; export const getAwaitingSubmissionFromBlob = (containerName) => { return axios diff --git a/actions/services/integrationDirectService.js b/actions/services/integrationDirectService.js index 51a54602..b2ebe7f3 100644 --- a/actions/services/integrationDirectService.js +++ b/actions/services/integrationDirectService.js @@ -1,5 +1,5 @@ -import axios from "axios"; import { consoleLogger } from "../core/logger"; +import { requestJson } from "../clients/endpointClient"; export const createCRMTask = async (formValues) => { var queryUrl = "/api/endpoint/createcrmtask_api"; @@ -12,8 +12,7 @@ export const createCRMTask = async (formValues) => { }; try { - const res = await axios(config); - return res.data; + return await requestJson(config); } catch (error) { consoleLogger(error); } diff --git a/actions/services/notifyDirectService.js b/actions/services/notifyDirectService.js index 557ae2cc..b36b64de 100644 --- a/actions/services/notifyDirectService.js +++ b/actions/services/notifyDirectService.js @@ -1,5 +1,5 @@ -import axios from "axios"; import { consoleLogger } from "../core/logger"; +import { requestJson } from "../clients/endpointClient"; export const sendEmail = async ( templateId, @@ -15,8 +15,11 @@ export const sendEmail = async ( }; try { - const response = await axios.post("/api/email/notify", mailData); - return response.data; + return await requestJson({ + method: "post", + url: "/api/email/notify", + data: mailData + }); } catch (error) { consoleLogger(error); throw error; diff --git a/actions/services/portalDirectService.js b/actions/services/portalDirectService.js index 1e112810..3035396f 100644 --- a/actions/services/portalDirectService.js +++ b/actions/services/portalDirectService.js @@ -1,27 +1,7 @@ import axios from "axios"; import { BASE_URL } from "../core/env"; import { consoleLogger } from "../core/logger"; -import { hashAPIPath } from "../core/hash"; - -const buildHashedQueryUrl = async (queryUrl) => { - try { - const signRes = await axios.get( - "/api/endpoint/gethash_api?path=" + encodeURIComponent(queryUrl) - ); - - if (!signRes?.data?.hash) { - throw new Error("Hash signature unavailable"); - } - - return queryUrl + signRes.data.hash; - } catch (error) { - if (typeof window === "undefined" && process.env.HASHKEY) { - return queryUrl + hashAPIPath(queryUrl); - } - - throw error; - } -}; +import { buildHashedQueryUrl } from "../clients/relayClient"; export const getMyCases = (loggedInUserId) => { return axios diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index 51bf914b..21ea2e49 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -1444,3 +1444,44 @@ Validation: Follow-ups: - P2-S3 planned slices are now complete; no further mandatory relay policy rollout slices remain for this stream. + +--- + +### CL-039: TASK22260 actions façade increment — shared relay client extraction + +date: 2026-03-25 +author: Cline +scope: `actions/clients/{relayClient,index}.js`, `actions/services/{accountDirectService,portalDirectService,documentDirectService}.js`, `actions/index.js`, `actions/clients/README.md` +type: change +rationale: Continue Priority 1 façade decomposition by extracting duplicated hash-signing relay helper logic into a dedicated client module while preserving existing service/public export contracts. +impact: Reduces duplication and drift risk in security-sensitive relay signing helper logic without changing call-site behavior. +status: completed + +Summary: + +- Added a new shared client wrapper: + - `actions/clients/relayClient.js` exporting `buildHashedQueryUrl` +- Added `actions/clients/index.js` barrel and exposed client exports via `actions/index.js`. +- Updated direct services to consume shared relay client helper instead of duplicating local helper implementations: + - `actions/services/accountDirectService.js` + - `actions/services/portalDirectService.js` + - `actions/services/documentDirectService.js` +- Updated `actions/clients/README.md` to reflect the now-implemented relay client extraction and future incremental client split path. + +Validation: + +- `npm run lint` -> warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) +- Verified no remaining duplicated local `buildHashedQueryUrl` definitions across `actions/services/*DirectService.js` + +Follow-ups: + +- Optional next TASK22260 increment: extract common axios invocation helpers into dedicated clients (`endpointClient`, `fileClient`, `notifyClient`) while keeping `actions/index.js` API stable. + +Addendum (same TASK22260 slice): + +- Added shared `endpointClient` with `getJson` and `requestJson` helpers (`actions/clients/endpointClient.js`) and exported it via `actions/clients/index.js`. +- Migrated additional direct services to consume shared endpoint client helpers: + - `actions/services/notifyDirectService.js` (POST via `requestJson`) + - `actions/services/integrationDirectService.js` (POST via `requestJson`) + - `actions/services/adminDirectService.js` (GET flows via `getJson`) +- Updated `actions/clients/README.md` to include `endpointClient` in current extracted clients.