Merged PR 2428: Add documents API grouping facade slice
Add documents API grouping facade slice Related work items: #23754
This commit is contained in:
@@ -201,7 +201,7 @@ export const getBasicSearchDetailsPaged = async (
|
|||||||
|
|
||||||
export const getSearchDocumentDetails = (incidentID) => {
|
export const getSearchDocumentDetails = (incidentID) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getsearchdocumentdetails_api?incidentid=" + incidentID
|
"/api/documents/get-search-document-details?incidentid=" + incidentID
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
throw error;
|
throw error;
|
||||||
@@ -210,7 +210,7 @@ export const getSearchDocumentDetails = (incidentID) => {
|
|||||||
|
|
||||||
export const getSearchDocumentTypes = (incidentID) => {
|
export const getSearchDocumentTypes = (incidentID) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getsearchdocumentTypes_api?incidentid=" + incidentID
|
"/api/documents/get-search-document-types?incidentid=" + incidentID
|
||||||
).catch((error) => {
|
).catch((error) => {
|
||||||
consoleLogger(error);
|
consoleLogger(error);
|
||||||
});
|
});
|
||||||
@@ -225,7 +225,7 @@ export const getSearchDocumentDetailsPaged = (
|
|||||||
documentType
|
documentType
|
||||||
) => {
|
) => {
|
||||||
return getJson(
|
return getJson(
|
||||||
"/api/endpoint/getsearchdocumentdetailspaged_api?incidentid=" +
|
"/api/documents/get-search-document-details-paged?incidentid=" +
|
||||||
incidentID +
|
incidentID +
|
||||||
"&pageNumber=" +
|
"&pageNumber=" +
|
||||||
pageNumber +
|
pageNumber +
|
||||||
|
|||||||
@@ -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 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
|
- 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
|
- 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
|
- legacy watched-case endpoint routes remain canonical and stable
|
||||||
- no old route removal or migration has occurred
|
- no old route removal or migration has occurred
|
||||||
- runtime behaviour is intended to remain unchanged because the grouped routes delegate to the existing handlers
|
- 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
|
- 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.
|
- `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
|
### Stage 3 — Documentation Maturity
|
||||||
|
|||||||
@@ -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
|
### CL-2026-06-25-API-GROUPING-FACADE-SUBSCRIPTIONS-VERTICAL-SLICE-COMPLETION: watched-case proxy read adoption and scope closure
|
||||||
|
|
||||||
date: 2026-06-25
|
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();
|
||||||
Reference in New Issue
Block a user