feat(map): render classified unit identity aspects

This commit is contained in:
Codex
2026-07-24 11:43:20 +03:00
parent 49f0c449c1
commit c859ae4db0
11 changed files with 320 additions and 7 deletions
@@ -615,6 +615,117 @@ test("v4 consumer rejects bindings that omit signal_state and snapshots outside
}
});
test("consumer contracts preserve bounded string arrays for classified identity aspects", async () => {
const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-identity-"));
const fieldProjection = ["device_imei", "available_user_logins", "custom_fields"];
const product = {
id: "fleet.units.identity.current.v1",
version: "1.0.0",
ontologyRevision: "ontology.map.moving_object.v3",
deliveryMode: "snapshot+patch",
semanticTypes: ["map.moving_object"],
fields: [...fieldProjection],
fieldContracts: {
device_imei: { type: "string", required: false },
available_user_logins: { type: "string_array", required: false },
custom_fields: { type: "string_array", required: false },
},
active: true,
};
const policy = {
id: "map-moving-object-unit-identity-current-v1",
version: "1.0.0",
dataProductId: product.id,
productVersion: product.version,
dataClass: "restricted",
freshness: "none",
staleAfterMs: null,
terminalStatuses: [],
consumerContract: {
ontologyRevision: product.ontologyRevision,
deliveryMode: "snapshot+patch",
semanticTypes: ["map.moving_object"],
fieldProjection,
fieldTypes: {
device_imei: "string",
available_user_logins: "string_array",
custom_fields: "string_array",
},
geometryTypes: [],
subjectIdentity: "semantic-type+source-id",
snapshotMode: "atomic-replace",
patchMode: "atomic",
},
removeMode: "canonical-tombstone-or-snapshot-rebase",
};
const target = {
application: { id: "77777777-7777-4777-8777-777777777777" },
page: { id: "map" },
binding: {
id: "trike-unit-identity",
dataProductId: product.id,
slotId: "identity",
delivery: "snapshot+patch",
dataClass: "restricted",
semanticTypes: ["map.moving_object"],
fieldProjection,
},
};
let invalid = false;
const manager = createFoundryDataProductConsumerManager({
stateDir,
dataPlaneUrl: "http://edp.test",
resolveTarget: async () => structuredClone(target),
readReaderToken: async () => "ndc_edprb_identity-reader-capability",
inspectReaderGrant: async () => ({ product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
resolvePolicy: () => policy,
sanitizeSnapshot: (value) => value,
sanitizePatch: (value) => value,
fetchImpl: async () => Response.json({
schemaVersion: "nodedc.data-product.snapshot/v1",
dataProduct: { id: product.id, version: product.version },
generatedAt: "2026-07-24T08:00:01.000Z",
cursor: "1",
facts: [{
sourceId: "gelios-unit-5015",
semanticType: "map.moving_object",
observedAt: "2026-07-24T08:00:00.000Z",
receivedAt: "2026-07-24T08:00:00.500Z",
attributes: {
device_imei: "867236078012345",
available_user_logins: invalid ? "not-an-array" : ["vasya", "dispatcher"],
custom_fields: ["Serial number=5015"],
},
}],
}),
});
try {
const input = {
applicationId: target.application.id,
pageId: target.page.id,
bindingId: target.binding.id,
};
const plan = await manager.plan(input);
await manager.apply({ ...input, planId: plan.planId });
assert.deepEqual(
(await manager.snapshot(target)).facts[0].attributes.available_user_logins,
["vasya", "dispatcher"],
);
assert.equal(plan.configuration.policy.dataClass, "restricted");
assert.equal(plan.configuration.target.dataClass, "restricted");
invalid = true;
const invalidPlan = await manager.plan(input);
await assert.rejects(
manager.apply({ ...input, planId: invalidPlan.planId }),
/data_product_consumer_fact_contract_invalid/,
);
} finally {
await manager.shutdown();
await rm(stateDir, { recursive: true, force: true });
}
});
const zoneV2Projection = [
"display_name",
"geometry_kind",