feat(connectors): version provider packages and ontology contracts
This commit is contained in:
+74
@@ -0,0 +1,74 @@
|
||||
import { DATA_PRODUCT_PUBLISH_SCHEMA_VERSION } from "../../../../src/data-product.mjs";
|
||||
|
||||
export const geliosUnitsCurrentFixtureV1 = Object.freeze({
|
||||
sourceResponse: {
|
||||
units: [{
|
||||
id: 1001,
|
||||
name: "Synthetic unit 1001",
|
||||
hw_id: "restricted-fixture-value",
|
||||
phone: "restricted-fixture-value",
|
||||
lastMsg: {
|
||||
time: 1784203200,
|
||||
lat: "55.7558",
|
||||
lon: "37.6173",
|
||||
speed: 24.5,
|
||||
course: 91,
|
||||
height: 164,
|
||||
gps_valid: true,
|
||||
sats: 11,
|
||||
hdop: 0.8,
|
||||
accuracy: 4.2,
|
||||
params: "restricted-fixture-value",
|
||||
},
|
||||
}, {
|
||||
id: "001002",
|
||||
name: "Synthetic unit 1002",
|
||||
}],
|
||||
},
|
||||
collectionContext: {
|
||||
receivedAt: "2026-07-16T12:00:00.000Z",
|
||||
},
|
||||
expectedPublish: {
|
||||
schemaVersion: DATA_PRODUCT_PUBLISH_SCHEMA_VERSION,
|
||||
batch: {
|
||||
runId: "gelios-fixture-run-20260716",
|
||||
sequence: 0,
|
||||
idempotencyKey: "gelios-fixture-run-20260716.batch-0",
|
||||
},
|
||||
facts: [{
|
||||
sourceId: "gelios-unit-1001",
|
||||
semanticType: "map.moving_object",
|
||||
observedAt: "2026-07-16T12:00:00.000Z",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: [37.6173, 55.7558],
|
||||
},
|
||||
attributes: {
|
||||
course_degrees: 91,
|
||||
display_name: "Synthetic unit 1001",
|
||||
elevation_meters: 164,
|
||||
hdop: 0.8,
|
||||
horizontal_accuracy_meters: 4.2,
|
||||
object_kind: "tracked_unit",
|
||||
operational_status: "active",
|
||||
position_source: "gelios",
|
||||
position_valid: true,
|
||||
quality_flags: [],
|
||||
satellite_count: 11,
|
||||
speed_kph: 24.5,
|
||||
},
|
||||
}, {
|
||||
sourceId: "gelios-unit-001002",
|
||||
semanticType: "map.moving_object",
|
||||
observedAt: "2026-07-16T12:00:00.000Z",
|
||||
attributes: {
|
||||
display_name: "Synthetic unit 1002",
|
||||
object_kind: "tracked_unit",
|
||||
operational_status: "no_position",
|
||||
position_source: "gelios",
|
||||
position_valid: false,
|
||||
quality_flags: ["missing_geometry"],
|
||||
},
|
||||
}],
|
||||
},
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
export {
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
GELIOS_PROVIDER_PACKAGE_ID,
|
||||
GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
GELIOS_UNIT_SOURCE_ID_PREFIX,
|
||||
geliosProviderPackageV1,
|
||||
} from "./package.mjs";
|
||||
|
||||
export { geliosUnitsCurrentFixtureV1 } from "./fixtures/units-current.fixture.mjs";
|
||||
@@ -0,0 +1,374 @@
|
||||
import {
|
||||
L2_TEMPLATE_SCHEMA_VERSION,
|
||||
PROVIDER_PACKAGE_SCHEMA_VERSION,
|
||||
SEMANTIC_MAPPING_SCHEMA_VERSION,
|
||||
} from "../../../src/provider-package.mjs";
|
||||
|
||||
export const GELIOS_PROVIDER_PACKAGE_ID = "gelios.provider.v1";
|
||||
export const GELIOS_PROVIDER_PACKAGE_VERSION = "1.0.0";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_ID = "fleet.positions.current.v1";
|
||||
export const GELIOS_POSITIONS_DATA_PRODUCT_VERSION = "1.0.0";
|
||||
export const GELIOS_POSITIONS_ONTOLOGY_REVISION = "ontology.map.moving_object.v1";
|
||||
export const GELIOS_UNIT_SOURCE_ID_PREFIX = "gelios-unit-";
|
||||
|
||||
const targetFields = Object.freeze([
|
||||
"course_degrees",
|
||||
"display_name",
|
||||
"elevation_meters",
|
||||
"geometry",
|
||||
"hdop",
|
||||
"horizontal_accuracy_meters",
|
||||
"object_kind",
|
||||
"operational_status",
|
||||
"position_source",
|
||||
"position_valid",
|
||||
"quality_flags",
|
||||
"satellite_count",
|
||||
"speed_kph",
|
||||
]);
|
||||
|
||||
export const geliosProviderPackageV1 = deepFreeze({
|
||||
schemaVersion: PROVIDER_PACKAGE_SCHEMA_VERSION,
|
||||
id: GELIOS_PROVIDER_PACKAGE_ID,
|
||||
providerId: "gelios",
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
manifest: {
|
||||
id: "gelios.provider.manifest.v1",
|
||||
providerId: "gelios",
|
||||
version: GELIOS_PROVIDER_PACKAGE_VERSION,
|
||||
ontology: {
|
||||
packageId: "gelios",
|
||||
revision: "ontology.gelios.v1",
|
||||
},
|
||||
authModeIds: ["gelios.rest-bearer.v1"],
|
||||
capabilityIds: ["gelios.units.current.read"],
|
||||
fieldPolicyIds: ["gelios.positions.current.fields.v1"],
|
||||
collectionProfileIds: [
|
||||
"gelios.positions.current.realtime.v1",
|
||||
"gelios.positions.current.manual.v1",
|
||||
],
|
||||
dataProductIds: [GELIOS_POSITIONS_DATA_PRODUCT_ID],
|
||||
mappingContractIds: ["gelios.units.to.fleet.positions.current.v1"],
|
||||
l2TemplateIds: ["gelios.positions.current.l2.v1"],
|
||||
},
|
||||
authModes: [{
|
||||
id: "gelios.rest-bearer.v1",
|
||||
kind: "api_token",
|
||||
credentialOwner: "ndc_l2_credentials",
|
||||
bindingCardinality: "one_per_connection",
|
||||
transport: {
|
||||
placement: "header",
|
||||
name: "Authorization",
|
||||
},
|
||||
tokenLifecycle: {
|
||||
artifacts: ["access", "refresh"],
|
||||
requestArtifact: "access",
|
||||
refreshMode: "operator_managed",
|
||||
},
|
||||
secretHandling: {
|
||||
store: "ndc_l2_credentials",
|
||||
exposeToGraph: false,
|
||||
persistInPackage: false,
|
||||
},
|
||||
}],
|
||||
capabilities: [{
|
||||
id: "gelios.units.current.read",
|
||||
classification: "read",
|
||||
status: "implemented",
|
||||
authModeId: "gelios.rest-bearer.v1",
|
||||
request: {
|
||||
method: "GET",
|
||||
baseUrl: "https://api.geliospro.com",
|
||||
path: "/api/v1/units",
|
||||
query: {},
|
||||
response: {
|
||||
collectionPaths: ["units", "data", "data.units", "result", "$"],
|
||||
pagination: "single_bounded_response",
|
||||
},
|
||||
},
|
||||
entityScope: {
|
||||
mode: "all_visible_to_credential",
|
||||
refresh: "each_collection_run",
|
||||
businessEntityFilter: "forbidden",
|
||||
},
|
||||
}],
|
||||
fieldPolicies: [{
|
||||
id: "gelios.positions.current.fields.v1",
|
||||
version: "1.0.0",
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
targetFields: [...targetFields],
|
||||
unknownSourceFields: "drop",
|
||||
dynamicSourceFields: "drop_until_classified",
|
||||
restrictedSourcePaths: [
|
||||
"decrypt_key",
|
||||
"hw_id",
|
||||
"imei",
|
||||
"info",
|
||||
"lmsg.address",
|
||||
"lmsg.params",
|
||||
"lmsg.sensors",
|
||||
"lastMsg.address",
|
||||
"lastMsg.params",
|
||||
"lastMsg.sensors",
|
||||
"lastMessage.address",
|
||||
"lastMessage.params",
|
||||
"lastMessage.sensors",
|
||||
"phone",
|
||||
"phone2",
|
||||
"sensors",
|
||||
],
|
||||
}],
|
||||
collectionProfiles: [{
|
||||
id: "gelios.positions.current.realtime.v1",
|
||||
version: "1.0.0",
|
||||
mode: "realtime",
|
||||
schedule: { intervalMs: 10000 },
|
||||
capabilityIds: ["gelios.units.current.read"],
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
mappingContractId: "gelios.units.to.fleet.positions.current.v1",
|
||||
fieldPolicyId: "gelios.positions.current.fields.v1",
|
||||
l2TemplateId: "gelios.positions.current.l2.v1",
|
||||
entityScope: {
|
||||
mode: "all_visible_to_credential",
|
||||
refresh: "each_collection_run",
|
||||
businessEntityFilter: "forbidden",
|
||||
},
|
||||
batching: { maxFacts: 5000 },
|
||||
cardinality: {
|
||||
maxCurrentEntities: 5000,
|
||||
onExceed: "require_partitioned_data_product",
|
||||
},
|
||||
retry: {
|
||||
maxAttempts: 4,
|
||||
backoff: "exponential_with_jitter",
|
||||
},
|
||||
}, {
|
||||
id: "gelios.positions.current.manual.v1",
|
||||
version: "1.0.0",
|
||||
mode: "manual",
|
||||
capabilityIds: ["gelios.units.current.read"],
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
mappingContractId: "gelios.units.to.fleet.positions.current.v1",
|
||||
fieldPolicyId: "gelios.positions.current.fields.v1",
|
||||
l2TemplateId: "gelios.positions.current.l2.v1",
|
||||
entityScope: {
|
||||
mode: "all_visible_to_credential",
|
||||
refresh: "each_collection_run",
|
||||
businessEntityFilter: "forbidden",
|
||||
},
|
||||
batching: { maxFacts: 5000 },
|
||||
cardinality: {
|
||||
maxCurrentEntities: 5000,
|
||||
onExceed: "require_partitioned_data_product",
|
||||
},
|
||||
retry: {
|
||||
maxAttempts: 4,
|
||||
backoff: "exponential_with_jitter",
|
||||
},
|
||||
}],
|
||||
dataProducts: [{
|
||||
id: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
deliveryMode: "snapshot+patch",
|
||||
semanticTypes: ["map.moving_object"],
|
||||
fields: [...targetFields],
|
||||
history: {
|
||||
mode: "sampled",
|
||||
intervalMs: 60000,
|
||||
strategy: "latest-per-entity-per-bucket",
|
||||
retentionDays: 90,
|
||||
},
|
||||
}],
|
||||
mappingContracts: [{
|
||||
schemaVersion: SEMANTIC_MAPPING_SCHEMA_VERSION,
|
||||
id: "gelios.units.to.fleet.positions.current.v1",
|
||||
version: "1.0.0",
|
||||
sourceCapabilityId: "gelios.units.current.read",
|
||||
fieldPolicyId: "gelios.positions.current.fields.v1",
|
||||
target: {
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
version: GELIOS_POSITIONS_DATA_PRODUCT_VERSION,
|
||||
ontologyRevision: GELIOS_POSITIONS_ONTOLOGY_REVISION,
|
||||
semanticType: "map.moving_object",
|
||||
},
|
||||
derivations: {
|
||||
position_valid: {
|
||||
kind: "boolean_rule",
|
||||
rules: [
|
||||
"geometry.coordinates_finite_wgs84",
|
||||
"provider.gps_valid_not_false",
|
||||
],
|
||||
default: false,
|
||||
},
|
||||
operational_status: {
|
||||
kind: "ordered_rules",
|
||||
rules: [
|
||||
"position_invalid.no_position",
|
||||
"observed_age_gt_limit.stale",
|
||||
"otherwise.active",
|
||||
],
|
||||
default: "active",
|
||||
parameters: { staleAfterMs: 60000 },
|
||||
},
|
||||
quality_flags: {
|
||||
kind: "flag_set",
|
||||
rules: [
|
||||
"geometry_missing.missing_geometry",
|
||||
"provider_gps_false.gps_invalid",
|
||||
"satellite_count_zero.no_satellites",
|
||||
"observed_age_gt_limit.stale",
|
||||
],
|
||||
default: [],
|
||||
parameters: { staleAfterMs: 60000 },
|
||||
},
|
||||
},
|
||||
fact: {
|
||||
sourceId: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["id", "unit_id", "unitId"],
|
||||
coerce: "string",
|
||||
prefix: GELIOS_UNIT_SOURCE_ID_PREFIX,
|
||||
},
|
||||
semanticType: { constant: "map.moving_object" },
|
||||
observedAt: {
|
||||
strategy: "first_non_empty",
|
||||
paths: [
|
||||
"lmsg.time",
|
||||
"lmsg.ts",
|
||||
"lmsg.timestamp",
|
||||
"last_msg.time",
|
||||
"last_msg.timestamp",
|
||||
"lastMsg.time",
|
||||
"lastMessage.time",
|
||||
"last_msg_time",
|
||||
"lmsg_time",
|
||||
],
|
||||
coerce: "unix_or_iso_timestamp",
|
||||
fallback: "collection_received_at",
|
||||
},
|
||||
geometry: {
|
||||
type: "Point",
|
||||
longitude: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.lon", "lmsg.lng", "lmsg.longitude", "last_msg.lon", "lastMsg.lon", "lastMsg.lng", "lastMsg.longitude", "lastMessage.lon", "lastMessage.lng", "lastMessage.longitude", "lon", "lng", "longitude"],
|
||||
coerce: "number",
|
||||
},
|
||||
latitude: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.lat", "lmsg.latitude", "last_msg.lat", "lastMsg.lat", "lastMsg.latitude", "lastMessage.lat", "lastMessage.latitude", "lat", "latitude"],
|
||||
coerce: "number",
|
||||
},
|
||||
omitIfInvalid: true,
|
||||
},
|
||||
attributes: {
|
||||
course_degrees: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.course", "lmsg.angle", "last_msg.course", "lastMsg.course", "lastMsg.angle", "lastMessage.course", "lastMessage.angle", "course"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
display_name: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["name", "unit_name", "title", "label"],
|
||||
coerce: "string",
|
||||
},
|
||||
elevation_meters: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.height", "lmsg.altitude", "last_msg.height", "lastMsg.height", "lastMsg.altitude", "lastMessage.height", "lastMessage.altitude", "height", "altitude"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
hdop: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.hdop", "last_msg.hdop", "lastMsg.hdop", "lastMessage.hdop", "gps_hdop", "hdop"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
horizontal_accuracy_meters: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.accuracy", "last_msg.accuracy", "lastMsg.accuracy", "lastMessage.accuracy", "gps_accuracy_m", "accuracy_m", "accuracy"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
object_kind: { constant: "tracked_unit" },
|
||||
operational_status: { derive: "operational_status" },
|
||||
position_source: { constant: "gelios" },
|
||||
position_valid: { derive: "position_valid" },
|
||||
quality_flags: { derive: "quality_flags" },
|
||||
satellite_count: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.sats", "lmsg.satellites", "last_msg.sats", "lastMsg.sats", "lastMsg.satellites", "lastMessage.sats", "lastMessage.satellites", "gps_sats", "sats", "satellites"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
speed_kph: {
|
||||
strategy: "first_non_empty",
|
||||
paths: ["lmsg.speed", "last_msg.speed", "lastMsg.speed", "lastMessage.speed", "speed"],
|
||||
coerce: "number",
|
||||
omitIfMissing: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}],
|
||||
l2Templates: [{
|
||||
schemaVersion: L2_TEMPLATE_SCHEMA_VERSION,
|
||||
id: "gelios.positions.current.l2.v1",
|
||||
version: "1.0.0",
|
||||
runtime: "ndc_l2",
|
||||
instanceMode: "one_connection_per_workflow",
|
||||
connectionParameters: [
|
||||
"tenant_id",
|
||||
"connection_id",
|
||||
"collection_profile_id",
|
||||
"provider_credential_ref",
|
||||
],
|
||||
credentialBindings: [{
|
||||
role: "provider",
|
||||
owner: "ndc_l2_credentials",
|
||||
management: "connection_input",
|
||||
authModeId: "gelios.rest-bearer.v1",
|
||||
}, {
|
||||
role: "publisher",
|
||||
owner: "ndc_l2_credentials",
|
||||
management: "control_plane_managed",
|
||||
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
||||
}],
|
||||
steps: [{
|
||||
id: "collection.trigger",
|
||||
kind: "collection_trigger",
|
||||
collectionProfileDriven: true,
|
||||
}, {
|
||||
id: "provider.fetch",
|
||||
kind: "provider_request",
|
||||
capabilityId: "gelios.units.current.read",
|
||||
}, {
|
||||
id: "provider.extract-items",
|
||||
kind: "extract_items",
|
||||
capabilityId: "gelios.units.current.read",
|
||||
}, {
|
||||
id: "ontology.map",
|
||||
kind: "semantic_mapping",
|
||||
mappingContractId: "gelios.units.to.fleet.positions.current.v1",
|
||||
}, {
|
||||
id: "data-product.publish",
|
||||
kind: "data_product_publish",
|
||||
dataProductId: GELIOS_POSITIONS_DATA_PRODUCT_ID,
|
||||
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
||||
}],
|
||||
invariants: {
|
||||
oneConnectionPerInstance: true,
|
||||
providerSecretByReferenceOnly: true,
|
||||
publisherBindingControlPlaneManaged: true,
|
||||
callerScopeForbidden: true,
|
||||
providerSpecificServiceForbidden: true,
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
function deepFreeze(value) {
|
||||
if (!value || typeof value !== "object" || Object.isFrozen(value)) return value;
|
||||
Object.freeze(value);
|
||||
for (const child of Object.values(value)) deepFreeze(child);
|
||||
return value;
|
||||
}
|
||||
Reference in New Issue
Block a user