fix(map): accept canonical gateway endpoints
This commit is contained in:
@@ -7,6 +7,10 @@ import type {
|
||||
MapProviderId,
|
||||
MapRuntimeState,
|
||||
} from "./contracts.js";
|
||||
import {
|
||||
parseProviderEndpoint,
|
||||
type ProviderEndpoint,
|
||||
} from "./providerEndpoint.js";
|
||||
import {
|
||||
MapRuntimeError,
|
||||
applyCacheIntent,
|
||||
@@ -18,19 +22,6 @@ import {
|
||||
} from "./runtime.js";
|
||||
|
||||
type CesiumNamespace = typeof CesiumModule;
|
||||
type ProviderEndpoint = {
|
||||
assetId: number;
|
||||
type: "IMAGERY" | "TERRAIN" | "3DTILES";
|
||||
externalType?: "BING";
|
||||
credentialMode: "gateway";
|
||||
url?: string;
|
||||
options?: {
|
||||
url?: string;
|
||||
mapStyle?: string;
|
||||
};
|
||||
attributions?: readonly unknown[];
|
||||
};
|
||||
|
||||
export function CesiumMapRenderer({
|
||||
runtimeConfigUrl,
|
||||
camera,
|
||||
@@ -239,29 +230,7 @@ async function fetchProviderEndpoint(
|
||||
signal,
|
||||
);
|
||||
const expectedAssetId = configuration.assets[providerId];
|
||||
const expectedType =
|
||||
providerId === "imagery"
|
||||
? "IMAGERY"
|
||||
: providerId === "terrain"
|
||||
? "TERRAIN"
|
||||
: "3DTILES";
|
||||
if (
|
||||
document.assetId !== expectedAssetId ||
|
||||
document.type !== expectedType ||
|
||||
document.credentialMode !== "gateway"
|
||||
) {
|
||||
throw new MapRuntimeError("map_provider_contract_mismatch");
|
||||
}
|
||||
if (
|
||||
(providerId === "imagery" &&
|
||||
(document.externalType !== "BING" ||
|
||||
!isEndpointOptions(document.options) ||
|
||||
typeof document.options.url !== "string")) ||
|
||||
(providerId !== "imagery" && typeof document.url !== "string")
|
||||
) {
|
||||
throw new MapRuntimeError("map_provider_contract_mismatch");
|
||||
}
|
||||
return document as ProviderEndpoint;
|
||||
return parseProviderEndpoint(document, expectedAssetId, providerId);
|
||||
}
|
||||
|
||||
async function attachProvider(
|
||||
@@ -285,8 +254,12 @@ async function attachProvider(
|
||||
});
|
||||
if (providerId === "imagery") {
|
||||
const provider = await Cesium.BingMapsImageryProvider.fromUrl(resource, {
|
||||
key: "gateway-proxy",
|
||||
// Cesium requires a key-shaped value to build Bing metadata and tile
|
||||
// URLs. This is a public marker, not a credential: Map Gateway strips
|
||||
// it and injects the server-owned provider key upstream.
|
||||
key: "nodedc-gateway",
|
||||
mapStyle: bingMapStyle(Cesium, endpoint.options?.mapStyle),
|
||||
tileProtocol: "https",
|
||||
});
|
||||
const layer = viewer.imageryLayers.addImageryProvider(provider);
|
||||
if (settings.monochrome_enabled) {
|
||||
@@ -386,12 +359,6 @@ function safeErrorCode(error: unknown): string {
|
||||
return error instanceof MapRuntimeError ? error.code : "map_provider_unavailable";
|
||||
}
|
||||
|
||||
function isEndpointOptions(
|
||||
value: unknown,
|
||||
): value is NonNullable<ProviderEndpoint["options"]> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function tokenColor(
|
||||
Cesium: CesiumNamespace,
|
||||
container: HTMLElement,
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { MapProviderId } from "./contracts.js";
|
||||
import { MapRuntimeError } from "./runtime.js";
|
||||
|
||||
export type ProviderEndpoint = {
|
||||
assetId: string;
|
||||
type: "IMAGERY" | "TERRAIN" | "3DTILES";
|
||||
externalType?: "BING";
|
||||
credentialMode: "gateway";
|
||||
url?: string;
|
||||
options?: {
|
||||
url?: string;
|
||||
mapStyle?: string;
|
||||
};
|
||||
attributions: readonly unknown[];
|
||||
};
|
||||
|
||||
export function parseProviderEndpoint(
|
||||
document: Record<string, unknown>,
|
||||
expectedAssetId: number,
|
||||
providerId: MapProviderId,
|
||||
): ProviderEndpoint {
|
||||
const expectedType =
|
||||
providerId === "imagery"
|
||||
? "IMAGERY"
|
||||
: providerId === "terrain"
|
||||
? "TERRAIN"
|
||||
: "3DTILES";
|
||||
if (
|
||||
document.assetId !== String(expectedAssetId) ||
|
||||
document.type !== expectedType ||
|
||||
document.credentialMode !== "gateway" ||
|
||||
!Array.isArray(document.attributions)
|
||||
) {
|
||||
throw new MapRuntimeError("map_provider_contract_mismatch");
|
||||
}
|
||||
if (
|
||||
(providerId === "imagery" &&
|
||||
(document.externalType !== "BING" ||
|
||||
!isEndpointOptions(document.options) ||
|
||||
typeof document.options.url !== "string")) ||
|
||||
(providerId !== "imagery" && typeof document.url !== "string")
|
||||
) {
|
||||
throw new MapRuntimeError("map_provider_contract_mismatch");
|
||||
}
|
||||
return document as ProviderEndpoint;
|
||||
}
|
||||
|
||||
function isEndpointOptions(
|
||||
value: unknown,
|
||||
): value is NonNullable<ProviderEndpoint["options"]> {
|
||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import assert from "node:assert/strict";
|
||||
import test from "node:test";
|
||||
|
||||
import { parseProviderEndpoint } from "../dist/providerEndpoint.js";
|
||||
|
||||
const attributions = [{ html: "provider", collapsible: true }];
|
||||
|
||||
test("provider endpoint accepts the canonical string asset identifiers", () => {
|
||||
const imagery = parseProviderEndpoint({
|
||||
assetId: "2",
|
||||
type: "IMAGERY",
|
||||
externalType: "BING",
|
||||
credentialMode: "gateway",
|
||||
options: {
|
||||
url: "https://dev.virtualearth.net/REST/v1/Imagery/Metadata/Aerial",
|
||||
mapStyle: "Aerial",
|
||||
},
|
||||
attributions,
|
||||
}, 2, "imagery");
|
||||
const terrain = parseProviderEndpoint({
|
||||
assetId: "1",
|
||||
type: "TERRAIN",
|
||||
credentialMode: "gateway",
|
||||
url: "https://assets.ion.cesium.com/terrain/",
|
||||
attributions,
|
||||
}, 1, "terrain");
|
||||
const buildings = parseProviderEndpoint({
|
||||
assetId: "96188",
|
||||
type: "3DTILES",
|
||||
credentialMode: "gateway",
|
||||
url: "https://assets.ion.cesium.com/buildings/tileset.json",
|
||||
attributions,
|
||||
}, 96188, "buildings");
|
||||
|
||||
assert.equal(imagery.assetId, "2");
|
||||
assert.equal(terrain.assetId, "1");
|
||||
assert.equal(buildings.assetId, "96188");
|
||||
});
|
||||
|
||||
test("provider endpoint rejects numeric, mismatched and credential-bearing shapes", () => {
|
||||
for (const document of [
|
||||
{
|
||||
assetId: 1,
|
||||
type: "TERRAIN",
|
||||
credentialMode: "gateway",
|
||||
url: "https://assets.ion.cesium.com/terrain/",
|
||||
attributions,
|
||||
},
|
||||
{
|
||||
assetId: "2",
|
||||
type: "TERRAIN",
|
||||
credentialMode: "gateway",
|
||||
url: "https://assets.ion.cesium.com/terrain/",
|
||||
attributions,
|
||||
},
|
||||
{
|
||||
assetId: "1",
|
||||
type: "TERRAIN",
|
||||
credentialMode: "browser",
|
||||
url: "https://assets.ion.cesium.com/terrain/",
|
||||
attributions,
|
||||
},
|
||||
]) {
|
||||
assert.throws(
|
||||
() => parseProviderEndpoint(document, 1, "terrain"),
|
||||
/map_provider_contract_mismatch/,
|
||||
);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user