metis-desk/scripts/embedded-relations-fixture.js

280 lines
9.2 KiB
JavaScript
Raw Normal View History

2026-10-24 23:19:17 +00:00
export async function prepareEmbeddedRelations(page) {
// Grafo sintético de lectura para L04.3. Solo lo ejecuta el cliente MCP de pruebas.
const f = page.adoption;
const companyId = f.detail.company.id;
const inventory = f.inventory;
const resources = f.snapshots[`/resources/${companyId}`].resources;
const actorId = inventory.assets[0].history[0].actorId;
const asset = inventory.assets.find((a) => a.id === page.embedded.asset);
const domain = resources.find((r) => r.directory);
const smb = resources.find((r) => r.networkShare);
let sequence = 1000;
const uuid = () => `019a4300-0000-7000-b000-${String(++sequence).padStart(12, "0")}`;
const shares = Array.from({ length: 50 }, (_, i) => {
const result = structuredClone(smb);
const name = `L043-SHARE-${String(i + 1).padStart(2, "0")} · Administración Peña`;
const address = result.networkShare.address
.replace(result.networkShare.host, asset.values.name)
.replace(result.networkShare.shareName, `L043-area-${i + 1}`);
const old = result.networkShare.address;
result.id = uuid();
result.values.name = name;
result.values.characteristics = Object.fromEntries(
Object.entries(result.values.characteristics).map(([key, value]) => [
key,
value === old ? address : value === smb.networkShare.host ? asset.values.name : value,
])
);
result.networkShare = {
...result.networkShare,
address,
normalizedAddress: address.toLowerCase(),
host: asset.values.name,
shareName: `L043-area-${i + 1}`,
};
result.history = [{ ...result.history[0], id: uuid(), values: structuredClone(result.values) }];
return result;
});
resources.push(...shares);
const reference = (providerId, id) => ({
providerId,
entityType: providerId === "assets" ? "assets:asset" : "resources:resource",
id,
companyId,
});
const summary = (ref, name, subtitle) => ({
status: "resolved",
summary: { reference: ref, name, subtitle },
});
const publications = shares.map((r) => ({
id: uuid(),
companyId,
assetId: asset.id,
resourceId: r.id,
predecessorId: null,
startedAt: "2026-03-01T10:00:00.000Z",
startedBy: actorId,
endedAt: null,
endedBy: null,
}));
const history = shares.slice(0, 25).map((r) => ({
id: uuid(),
companyId,
assetId: asset.id,
resourceId: r.id,
predecessorId: null,
startedAt: "2026-02-01T10:00:00.000Z",
startedBy: actorId,
endedAt: "2026-02-10T10:00:00.000Z",
endedBy: actorId,
}));
const computers = inventory.assets
.filter((a) => a.values.status === "operational" && a.specification.templateName === "Portátil")
.slice(0, 50);
const memberships = computers.map((a) => ({
typeId: "assets:directory-membership",
origin: { kind: "record", id: uuid() },
source: reference("assets", a.id),
target: reference("resources", domain.id),
stateCode: "active",
roleCode: "member",
}));
const publicationView = (p) => ({
typeId: "assets:network-share-publication",
origin: { kind: "record", id: p.id },
source: reference("assets", p.assetId),
target: reference("resources", p.resourceId),
stateCode: p.endedAt ? "ended" : "active",
});
const summaries = (items, fromAsset) =>
items.map((item) => {
const ref = fromAsset ? item.target : item.source;
const entity = (fromAsset ? resources : inventory.assets).find((e) => e.id === ref.id);
return summary(
ref,
entity.values.name,
fromAsset
? (entity.networkShare?.address ?? entity.directory?.normalizedDns ?? "")
: entity.specification.templateName
);
});
const slice = (items, cursor, size = 20) => {
const offset = cursor ? items.findIndex((x) => (x.origin?.id ?? x.id) === cursor) + 1 : 0;
const values = items.slice(offset, offset + size);
return {
items: values,
nextCursor:
offset + size < items.length ? (values.at(-1).origin?.id ?? values.at(-1).id) : null,
};
};
page.embeddedData = {
shares,
domain: domain.id,
publications,
memberships,
requests: [],
fault: null,
held: null,
removed: null,
publicationResponse: null,
directoryResponse: null,
releases: [],
};
page.embedded.release = () => page.embeddedData.release?.();
const endpoint = `${page.adoptionOrigin}/api/asset-network-share-publications/${companyId}`;
async function publicationResponse(route, _url, params) {
const d = page.embeddedData;
const fromAsset = params.get("anchorType") === "assets:asset";
const anchor = params.get("anchorId");
const periods = (
params.get("includeEnded") === "true" ? [...history, ...publications] : publications
).filter((p) => (fromAsset ? p.assetId : p.resourceId) === anchor && p.id !== d.removed);
const pg = slice(periods, params.get("cursor"), Number(params.get("pageSize")));
const items = pg.items.map(publicationView);
const response = {
page: { items, nextCursor: pg.nextCursor },
summaries: summaries(items, fromAsset),
publications: pg.items,
};
d.publicationResponse = response;
if (d.fault === "publications") {
return route.fulfill({ status: 503, json: { code: "INTERNAL_ERROR" } });
}
if (d.fault === "denied") {
return route.fulfill({ status: 403, json: { code: "FORBIDDEN" } });
}
if (d.fault === "hold-publications") {
await new Promise((resolve) => {
d.release = resolve;
d.releases.push(resolve);
});
}
return route.fulfill({ json: response });
}
async function optionResponse(route, url, params) {
const d = page.embeddedData;
const kind = url.pathname.endsWith("/asset-options") ? "asset" : "share";
const search = params.get("search") ?? "";
const cursor = params.get("cursor");
const items = (
kind === "asset"
? inventory.assets
.filter((a) => a.values.status === "operational")
.map((a) => ({
id: a.id,
name: a.values.name,
templateName: a.specification.templateName,
status: a.values.status,
}))
: shares.map((r) => ({ id: r.id, name: r.values.name, address: r.networkShare.address }))
).filter((x) =>
`${x.name} ${x.address ?? ""}`.toLowerCase().includes(search.trim().toLowerCase())
);
const response = slice(items, cursor);
if (d.fault === "options") {
return route.fulfill({ status: 503, json: { code: "INTERNAL_ERROR" } });
}
if (search === "ANTIGUA") {
d.held = true;
await new Promise((resolve) => {
d.release = resolve;
d.releases.push(resolve);
});
// Una respuesta antigua contiene una opción distinguible, aunque su petición haya sido cancelada.
response.items = [
{ id: shares[0].id, name: "RESPUESTA ANTIGUA", address: shares[0].networkShare.address },
];
}
try {
await route.fulfill({ json: response });
} catch (error) {
if (!(String(error).includes("intercepted") || String(error).includes("Target page"))) {
throw error;
}
d.requests.push({ aborted: search });
}
}
await page.route(`${endpoint}/**`, async (route) => {
if (route.request().method() !== "GET") {
return route.fallback();
}
const url = new URL(route.request().url());
page.embeddedData.requests.push({
path: url.pathname,
query: Object.fromEntries(url.searchParams),
});
return url.pathname.endsWith("/relationships")
? publicationResponse(route, url, url.searchParams)
: optionResponse(route, url, url.searchParams);
});
await page.route(
`${page.adoptionOrigin}/api/asset-directory-memberships/${companyId}/**`,
async (route) => {
if (route.request().method() !== "GET") {
return route.fallback();
}
const url = new URL(route.request().url());
const p = url.searchParams;
page.embeddedData.requests.push({ path: url.pathname, query: Object.fromEntries(p) });
if (url.pathname.endsWith("/eligibility")) {
const id = url.pathname.split("/").at(-2);
const candidate = inventory.assets.find((asset) => asset.id === id);
const template = candidate?.specification.templateName;
return route.fulfill({
json: {
operational: candidate?.values.status === "operational",
computer: ["Portátil", "PC de sobremesa"].includes(template),
server: template === "Servidor",
},
});
}
if (!url.pathname.endsWith("/relationships")) {
return route.fallback();
}
const fromAsset = p.get("anchorType") === "assets:asset";
const anchor = p.get("anchorId");
const pg = slice(
memberships.filter((m) => (fromAsset ? m.source.id : m.target.id) === anchor),
p.get("cursor"),
Number(p.get("pageSize"))
);
const response = { page: pg, summaries: summaries(pg.items, fromAsset) };
page.embeddedData.directoryResponse = response;
return route.fulfill({ json: response });
}
);
return {
shares: shares.length,
publications: publications.length,
memberships: memberships.length,
};
}