From 6d4341cd190628ad0270118394d6854aac731664 Mon Sep 17 00:00:00 2001 From: Andrew Chou Date: Wed, 9 Oct 2024 10:49:47 -0400 Subject: [PATCH] feat: add endpoint for maps plugin to get info about custom map --- src/fastify-plugins/maps.js | 34 ++++++++++++++++- test/fastify-plugins/maps.js | 73 ++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/src/fastify-plugins/maps.js b/src/fastify-plugins/maps.js index 1cfc43931..ec9cd3cf3 100644 --- a/src/fastify-plugins/maps.js +++ b/src/fastify-plugins/maps.js @@ -1,3 +1,5 @@ +import fs from 'node:fs/promises' +import path from 'node:path' import { fetch } from 'undici' import { Server as SMPServerPlugin } from 'styled-map-package' @@ -19,9 +21,39 @@ export const FALLBACK_MAP_PREFIX = 'fallback' /** @type {FastifyPluginAsync} */ export async function plugin(fastify, opts) { if (opts.customMapPath) { + const { customMapPath } = opts + + fastify.get(`/${CUSTOM_MAP_PREFIX}/info`, async () => { + const baseUrl = new URL(fastify.prefix, fastify.listeningOrigin) + + if (!baseUrl.href.endsWith('/')) { + baseUrl.href += '/' + } + + const style = await ( + await fetch(new URL(`${CUSTOM_MAP_PREFIX}/style.json`, baseUrl)) + ).json() + + const stats = await fs.stat(customMapPath) + + const styleJsonName = + typeof style === 'object' && + style && + 'name' in style && + typeof style.name === 'string' + ? style.name + : undefined + + return { + created: stats.ctime, + size: stats.size, + name: styleJsonName || path.parse(customMapPath).name, + } + }) + fastify.register(SMPServerPlugin, { prefix: CUSTOM_MAP_PREFIX, - filepath: opts.customMapPath, + filepath: customMapPath, }) } diff --git a/test/fastify-plugins/maps.js b/test/fastify-plugins/maps.js index 74893db7a..d1ce9b124 100644 --- a/test/fastify-plugins/maps.js +++ b/test/fastify-plugins/maps.js @@ -1,4 +1,5 @@ import assert from 'node:assert/strict' +import path from 'node:path' import test from 'node:test' import Fastify from 'fastify' import { Reader } from 'styled-map-package' @@ -13,6 +14,7 @@ import { DEFAULT_FALLBACK_MAP_FILE_PATH, DEFAULT_ONLINE_STYLE_URL, } from '../../src/mapeo-manager.js' +import { hashObject } from '../../src/utils.js' const SAMPLE_SMP_FIXTURE_PATH = new URL( '../fixtures/maps/maplibre-demotiles.smp', @@ -223,6 +225,77 @@ test('/style.json resolves style.json of fallback map when custom and online are ) }) +test('custom map info endpoint not available when custom map path not provided', async (t) => { + const server = setup(t) + + server.register(MapServerPlugin, { + defaultOnlineStyleUrl: DEFAULT_ONLINE_STYLE_URL, + fallbackMapPath: DEFAULT_FALLBACK_MAP_FILE_PATH, + }) + + const address = await server.listen() + + mockAgent.enableNetConnect(new URL(address).host) + + const response = await server.inject({ + method: 'GET', + url: `/${CUSTOM_MAP_PREFIX}/info`, + }) + + assert.equal(response.statusCode, 404) +}) + +test('custom map info endpoint returns expected error when custom map is invalid', async (t) => { + const server = setup(t) + + const nonExistentFile = + path.parse(SAMPLE_SMP_FIXTURE_PATH).dir + '/does/not/exist.smp' + + server.register(MapServerPlugin, { + customMapPath: nonExistentFile, + defaultOnlineStyleUrl: DEFAULT_ONLINE_STYLE_URL, + fallbackMapPath: DEFAULT_FALLBACK_MAP_FILE_PATH, + }) + + const address = await server.listen() + + mockAgent.enableNetConnect(new URL(address).host) + + const response = await server.inject({ + method: 'GET', + url: `/${CUSTOM_MAP_PREFIX}/info`, + }) + + assert.equal(response.statusCode, 500, 'returns server error status code') +}) + +test('custom map info endpoint returns expected info when available', async (t) => { + const server = setup(t) + + server.register(MapServerPlugin, { + customMapPath: SAMPLE_SMP_FIXTURE_PATH, + defaultOnlineStyleUrl: DEFAULT_ONLINE_STYLE_URL, + fallbackMapPath: DEFAULT_FALLBACK_MAP_FILE_PATH, + }) + + const address = await server.listen() + + mockAgent.enableNetConnect(new URL(address).host) + + const response = await server.inject({ + method: 'GET', + url: `/${CUSTOM_MAP_PREFIX}/info`, + }) + + assert.equal(response.statusCode, 200) + + assert.equal( + hashObject(response.json()), + 'ef2ae6b5a1298e608c743a91bb7fba7c55ea63427e34cfb52b808eaaa6625b61', + 'custom map info matches snapshot' + ) +}) + /** * @param {import('node:test').TestContext} t */