TASK22269: group migrate remaining account/portal signed GET flows
This commit is contained in:
@@ -1,14 +1,21 @@
|
||||
import { requestJson } from "./endpointClient";
|
||||
import { buildHashedQueryUrl } from "./relayClient";
|
||||
|
||||
export const buildSignedUrl = async (queryUrl, config = {}) => {
|
||||
const { baseUrl = "" } = config;
|
||||
|
||||
return `${baseUrl}${await buildHashedQueryUrl(queryUrl)}`;
|
||||
};
|
||||
|
||||
const signedRequestJson = async ({ method, queryUrl, data, config = {} }) => {
|
||||
const signedUrl = await buildHashedQueryUrl(queryUrl);
|
||||
const { baseUrl, ...requestConfig } = config;
|
||||
const signedUrl = await buildSignedUrl(queryUrl, { baseUrl });
|
||||
|
||||
return requestJson({
|
||||
method,
|
||||
url: signedUrl,
|
||||
data,
|
||||
...config
|
||||
...requestConfig
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BASE_URL } from "../core/env";
|
||||
import { consoleLogger } from "../core/logger";
|
||||
import { buildHashedQueryUrl } from "../clients/relayClient";
|
||||
import { getJson, requestJson } from "../clients/endpointClient";
|
||||
import { getSignedJson } from "../clients/signedRequestClient";
|
||||
|
||||
export const getPersonalAccount = (contactid) => {
|
||||
return getJson(
|
||||
@@ -78,13 +78,11 @@ export const getPortalLogin = async (emailAddress) => {
|
||||
var queryUrl =
|
||||
"/api/endpoint/getportallogin_api?emailAddress=" + emailAddress;
|
||||
|
||||
return getJson(BASE_URL + (await buildHashedQueryUrl(queryUrl))).catch(
|
||||
(error) => {
|
||||
consoleLogger(error);
|
||||
return getSignedJson(queryUrl, { baseUrl: BASE_URL }).catch((error) => {
|
||||
consoleLogger(error);
|
||||
|
||||
return JSON.stringify(error);
|
||||
}
|
||||
);
|
||||
return JSON.stringify(error);
|
||||
});
|
||||
};
|
||||
|
||||
export const getPortalLoginProxy = async (emailAddress) => {
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { BASE_URL } from "../core/env";
|
||||
import { consoleLogger } from "../core/logger";
|
||||
import { buildHashedQueryUrl } from "../clients/relayClient";
|
||||
import { getJson, requestJson } from "../clients/endpointClient";
|
||||
import { deleteSignedJson } from "../clients/signedRequestClient";
|
||||
import {
|
||||
deleteSignedJson,
|
||||
buildSignedUrl
|
||||
} from "../clients/signedRequestClient";
|
||||
import {
|
||||
buildFileQuery,
|
||||
withBaseUrl,
|
||||
@@ -207,7 +209,7 @@ export const sendCaseCompleteMessage = async (
|
||||
inv
|
||||
});
|
||||
|
||||
var signedQueryUrl = await buildHashedQueryUrl(hashQueryPath);
|
||||
var signedQueryUrl = await buildSignedUrl(hashQueryPath);
|
||||
|
||||
queryUrl = appendQuerySuffix(
|
||||
queryUrl,
|
||||
@@ -260,7 +262,7 @@ export const sendRepCompleteMessage = async (
|
||||
}
|
||||
);
|
||||
|
||||
var queryUrl = await buildHashedQueryUrl(hashQueryPath);
|
||||
var queryUrl = await buildSignedUrl(hashQueryPath);
|
||||
|
||||
var config = {
|
||||
method: "get",
|
||||
|
||||
@@ -2832,3 +2832,42 @@ Validation:
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
### CL-079: TASK22269 Slice B1.4 — signed GET consolidation bundle (portal + account)
|
||||
|
||||
date: 2026-03-25
|
||||
author: Cline
|
||||
scope: `actions/clients/signedRequestClient.js`, `actions/services/{portalDirectService,accountDirectService}.js`, `tests/{serviceHarness,phase22/portal-service-behaviour,phase7/service-behaviour}.cjs`
|
||||
type: change
|
||||
rationale: Continue grouped signed-request migration by consolidating remaining direct signed-GET composition paths onto shared signed helper primitives while preserving route behavior contracts.
|
||||
impact: Reduces residual signing duplication and standardizes signed URL creation across portal/account service read/message flows.
|
||||
status: completed
|
||||
|
||||
Summary:
|
||||
|
||||
- Enhanced `signedRequestClient`:
|
||||
- added `buildSignedUrl(queryUrl, { baseUrl? })` helper for signed URL generation reuse
|
||||
- updated internal signed request execution to use `buildSignedUrl`
|
||||
- Migrated account signed GET candidate:
|
||||
- `accountDirectService.getPortalLogin` now uses `getSignedJson(queryUrl, { baseUrl: BASE_URL })`
|
||||
- preserved existing error semantics (`consoleLogger` + `JSON.stringify(error)`)
|
||||
- Migrated portal signed GET candidates:
|
||||
- `portalDirectService.sendRepCompleteMessage` now uses `buildSignedUrl(hashQueryPath)`
|
||||
- `portalDirectService.sendCaseCompleteMessage` now uses `buildSignedUrl(hashQueryPath)` + existing signed suffix append behavior
|
||||
- preserved existing request method/URL shape and catch-path behavior
|
||||
- Updated test harness and suites:
|
||||
- `tests/serviceHarness.cjs` now provides defaults for `buildSignedUrl`, `getSignedJson`, `postSignedJson`
|
||||
- `tests/phase22/portal-service-behaviour.test.cjs` includes assertion for `sendRepCompleteMessage` signed-helper delegation
|
||||
- `tests/phase7/service-behaviour.test.cjs` account portal-login expectations aligned to request-config path used by shared signed helper
|
||||
|
||||
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:
|
||||
|
||||
- Remaining special-case signed pattern is now primarily the signed-suffix append composition in `sendCaseCompleteMessage` (already using shared `buildSignedUrl`), with broader module migrations to be planned in future bounded slices.
|
||||
|
||||
@@ -134,6 +134,47 @@ test("portal/sendRepCompleteMessage rejects when hash signing fails before reque
|
||||
assert.strictEqual(logger.calls.length, 0);
|
||||
});
|
||||
|
||||
test("portal/sendRepCompleteMessage requests signed URL from shared signed helper", async () => {
|
||||
const axios = createAxiosMock();
|
||||
const logger = createLoggerMock();
|
||||
|
||||
const signedCalls = [];
|
||||
const requestCalls = [];
|
||||
|
||||
const portal = loadServiceModule("portalDirectService.js", {
|
||||
axios,
|
||||
BASE_URL: "",
|
||||
consoleLogger: logger.consoleLogger,
|
||||
buildSignedUrl: async (queryUrl) => {
|
||||
signedCalls.push(queryUrl);
|
||||
return `${queryUrl}&hash=signed-portal`;
|
||||
},
|
||||
requestJson: async (config) => {
|
||||
requestCalls.push(config);
|
||||
return { ok: true };
|
||||
}
|
||||
});
|
||||
|
||||
const result = await portal.sendRepCompleteMessage(
|
||||
"container-x",
|
||||
"CASE-99",
|
||||
"rep-a"
|
||||
);
|
||||
|
||||
assert.deepStrictEqual(normalize(result), { ok: true });
|
||||
assert.strictEqual(signedCalls.length, 1);
|
||||
assert.strictEqual(
|
||||
signedCalls[0],
|
||||
"/api/file/createrepcompletemessage_api?container=container-x&tempcaseref=CASE-99&repid=rep-a"
|
||||
);
|
||||
assert.strictEqual(requestCalls.length, 1);
|
||||
assert.strictEqual(requestCalls[0].method, "get");
|
||||
assert.strictEqual(
|
||||
requestCalls[0].url,
|
||||
"/api/file/createrepcompletemessage_api?container=container-x&tempcaseref=CASE-99&repid=rep-a&hash=signed-portal"
|
||||
);
|
||||
});
|
||||
|
||||
test("portal/deleteWatchedCases logs and returns undefined when signed delete fails", async () => {
|
||||
const axios = createAxiosMock();
|
||||
const logger = createLoggerMock();
|
||||
|
||||
@@ -256,9 +256,13 @@ test("account/getPortalLogin appends hash and returns res.data", async () => {
|
||||
return { data: { hash: "&hash=login123" } };
|
||||
}
|
||||
|
||||
return { data: { value: [{ id: "user-1" }] } };
|
||||
throw new Error("Unexpected get url: " + url);
|
||||
};
|
||||
|
||||
axios.requestHandler = async () => ({
|
||||
data: { value: [{ id: "user-1" }] }
|
||||
});
|
||||
|
||||
const account = loadServiceModule("accountDirectService.js", {
|
||||
axios,
|
||||
BASE_URL: "http://example.local",
|
||||
@@ -274,8 +278,9 @@ test("account/getPortalLogin appends hash and returns res.data", async () => {
|
||||
signCalls[0],
|
||||
"/api/endpoint/gethash_api?path=%2Fapi%2Fendpoint%2Fgetportallogin_api%3FemailAddress%3Dperson%40example.com"
|
||||
);
|
||||
assert.strictEqual(axios.calls[1].config.method, "get");
|
||||
assert.strictEqual(
|
||||
axios.calls[1].url,
|
||||
axios.calls[1].config.url,
|
||||
"http://example.local/api/endpoint/getportallogin_api?emailAddress=person@example.com&hash=login123"
|
||||
);
|
||||
});
|
||||
@@ -290,9 +295,11 @@ test("account/getPortalLogin returns JSON stringified error on failure", async (
|
||||
return { data: { hash: "&hash=err" } };
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
throw new Error("Unexpected get url: " + url);
|
||||
};
|
||||
|
||||
axios.requestHandler = async () => Promise.reject(error);
|
||||
|
||||
const account = loadServiceModule("accountDirectService.js", {
|
||||
axios,
|
||||
BASE_URL: "",
|
||||
|
||||
@@ -107,6 +107,15 @@ const loadServiceModule = (fileName, injected = {}) => {
|
||||
return queryUrl + hashResponse.data.hash;
|
||||
};
|
||||
|
||||
const defaultBuildSignedUrl = async (queryUrl, config = {}) => {
|
||||
const { baseUrl = "" } = config;
|
||||
const hashedUrl = await (
|
||||
injected.buildHashedQueryUrl || defaultBuildHashedQueryUrl
|
||||
)(queryUrl);
|
||||
|
||||
return `${baseUrl}${hashedUrl}`;
|
||||
};
|
||||
|
||||
const defaultGetSignedFileJson = async (queryUrl) => {
|
||||
const hashedUrl = await (
|
||||
injected.buildHashedQueryUrl || defaultBuildHashedQueryUrl
|
||||
@@ -138,6 +147,33 @@ const loadServiceModule = (fileName, injected = {}) => {
|
||||
});
|
||||
};
|
||||
|
||||
const defaultGetSignedJson = async (queryUrl, config = {}) => {
|
||||
const { baseUrl, ...requestConfig } = config;
|
||||
const signedUrl = await (
|
||||
injected.buildSignedUrl || defaultBuildSignedUrl
|
||||
)(queryUrl, { baseUrl });
|
||||
|
||||
return (injected.requestJson || defaultRequestJson)({
|
||||
method: "get",
|
||||
url: signedUrl,
|
||||
...requestConfig
|
||||
});
|
||||
};
|
||||
|
||||
const defaultPostSignedJson = async (queryUrl, data, config = {}) => {
|
||||
const { baseUrl, ...requestConfig } = config;
|
||||
const signedUrl = await (
|
||||
injected.buildSignedUrl || defaultBuildSignedUrl
|
||||
)(queryUrl, { baseUrl });
|
||||
|
||||
return (injected.requestJson || defaultRequestJson)({
|
||||
method: "post",
|
||||
url: signedUrl,
|
||||
data,
|
||||
...requestConfig
|
||||
});
|
||||
};
|
||||
|
||||
const defaultBuildFileQuery = (pathValue, params = {}, options = {}) => {
|
||||
const { encode = false } = options;
|
||||
const entries = Object.entries(params).filter(([, value]) => {
|
||||
@@ -184,7 +220,10 @@ const loadServiceModule = (fileName, injected = {}) => {
|
||||
getSignedFileJson:
|
||||
injected.getSignedFileJson || defaultGetSignedFileJson,
|
||||
downloadFileBlob: injected.downloadFileBlob || defaultDownloadFileBlob,
|
||||
getSignedJson: injected.getSignedJson || defaultGetSignedJson,
|
||||
postSignedJson: injected.postSignedJson || defaultPostSignedJson,
|
||||
deleteSignedJson: injected.deleteSignedJson || defaultDeleteSignedJson,
|
||||
buildSignedUrl: injected.buildSignedUrl || defaultBuildSignedUrl,
|
||||
buildHashedQueryUrl:
|
||||
injected.buildHashedQueryUrl || defaultBuildHashedQueryUrl,
|
||||
buildFileQuery: injected.buildFileQuery || defaultBuildFileQuery,
|
||||
|
||||
Reference in New Issue
Block a user