feat(platform): complete the Gelios external data loop
This commit is contained in:
@@ -15,6 +15,9 @@ import {
|
||||
geliosUnitsCurrentFixtureV1,
|
||||
} from "../providers/gelios/v1/index.mjs";
|
||||
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 { normalizeDataProductDefinition } from "../../../services/external-data-plane/src/data-product-policy.mjs";
|
||||
|
||||
const expectedFields = [
|
||||
@@ -35,6 +38,197 @@ const expectedFields = [
|
||||
|
||||
assert.deepEqual(validateProviderPackage(geliosProviderPackageV1), { ok: true, errors: [] });
|
||||
assert.deepEqual(validateProviderPackage(geliosProviderPackageV2), { ok: true, errors: [] });
|
||||
assert.deepEqual(validateProviderPackage(geliosProviderPackageV3), { ok: true, errors: [] });
|
||||
assert.deepEqual(validateProviderPackage(geliosProviderPackageV4), { ok: true, errors: [] });
|
||||
assert.deepEqual(validateProviderPackage(geliosProviderPackageV5), { ok: true, errors: [] });
|
||||
|
||||
const strictProduct = geliosProviderPackageV5.dataProducts[0];
|
||||
const registeredStrictProduct = JSON.parse(await readFile(new URL(
|
||||
"../../../services/external-data-plane/definitions/fleet.positions.current.v4.json",
|
||||
import.meta.url,
|
||||
), "utf8"));
|
||||
const geliosOntologyEntities = JSON.parse(await readFile(new URL(
|
||||
"../../../services/ontology-core/catalog/domain-packages/gelios/entities.json",
|
||||
import.meta.url,
|
||||
), "utf8"));
|
||||
assert.equal(geliosProviderPackageV5.id, "gelios.provider.v5");
|
||||
assert.equal(strictProduct.id, "fleet.positions.current.v4");
|
||||
assert.deepEqual(strictProduct, registeredStrictProduct);
|
||||
assert.deepEqual(strictProduct.fieldContracts.signal_state.enum, ["active", "inactive"]);
|
||||
assert.deepEqual(strictProduct.fieldContracts.movement_state.enum, ["moving", "stopped"]);
|
||||
for (const ontologyEntityId of ["gelios.signal_state", "gelios.movement_state"]) {
|
||||
const valueContract = geliosOntologyEntities.entities.find((entity) => entity.id === ontologyEntityId)?.valueContract;
|
||||
assert.ok(valueContract, `${ontologyEntityId} must expose an Ontology valueContract`);
|
||||
assert.deepEqual(
|
||||
strictProduct.fieldContracts[valueContract.field].enum,
|
||||
valueContract.values.map(({ value: state }) => state),
|
||||
`${valueContract.field} Data Product enum must equal the Ontology value contract`,
|
||||
);
|
||||
}
|
||||
|
||||
const withInvalidClosedStateDefault = structuredClone(geliosProviderPackageV5);
|
||||
withInvalidClosedStateDefault.mappingContracts[0].derivations.signal_state.default = "invented";
|
||||
assert.equal(
|
||||
validateProviderPackage(withInvalidClosedStateDefault).errors.includes(
|
||||
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.signal_state.derivation.default_not_in_field_contract_enum",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const withInvalidClosedStateRule = structuredClone(geliosProviderPackageV5);
|
||||
withInvalidClosedStateRule.mappingContracts[0].derivations.movement_state.rules[0] = "integer_speed_gt_threshold.teleporting";
|
||||
assert.equal(
|
||||
validateProviderPackage(withInvalidClosedStateRule).errors.includes(
|
||||
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.movement_state.derivation.rules[0]_not_in_field_contract_enum",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const withOptionalRequiredState = structuredClone(geliosProviderPackageV5);
|
||||
withOptionalRequiredState.mappingContracts[0].fact.attributes.signal_state.omitIfMissing = true;
|
||||
assert.equal(
|
||||
validateProviderPackage(withOptionalRequiredState).errors.includes(
|
||||
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.signal_state_required_but_mapping_can_omit",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const withMalformedFieldContract = structuredClone(geliosProviderPackageV5);
|
||||
withMalformedFieldContract.dataProducts[0].fieldContracts.signal_state = null;
|
||||
assert.doesNotThrow(() => validateProviderPackage(withMalformedFieldContract));
|
||||
assert.equal(validateProviderPackage(withMalformedFieldContract).ok, false);
|
||||
|
||||
const withUnboundedNumericMapping = structuredClone(geliosProviderPackageV5);
|
||||
delete withUnboundedNumericMapping.mappingContracts[0].fact.attributes.speed_kph.minimum;
|
||||
assert.equal(
|
||||
validateProviderPackage(withUnboundedNumericMapping).errors.includes(
|
||||
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.speed_kph.minimum_must_match_field_contract",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const withNonOmittingBoundedMapping = structuredClone(geliosProviderPackageV5);
|
||||
withNonOmittingBoundedMapping.mappingContracts[0].fact.attributes.course_degrees.omitIfInvalid = false;
|
||||
assert.equal(
|
||||
validateProviderPackage(withNonOmittingBoundedMapping).errors.includes(
|
||||
"mappingContract.gelios.units.to.fleet.positions.current.v5.fact.attributes.course_degrees.bounded_number_must_omit_invalid",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
const canonicalMonitoringProduct = geliosProviderPackageV4.dataProducts[0];
|
||||
const canonicalMonitoringMapping = geliosProviderPackageV4.mappingContracts[0];
|
||||
const registeredCanonicalMonitoringProduct = JSON.parse(await readFile(new URL(
|
||||
"../../../services/external-data-plane/definitions/fleet.positions.current.v3.json",
|
||||
import.meta.url,
|
||||
), "utf8"));
|
||||
assert.equal(geliosProviderPackageV4.id, "gelios.provider.v4");
|
||||
assert.equal(geliosProviderPackageV4.version, "4.0.0");
|
||||
assert.deepEqual(geliosProviderPackageV4.manifest.ontology, {
|
||||
packageId: "gelios",
|
||||
revision: "ontology.gelios.v1_1",
|
||||
});
|
||||
assert.deepEqual(geliosProviderPackageV4.collectionProfiles[0].capabilityIds, [
|
||||
"gelios.monitoring_config.current.read",
|
||||
"gelios.units.current.read",
|
||||
]);
|
||||
assert.equal(
|
||||
geliosProviderPackageV4.capabilities.find((item) => item.id === "gelios.monitoring_config.current.read").request.path,
|
||||
"/api/v1/users/me/monitoring-config",
|
||||
);
|
||||
assert.equal(
|
||||
geliosProviderPackageV4.capabilities.find((item) => item.id === "gelios.units.current.read").request.query.incltrip,
|
||||
"true",
|
||||
);
|
||||
assert.deepEqual(canonicalMonitoringProduct, registeredCanonicalMonitoringProduct);
|
||||
assert.deepEqual(canonicalMonitoringProduct.fields.filter((field) => field.endsWith("_state")), [
|
||||
"movement_state",
|
||||
"signal_state",
|
||||
]);
|
||||
for (const forbiddenField of [
|
||||
"availability_state",
|
||||
"freshness_state",
|
||||
"operational_status",
|
||||
"position_state",
|
||||
"state_policy_version",
|
||||
]) {
|
||||
assert.equal(canonicalMonitoringProduct.fields.includes(forbiddenField), false);
|
||||
}
|
||||
assert.deepEqual(canonicalMonitoringMapping.derivations.signal_state.rules, [
|
||||
"last_message_missing.inactive",
|
||||
"last_message_age_lt_resolved_monitoring_limit.active",
|
||||
"otherwise.inactive",
|
||||
]);
|
||||
assert.equal(canonicalMonitoringMapping.derivations.signal_state.default, "inactive");
|
||||
assert.equal(canonicalMonitoringMapping.derivations.signal_state.parameters.activeDurationPath, "signalActiveDuration");
|
||||
assert.equal(
|
||||
canonicalMonitoringMapping.derivations.signal_state.parameters.somewhatInactiveDurationPath,
|
||||
"signalSomewhatInactiveDuration",
|
||||
);
|
||||
assert.equal(canonicalMonitoringMapping.derivations.signal_state.parameters.collectionProfileFallbackSeconds, 120);
|
||||
assert.equal(
|
||||
canonicalMonitoringMapping.derivations.signal_state.parameters.thresholdResolution,
|
||||
"somewhatInactive_then_active_then_profileFallback",
|
||||
);
|
||||
assert.equal(canonicalMonitoringMapping.derivations.movement_state.parameters.movingThresholdKph, 2);
|
||||
assert.equal(canonicalMonitoringMapping.derivations.movement_state.default, "stopped");
|
||||
assert.deepEqual(
|
||||
geliosProviderPackageV4.l2Templates[0].steps.map((step) => step.kind),
|
||||
[
|
||||
"collection_trigger",
|
||||
"provider_request",
|
||||
"provider_request",
|
||||
"extract_items",
|
||||
"semantic_mapping",
|
||||
"data_product_publish",
|
||||
],
|
||||
);
|
||||
|
||||
const stateAwareProduct = geliosProviderPackageV3.dataProducts[0];
|
||||
const stateAwareMapping = geliosProviderPackageV3.mappingContracts[0];
|
||||
const registeredStateAwareProduct = JSON.parse(await readFile(new URL(
|
||||
"../../../services/external-data-plane/definitions/fleet.positions.current.v2.json",
|
||||
import.meta.url,
|
||||
), "utf8"));
|
||||
assert.equal(geliosProviderPackageV3.id, "gelios.provider.v3");
|
||||
assert.equal(geliosProviderPackageV3.version, "3.0.0");
|
||||
assert.equal(geliosProviderPackageV3.authModes[0].id, "gelios.rest-rotating-bearer.v3");
|
||||
assert.deepEqual(geliosProviderPackageV3.authModes[0].transport, {
|
||||
placement: "header",
|
||||
name: "Authorization",
|
||||
});
|
||||
assert.deepEqual(geliosProviderPackageV3.authModes[0].tokenLifecycle, {
|
||||
artifacts: ["access", "refresh"],
|
||||
requestArtifact: "access",
|
||||
refreshMode: "runtime_managed",
|
||||
});
|
||||
assert.equal(geliosProviderPackageV3.capabilities[0].request.baseUrl, "https://api.geliospro.com");
|
||||
assert.equal(geliosProviderPackageV3.capabilities[0].request.path, "/api/v1/units");
|
||||
assert.deepEqual(geliosProviderPackageV3.capabilities[0].request.query, {});
|
||||
assert.equal(
|
||||
geliosProviderPackageV3.l2Templates[0].credentialBindings.find((binding) => binding.role === "provider").authModeId,
|
||||
"gelios.rest-rotating-bearer.v3",
|
||||
);
|
||||
assert.equal(stateAwareProduct.id, "fleet.positions.current.v2");
|
||||
assert.equal(stateAwareProduct.version, "2.0.0");
|
||||
assert.equal(stateAwareProduct.ontologyRevision, "ontology.map.moving_object.v2");
|
||||
assert.deepEqual(stateAwareProduct, registeredStateAwareProduct);
|
||||
assert.deepEqual(
|
||||
["availability_state", "freshness_state", "motion_state", "position_state"].map((field) => (
|
||||
stateAwareMapping.fact.attributes[field]
|
||||
)),
|
||||
[
|
||||
{ derive: "availability_state" },
|
||||
{ derive: "freshness_state" },
|
||||
{ derive: "motion_state" },
|
||||
{ derive: "position_state" },
|
||||
],
|
||||
);
|
||||
assert.equal(stateAwareMapping.fact.attributes.state_policy_version.constant, "map-moving-object-state/v1");
|
||||
assert.equal(stateAwareMapping.derivations.motion_state.parameters.movingThresholdKph, 1);
|
||||
assert.equal(stateAwareMapping.derivations.position_state.parameters.minSatelliteCount, 4);
|
||||
assert.equal(stateAwareMapping.derivations.position_state.parameters.maxHdop, 5);
|
||||
assert.equal(stateAwareMapping.derivations.position_state.parameters.maxHorizontalAccuracyMeters, 100);
|
||||
|
||||
const geliosSdkAuth = geliosProviderPackageV2.authModes[0];
|
||||
const geliosSdkUnitsRead = geliosProviderPackageV2.capabilities[0];
|
||||
@@ -318,7 +512,7 @@ for (const profile of withUnexecutedSecondCapability.collectionProfiles) {
|
||||
}
|
||||
assert.equal(
|
||||
validateProviderPackage(withUnexecutedSecondCapability).errors.includes(
|
||||
"collectionProfile.gelios.positions.current.realtime.v1.capabilityIds_must_select_one_v1_read",
|
||||
"collectionProfile.gelios.positions.current.realtime.v1.template_capability_chain_mismatch",
|
||||
),
|
||||
true,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user