Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rd-324: Handle invalid map style in constructor #121

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 43 additions & 3 deletions src/Map.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import maplibregl from "maplibre-gl";
import maplibregl, { AJAXError } from "maplibre-gl";
import { Base64 } from "js-base64";
import type {
StyleSpecification,
Expand Down Expand Up @@ -29,14 +29,15 @@ import { getBrowserLanguage, Language, type LanguageInfo } from "./language";
import { styleToStyle } from "./mapstyle";
import { MaptilerTerrainControl } from "./MaptilerTerrainControl";
import { MaptilerNavigationControl } from "./MaptilerNavigationControl";
import { geolocation, getLanguageInfoFromFlag, toLanguageInfo } from "@maptiler/client";
import { MapStyle, geolocation, getLanguageInfoFromFlag, toLanguageInfo } from "@maptiler/client";
import { MaptilerGeolocateControl } from "./MaptilerGeolocateControl";
import { ScaleControl } from "./MLAdapters/ScaleControl";
import { FullscreenControl } from "./MLAdapters/FullscreenControl";

import Minimap from "./Minimap";
import type { MinimapOptionsInput } from "./Minimap";
import { CACHE_API_AVAILABLE, registerLocalCacheProtocol } from "./caching";
import { getRequestlogger } from "./RequestLogger";

export type LoadWithTerrainEvent = {
type: "loadWithTerrain";
Expand Down Expand Up @@ -223,6 +224,33 @@ export class Map extends maplibregl.Map {
attributionControl: options.forceNoAttributionControl === true ? false : attributionControlOptions,
});

// Safeguard for distant styles at non-http 2xx status URLs
this.on("error", (event) => {
if (event.error instanceof AJAXError) {
const err = event.error as AJAXError;
const url = err.url;

// Looking into the request logger if a record with such url exists
// in order to get its resourceType
const requestLogger = getRequestlogger();
const requestRecord = requestLogger.get(url);

// If the AJAXError is for a "Style" we fall back to the default style, and add a console warning.
if (requestRecord && requestRecord.resourceType === "Style") {
let warning = `The style at URL ${url} could not be loaded. `;
// Loading a new style failed. If a style is not already in place,
// the default one is loaded instead + warning in console
if (!this.getStyle()) {
this.setStyle(MapStyle.STREETS);
warning += `Loading default style "${MapStyle.STREETS.getDefaultVariant().getId()}" as a fallback.`;
} else {
warning += "Leaving the style as is.";
}
console.warn(warning);
}
}
});

if (config.caching && !CACHE_API_AVAILABLE) {
console.warn(
"The cache API is only available in secure contexts. More info at https://developer.mozilla.org/en-US/docs/Web/API/Cache",
Expand Down Expand Up @@ -632,7 +660,19 @@ export class Map extends maplibregl.Map {
this.forceLanguageUpdate = false;
});

return super.setStyle(styleToStyle(style), options);
const compatibleStyle = styleToStyle(
style,
// onStyleUrlNotFound callback
// This callback is async (performs a header fetch)
// and will most likely run after the `super.setStyle()` below
// (styleURL: string) => {
// console.warn(`The style URL ${styleURL} does not yield a valid style. Loading the default style instead.`);
// this.setStyle(MapStyle.STREETS);
// },
);

super.setStyle(compatibleStyle, options);
return this;
}

/**
Expand Down
68 changes: 68 additions & 0 deletions src/RequestLogger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import type { ResourceType } from "maplibre-gl";

/**
* Entry for a request
*/
export type RequestRecord = {
/**
* URL, absolute or relative
*/
url: string;

/**
* Code for the type of resource
*/
resourceType?: ResourceType;

/**
* Timestamps (ms) just before perfoaming the request
*/
timestamp: [number];
};

/**
* Logs all the request (http and custom protocols) with resource types.
* Essentially a JS Map object.
*/
class RequestLogger {
private records = new Map<string, RequestRecord>();

/**
* Add a record. The unique key is the URL. If URL already exists,
* a new timestamp is added to the array for the already existing record.
*/
add(url: string, resourceType?: ResourceType) {
if (this.records.has(url)) {
const record = this.records.get(url);
record?.timestamp.push(+new Date());
} else {
this.records.set(url, {
url,
resourceType,
timestamp: [+new Date()],
} as RequestRecord);
}
}

/**
* Gets a record or `null`.
*/
get(url: string): RequestRecord | null {
if (this.records.has(url)) {
return this.records.get(url) as RequestRecord;
}
return null;
}
}

let requestLogger: RequestLogger | null = null;

/**
* Get a singleton RequetLogger instance
*/
export function getRequestlogger(): RequestLogger {
if (!requestLogger) {
requestLogger = new RequestLogger();
}
return requestLogger;
}
8 changes: 7 additions & 1 deletion src/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { defaults } from "./defaults";
import { config } from "./config";
import { MAPTILER_SESSION_ID } from "./config";
import { localCacheTransformRequest } from "./caching";
import { getRequestlogger } from "./RequestLogger";

export function enableRTL() {
// Prevent this from running server side
Expand Down Expand Up @@ -79,8 +80,13 @@ export function maptilerCloudTransformRequest(url: string, resourceType?: Resour
}
}

const localCacheTransformedReq = localCacheTransformRequest(reqUrl, resourceType);

const requestLogger = getRequestlogger();
requestLogger.add(localCacheTransformedReq, resourceType);

return {
url: localCacheTransformRequest(reqUrl, resourceType),
url: localCacheTransformedReq,
};
}

Expand Down
Loading