feat(foundry): support typed non-geometric subject aspects
This commit is contained in:
@@ -70,6 +70,19 @@ requireValue(new Set(consumerPolicyProducts).size === consumerPolicyProducts.len
|
||||
for (const policy of consumerPolicies) {
|
||||
const statusContract = policy.statusContract;
|
||||
const freshness = policy.freshness ?? statusContract?.freshness ?? "observed-at";
|
||||
const allowedPolicyKeys = new Set([
|
||||
"id",
|
||||
"version",
|
||||
"dataProductId",
|
||||
"productVersion",
|
||||
"freshness",
|
||||
"staleAfterMs",
|
||||
"terminalStatuses",
|
||||
"statusContract",
|
||||
"consumerContract",
|
||||
"removeMode",
|
||||
]);
|
||||
requireValue(Object.keys(policy).every((key) => allowedPolicyKeys.has(key)), `unsupported data product consumer policy key: ${policy.id ?? "unknown"}`);
|
||||
requireValue(typeof policy.id === "string" && /^[a-z0-9._-]{1,160}$/.test(policy.id), `invalid data product consumer policy id: ${policy.id ?? "unknown"}`);
|
||||
requireValue(typeof policy.version === "string" && /^\d+\.\d+\.\d+$/.test(policy.version), `invalid data product consumer policy version: ${policy.id ?? "unknown"}`);
|
||||
requireValue(typeof policy.dataProductId === "string" && /^[A-Za-z0-9._:-]{1,160}$/.test(policy.dataProductId), `invalid data product consumer policy product: ${policy.id ?? "unknown"}`);
|
||||
@@ -90,7 +103,14 @@ for (const policy of consumerPolicies) {
|
||||
requireValue(contract?.fieldTypes && typeof contract.fieldTypes === "object" && !Array.isArray(contract.fieldTypes), `invalid consumer field types: ${policy.id ?? "unknown"}`);
|
||||
requireValue(JSON.stringify(Object.keys(contract?.fieldTypes ?? {})) === JSON.stringify(contract?.fieldProjection ?? []), `consumer field type order/scope mismatch: ${policy.id ?? "unknown"}`);
|
||||
requireValue(Object.values(contract?.fieldTypes ?? {}).every((value) => ["string", "number", "boolean"].includes(value)), `invalid consumer field type: ${policy.id ?? "unknown"}`);
|
||||
requireValue(JSON.stringify(contract?.geometryTypes) === JSON.stringify(["Polygon", "MultiPolygon"]), `invalid consumer geometry types: ${policy.id ?? "unknown"}`);
|
||||
const supportedGeometryTypes = ["Point", "LineString", "Polygon", "MultiPolygon"];
|
||||
requireValue(
|
||||
Array.isArray(contract?.geometryTypes)
|
||||
&& contract.geometryTypes.length <= supportedGeometryTypes.length
|
||||
&& new Set(contract.geometryTypes).size === contract.geometryTypes.length
|
||||
&& contract.geometryTypes.every((value) => supportedGeometryTypes.includes(value)),
|
||||
`invalid consumer geometry types: ${policy.id ?? "unknown"}`,
|
||||
);
|
||||
requireValue(contract?.subjectIdentity === "semantic-type+source-id", `invalid consumer subject identity: ${policy.id ?? "unknown"}`);
|
||||
requireValue(contract?.snapshotMode === "atomic-replace" && contract?.patchMode === "atomic", `invalid consumer commit modes: ${policy.id ?? "unknown"}`);
|
||||
}
|
||||
@@ -147,6 +167,44 @@ requireValue(JSON.stringify(zoneV2Policy?.consumerContract?.fieldProjection) ===
|
||||
requireValue(JSON.stringify(zoneV2Policy?.consumerContract?.geometryTypes) === JSON.stringify(["Polygon", "MultiPolygon"]), "map.zones.current.v2 geometry contract must be Polygon/MultiPolygon");
|
||||
requireValue(!/(provider|tenant|connection|endpoint|credential|token|secret|authorization)/i.test(JSON.stringify(zoneV2Policy)), "map.zones.current.v2 consumer policy crosses the transport/secret boundary");
|
||||
|
||||
const unitProfileV1Policy = consumerPolicies.find((policy) => policy.dataProductId === "fleet.units.profile.current.v1" && policy.productVersion === "1.0.0");
|
||||
const unitProfileV1Projection = [
|
||||
"corrected_engine_hours_factor",
|
||||
"corrected_mileage_factor",
|
||||
"display_name",
|
||||
"filter_by_satellite_count_enabled",
|
||||
"filter_by_satellite_count_value",
|
||||
"filter_emissions",
|
||||
"hardware_manufacturer_name",
|
||||
"hardware_port",
|
||||
"hardware_type_class",
|
||||
"hardware_type_name",
|
||||
"limit_acceleration",
|
||||
"lost_connection_enabled",
|
||||
"lost_connection_time_value",
|
||||
"maximum_permissible_speed",
|
||||
"maximum_valid_height",
|
||||
"maximum_valid_speed",
|
||||
"mileage_by_ignition",
|
||||
"minimum_movement_speed",
|
||||
"minimum_movement_time",
|
||||
"minimum_parking_time",
|
||||
"minimum_stop_time",
|
||||
"minimum_trip_distance",
|
||||
"minimum_valid_height",
|
||||
"speed_parameter",
|
||||
"trip_detection_type",
|
||||
"unit_type_class",
|
||||
"unit_type_name",
|
||||
"use_odometer",
|
||||
];
|
||||
requireValue(unitProfileV1Policy?.id === "map-moving-object-unit-profile-current-v1" && unitProfileV1Policy?.version === "1.0.0", "fleet.units.profile.current.v1 consumer policy is missing");
|
||||
requireValue(unitProfileV1Policy?.freshness === "none" && unitProfileV1Policy?.staleAfterMs === null, "fleet.units.profile.current.v1 must not invent freshness states");
|
||||
requireValue(JSON.stringify(unitProfileV1Policy?.consumerContract?.semanticTypes) === JSON.stringify(["map.moving_object"]), "fleet.units.profile.current.v1 semantic scope must be exact");
|
||||
requireValue(JSON.stringify(unitProfileV1Policy?.consumerContract?.fieldProjection) === JSON.stringify(unitProfileV1Projection), "fleet.units.profile.current.v1 field projection must be exact");
|
||||
requireValue(JSON.stringify(unitProfileV1Policy?.consumerContract?.geometryTypes) === JSON.stringify([]), "fleet.units.profile.current.v1 must remain a non-geometric aspect");
|
||||
requireValue(!/(provider|tenant|endpoint|credential|token|secret|authorization)/i.test(JSON.stringify(unitProfileV1Policy)), "fleet.units.profile.current.v1 consumer policy crosses the transport/secret boundary");
|
||||
|
||||
const ids = components.components.map((component) => component.id);
|
||||
requireValue(new Set(ids).size === ids.length, "component ids must be unique");
|
||||
const candidateIds = candidates.candidates.map((component) => component.id);
|
||||
|
||||
Reference in New Issue
Block a user