diff --git a/actions/clients/fileClient.js b/actions/clients/fileClient.js index e639bb30..c58c9748 100644 --- a/actions/clients/fileClient.js +++ b/actions/clients/fileClient.js @@ -1,17 +1,12 @@ import { getJson, requestJson } from "./endpointClient"; -import { buildHashedQueryUrl } from "./relayClient"; +import { getSignedJson, postSignedJson } from "./signedRequestClient"; export const getFileJson = (url) => { return getJson(url); }; export const getSignedFileJson = async (queryUrl) => { - const signedUrl = await buildHashedQueryUrl(queryUrl); - - return requestJson({ - method: "get", - url: signedUrl - }); + return getSignedJson(queryUrl); }; export const downloadFileBlob = (url) => { @@ -23,12 +18,5 @@ export const downloadFileBlob = (url) => { }; export const postSignedFileJson = async (queryUrl, data, config = {}) => { - const signedUrl = await buildHashedQueryUrl(queryUrl); - - return requestJson({ - method: "post", - url: signedUrl, - data, - ...config - }); + return postSignedJson(queryUrl, data, config); }; diff --git a/memory-bank/change-log.md b/memory-bank/change-log.md index f41fef9f..489c8b30 100644 --- a/memory-bank/change-log.md +++ b/memory-bank/change-log.md @@ -2801,3 +2801,34 @@ Validation: Follow-ups: - Next bounded signed GET candidate in portal service is `sendRepCompleteMessage` (single signed URL + GET request path). + +--- + +### CL-078: TASK22269 Slice B1.3 — fileClient signed helper delegation bundle + +date: 2026-03-25 +author: Cline +scope: `actions/clients/fileClient.js`, `tests/phase22/file-client-behaviour.test.cjs` +type: change +rationale: Continue grouped signed-request consolidation by reducing duplicate signing logic in `fileClient` and delegating signed GET/POST operations to shared `signedRequestClient` helpers. +impact: Centralizes signed method execution behavior in one helper layer and lowers drift risk across file-service call paths. +status: completed + +Summary: + +- Updated `actions/clients/fileClient.js`: + - replaced direct `buildHashedQueryUrl + requestJson` logic in: + - `getSignedFileJson` -> now delegates to `getSignedJson` + - `postSignedFileJson` -> now delegates to `postSignedJson` + - retained `downloadFileBlob` and `getFileJson` behavior unchanged. +- Updated `tests/phase22/file-client-behaviour.test.cjs` to assert delegation contracts for `getSignedJson` and `postSignedJson` rather than direct signing internals. + +Validation: + +- `npm run lint` -> pass with warnings only (pre-existing `react-hooks/exhaustive-deps`; no new lint errors) +- `node tests/phase22/index.test.cjs` -> pass +- `node tests/phase7/service-behaviour.test.cjs` -> pass (13/13) + +Follow-ups: + +- Candidate map now indicates remaining explicit signed request composition is primarily in account/portal signed GET edge paths (`getPortalLogin`, `sendRepCompleteMessage`, and signed suffix append flow in `sendCaseCompleteMessage`) for future bounded slices. diff --git a/tests/phase22/file-client-behaviour.test.cjs b/tests/phase22/file-client-behaviour.test.cjs index 767aad88..e648c954 100644 --- a/tests/phase22/file-client-behaviour.test.cjs +++ b/tests/phase22/file-client-behaviour.test.cjs @@ -24,8 +24,11 @@ const loadFileClientModule = (injected = {}) => { requestJson: async () => { throw new Error("requestJson not injected"); }, - buildHashedQueryUrl: async () => { - throw new Error("buildHashedQueryUrl not injected"); + getSignedJson: async () => { + throw new Error("getSignedJson not injected"); + }, + postSignedJson: async () => { + throw new Error("postSignedJson not injected"); }, ...injected }; @@ -56,15 +59,10 @@ test("clients/fileClient getFileJson delegates to getJson", async () => { test("clients/fileClient getSignedFileJson signs url and requests json", async () => { const signedCalls = []; - const requestCalls = []; const mod = loadFileClientModule({ - buildHashedQueryUrl: async (queryUrl) => { + getSignedJson: async (queryUrl) => { signedCalls.push(queryUrl); - return queryUrl + "&hash=signed"; - }, - requestJson: async (config) => { - requestCalls.push(config); return { deleted: true }; } }); @@ -77,11 +75,9 @@ test("clients/fileClient getSignedFileJson signs url and requests json", async ( deleted: true }); assert.strictEqual(signedCalls.length, 1); - assert.strictEqual(requestCalls.length, 1); - assert.strictEqual(requestCalls[0].method, "get"); assert.strictEqual( - requestCalls[0].url, - "/api/file/deleteblobcase?container=a&casefolderID=b&hash=signed" + signedCalls[0], + "/api/file/deleteblobcase?container=a&casefolderID=b" ); }); @@ -106,15 +102,12 @@ test("clients/fileClient downloadFileBlob requests blob response", async () => { test("clients/fileClient postSignedFileJson signs url and posts payload", async () => { const signedCalls = []; - const requestCalls = []; + const postedCalls = []; const mod = loadFileClientModule({ - buildHashedQueryUrl: async (queryUrl) => { + postSignedJson: async (queryUrl, data, config) => { signedCalls.push(queryUrl); - return queryUrl + "&hash=signed-post"; - }, - requestJson: async (config) => { - requestCalls.push(config); + postedCalls.push({ data, config }); return { uploaded: true }; } }); @@ -132,18 +125,14 @@ test("clients/fileClient postSignedFileJson signs url and posts payload", async uploaded: true }); assert.strictEqual(signedCalls.length, 1); - assert.strictEqual(requestCalls.length, 1); - assert.strictEqual(requestCalls[0].method, "post"); - assert.strictEqual( - requestCalls[0].url, - "/api/file/uploadsinglefile&hash=signed-post" - ); + assert.strictEqual(postedCalls.length, 1); + assert.strictEqual(signedCalls[0], "/api/file/uploadsinglefile"); assert.deepStrictEqual( - JSON.parse(JSON.stringify(requestCalls[0].data)), + JSON.parse(JSON.stringify(postedCalls[0].data)), payload ); assert.strictEqual( - requestCalls[0].headers["content-type"], + postedCalls[0].config.headers["content-type"], "multipart/form-data" ); });