Merged PR 2428: Add documents API grouping facade slice

Add documents API grouping facade slice

Related work items: #23754
This commit is contained in:
Robert Bond
2026-06-25 08:24:58 +00:00
parent 153f0d679e
commit 33dcb6b863
7 changed files with 308 additions and 3 deletions
+3 -3
View File
@@ -201,7 +201,7 @@ export const getBasicSearchDetailsPaged = async (
export const getSearchDocumentDetails = (incidentID) => {
return getJson(
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" + incidentID
"/api/documents/get-search-document-details?incidentid=" + incidentID
).catch((error) => {
consoleLogger(error);
throw error;
@@ -210,7 +210,7 @@ export const getSearchDocumentDetails = (incidentID) => {
export const getSearchDocumentTypes = (incidentID) => {
return getJson(
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID
"/api/documents/get-search-document-types?incidentid=" + incidentID
).catch((error) => {
consoleLogger(error);
});
@@ -225,7 +225,7 @@ export const getSearchDocumentDetailsPaged = (
documentType
) => {
return getJson(
"/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" +
"/api/documents/get-search-document-details-paged?incidentid=" +
incidentID +
"&pageNumber=" +
pageNumber +
+29
View File
@@ -9,6 +9,10 @@ Pilot status update (2026-06-25):
- first additive grouping façade pilot implemented for watched-case APIs under `pages/api/subscriptions/`
- first internal consumer (`actions/services/portalDirectService.js` watched-case methods) now targets the grouped façade routes
- the active watched-case vertical slice now includes grouped adoption for both normal watched-case reads and proxy watched-case reads used by the current UI journey
- second additive grouping façade vertical slice implemented for the active published-documents metadata journey under `pages/api/documents/`
- active published-document metadata consumer (`actions/services/searchDirectService.js`) now targets grouped documents façade routes for details, paged details, and document-type reads
- published document download remains on the existing grouped runtime path `pages/api/documents/download/[id].js`
- legacy document metadata endpoint routes remain canonical and stable behind the additive façade wrappers
- legacy watched-case endpoint routes remain canonical and stable
- no old route removal or migration has occurred
- runtime behaviour is intended to remain unchanged because the grouped routes delegate to the existing handlers
@@ -189,6 +193,31 @@ It means:
- runtime behaviour remains unchanged through delegation
- `deletewatchedcasesproxy_api` remains a legacy/support route outside the grouped pilot scope because it is not part of the currently adopted normal watched-case service path.
#### Additional vertical-slice proof point — published documents
- The active published-documents journey now has a second complete additive grouping slice for metadata discovery only:
- grouped façade routes exist for the active metadata/type reads under `pages/api/documents/`
- `actions/services/searchDirectService.js` targets those grouped routes for:
- `getSearchDocumentDetails`
- `getSearchDocumentDetailsPaged`
- `getSearchDocumentTypes`
- legacy endpoint handlers remain present and canonical behind the façade:
- `pages/api/endpoint/getsearchdocumentdetails_api.js`
- `pages/api/endpoint/getsearchdocumentdetailspaged_api.js`
- `pages/api/endpoint/getsearchdocumentTypes_api.js`
- runtime behaviour remains unchanged through direct delegation and unchanged service-layer query contracts
- The active bounded audit also established that:
- the current UI document journey is routed through `components/case/documents.js`
- the current common metadata service layer is `actions/services/searchDirectService.js`
- the existing grouped route `pages/api/documents/download/[id].js` remains the runtime download path and was intentionally left unchanged
- `getsearchdocumenthistory_api.js` and `getsearchdocumenthistorypaged_api.js` remain present as legacy/support routes outside the active grouped adoption scope because they were not surfaced in the currently active UI journey
- For this documents slice, a complete vertical grouping slice means:
- active document metadata/type journey calls have grouped façade routes
- the active common service layer uses those grouped routes
- legacy handlers remain present and canonical
- download behaviour remains unchanged
- no route migration, deletion, or contract change has occurred
---
### Stage 3 — Documentation Maturity
+72
View File
@@ -18,6 +18,78 @@ Follow-ups:
---
### CL-2026-06-25-API-GROUPING-FACADE-DOCUMENTS-VERTICAL-SLICE: active published-document metadata grouping and service adoption
date: 2026-06-25
author: Cline
scope: `pages/api/documents/get-search-document-details.js`, `pages/api/documents/get-search-document-details-paged.js`, `pages/api/documents/get-search-document-types.js`, `actions/services/searchDirectService.js`, `tests/phase22/api-grouping-facade-documents.test.cjs`, `context/api-grouping-adoption-roadmap.md`
type: change
rationale: Implement the second additive API grouping façade vertical slice by grouping the active published-document metadata journey under `pages/api/documents/` and adopting the grouped routes only in the existing common document metadata service layer, while preserving the existing download route and legacy endpoint handlers unchanged.
impact: Runtime behaviour is intended to remain unchanged; grouped document façade routes now cover the active metadata/type journey calls, `actions/services/searchDirectService.js` now targets those grouped routes, legacy endpoint handlers remain canonical, the existing grouped download route remains unchanged, and no migration, route deletion, auth change, CRM query change, hash-link generation change, payload change, or response-contract change has occurred.
status: completed
Summary:
- Confirmed the required context was read before implementation:
- `context/api-grouping-plan.md`
- `context/api-grouping-adoption-roadmap.md`
- `context/journey-architecture-map.md`
- `context/api-route-map.md`
- `context/architecture.md`
- `memory-bank/change-log.md`
- Performed a bounded audit of the active published-documents journey across services, API handlers, and frontend components.
- Audit findings established that:
- active document metadata retrieval is surfaced through `components/case/documents.js`
- active document metadata/type calls already route through the common service layer `actions/services/searchDirectService.js`
- published-document downloads already use the grouped runtime route `pages/api/documents/download/[id].js`
- legacy metadata handlers generate `pinswg_hashlink` values that continue to point at the unchanged grouped download route
- `getsearchdocumenthistory_api.js` and `getsearchdocumenthistorypaged_api.js` remain present but were not surfaced in the currently active UI journey and therefore remained outside grouped adoption scope
- Added grouped façade routes under `pages/api/documents/` for the active metadata journey only:
- `get-search-document-details.js`
- `get-search-document-details-paged.js`
- `get-search-document-types.js`
- Implemented each façade as the smallest safe compatibility wrapper:
- import the existing legacy endpoint handler
- delegate `req` and `res` directly to that existing handler
- Updated only the suitable common service layer in `actions/services/searchDirectService.js` to target grouped façade routes for:
- `getSearchDocumentDetails`
- `getSearchDocumentDetailsPaged`
- `getSearchDocumentTypes`
- Left the following unchanged:
- CRM queries
- download behaviour
- hash-link generation
- authentication
- response contracts
- payloads
- file locations of legacy handlers
- Added focused characterization coverage to prove:
- grouped document façade routes delegate correctly
- active document service methods now target grouped document routes
- legacy document endpoint handlers remain present
- active service-layer query-contract cues remain unchanged
- the runtime download path remains `/api/documents/download/[id]`
- Updated the adoption roadmap to record the active documents grouping slice and its bounded scope.
Validation:
- Focused validation only intended for this slice:
- `npx eslint actions/services/searchDirectService.js pages/api/documents/get-search-document-details.js pages/api/documents/get-search-document-details-paged.js pages/api/documents/get-search-document-types.js tests/phase22/api-grouping-facade-documents.test.cjs context/api-grouping-adoption-roadmap.md memory-bank/change-log.md`
- `node tests/phase22/api-grouping-facade-documents.test.cjs`
- No repository-wide validation run.
Follow-ups:
- The active Documents journey is now a complete vertical grouping slice for the bounded published-document metadata/type path:
- grouped façade routes
- active service-layer adoption
- legacy canonical handlers retained
- unchanged grouped download path
- unchanged behaviour
- Any future expansion beyond this bounded scope should be a separate slice, for example if history/support routes are later proven to be part of an actively adopted user journey.
---
### CL-2026-06-25-API-GROUPING-FACADE-SUBSCRIPTIONS-VERTICAL-SLICE-COMPLETION: watched-case proxy read adoption and scope closure
date: 2026-06-25
@@ -0,0 +1,5 @@
import legacyGetSearchDocumentDetailsPagedHandler from "../endpoint/getsearchdocumentdetailspaged_api";
export default async function getSearchDocumentDetailsPagedFacade(req, res) {
return legacyGetSearchDocumentDetailsPagedHandler(req, res);
}
@@ -0,0 +1,5 @@
import legacyGetSearchDocumentDetailsHandler from "../endpoint/getsearchdocumentdetails_api";
export default async function getSearchDocumentDetailsFacade(req, res) {
return legacyGetSearchDocumentDetailsHandler(req, res);
}
@@ -0,0 +1,5 @@
import legacyGetSearchDocumentTypesHandler from "../endpoint/getsearchdocumentTypes_api";
export default async function getSearchDocumentTypesFacade(req, res) {
return legacyGetSearchDocumentTypesHandler(req, res);
}
@@ -0,0 +1,189 @@
const assert = require("assert");
const fs = require("fs");
const path = require("path");
const { loadModule, createRes } = require("../phase21/_shared.cjs");
const rootDir = path.resolve(__dirname, "..", "..");
const tests = [];
const test = (name, fn) => tests.push({ name, fn });
const read = (relativePath) =>
fs.readFileSync(path.join(rootDir, relativePath), "utf8");
test("documents get-search-document-details facade delegates to legacy handler", async () => {
const calls = [];
const mod = loadModule(
"pages/api/documents/get-search-document-details.js",
{
legacyGetSearchDocumentDetailsHandler: async (req, res) => {
calls.push({ req, res });
return res.status(200).json({ ok: true, route: "details" });
}
}
);
const req = { method: "GET", query: { incidentid: "inc-1" } };
const res = createRes();
await mod.default(req, res);
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].req, req);
assert.strictEqual(calls[0].res, res);
assert.strictEqual(res.state.statusCode, 200);
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
ok: true,
route: "details"
});
});
test("documents get-search-document-details-paged facade delegates to legacy handler", async () => {
const calls = [];
const mod = loadModule(
"pages/api/documents/get-search-document-details-paged.js",
{
legacyGetSearchDocumentDetailsPagedHandler: async (req, res) => {
calls.push({ req, res });
return res
.status(200)
.json({ ok: true, route: "details-paged" });
}
}
);
const req = {
method: "GET",
query: {
incidentid: "inc-2",
pageNumber: "1",
orderby: "createdon",
fieldSort: "asc",
showNumberOfRecords: "10",
documentType: "all"
}
};
const res = createRes();
await mod.default(req, res);
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].req, req);
assert.strictEqual(calls[0].res, res);
assert.strictEqual(res.state.statusCode, 200);
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), {
ok: true,
route: "details-paged"
});
});
test("documents get-search-document-types facade delegates to legacy handler", async () => {
const calls = [];
const mod = loadModule("pages/api/documents/get-search-document-types.js", {
legacyGetSearchDocumentTypesHandler: async (req, res) => {
calls.push({ req, res });
return res
.status(200)
.json([{ pinswg_isharedocumentlocations: 1, count: 2 }]);
}
});
const req = { method: "GET", query: { incidentid: "inc-3" } };
const res = createRes();
await mod.default(req, res);
assert.strictEqual(calls.length, 1);
assert.strictEqual(calls[0].req, req);
assert.strictEqual(calls[0].res, res);
assert.strictEqual(res.state.statusCode, 200);
assert.deepStrictEqual(JSON.parse(JSON.stringify(res.state.jsonBody)), [
{ pinswg_isharedocumentlocations: 1, count: 2 }
]);
});
test("legacy document metadata endpoint files remain present", async () => {
const legacyFiles = [
"pages/api/endpoint/getsearchdocumentdetails_api.js",
"pages/api/endpoint/getsearchdocumentdetailspaged_api.js",
"pages/api/endpoint/getsearchdocumentTypes_api.js",
"pages/api/endpoint/getsearchdocumenthistory_api.js",
"pages/api/endpoint/getsearchdocumenthistorypaged_api.js",
"pages/api/documents/download/[id].js"
];
legacyFiles.forEach((filePath) => {
assert.strictEqual(
fs.existsSync(path.join(rootDir, filePath)),
true,
filePath
);
});
});
test("searchDirectService document journey methods now target grouped documents facade routes", async () => {
const source = read("actions/services/searchDirectService.js");
assert.match(source, /\/api\/documents\/get-search-document-details\?/);
assert.match(
source,
/\/api\/documents\/get-search-document-details-paged\?/
);
assert.match(source, /\/api\/documents\/get-search-document-types\?/);
});
test("searchDirectService no longer targets legacy document metadata routes for adopted methods", async () => {
const source = read("actions/services/searchDirectService.js");
assert.doesNotMatch(
source,
/\/api\/endpoint\/getsearchdocumentdetails_api/
);
assert.doesNotMatch(
source,
/\/api\/endpoint\/getsearchdocumentdetailspaged_api/
);
assert.doesNotMatch(source, /\/api\/endpoint\/getsearchdocumentTypes_api/);
});
test("document runtime contract cues remain unchanged in searchDirectService", async () => {
const source = read("actions/services/searchDirectService.js");
assert.match(source, /incidentid=/);
assert.match(source, /pageNumber=/);
assert.match(source, /orderby=/);
assert.match(source, /fieldSort=/);
assert.match(source, /showNumberOfRecords=/);
assert.match(source, /documentType=/);
});
test("document download route remains the runtime download path", async () => {
const metadataDetailsSource = read(
"pages/api/endpoint/getsearchdocumentdetails_api.js"
);
const metadataPagedSource = read(
"pages/api/endpoint/getsearchdocumentdetailspaged_api.js"
);
assert.match(metadataDetailsSource, /\/api\/documents\/download\//);
assert.match(metadataPagedSource, /\/api\/documents\/download\//);
});
async function run() {
let failures = 0;
for (const { name, fn } of tests) {
try {
await fn();
console.log(`${name}`);
} catch (error) {
failures += 1;
console.error(`${name}`);
console.error(error);
}
}
if (failures > 0) {
process.exit(1);
}
}
run();