NODEDC_PLATFORM/packages/external-provider-contract/providers/gelios/v1/package.mjs

375 lines
12 KiB
JavaScript

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;
}