feat(platform): add replaceable geozone data layer

This commit is contained in:
Codex
2026-07-20 21:48:27 +03:00
parent 8a7465cf0e
commit 3446f3edc2
31 changed files with 1342 additions and 128 deletions
@@ -72,6 +72,32 @@ assert.equal(validateDataProductPublish({
...publish,
facts: [{ ...fact, geometry: { type: "Point", coordinates: [37.6173, 90.0001] } }],
}).errors.includes("facts[0].geometry.latitude_out_of_range"), true);
const zoneGenerationAt = "2026-07-15T10:10:00.000Z";
const zoneFact = {
sourceId: "gelios-zone-42",
semanticType: "map.zone",
observedAt: zoneGenerationAt,
attributes: { display_name: "Zone 42" },
geometry: {
type: "Polygon",
coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.60, 55.74]]],
},
};
const replacement = {
...publish,
batch: { ...publish.batch, mode: "replace", generationAt: zoneGenerationAt },
facts: [zoneFact],
};
assert.equal(validateDataProductPublish(replacement).ok, true);
assert.equal(validateDataProductPublish({ ...replacement, facts: [] }).ok, true);
assert.equal(validateDataProductPublish({
...replacement,
facts: [{ ...zoneFact, observedAt: "2026-07-15T10:09:59.000Z" }],
}).errors.includes("facts[0].observedAt_must_equal_generationAt"), true);
assert.equal(validateDataProductPublish({
...replacement,
facts: [{ ...zoneFact, geometry: { ...zoneFact.geometry, coordinates: [[[37.60, 55.74], [37.62, 55.74], [37.62, 55.76], [37.61, 55.75]]] } }],
}).errors.includes("facts[0].geometry[0]_linear_ring_must_be_closed"), true);
const canonicalFact = { ...fact, receivedAt: "2026-07-15T10:00:01.000Z" };
const snapshot = {
@@ -147,7 +173,11 @@ assert.equal(validateDataProductPatch(patch).ok, true);
assert.equal(validateDataProductPatch({
...patch,
operations: [{ op: "delete", fact: canonicalFact }],
}).errors.includes("operations[0].op_must_be_upsert"), true);
}).errors.includes("operations[0].op_invalid"), true);
assert.equal(validateDataProductPatch({
...patch,
operations: [{ op: "remove", sourceId: "gelios-zone-42", semanticType: "map.zone" }],
}).ok, true);
assert.equal(validateDataProductPatch({
...patch,
operations: [{ op: "upsert", fact: { ...canonicalFact, attributes: attributesWithSerializedSize((64 * 1024) + 1) } }],
@@ -18,6 +18,7 @@ import { geliosProviderPackageV2 } from "../providers/gelios/v2/index.mjs";
import { geliosProviderPackageV3 } from "../providers/gelios/v3/index.mjs";
import { geliosProviderPackageV4 } from "../providers/gelios/v4/index.mjs";
import { geliosProviderPackageV5 } from "../providers/gelios/v5/index.mjs";
import { geliosProviderPackageV6 } from "../providers/gelios/v6/index.mjs";
import { normalizeDataProductDefinition } from "../../../services/external-data-plane/src/data-product-policy.mjs";
const expectedFields = [
@@ -41,6 +42,24 @@ assert.deepEqual(validateProviderPackage(geliosProviderPackageV2), { ok: true, e
assert.deepEqual(validateProviderPackage(geliosProviderPackageV3), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV4), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV5), { ok: true, errors: [] });
assert.deepEqual(validateProviderPackage(geliosProviderPackageV6), { ok: true, errors: [] });
const zonesProduct = geliosProviderPackageV6.dataProducts.find((product) => product.id === "map.zones.current.v1");
const registeredZonesProduct = JSON.parse(await readFile(new URL(
"../../../services/external-data-plane/definitions/map.zones.current.v1.json",
import.meta.url,
), "utf8"));
assert.deepEqual(zonesProduct, registeredZonesProduct);
assert.deepEqual(zonesProduct.semanticTypes, ["map.zone"]);
assert.equal(zonesProduct.fieldContracts.geometry.type, "geometry");
const zonesCapability = geliosProviderPackageV6.capabilities.find((capability) => capability.id === "gelios.geozones.current.read");
assert.equal(zonesCapability.request.query.pl, 100);
assert.equal(zonesCapability.request.query.po, 0);
assert.equal(zonesCapability.request.response.pagination.maxItems, 5000);
assert.equal(zonesCapability.request.response.pagination.totalPath, "paginationMetadata.totalCount");
const zonesMapping = geliosProviderPackageV6.mappingContracts.find((mapping) => mapping.id === "gelios.geozones.to.map.zones.current.v1");
assert.deepEqual(zonesMapping.fact.observedAt.paths, ["updatedAt"]);
assert.equal(zonesMapping.fact.observedAt.fallback, "collection_received_at");
const strictProduct = geliosProviderPackageV5.dataProducts[0];
const registeredStrictProduct = JSON.parse(await readFile(new URL(
@@ -0,0 +1,95 @@
import assert from "node:assert/strict";
import {
ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
normalizeZoneSourceGeneration,
validateDataProductPublish,
} from "../src/index.mjs";
const generatedAt = "2026-07-20T18:00:00.000Z";
const live = normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "gelios-rest-v1",
sourceRevision: "gelios-rest-v1",
generatedAt,
complete: true,
zones: [{
id: 42,
name: "Polygon zone",
type: "polygon",
points: [
{ latitude: 55.74, longitude: 37.60 },
{ latitude: 55.74, longitude: 37.62 },
{ latitude: 55.76, longitude: 37.62 },
],
color: "#ff00ff",
}, {
id: 43,
name: "Circle zone",
type: "circle",
points: [{ latitude: 55.75, longitude: 37.61 }],
radius: 150,
}],
});
assert.equal(live.complete, true);
assert.equal(live.sourceKind, "live_api");
assert.equal(live.facts.length, 2);
assert.deepEqual(live.facts.map((fact) => fact.sourceId), ["gelios-zone-42", "gelios-zone-43"]);
assert.equal(live.facts.every((fact) => fact.observedAt === generatedAt), true);
assert.equal(live.facts.every((fact) => fact.geometry.type === "Polygon"), true);
assert.equal(validateDataProductPublish({
schemaVersion: "nodedc.data-product.publish/v1",
batch: { runId: "zones-live-1", sequence: 0, idempotencyKey: "zones-live-1.batch-0", mode: "replace", generationAt: generatedAt },
facts: live.facts,
}).ok, true);
const snapshotDigest = "a".repeat(64);
const snapshot = normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "mmap-snapshot-v1",
snapshotDigest,
generatedAt,
complete: true,
zones: [{
snapshotKey: "legacy-zone-a",
name: "Snapshot zone",
type: "polygon",
points: "55.74,37.60;55.74,37.62;55.76,37.62",
}],
}, { identityCrosswalk: { "legacy-zone-a": 42 } });
assert.equal(snapshot.sourceKind, "versioned_snapshot");
assert.equal(snapshot.facts[0].sourceId, live.facts[0].sourceId);
assert.match(snapshot.sourceRevision, new RegExp(`@sha256:${snapshotDigest}$`));
assert.throws(() => normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "mmap-snapshot-v1",
snapshotDigest,
generatedAt,
complete: true,
zones: [{ snapshotKey: "unknown", name: "Unknown", type: "polygon", points: "55.74,37.60;55.74,37.62;55.76,37.62" }],
}), /zone_source_identity_crosswalk_required/);
assert.throws(() => normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "mmap-snapshot-v1",
snapshotDigest,
generatedAt,
complete: true,
zones: [{ id: 42, name: "Uncrossed", type: "polygon", points: "55.74,37.60;55.74,37.62;55.76,37.62" }],
}), /zone_source_identity_crosswalk_required/);
assert.throws(() => normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "gelios-rest-v1",
generatedAt,
complete: true,
zones: [{ name: "No native id", type: "polygon", points: "55.74,37.60;55.74,37.62;55.76,37.62" }],
}), /zone_source_native_identity_required/);
assert.throws(() => normalizeZoneSourceGeneration({
schemaVersion: ZONE_SOURCE_GENERATION_SCHEMA_VERSION,
adapterId: "gelios-rest-v1",
generatedAt,
complete: false,
zones: [],
}), /zone_source_generation_incomplete/);
console.log("external-provider zone source: ok");