mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 10:24:02 +00:00
fix: request HTTPS tiles for ion-backed Bing imagery
This commit is contained in:
1 parent
8b8ccc9a81
commit
e5eac0c7f8
4 files changed
+204
-1
No files matched your search
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
- Bing Aerial and Bing Labels request HTTPS tiles on HTTP localhost through
|
||||
their ion-backed providers, retaining ion and per-tile attribution without
|
||||
widening the Content Security Policy.
|
||||
|
||||
- Treat explicitly current, complete empty vessel coverage as a successful update,
|
||||
clearing obsolete contacts while retaining stale or incomplete snapshots.
|
||||
|
||||
|
||||
@@ -5018,6 +5018,12 @@ photoreal globe is hidden.
|
||||
|
||||
Map credentials are passed to each source constructor rather than changing SDK-wide Google or ion defaults.
|
||||
|
||||
Bing Aerial and Bing Labels resolve their ion imagery endpoint with the supplied
|
||||
token, then construct the Bing provider with `tileProtocol: 'https'`. Both styles
|
||||
request HTTPS tiles even on HTTP localhost, with the existing Content Security
|
||||
Policy unchanged. Ion attribution and Bing's per-tile credits are preserved;
|
||||
other ion imagery assets continue through the SDK's standard ion factory.
|
||||
|
||||
The controller accepts other source registries without adding provider branches.
|
||||
Each instance caches source construction, ignores superseded scene changes and
|
||||
releases imagery layers/listeners on replacement. Destroy invalidates pending
|
||||
|
||||
@@ -25,5 +25,42 @@ export function createEsriImagery() {
|
||||
export function createIonImagery(style, accessToken) {
|
||||
accessToken = String(accessToken || '').trim();
|
||||
if (!accessToken) throw new Error('Ion imagery requires an explicit token');
|
||||
if (
|
||||
style === Cesium.IonWorldImageryStyle.AERIAL ||
|
||||
style === Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS
|
||||
) {
|
||||
return createBingIonImagery(style, accessToken);
|
||||
}
|
||||
return Cesium.IonImageryProvider.fromAssetId(style, { accessToken });
|
||||
}
|
||||
|
||||
async function createBingIonImagery(style, accessToken) {
|
||||
const endpoint = await new Cesium.Resource({
|
||||
url: `https://api.cesium.com/v1/assets/${style}/endpoint`,
|
||||
queryParameters: { access_token: accessToken },
|
||||
}).fetchJson();
|
||||
if (
|
||||
endpoint?.type !== 'IMAGERY' ||
|
||||
endpoint.externalType !== 'BING' ||
|
||||
typeof endpoint.options?.url !== 'string'
|
||||
) {
|
||||
throw new Error('Ion world imagery did not return a Bing imagery endpoint');
|
||||
}
|
||||
// Bing otherwise follows the page's HTTP scheme on localhost. Resolve its
|
||||
// ion-issued credentials through public APIs so tiles use HTTPS without
|
||||
// weakening CSP, changing SDK defaults or touching provider internals.
|
||||
const { url, ...options } = endpoint.options;
|
||||
const provider = await Cesium.BingMapsImageryProvider.fromUrl(url, {
|
||||
...options,
|
||||
tileProtocol: 'https',
|
||||
});
|
||||
const ionCredits = (endpoint.attributions ?? []).map(
|
||||
({ html, collapsible }) => new Cesium.Credit(html, collapsible === false),
|
||||
);
|
||||
const getTileCredits = provider.getTileCredits.bind(provider);
|
||||
provider.getTileCredits = (x, y, level) => [
|
||||
...ionCredits,
|
||||
...(getTileCredits(x, y, level) ?? []),
|
||||
];
|
||||
return provider;
|
||||
}
|
||||
@@ -89,7 +89,7 @@ test('imagery and terrain pass their own ion token without relying on SDK defaul
|
||||
calls.push({ kind: 'terrain', resource, options });
|
||||
return { id: 'terrain' };
|
||||
};
|
||||
await createIonImagery(Cesium.IonWorldImageryStyle.AERIAL, 'imagery-token');
|
||||
await createIonImagery(12345, 'imagery-token');
|
||||
const result = await createWorldTerrain('terrain-token');
|
||||
assert.equal(calls[0].options.accessToken, 'imagery-token');
|
||||
assert.equal(calls[1].options.accessToken, 'terrain-token');
|
||||
@@ -104,6 +104,162 @@ test('imagery and terrain pass their own ion token without relying on SDK defaul
|
||||
}
|
||||
});
|
||||
|
||||
test('both Bing ion stacks use HTTPS and retain ion and per-tile attribution', async (t) => {
|
||||
const defaultToken = Cesium.Ion.defaultAccessToken;
|
||||
const metadataCalls = [];
|
||||
const bingCalls = [];
|
||||
const perTileCalls = [];
|
||||
const bingCredit = new Cesium.Credit('Bing tile credit', true);
|
||||
const endpoint = {
|
||||
type: 'IMAGERY',
|
||||
externalType: 'BING',
|
||||
options: Object.freeze({
|
||||
url: 'https://dev.virtualearth.net',
|
||||
key: 'ion-issued-bing-fixture',
|
||||
mapStyle: 'Aerial',
|
||||
tileProtocol: 'http',
|
||||
}),
|
||||
attributions: [
|
||||
{ html: 'Ion required credit', collapsible: false },
|
||||
{ html: 'Ion optional credit', collapsible: true },
|
||||
],
|
||||
};
|
||||
t.mock.method(Cesium.Resource.prototype, 'fetchJson', async function () {
|
||||
metadataCalls.push({
|
||||
url: this.url,
|
||||
token: this.queryParameters.access_token,
|
||||
});
|
||||
const mapStyle = this.url.includes(
|
||||
`/assets/${Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS}/`,
|
||||
)
|
||||
? 'AerialWithLabels'
|
||||
: 'Aerial';
|
||||
return {
|
||||
...endpoint,
|
||||
options: Object.freeze({ ...endpoint.options, mapStyle }),
|
||||
};
|
||||
});
|
||||
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', () =>
|
||||
assert.fail('Bing must not inherit the local HTTP page protocol'),
|
||||
);
|
||||
t.mock.method(
|
||||
Cesium.BingMapsImageryProvider,
|
||||
'fromUrl',
|
||||
async (url, options) => {
|
||||
bingCalls.push({ url, options });
|
||||
const provider = {
|
||||
errorEvent: new Cesium.Event(),
|
||||
getTileCredits(x, y, level) {
|
||||
assert.equal(this, provider);
|
||||
perTileCalls.push([x, y, level]);
|
||||
return level === 0 ? undefined : [bingCredit];
|
||||
},
|
||||
};
|
||||
return provider;
|
||||
},
|
||||
);
|
||||
for (const style of [
|
||||
Cesium.IonWorldImageryStyle.AERIAL,
|
||||
Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS,
|
||||
]) {
|
||||
const provider = await createIonImagery(style, ' own-ion-token ');
|
||||
const credits = provider.getTileCredits(3, 4, 5);
|
||||
assert.deepEqual(
|
||||
credits.map(({ html, showOnScreen }) => ({ html, showOnScreen })),
|
||||
[
|
||||
{ html: 'Ion required credit', showOnScreen: true },
|
||||
{ html: 'Ion optional credit', showOnScreen: false },
|
||||
{ html: 'Bing tile credit', showOnScreen: true },
|
||||
],
|
||||
);
|
||||
assert.equal(credits[2], bingCredit);
|
||||
assert.equal(provider.getTileCredits(0, 0, 0).length, 2);
|
||||
assert.ok(provider.errorEvent instanceof Cesium.Event);
|
||||
assert.equal(metadataCalls.at(-1).token, 'own-ion-token');
|
||||
const requested = new URL(metadataCalls.at(-1).url);
|
||||
assert.equal(requested.origin, 'https://api.cesium.com');
|
||||
assert.equal(requested.pathname, `/v1/assets/${style}/endpoint`);
|
||||
}
|
||||
assert.deepEqual(perTileCalls, [
|
||||
[3, 4, 5],
|
||||
[0, 0, 0],
|
||||
[3, 4, 5],
|
||||
[0, 0, 0],
|
||||
]);
|
||||
assert.equal(bingCalls.length, 2);
|
||||
for (const [index, { url, options }] of bingCalls.entries()) {
|
||||
assert.equal(url, 'https://dev.virtualearth.net');
|
||||
assert.deepEqual(options, {
|
||||
key: 'ion-issued-bing-fixture',
|
||||
mapStyle: index === 0 ? 'Aerial' : 'AerialWithLabels',
|
||||
tileProtocol: 'https',
|
||||
});
|
||||
}
|
||||
assert.equal(endpoint.options.tileProtocol, 'http');
|
||||
assert.equal(Cesium.Ion.defaultAccessToken, defaultToken);
|
||||
});
|
||||
|
||||
test('Bing ion metadata and construction failures propagate without another source', async (t) => {
|
||||
const style = Cesium.IonWorldImageryStyle.AERIAL;
|
||||
const metadataError = new Error('ion metadata fixture failure');
|
||||
const providerError = new Error('Bing construction fixture failure');
|
||||
let metadata = () => {
|
||||
throw metadataError;
|
||||
};
|
||||
let bingCalls = 0;
|
||||
t.mock.method(Cesium.Resource.prototype, 'fetchJson', async () => metadata());
|
||||
t.mock.method(Cesium.BingMapsImageryProvider, 'fromUrl', async () => {
|
||||
bingCalls += 1;
|
||||
throw providerError;
|
||||
});
|
||||
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', () =>
|
||||
assert.fail('Bing failures must not silently use an alternate provider'),
|
||||
);
|
||||
await assert.rejects(createIonImagery(style, 'test-token'), metadataError);
|
||||
for (const endpoint of [
|
||||
undefined,
|
||||
{
|
||||
type: 'TERRAIN',
|
||||
externalType: 'BING',
|
||||
options: { url: 'https://example.test' },
|
||||
},
|
||||
{
|
||||
type: 'IMAGERY',
|
||||
externalType: 'OTHER',
|
||||
options: { url: 'https://example.test' },
|
||||
},
|
||||
{ type: 'IMAGERY', externalType: 'BING', options: {} },
|
||||
]) {
|
||||
metadata = () => endpoint;
|
||||
await assert.rejects(
|
||||
createIonImagery(style, 'test-token'),
|
||||
/Bing imagery endpoint/,
|
||||
);
|
||||
}
|
||||
assert.equal(bingCalls, 0);
|
||||
metadata = () => ({
|
||||
type: 'IMAGERY',
|
||||
externalType: 'BING',
|
||||
options: { url: 'https://dev.virtualearth.net', key: 'bing-fixture' },
|
||||
});
|
||||
await assert.rejects(createIonImagery(style, 'test-token'), providerError);
|
||||
assert.equal(bingCalls, 1);
|
||||
});
|
||||
|
||||
test('other ion imagery assets keep the standard SDK factory', async (t) => {
|
||||
const expected = {};
|
||||
const calls = [];
|
||||
t.mock.method(Cesium.Resource.prototype, 'fetchJson', () =>
|
||||
assert.fail('generic assets must use the SDK ion factory'),
|
||||
);
|
||||
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', async (...args) => {
|
||||
calls.push(args);
|
||||
return expected;
|
||||
});
|
||||
assert.equal(await createIonImagery(12345, ' custom-token '), expected);
|
||||
assert.deepEqual(calls, [[12345, { accessToken: 'custom-token' }]]);
|
||||
});
|
||||
|
||||
test('cancellation after ion metadata prevents terrain construction', async () => {
|
||||
const originalResource = Cesium.IonResource.fromAssetId;
|
||||
const originalTerrain = Cesium.CesiumTerrainProvider.fromUrl;
|
||||
|
||||
Reference in new issue
Block a user