From d15d1a89baa9f75da23b3e1ac39030f9a99b224e Mon Sep 17 00:00:00 2001 From: Chad Burt Date: Wed, 21 Feb 2024 21:19:51 -0800 Subject: [PATCH] Working --- packages/geojson-inspector/src/index.ts | 111 ++++++++++++++++++++++- packages/geojson-inspector/src/types.ts | 24 +++++ packages/geojson-inspector/wrangler.toml | 2 + 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 packages/geojson-inspector/src/types.ts diff --git a/packages/geojson-inspector/src/index.ts b/packages/geojson-inspector/src/index.ts index f991876d..ef5794b1 100644 --- a/packages/geojson-inspector/src/index.ts +++ b/packages/geojson-inspector/src/index.ts @@ -8,6 +8,8 @@ * Learn more at https://developers.cloudflare.com/workers/ */ +import { FailedInspectorResponse, InspectorResponse } from './types'; + export interface Env { // Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/ // MY_KV_NAMESPACE: KVNamespace; @@ -40,6 +42,113 @@ export default { return new Response('Please provide a location', { status: 400 }); } // https://uploads.seasketch.org/projects/cburt/public/4b08062c-9218-45d7-a00c-5147e103e02b.geojson.json - return new Response('Hello World!'); + // fetch the geojson from the provided location + const startT = performance.now(); + const response = await fetch(location, { + headers: { + accept: 'application/json', + }, + }); + const endT = performance.now(); + let failedResponse: FailedInspectorResponse; + + // if the fetch fails return an error + if (!response.ok) { + if (response.status === 404) { + failedResponse = { + location, + error: 'Server returned 404. Location not found', + errorsStatus: 404, + }; + } else if (response.status >= 400 && response.status < 500) { + failedResponse = { + location, + error: 'Client error. Please check the location', + errorsStatus: response.status, + }; + } else { + const text = await response.text(); + failedResponse = { + location, + error: 'Server error. Could not retrieve data from location.\n' + text, + errorsStatus: response.status, + }; + } + return new Response(JSON.stringify(failedResponse), { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, max-age=10, s-maxage=10', + }, + }); + } else { + const contentLength = response.headers.get('content-length'); + const contentType = response.headers.get('content-type'); + const cacheControl = response.headers.get('cache-control'); + try { + const geojson: any = await response.json(); + if (!('type' in geojson)) { + failedResponse = { + location, + error: 'Response is not a valid GeoJSON', + errorsStatus: 200, + }; + return new Response(JSON.stringify(failedResponse), { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, max-age=10, s-maxage=10', + }, + }); + } else if (geojson.type !== 'FeatureCollection' && geojson.type !== 'Feature') { + failedResponse = { + location, + error: 'GeoJSON object must be a Feature or FeatureCollection', + errorsStatus: 200, + }; + return new Response(JSON.stringify(failedResponse), { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, max-age=10, s-maxage=10', + }, + }); + } else { + const rootType = geojson.type; + return new Response( + JSON.stringify({ + location, + contentLength: contentLength ? parseInt(contentLength) : 0, + contentType: contentType || '', + cacheControl: cacheControl || '', + latency: endT - startT, + rootType, + featureCount: rootType === 'FeatureCollection' ? geojson.features.length : 1, + geometryType: rootType === 'FeatureCollection' ? geojson.features[0]?.geometry?.type || 'Unknown' : geojson.geometry.type, + } as InspectorResponse), + { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, max-age=120, s-maxage=120', + }, + } + ); + } + } catch (e) { + failedResponse = { + location, + error: 'Failed to parse response as JSON', + errorsStatus: 200, + }; + return new Response(JSON.stringify(failedResponse), { + status: 200, + headers: { + 'Content-Type': 'application/json', + 'Cache-Control': 'public, max-age=10, s-maxage=10', + }, + }); + } + } }, }; diff --git a/packages/geojson-inspector/src/types.ts b/packages/geojson-inspector/src/types.ts new file mode 100644 index 00000000..accd898d --- /dev/null +++ b/packages/geojson-inspector/src/types.ts @@ -0,0 +1,24 @@ +interface BaseInspectorResponse { + location: string; +} + +export interface FailedInspectorResponse extends BaseInspectorResponse { + error: string; + errorsStatus: number; +} + +export interface SuccessfulInspectorResponse extends BaseInspectorResponse { + contentLength: number; + contentType: string; + cacheControl: string; + latency: number; + featureCount: number; + geometryType: 'Point' | 'MultiPoint' | 'LineString' | 'MultiLineString' | 'Polygon' | 'MultiPolygon'; + rootType: 'FeatureCollection' | 'Feature'; +} + +export type InspectorResponse = FailedInspectorResponse | SuccessfulInspectorResponse; + +export function isSuccessfulInspectorResponse(response: InspectorResponse): response is SuccessfulInspectorResponse { + return (response as FailedInspectorResponse).error === undefined; +} diff --git a/packages/geojson-inspector/wrangler.toml b/packages/geojson-inspector/wrangler.toml index 6ee2973d..50eabf89 100644 --- a/packages/geojson-inspector/wrangler.toml +++ b/packages/geojson-inspector/wrangler.toml @@ -1,6 +1,8 @@ name = "geojson-inspector" main = "src/index.ts" compatibility_date = "2024-02-08" +# https://github.com/cloudflare/workerd/issues/787 +compatibility_flags = ["no_minimal_subrequests"] # Variable bindings. These are arbitrary, plaintext strings (similar to environment variables) # Note: Use secrets to store sensitive data.