375 lines
12 KiB
JavaScript
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;
|
|
}
|