205 lines
6.8 KiB
JavaScript
205 lines
6.8 KiB
JavaScript
#!/usr/bin/env node
|
|
import { createHash } from "node:crypto";
|
|
import { readFile, writeFile } from "node:fs/promises";
|
|
import { resolve } from "node:path";
|
|
|
|
import {
|
|
compileL2ExecutionPlan,
|
|
instantiateL2Connection,
|
|
} from "../../packages/external-provider-contract/src/index.mjs";
|
|
import {
|
|
geliosProviderPackageV12,
|
|
geliosTelemetryFieldRegistryV5,
|
|
} from "../../packages/external-provider-contract/providers/gelios/v12/index.mjs";
|
|
|
|
const PUBLIC_PROFILE_METADATA = Object.freeze({
|
|
"gelios.positions.current.realtime.v7": {
|
|
cadence: "hot",
|
|
dataClass: "operational",
|
|
},
|
|
"gelios.positions.current.manual.v7": {
|
|
cadence: "on_demand",
|
|
dataClass: "operational",
|
|
},
|
|
"gelios.geozones.current.realtime.v1": {
|
|
cadence: "cold",
|
|
dataClass: "operational",
|
|
},
|
|
"gelios.units.profile.cold.v1": {
|
|
cadence: "cold",
|
|
dataClass: "operational",
|
|
},
|
|
"gelios.units.contacts.cold.v1": {
|
|
cadence: "cold",
|
|
dataClass: "restricted",
|
|
},
|
|
"gelios.units.identity.warm.v1": {
|
|
cadence: "warm",
|
|
dataClass: "restricted",
|
|
},
|
|
});
|
|
|
|
const engineRoot = resolve(
|
|
process.env.NODEDC_ENGINE_SOURCE_ROOT || "../NODEDC_ENGINE_INFRA",
|
|
);
|
|
const executionCatalogPath = resolve(
|
|
engineRoot,
|
|
"nodedc-source/server/assets/execution-plans/v1/catalog.json",
|
|
);
|
|
const securityCatalogPath = resolve(
|
|
engineRoot,
|
|
"nodedc-source/server/assets/provider-packages/v1/catalog.json",
|
|
);
|
|
|
|
const executionCatalog = JSON.parse(
|
|
await readFile(executionCatalogPath, "utf8"),
|
|
);
|
|
executionCatalog.packages = executionCatalog.packages.filter(
|
|
(providerPackage) => providerPackage.id !== geliosProviderPackageV12.id,
|
|
);
|
|
executionCatalog.packages.push(executionCatalogEntry());
|
|
await writeFile(executionCatalogPath, `${JSON.stringify(executionCatalog, null, 2)}\n`);
|
|
|
|
const securityCatalog = JSON.parse(
|
|
await readFile(securityCatalogPath, "utf8"),
|
|
);
|
|
securityCatalog.packages = securityCatalog.packages.filter(
|
|
(providerPackage) => ![
|
|
"gelios.provider.v11",
|
|
geliosProviderPackageV12.id,
|
|
].includes(providerPackage.id),
|
|
);
|
|
securityCatalog.packages.push(securityCatalogEntry());
|
|
await writeFile(securityCatalogPath, `${JSON.stringify(securityCatalog, null, 2)}\n`);
|
|
|
|
console.log(JSON.stringify({
|
|
ok: true,
|
|
providerPackage: geliosProviderPackageV12.id,
|
|
executionCatalogPath,
|
|
securityCatalogPath,
|
|
historicalExecutionPackagePreserved: executionCatalog.packages.some(
|
|
(providerPackage) => providerPackage.id === "gelios.provider.v11",
|
|
),
|
|
activeIdentityAuthority: geliosProviderPackageV12.id,
|
|
}, null, 2));
|
|
|
|
function executionCatalogEntry() {
|
|
const profiles = geliosProviderPackageV12.collectionProfiles.map((profile) => {
|
|
const connection = instantiateL2Connection(geliosProviderPackageV12, {
|
|
tenantId: "tenant-catalog-build",
|
|
connectionId: `catalog-${profile.id.replaceAll(".", "-")}`,
|
|
collectionProfileId: profile.id,
|
|
providerCredentialRef: "ndc-credref:catalog-build-provider-v12",
|
|
});
|
|
const plan = compileL2ExecutionPlan(
|
|
geliosProviderPackageV12,
|
|
connection,
|
|
profile.dataProductId === "fleet.positions.current.v5"
|
|
? { telemetryFieldRegistry: geliosTelemetryFieldRegistryV5 }
|
|
: {},
|
|
);
|
|
const { packageDigest: _packageDigest, ...artifacts } = plan.artifacts;
|
|
const publicMetadata = PUBLIC_PROFILE_METADATA[profile.id];
|
|
if (!publicMetadata) {
|
|
throw new Error(`gelios_v12_public_profile_metadata_missing:${profile.id}`);
|
|
}
|
|
return {
|
|
id: profile.id,
|
|
dataProductId: profile.dataProductId,
|
|
...publicMetadata,
|
|
capabilityIds: [...profile.capabilityIds],
|
|
stepSignatures: plan.steps.map((step) => ({
|
|
id: step.id,
|
|
kind: step.kind,
|
|
...(step.config.capabilityId ? { capabilityId: step.config.capabilityId } : {}),
|
|
...(step.config.mappingContractId ? {
|
|
mappingContractId: step.config.mappingContractId,
|
|
} : {}),
|
|
...(step.config.dataProductId ? { dataProductId: step.config.dataProductId } : {}),
|
|
...(step.config.nodeType ? { nodeType: step.config.nodeType } : {}),
|
|
})),
|
|
artifacts,
|
|
executionPlanTemplate: plan,
|
|
};
|
|
});
|
|
return {
|
|
id: geliosProviderPackageV12.id,
|
|
providerId: geliosProviderPackageV12.providerId,
|
|
version: geliosProviderPackageV12.version,
|
|
contractDigest: canonicalDigest(geliosProviderPackageV12),
|
|
providerCredential: {
|
|
authModeId: "gelios.rest-rotating-bearer.v3",
|
|
credentialType: "ndcProviderRotatingAccessApi",
|
|
},
|
|
publisher: {
|
|
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
|
credentialType: "ndcDataProductWriterApi",
|
|
},
|
|
capabilities: geliosProviderPackageV12.capabilities.map((capability) => ({
|
|
id: capability.id,
|
|
contractDigest: canonicalDigest(capability),
|
|
requestDigest: canonicalDigest(capability.request),
|
|
method: capability.request.method,
|
|
url: requestUrl(capability.request),
|
|
})),
|
|
profiles,
|
|
};
|
|
}
|
|
|
|
function securityCatalogEntry() {
|
|
const productsByCapability = new Map(
|
|
geliosProviderPackageV12.capabilities.map((capability) => [capability.id, new Set()]),
|
|
);
|
|
for (const profile of geliosProviderPackageV12.collectionProfiles) {
|
|
if (profile.dataProductId !== "fleet.units.identity.current.v1") continue;
|
|
for (const capabilityId of profile.capabilityIds) {
|
|
productsByCapability.get(capabilityId)?.add(profile.dataProductId);
|
|
}
|
|
}
|
|
return {
|
|
id: geliosProviderPackageV12.id,
|
|
version: geliosProviderPackageV12.version,
|
|
providerId: geliosProviderPackageV12.providerId,
|
|
providerCredential: {
|
|
authModeId: "gelios.rest-rotating-bearer.v3",
|
|
credentialType: "ndcProviderRotatingAccessApi",
|
|
},
|
|
capabilities: geliosProviderPackageV12.capabilities
|
|
.filter((capability) => productsByCapability.get(capability.id)?.size)
|
|
.map((capability) => ({
|
|
id: capability.id,
|
|
classification: capability.classification,
|
|
status: capability.status,
|
|
request: {
|
|
method: capability.request.method,
|
|
url: requestUrl(capability.request),
|
|
},
|
|
dataProductIds: [...productsByCapability.get(capability.id)].sort(),
|
|
})),
|
|
publisher: {
|
|
nodeType: "n8n-nodes-ndc.ndcDataProductPublish",
|
|
credentialType: "ndcDataProductWriterApi",
|
|
},
|
|
};
|
|
}
|
|
|
|
function requestUrl(request) {
|
|
const url = new URL(request.path, request.baseUrl);
|
|
for (const [name, value] of Object.entries(request.query || {})) {
|
|
url.searchParams.append(name, String(value));
|
|
}
|
|
return url.toString();
|
|
}
|
|
|
|
function canonicalDigest(value) {
|
|
return `sha256:${createHash("sha256").update(JSON.stringify(stableValue(value)), "utf8").digest("hex")}`;
|
|
}
|
|
|
|
function stableValue(value) {
|
|
if (Array.isArray(value)) return value.map(stableValue);
|
|
if (!value || typeof value !== "object") return value;
|
|
return Object.fromEntries(
|
|
Object.keys(value).sort().map((key) => [key, stableValue(value[key])]),
|
|
);
|
|
}
|