From fae607a373c4552f5d039e99e8648e347652db64 Mon Sep 17 00:00:00 2001 From: Blayne Chard Date: Wed, 6 Jul 2022 15:14:22 +1200 Subject: [PATCH 1/2] feat(cli): add bmc serve to create a server from a bundled config --- packages/cli/src/cli/index.ts | 2 + .../src/cli/screenshot/action.screenshot.ts | 15 +-- packages/cli/src/cli/server/action.serve.ts | 53 +++++++++ packages/server/src/cli.ts | 105 +----------------- packages/server/src/server.ts | 38 ++++++- 5 files changed, 98 insertions(+), 115 deletions(-) create mode 100644 packages/cli/src/cli/server/action.serve.ts diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index 46042d62f..0a8b8acbc 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -7,6 +7,7 @@ import { CommandJobCreate } from './cogify/action.job.js'; import { CommandBundle } from './config/action.bundle.js'; import { CommandImport } from './config/action.import.js'; import { CommandScreenShot } from './screenshot/action.screenshot.js'; +import { CommandServe } from './server/action.serve.js'; import { CommandSprites } from './sprites/action.sprites.js'; export class BasemapsConfigCommandLine extends BaseCommandLine { @@ -25,6 +26,7 @@ export class BasemapsConfigCommandLine extends BaseCommandLine { this.addAction(new CommandScreenShot()); this.addAction(new CommandSprites()); + this.addAction(new CommandServe()); this.addAction(new CommandList()); } diff --git a/packages/cli/src/cli/screenshot/action.screenshot.ts b/packages/cli/src/cli/screenshot/action.screenshot.ts index dc831f371..a97e6195f 100644 --- a/packages/cli/src/cli/screenshot/action.screenshot.ts +++ b/packages/cli/src/cli/screenshot/action.screenshot.ts @@ -108,22 +108,11 @@ export class CommandScreenShot extends CommandLineAction { } async startServer(port: number, config: string, logger: LogType): Promise { - // Bundle Config - const configJson = await fsa.readJson(config); - const mem = ConfigProviderMemory.fromJson(configJson); - Config.setConfigProvider(mem); - - // Setup the assets - if (this.assets.value) { - const isExists = await fsa.exists(this.assets.value); - if (!isExists) throw new Error('--asset path is missing'); - process.env[Env.AssetLocation] = this.assets.value; - } - // Start server - const server = createServer(logger); + const server = await createServer({ config, assets: this.assets.value }, logger); return await new Promise((resolve) => server.listen(port, '0.0.0.0', () => resolve(server))); } + async takeScreenshots(host: string, chrome: Browser, logger: LogType): Promise { const tiles = this.tiles.value ?? DefaultTestTiles; const outputPath = this.output.value ?? DefaultOutput; diff --git a/packages/cli/src/cli/server/action.serve.ts b/packages/cli/src/cli/server/action.serve.ts new file mode 100644 index 000000000..91e6b6f19 --- /dev/null +++ b/packages/cli/src/cli/server/action.serve.ts @@ -0,0 +1,53 @@ +import { CommandLineAction } from '@rushstack/ts-command-line'; + +import { createServer } from '@basemaps/server'; +import { Const, Env, LogConfig } from '@basemaps/shared'; +const DefaultPort = 5000; + +export class CommandServe extends CommandLineAction { + config = this.defineStringParameter({ + argumentName: 'CONFIG', + parameterLongName: '--config', + description: 'Configuration source to use', + }); + assets = this.defineStringParameter({ + argumentName: 'ASSETS', + parameterLongName: '--assets', + description: 'Where the assets (sprites, fonts) are located', + }); + port = this.defineIntegerParameter({ + argumentName: 'PORT', + parameterLongName: '--port', + description: 'port to use', + defaultValue: DefaultPort, + }); + + public constructor() { + super({ + actionName: 'serve', + summary: 'Cli tool to create sprite sheet', + documentation: 'Create a sprite sheet from a folder of sprites', + }); + } + + protected onDefineParameters(): void { + //noop + } + + protected async onExecute(): Promise { + const logger = LogConfig.get(); + + const config = this.config.value ?? 'dynamodb://' + Const.TileMetadata.TableName; + const port = this.port.value; + const assets = this.assets.value; + + // Force a default url base so WMTS requests know their relative url + const ServerUrl = Env.get(Env.PublicUrlBase) ?? `http://localhost:${port}`; + process.env[Env.PublicUrlBase] = ServerUrl; + + const server = await createServer({ config, assets }, logger); + server.listen(port ?? DefaultPort, '0.0.0.0', () => { + logger.info({ url: ServerUrl }, 'ServerStarted'); + }); + } +} diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts index adbee01f2..28bdca574 100644 --- a/packages/server/src/cli.ts +++ b/packages/server/src/cli.ts @@ -1,25 +1,9 @@ -import { ConfigJson, ConfigProvider, ConfigProviderDynamo, ConfigProviderMemory } from '@basemaps/config'; -import { TileSetLocal } from '@basemaps/lambda-tiler/build/cli/tile.set.local.js'; -import { TileSets } from '@basemaps/lambda-tiler/build/tile.set.cache.js'; -import { Config, Const, Env, fsa, LogConfig, LogType } from '@basemaps/shared'; +import { Const, Env, LogConfig } from '@basemaps/shared'; import { BaseCommandLine, CliInfo } from '@basemaps/shared/build/cli/base.js'; -import { basename, dirname } from 'path'; import { createServer } from './server.js'; CliInfo.package = 'basemaps/server'; -const BaseProvider: ConfigProvider = { - id: 'pv_linz', - version: 1, - serviceIdentification: {}, - serviceProvider: { - name: 'basemaps/server', - contact: { - address: {}, - }, - }, -} as any; - const DefaultPort = 5000; export class BasemapsServerCommand extends BaseCommandLine { @@ -28,15 +12,6 @@ export class BasemapsServerCommand extends BaseCommandLine { parameterLongName: '--config', description: 'Configuration source to use', }); - ignoreConfig = this.defineFlagParameter({ - parameterLongName: '--no-config', - description: 'Assume no config and just load tiffs from the configuration path', - }); - bundle = this.defineStringParameter({ - argumentName: 'BUNDLE', - parameterLongName: '--bundle', - description: 'Compile the configuration into a bundle and output path', - }); assets = this.defineStringParameter({ argumentName: 'ASSETS', parameterLongName: '--assets', @@ -51,39 +26,11 @@ export class BasemapsServerCommand extends BaseCommandLine { constructor() { super({ - toolFilename: 'bms', + toolFilename: 'basemaps-server', toolDescription: 'Create a WMTS/XYZ Tile server from basemaps config', }); } - static args = []; - - async loadTiffs(tiffPath: string, serverUrl: string, logger: LogType): Promise { - const config = new ConfigProviderMemory(); - Config.setConfigProvider(config); - - const tifSets = new Map(); - - for await (const file of fsa.details(tiffPath)) { - const lowerPath = file.path.toLowerCase(); - if (lowerPath.endsWith('.tiff') || lowerPath.endsWith('.tif')) { - const tiffPath = dirname(file.path); - if (tifSets.has(tiffPath)) continue; - - const tileSet = basename(tiffPath); - const tsl = new TileSetLocal(tileSet, tiffPath); - tifSets.set(tiffPath, tsl); - - await tsl.load(); - TileSets.add(tsl, new Date('3000-01-01').getTime()); - - const wmtsUrl = `${serverUrl}/v1/tiles/${tileSet}/WMTSCapabilities.xml`; - logger.info({ tileSetId: tileSet, wmtsUrl }, 'TileSet:Loaded'); - if (!config.objects.has('pv_linz')) config.put(BaseProvider); - } - } - } - async onExecute(): Promise { await super.onExecute(); @@ -91,57 +38,15 @@ export class BasemapsServerCommand extends BaseCommandLine { logger.level = 'debug'; const config = this.config.value ?? 'dynamodb://' + Const.TileMetadata.TableName; const port = this.port.value; + const assets = this.assets.value; const ServerUrl = Env.get(Env.PublicUrlBase) ?? `http://localhost:${port}`; // Force a default url base so WMTS requests know their relative url process.env[Env.PublicUrlBase] = ServerUrl; - if (config.startsWith('dynamodb://')) { - // Load config from dynamodb table - const table = config.slice('dynamodb://'.length); - logger.info({ path: config, table, mode: 'dynamo' }, 'Starting Server'); - Config.setConfigProvider(new ConfigProviderDynamo(table)); - } else if (config.endsWith('.json')) { - // Bundled config - logger.info({ path: config, mode: 'config' }, 'Starting Server'); - const configJson = await fsa.read(config); - const mem = ConfigProviderMemory.fromJson(JSON.parse(configJson.toString())); - Config.setConfigProvider(mem); - } else if (this.ignoreConfig.value) { - // Load config directly from tiff files - logger.info({ path: config, mode: 'tiffs' }, 'Starting Server'); - await this.loadTiffs(config, ServerUrl, logger); - } else { - const mem = await ConfigJson.fromPath(config, logger); - const bundlePath = this.bundle.value; - if (bundlePath) { - await fsa.writeJson(bundlePath, mem.toJson()); - logger.info({ path: bundlePath }, 'ConfigBundled'); - return; - } - // Assume the folder is a collection of config files - logger.info({ path: config, mode: 'config' }, 'Starting Server'); - - mem.createVirtualTileSets(); - Config.setConfigProvider(mem); - } - - if (this.assets.value) { - const isExists = await fsa.exists(this.assets.value); - if (!isExists) throw new Error('--asset path is missing'); - process.env[Env.AssetLocation] = this.assets.value; - } - - if (this.bundle.value != null) { - throw new Error('--bundle path provided without providing a configuration path'); - } - - createServer(logger).listen(port ?? DefaultPort, '0.0.0.0', () => { + const server = await createServer({ config, assets }, logger); + server.listen(port ?? DefaultPort, '0.0.0.0', () => { logger.info({ url: ServerUrl }, 'ServerStarted'); }); } } - -new BasemapsServerCommand().executeWithoutErrorHandling().catch((c) => { - console.error(c); -}); diff --git a/packages/server/src/server.ts b/packages/server/src/server.ts index 6033d477a..f82bf81fd 100644 --- a/packages/server/src/server.ts +++ b/packages/server/src/server.ts @@ -1,5 +1,6 @@ +import { Config, ConfigJson, ConfigProviderDynamo, ConfigProviderMemory } from '@basemaps/config'; import { handler } from '@basemaps/lambda-tiler'; -import { LogType } from '@basemaps/shared'; +import { Env, fsa, LogType } from '@basemaps/shared'; import fastifyStatic from '@fastify/static'; import { lf } from '@linzjs/lambda'; import { ALBEvent, ALBResult, APIGatewayProxyResultV2, CloudFrontRequestResult, Context } from 'aws-lambda'; @@ -30,9 +31,42 @@ function getLandingLocation(): string | null { } } -export function createServer(logger: LogType): FastifyInstance { +export interface ServerOptions { + /** Path to assets */ + assets?: string; + + /** Path to configuration or a dynamdb table */ + config: string; +} + +export async function createServer(opts: ServerOptions, logger: LogType): Promise { const BasemapsServer = fastify(); + if (opts.config.startsWith('dynamodb://')) { + // Load config from dynamodb table + const table = opts.config.slice('dynamodb://'.length); + logger.info({ path: opts.config, table, mode: 'dynamo' }, 'Starting Server'); + Config.setConfigProvider(new ConfigProviderDynamo(table)); + } else if (opts.config.endsWith('.json')) { + // Bundled config + logger.info({ path: opts.config, mode: 'config:bundle' }, 'Starting Server'); + const configJson = await fsa.read(opts.config); + const mem = ConfigProviderMemory.fromJson(JSON.parse(configJson.toString())); + Config.setConfigProvider(mem); + } else { + const mem = await ConfigJson.fromPath(opts.config, logger); + logger.info({ path: opts.config, mode: 'config' }, 'Starting Server'); + mem.createVirtualTileSets(); + Config.setConfigProvider(mem); + } + + if (opts.assets) { + const isExists = await fsa.exists(opts.assets); + if (!isExists) throw new Error(`--assets path "${opts.assets}" does not exist`); + logger.info({ path: opts.assets }, 'Config:Assets'); + process.env[Env.AssetLocation] = opts.assets; + } + const landingLocation = getLandingLocation(); if (landingLocation == null) { logger.warn('Server:Landing:Failed'); From 314a42b7cc7903204493e539ec7d52b7cf9e5360 Mon Sep 17 00:00:00 2001 From: Blayne Chard Date: Wed, 6 Jul 2022 16:13:59 +1200 Subject: [PATCH 2/2] refactor: fixup lint --- packages/cli/src/cli/screenshot/action.screenshot.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/cli/screenshot/action.screenshot.ts b/packages/cli/src/cli/screenshot/action.screenshot.ts index a97e6195f..f76a4f2f2 100644 --- a/packages/cli/src/cli/screenshot/action.screenshot.ts +++ b/packages/cli/src/cli/screenshot/action.screenshot.ts @@ -1,12 +1,11 @@ -import { Config, Env, fsa, LogConfig, LogType } from '@basemaps/shared'; +import { createServer } from '@basemaps/server'; +import { Env, fsa, LogConfig, LogType } from '@basemaps/shared'; +import { CommandLineAction, CommandLineStringParameter } from '@rushstack/ts-command-line'; +import { FastifyInstance } from 'fastify/types/instance'; import { mkdir } from 'fs/promises'; +import getPort from 'get-port'; import { Browser, chromium } from 'playwright'; -import { CommandLineAction, CommandLineStringParameter } from '@rushstack/ts-command-line'; import { z } from 'zod'; -import getPort from 'get-port'; -import { createServer } from '@basemaps/server'; -import { FastifyInstance } from 'fastify/types/instance'; -import { ConfigBundled, ConfigProviderMemory } from '@basemaps/config'; export const DefaultTestTiles = './test-tiles/default.test.tiles.json'; export const DefaultHost = 'basemaps.linz.govt.nz';