@basemaps/lambda-tiler 6.33.0 → 6.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +47 -0
  2. package/build/__tests__/config.data.d.ts +2 -1
  3. package/build/__tests__/config.data.d.ts.map +1 -1
  4. package/build/__tests__/config.data.js +14 -1
  5. package/build/__tests__/config.data.js.map +1 -1
  6. package/build/__tests__/tile.style.json.test.js +17 -3
  7. package/build/__tests__/tile.style.json.test.js.map +1 -1
  8. package/build/__tests__/wmts.capability.test.js +17 -0
  9. package/build/__tests__/wmts.capability.test.js.map +1 -1
  10. package/build/__tests__/xyz.util.d.ts +1 -1
  11. package/build/__tests__/xyz.util.d.ts.map +1 -1
  12. package/build/__tests__/xyz.util.js +2 -2
  13. package/build/__tests__/xyz.util.js.map +1 -1
  14. package/build/arcgis/__tests__/arcgis.style.json.test.d.ts +2 -0
  15. package/build/arcgis/__tests__/arcgis.style.json.test.d.ts.map +1 -0
  16. package/build/arcgis/__tests__/arcgis.style.json.test.js +131 -0
  17. package/build/arcgis/__tests__/arcgis.style.json.test.js.map +1 -0
  18. package/build/arcgis/__tests__/vector.tiler.server.test.d.ts +2 -0
  19. package/build/arcgis/__tests__/vector.tiler.server.test.d.ts.map +1 -0
  20. package/build/arcgis/__tests__/vector.tiler.server.test.js +54 -0
  21. package/build/arcgis/__tests__/vector.tiler.server.test.js.map +1 -0
  22. package/build/arcgis/arcgis.info.d.ts +3 -0
  23. package/build/arcgis/arcgis.info.d.ts.map +1 -0
  24. package/build/arcgis/arcgis.info.js +25 -0
  25. package/build/arcgis/arcgis.info.js.map +1 -0
  26. package/build/arcgis/arcgis.style.json.d.ts +9 -0
  27. package/build/arcgis/arcgis.style.json.d.ts.map +1 -0
  28. package/build/arcgis/arcgis.style.json.js +75 -0
  29. package/build/arcgis/arcgis.style.json.js.map +1 -0
  30. package/build/arcgis/vector.tile.server.d.ts +8 -0
  31. package/build/arcgis/vector.tile.server.d.ts.map +1 -0
  32. package/build/arcgis/vector.tile.server.js +73 -0
  33. package/build/arcgis/vector.tile.server.js.map +1 -0
  34. package/build/index.d.ts +1 -3
  35. package/build/index.d.ts.map +1 -1
  36. package/build/index.js +15 -4
  37. package/build/index.js.map +1 -1
  38. package/build/routes/__tests__/attribution.test.js +3 -2
  39. package/build/routes/__tests__/attribution.test.js.map +1 -1
  40. package/build/routes/__tests__/fonts.test.js +46 -29
  41. package/build/routes/__tests__/fonts.test.js.map +1 -1
  42. package/build/routes/__tests__/health.test.js +4 -2
  43. package/build/routes/__tests__/health.test.js.map +1 -1
  44. package/build/routes/__tests__/sprites.test.js +7 -2
  45. package/build/routes/__tests__/sprites.test.js.map +1 -1
  46. package/build/routes/__tests__/tile.json.test.js +24 -2
  47. package/build/routes/__tests__/tile.json.test.js.map +1 -1
  48. package/build/routes/__tests__/tile.style.json.test.js +58 -7
  49. package/build/routes/__tests__/tile.style.json.test.js.map +1 -1
  50. package/build/routes/__tests__/wmts.test.js +17 -14
  51. package/build/routes/__tests__/wmts.test.js.map +1 -1
  52. package/build/routes/__tests__/xyz.test.js +4 -2
  53. package/build/routes/__tests__/xyz.test.js.map +1 -1
  54. package/build/routes/attribution.d.ts.map +1 -1
  55. package/build/routes/attribution.js +20 -10
  56. package/build/routes/attribution.js.map +1 -1
  57. package/build/routes/config.d.ts +22 -0
  58. package/build/routes/config.d.ts.map +1 -0
  59. package/build/routes/config.js +63 -0
  60. package/build/routes/config.js.map +1 -0
  61. package/build/routes/fonts.d.ts +0 -2
  62. package/build/routes/fonts.d.ts.map +1 -1
  63. package/build/routes/fonts.js +3 -66
  64. package/build/routes/fonts.js.map +1 -1
  65. package/build/routes/health.d.ts.map +1 -1
  66. package/build/routes/health.js +3 -2
  67. package/build/routes/health.js.map +1 -1
  68. package/build/routes/imagery.d.ts.map +1 -1
  69. package/build/routes/imagery.js +3 -2
  70. package/build/routes/imagery.js.map +1 -1
  71. package/build/routes/sprites.d.ts.map +1 -1
  72. package/build/routes/sprites.js +3 -29
  73. package/build/routes/sprites.js.map +1 -1
  74. package/build/routes/tile.json.d.ts.map +1 -1
  75. package/build/routes/tile.json.js +7 -4
  76. package/build/routes/tile.json.js.map +1 -1
  77. package/build/routes/tile.style.json.d.ts +4 -3
  78. package/build/routes/tile.style.json.d.ts.map +1 -1
  79. package/build/routes/tile.style.json.js +53 -12
  80. package/build/routes/tile.style.json.js.map +1 -1
  81. package/build/routes/tile.wmts.d.ts.map +1 -1
  82. package/build/routes/tile.wmts.js +7 -4
  83. package/build/routes/tile.wmts.js.map +1 -1
  84. package/build/routes/tile.xyz.d.ts.map +1 -1
  85. package/build/routes/tile.xyz.js +4 -2
  86. package/build/routes/tile.xyz.js.map +1 -1
  87. package/build/routes/tile.xyz.raster.d.ts.map +1 -1
  88. package/build/routes/tile.xyz.raster.js +4 -2
  89. package/build/routes/tile.xyz.raster.js.map +1 -1
  90. package/build/util/__test__/config.loader.test.d.ts +2 -0
  91. package/build/util/__test__/config.loader.test.d.ts.map +1 -0
  92. package/build/util/__test__/config.loader.test.js +79 -0
  93. package/build/util/__test__/config.loader.test.js.map +1 -0
  94. package/build/util/assets.provider.d.ts +24 -0
  95. package/build/util/assets.provider.d.ts.map +1 -0
  96. package/build/util/assets.provider.js +69 -0
  97. package/build/util/assets.provider.js.map +1 -0
  98. package/build/util/config.cache.d.ts +16 -0
  99. package/build/util/config.cache.d.ts.map +1 -0
  100. package/build/util/config.cache.js +40 -0
  101. package/build/util/config.cache.js.map +1 -0
  102. package/build/util/config.loader.d.ts +10 -0
  103. package/build/util/config.loader.d.ts.map +1 -0
  104. package/build/util/config.loader.js +47 -0
  105. package/build/util/config.loader.js.map +1 -0
  106. package/build/util/response.d.ts +2 -1
  107. package/build/util/response.d.ts.map +1 -1
  108. package/build/util/response.js +1 -0
  109. package/build/util/response.js.map +1 -1
  110. package/build/wmts.capability.d.ts +3 -1
  111. package/build/wmts.capability.d.ts.map +1 -1
  112. package/build/wmts.capability.js +5 -15
  113. package/build/wmts.capability.js.map +1 -1
  114. package/dist/index.js +68 -68
  115. package/dist/node_modules/.package-lock.json +4 -4
  116. package/dist/node_modules/node-abi/abi_registry.json +8 -1
  117. package/dist/node_modules/node-abi/package.json +1 -1
  118. package/dist/package-lock.json +8 -8
  119. package/dist/package.json +1 -1
  120. package/package.json +8 -9
  121. package/src/__tests__/config.data.ts +25 -1
  122. package/src/__tests__/tile.style.json.test.ts +19 -3
  123. package/src/__tests__/wmts.capability.test.ts +21 -0
  124. package/src/__tests__/xyz.util.ts +7 -2
  125. package/src/arcgis/__tests__/arcgis.style.json.test.ts +157 -0
  126. package/src/arcgis/__tests__/vector.tiler.server.test.ts +70 -0
  127. package/src/arcgis/arcgis.info.ts +26 -0
  128. package/src/arcgis/arcgis.style.json.ts +83 -0
  129. package/src/arcgis/vector.tile.server.ts +81 -0
  130. package/src/index.ts +18 -5
  131. package/src/routes/__tests__/attribution.test.ts +4 -2
  132. package/src/routes/__tests__/fonts.test.ts +54 -32
  133. package/src/routes/__tests__/health.test.ts +4 -2
  134. package/src/routes/__tests__/sprites.test.ts +7 -3
  135. package/src/routes/__tests__/tile.json.test.ts +30 -2
  136. package/src/routes/__tests__/tile.style.json.test.ts +68 -9
  137. package/src/routes/__tests__/wmts.test.ts +23 -17
  138. package/src/routes/__tests__/xyz.test.ts +4 -2
  139. package/src/routes/attribution.ts +23 -8
  140. package/src/routes/config.ts +83 -0
  141. package/src/routes/fonts.ts +4 -64
  142. package/src/routes/health.ts +4 -2
  143. package/src/routes/imagery.ts +3 -2
  144. package/src/routes/sprites.ts +4 -27
  145. package/src/routes/tile.json.ts +10 -4
  146. package/src/routes/tile.style.json.ts +58 -12
  147. package/src/routes/tile.wmts.ts +9 -4
  148. package/src/routes/tile.xyz.raster.ts +4 -2
  149. package/src/routes/tile.xyz.ts +5 -2
  150. package/src/util/__test__/config.loader.test.ts +116 -0
  151. package/src/util/assets.provider.ts +66 -0
  152. package/src/util/config.cache.ts +44 -0
  153. package/src/util/config.loader.ts +50 -0
  154. package/src/util/response.ts +3 -1
  155. package/src/wmts.capability.ts +9 -15
  156. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,81 @@
1
+ import { TileSetType } from '@basemaps/config';
2
+ import { GoogleTms } from '@basemaps/geo';
3
+ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
+ import { convertRelativeUrl } from '../routes/tile.style.json.js';
5
+ import { ConfigLoader } from '../util/config.loader.js';
6
+ import { NotFound } from '../util/response.js';
7
+ import { Validate } from '../util/validate.js';
8
+
9
+ /** Zoom level for the tilematrix which will be slice by 1 during the covertion to arcgis tileserve lods */
10
+ const MaxTileMatrixZoom = 18;
11
+ const MinTileMatrixZoom = 1;
12
+
13
+ export interface VectorTileServer {
14
+ Params: {
15
+ tileSet: string;
16
+ };
17
+ }
18
+
19
+ export async function arcgisTileServerGet(req: LambdaHttpRequest<VectorTileServer>): Promise<LambdaHttpResponse> {
20
+ const config = await ConfigLoader.load(req);
21
+
22
+ const tileSet = await config.TileSet.get(config.TileSet.id(req.params.tileSet));
23
+ if (tileSet?.type !== TileSetType.Vector) return NotFound();
24
+ const apiKey = Validate.apiKey(req);
25
+ const f = req.query.get('f');
26
+ if (f !== 'json') return NotFound();
27
+ const extent = {
28
+ xmin: GoogleTms.extent.x,
29
+ ymin: GoogleTms.extent.y,
30
+ xmax: GoogleTms.extent.right,
31
+ ymax: GoogleTms.extent.bottom,
32
+ // TODO where is wkid from
33
+ spatialReference: { wkid: 102100, latestWkid: GoogleTms.projection.code },
34
+ };
35
+ const vectorTileServer = {
36
+ currentVersion: 10.81,
37
+ name: tileSet.name,
38
+ capabilities: 'TilesOnly',
39
+ type: 'indexedVector',
40
+ defaultStyles: '',
41
+ tiles: [convertRelativeUrl(`/v1/tiles/${tileSet.name}/WebMercatorQuad/{z}/{x}/{y}.pbf`, apiKey)],
42
+ exportTilesAllowed: false,
43
+ maxExportTilesCount: 0,
44
+ initialExtent: extent,
45
+ fullExtent: extent,
46
+ minScale: 0.0,
47
+ maxScale: 0.0,
48
+ tileInfo: {
49
+ rows: 512,
50
+ cols: 512,
51
+ dpi: 96,
52
+ format: 'pbf',
53
+ origin: { x: GoogleTms.extent.x, y: GoogleTms.extent.bottom },
54
+ spatialReference: { wkid: 102100, latestWkid: GoogleTms.projection.code },
55
+ lods: GoogleTms.zooms.slice(MinTileMatrixZoom, MaxTileMatrixZoom).map((c, i) => {
56
+ return {
57
+ level: i,
58
+ resolution: c.scaleDenominator * 0.28e-3,
59
+ scale: c.scaleDenominator,
60
+ };
61
+ }),
62
+ },
63
+ maxzoom: 22,
64
+ minLOD: 0,
65
+ maxLOD: 15,
66
+ resourceInfo: {
67
+ styleVersion: 8,
68
+ tileCompression: 'gzip',
69
+ cacheInfo: { storageInfo: { packetSize: 128, storageFormat: 'compactV2' } },
70
+ },
71
+ };
72
+
73
+ const json = JSON.stringify(vectorTileServer, null, 2);
74
+ const data = Buffer.from(json);
75
+
76
+ const response = new LambdaHttpResponse(200, 'ok');
77
+ response.header(HttpHeader.CacheControl, 'no-store');
78
+ response.buffer(data, 'application/json');
79
+ req.set('bytes', data.byteLength);
80
+ return response;
81
+ }
package/src/index.ts CHANGED
@@ -1,6 +1,10 @@
1
1
  import { LogConfig } from '@basemaps/shared';
2
2
  import { LambdaHttpResponse, lf } from '@linzjs/lambda';
3
+ import { arcgisInfoGet } from './arcgis/arcgis.info.js';
4
+ import { arcgisStyleJsonGet } from './arcgis/arcgis.style.json.js';
5
+ import { arcgisTileServerGet } from './arcgis/vector.tile.server.js';
3
6
  import { tileAttributionGet } from './routes/attribution.js';
7
+ import { configImageryGet, configTileSetGet } from './routes/config.js';
4
8
  import { fontGet, fontList } from './routes/fonts.js';
5
9
  import { healthGet } from './routes/health.js';
6
10
  import { imageryGet } from './routes/imagery.js';
@@ -11,12 +15,15 @@ import { styleJsonGet } from './routes/tile.style.json.js';
11
15
  import { wmtsCapabilitiesGet } from './routes/tile.wmts.js';
12
16
  import { tileXyzGet } from './routes/tile.xyz.js';
13
17
  import { versionGet } from './routes/version.js';
14
- import { NotFound } from './util/response.js';
18
+ import { NotFound, OkResponse } from './util/response.js';
15
19
  import { CoSources } from './util/source.cache.js';
16
20
  import { St } from './util/source.tracer.js';
17
21
 
18
22
  export const handler = lf.http(LogConfig.get());
19
23
 
24
+ /** If the request takes too long, respond with a 408 timeout when there is approx 1 second remaining */
25
+ handler.router.timeoutEarlyMs = 1_000;
26
+
20
27
  handler.router.hook('request', (req) => {
21
28
  req.set('name', 'LambdaTiler');
22
29
 
@@ -24,11 +31,7 @@ handler.router.hook('request', (req) => {
24
31
  St.reset();
25
32
  });
26
33
 
27
- let totalRequests = 0;
28
34
  handler.router.hook('response', (req, res) => {
29
- totalRequests++;
30
- req.set('requestsTotal', totalRequests); // Number of requests served by this lambda
31
-
32
35
  if (St.requests.length > 0) {
33
36
  // TODO this could be relaxed to every say 5% of requests if logging gets too verbose.
34
37
  req.set('requests', St.requests.slice(0, 100)); // limit to 100 requests (some tiles need 100s of requests)
@@ -68,6 +71,10 @@ handler.router.get('/v1/version', versionGet);
68
71
  // Image Metadata
69
72
  handler.router.get('/v1/imagery/:imageryId/:fileName', imageryGet);
70
73
 
74
+ // Config
75
+ handler.router.get('/v1/config/:tileSet.json', configTileSetGet);
76
+ handler.router.get('/v1/config/:tileSet/:imageryId.json', configImageryGet);
77
+
71
78
  // Sprites
72
79
  handler.router.get('/v1/sprites/:spriteName', spriteGet);
73
80
 
@@ -94,3 +101,9 @@ handler.router.get('/v1/attribution/:tileSet/:tileMatrix/summary.json', tileAttr
94
101
  handler.router.get('/v1/tiles/:tileSet/:tileMatrix/WMTSCapabilities.xml', wmtsCapabilitiesGet);
95
102
  handler.router.get('/v1/tiles/:tileSet/WMTSCapabilities.xml', wmtsCapabilitiesGet);
96
103
  handler.router.get('/v1/tiles/WMTSCapabilities.xml', wmtsCapabilitiesGet);
104
+
105
+ // Arcgis Vector
106
+ handler.router.get('/v1/arcgis/rest/services/:tileSet/VectorTileServer', arcgisTileServerGet);
107
+ handler.router.post('/v1/arcgis/rest/services/:tileSet/VectorTileServer', OkResponse);
108
+ handler.router.get('/v1/arcgis/rest/services/:tileSet/VectorTileServer/root.json', arcgisStyleJsonGet);
109
+ handler.router.get('/v1/arcgis/rest/info', arcgisInfoGet);
@@ -1,11 +1,12 @@
1
1
  import { Attribution } from '@basemaps/attribution';
2
- import { Config, ConfigProviderMemory } from '@basemaps/config';
2
+ import { ConfigProviderMemory } from '@basemaps/config';
3
3
  import { Nztm2000QuadTms } from '@basemaps/geo';
4
4
  import { LogConfig } from '@basemaps/shared';
5
5
  import { HttpHeader } from '@linzjs/lambda';
6
6
  import o from 'ospec';
7
7
  import sinon from 'sinon';
8
8
  import { handler } from '../../index.js';
9
+ import { ConfigLoader } from '../../util/config.loader.js';
9
10
  import { FakeData, Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
10
11
  import { mockUrlRequest } from '../../__tests__/xyz.util.js';
11
12
 
@@ -283,7 +284,8 @@ o.spec('/v1/attribution', () => {
283
284
 
284
285
  o.beforeEach(() => {
285
286
  LogConfig.get().level = 'silent';
286
- Config.setConfigProvider(config);
287
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
288
+
287
289
  config.objects.clear();
288
290
 
289
291
  config.put(TileSetAerial);
@@ -1,9 +1,12 @@
1
- import { Env } from '@basemaps/shared';
1
+ import { base58, ConfigProviderMemory } from '@basemaps/config';
2
+ import { Env, getDefaultConfig } from '@basemaps/shared';
2
3
  import { fsa } from '@chunkd/fs';
3
4
  import o from 'ospec';
4
5
  import { handler } from '../../index.js';
5
- import { mockRequest } from '../../__tests__/xyz.util.js';
6
- import { fontList, getFonts } from '../fonts.js';
6
+ import { CachedConfig } from '../../util/config.cache.js';
7
+ import { CoSources } from '../../util/source.cache.js';
8
+ import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
9
+ import { fontList } from '../fonts.js';
7
10
  import { FsMemory } from './memory.fs.js';
8
11
 
9
12
  o.spec('/v1/fonts', () => {
@@ -11,52 +14,32 @@ o.spec('/v1/fonts', () => {
11
14
  o.before(() => {
12
15
  fsa.register('memory://', memory);
13
16
  });
17
+ const assetLocation = process.env[Env.AssetLocation];
14
18
 
15
19
  o.beforeEach(() => {
16
- process.env[Env.AssetLocation] = 'memory://';
20
+ process.env[Env.AssetLocation] = 'memory://config';
21
+ getDefaultConfig().assets = 'memory://';
17
22
  });
18
23
 
19
24
  o.afterEach(() => {
20
- delete process.env[Env.AssetLocation];
25
+ getDefaultConfig().assets = assetLocation;
26
+ CachedConfig.cache.clear();
27
+ CoSources.cache.clear();
21
28
  memory.files.clear();
22
29
  });
23
30
 
24
- o('should list font types', async () => {
25
- await Promise.all([
26
- fsa.write('memory://fonts/Roboto Thin/0-255.pbf', Buffer.from('')),
27
- fsa.write('memory://fonts/Roboto Thin/256-512.pbf', Buffer.from('')),
28
- fsa.write('memory://fonts/Roboto Black/0-255.pbf', Buffer.from('')),
29
- ]);
30
-
31
- const fonts = await getFonts('memory://fonts/');
32
- o(fonts).deepEquals(['Roboto Black', 'Roboto Thin']);
33
- });
34
-
35
- o('should return empty list if no fonts found', async () => {
36
- const res = await fontList(mockRequest('/v1/fonts.json'));
37
- o(res.status).equals(200);
38
- o(res.body).equals('[]');
39
- o(res.header('etag')).notEquals(undefined);
40
- o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
41
- });
42
-
43
- o('should return 404 if no assets defined', async () => {
44
- delete process.env[Env.AssetLocation];
31
+ o('should return 404 if no font found', async () => {
45
32
  const res = await fontList(mockRequest('/v1/fonts.json'));
46
33
  o(res.status).equals(404);
47
34
  });
48
35
 
49
36
  o('should return a list of fonts found', async () => {
50
- await Promise.all([
51
- fsa.write('memory://fonts/Roboto Thin/0-255.pbf', Buffer.from('')),
52
- fsa.write('memory://fonts/Roboto Thin/256-512.pbf', Buffer.from('')),
53
- fsa.write('memory://fonts/Roboto Black/0-255.pbf', Buffer.from('')),
54
- ]);
37
+ await fsa.write('memory://fonts.json', Buffer.from(JSON.stringify(['Roboto Black', 'Roboto Thin'])));
55
38
  const res = await fontList(mockRequest('/v1/fonts.json'));
56
39
  o(res.status).equals(200);
57
40
  o(res.header('content-type')).equals('application/json');
58
41
  o(res.header('content-encoding')).equals(undefined);
59
- o(res.body).equals(JSON.stringify(['Roboto Black', 'Roboto Thin']));
42
+ o(res._body?.toString()).equals(JSON.stringify(['Roboto Black', 'Roboto Thin']));
60
43
  });
61
44
 
62
45
  o('should get the correct font', async () => {
@@ -83,4 +66,43 @@ o.spec('/v1/fonts', () => {
83
66
  o(res255.header('etag')).notEquals(undefined);
84
67
  o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
85
68
  });
69
+
70
+ o('should return 404 if no asset location set', async () => {
71
+ getDefaultConfig().assets = undefined;
72
+ const res = await fontList(mockRequest('/v1/fonts.json'));
73
+ o(res.status).equals(404);
74
+ });
75
+
76
+ o('should get the correct utf8 font with default assets', async () => {
77
+ const cfgBundle = new ConfigProviderMemory();
78
+ await fsa.write('memory://linz-basemaps/bar.json', JSON.stringify(cfgBundle.toJson()));
79
+ await fsa.write('memory://config/fonts/🦄 🌈/0-255.pbf', Buffer.from(''));
80
+
81
+ const configLocation = base58.encode(Buffer.from('memory://linz-basemaps/bar.json'));
82
+ const res255 = await handler.router.handle(
83
+ mockUrlRequest('/v1/fonts/🦄 🌈/0-255.pbf', `?config=${configLocation}`, Api.header),
84
+ );
85
+ o(res255.status).equals(200);
86
+ o(res255.header('content-type')).equals('application/x-protobuf');
87
+ o(res255.header('content-encoding')).equals(undefined);
88
+ o(res255.header('etag')).notEquals(undefined);
89
+ o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
90
+ });
91
+
92
+ o('should get the correct utf8 font with config assets', async () => {
93
+ const cfgBundle = new ConfigProviderMemory();
94
+ cfgBundle.assets = 'memory://config/assets';
95
+ await fsa.write('memory://linz-basemaps/bar.json', JSON.stringify(cfgBundle.toJson()));
96
+ await fsa.write('memory://config/assets/fonts/🦄 🌈/0-255.pbf', Buffer.from(''));
97
+
98
+ const configLocation = base58.encode(Buffer.from('memory://linz-basemaps/bar.json'));
99
+ const res255 = await handler.router.handle(
100
+ mockUrlRequest('/v1/fonts/🦄 🌈/0-255.pbf', `?config=${configLocation}`, Api.header),
101
+ );
102
+ o(res255.status).equals(200);
103
+ o(res255.header('content-type')).equals('application/x-protobuf');
104
+ o(res255.header('content-encoding')).equals(undefined);
105
+ o(res255.header('etag')).notEquals(undefined);
106
+ o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
107
+ });
86
108
  });
@@ -1,9 +1,10 @@
1
- import { Config, ConfigProviderMemory } from '@basemaps/config';
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
2
  import { LogConfig } from '@basemaps/shared';
3
3
  import { LambdaAlbRequest, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import { Context } from 'aws-lambda';
5
5
  import o from 'ospec';
6
6
  import sinon from 'sinon';
7
+ import { ConfigLoader } from '../../util/config.loader.js';
7
8
  import { FakeData } from '../../__tests__/config.data.js';
8
9
 
9
10
  import { getTestBuffer, healthGet, TestTiles } from '../health.js';
@@ -29,11 +30,12 @@ o.spec('/v1/health', async () => {
29
30
  const fakeTileSet = FakeData.tileSetRaster('health');
30
31
  o.beforeEach(() => {
31
32
  config.objects.clear();
32
- Config.setConfigProvider(config);
33
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
33
34
  config.put(fakeTileSet);
34
35
  });
35
36
 
36
37
  o.afterEach(() => {
38
+ config.objects.clear();
37
39
  sandbox.restore();
38
40
  });
39
41
 
@@ -1,6 +1,7 @@
1
- import { Env } from '@basemaps/shared';
1
+ import { Env, getDefaultConfig } from '@basemaps/shared';
2
2
  import { fsa } from '@chunkd/fs';
3
3
  import o from 'ospec';
4
+ import { createSandbox } from 'sinon';
4
5
  import { gunzipSync, gzipSync } from 'zlib';
5
6
  import { handler } from '../../index.js';
6
7
  import { mockRequest } from '../../__tests__/xyz.util.js';
@@ -8,19 +9,22 @@ import { FsMemory } from './memory.fs.js';
8
9
 
9
10
  o.spec('/v1/sprites', () => {
10
11
  const memory = new FsMemory();
12
+ const sandbox = createSandbox();
11
13
  o.before(() => {
12
14
  fsa.register('memory://', memory);
13
15
  });
16
+ const assetLocation = process.env[Env.AssetLocation];
14
17
 
15
18
  o.beforeEach(() => {
16
19
  process.env[Env.AssetLocation] = 'memory://';
20
+ getDefaultConfig().assets = 'memory://';
17
21
  });
18
22
 
19
23
  o.afterEach(() => {
20
- delete process.env[Env.AssetLocation];
24
+ getDefaultConfig().assets = assetLocation;
21
25
  memory.files.clear();
26
+ sandbox.restore();
22
27
  });
23
-
24
28
  o('should return 404 if no assets defined', async () => {
25
29
  delete process.env[Env.AssetLocation];
26
30
  const res404 = await handler.router.handle(mockRequest('/v1/sprites/topographic.json'));
@@ -1,23 +1,33 @@
1
- import { Config, ConfigProviderMemory } from '@basemaps/config';
1
+ import { base58, ConfigProviderMemory } from '@basemaps/config';
2
2
  import { Env } from '@basemaps/shared';
3
+ import { fsa } from '@chunkd/fs';
3
4
  import o from 'ospec';
5
+ import sinon from 'sinon';
4
6
  import { handler } from '../../index.js';
7
+ import { ConfigLoader } from '../../util/config.loader.js';
5
8
  import { CoSources } from '../../util/source.cache.js';
6
9
  import { FakeData } from '../../__tests__/config.data.js';
7
10
  import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
11
+ import { FsMemory } from './memory.fs.js';
8
12
 
9
13
  o.spec('/v1/tiles/:tileSet/:tileMatrix/tile.json', () => {
10
14
  const config = new ConfigProviderMemory();
15
+ const sandbox = sinon.createSandbox();
16
+
11
17
  o.before(() => {
12
18
  process.env[Env.PublicUrlBase] = 'https://tiles.test';
13
- Config.setConfigProvider(config);
14
19
  });
15
20
 
16
21
  o.beforeEach(() => {
22
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
17
23
  config.objects.clear();
18
24
  CoSources.cache.clear();
19
25
  });
20
26
 
27
+ o.afterEach(() => {
28
+ sandbox.restore();
29
+ });
30
+
21
31
  o('should 404 if invalid url is given', async () => {
22
32
  const request = mockRequest('/v1/tiles/tile.json', 'get', Api.header);
23
33
 
@@ -123,6 +133,24 @@ o.spec('/v1/tiles/:tileSet/:tileMatrix/tile.json', () => {
123
133
  });
124
134
  });
125
135
 
136
+ o('should load from config bundle', async () => {
137
+ const memoryFs = new FsMemory();
138
+ fsa.register('memory://', memoryFs);
139
+ const fakeTileSet = FakeData.tileSetRaster('🦄 🌈');
140
+
141
+ const cfgBundle = new ConfigProviderMemory();
142
+ cfgBundle.put(fakeTileSet);
143
+ memoryFs.write('memory://linz-basemaps/bar.json', JSON.stringify(cfgBundle.toJson()));
144
+
145
+ const configLocation = base58.encode(Buffer.from('memory://linz-basemaps/bar.json'));
146
+ const request = mockUrlRequest('/v1/tiles/🦄 🌈/NZTM2000Quad/tile.json', `?config=${configLocation}`, Api.header);
147
+ const res = await handler.router.handle(request);
148
+ o(res.status).equals(200);
149
+
150
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
151
+ o(body.tiles[0].includes(`config=${configLocation}`)).equals(true);
152
+ });
153
+
126
154
  o('should serve convert zoom to tile matrix', async () => {
127
155
  const fakeTileSet = FakeData.tileSetVector('fake-vector');
128
156
  fakeTileSet.maxZoom = 15;
@@ -1,24 +1,29 @@
1
- import { Config, StyleJson } from '@basemaps/config';
1
+ import { ConfigProviderMemory, StyleJson } from '@basemaps/config';
2
2
  import { Env } from '@basemaps/shared';
3
3
  import o from 'ospec';
4
4
  import { createSandbox } from 'sinon';
5
5
  import { handler } from '../../index.js';
6
- import { Api, mockRequest } from '../../__tests__/xyz.util.js';
6
+ import { ConfigLoader } from '../../util/config.loader.js';
7
+ import { FakeData } from '../../__tests__/config.data.js';
8
+ import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
7
9
 
8
10
  o.spec('/v1/styles', () => {
9
11
  const host = 'https://tiles.test';
12
+ const config = new ConfigProviderMemory();
10
13
  const sandbox = createSandbox();
11
14
 
12
15
  o.before(() => {
13
16
  process.env[Env.PublicUrlBase] = host;
14
17
  });
15
- // o.beforeEach(() => {});
16
- o.afterEach(() => sandbox.restore());
18
+ o.beforeEach(() => {
19
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
20
+ });
21
+ o.afterEach(() => {
22
+ sandbox.restore();
23
+ config.objects.clear();
24
+ });
17
25
  o('should not found style json', async () => {
18
26
  const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', Api.header);
19
-
20
- sandbox.stub(Config.Style, 'get').resolves(null);
21
-
22
27
  const res = await handler.router.handle(request);
23
28
  o(res.status).equals(404);
24
29
  });
@@ -71,12 +76,12 @@ o.spec('/v1/styles', () => {
71
76
  };
72
77
 
73
78
  const fakeRecord = {
74
- id: 'st_topographic_production',
79
+ id: 'st_topographic',
75
80
  name: 'topographic',
76
81
  style: fakeStyle,
77
82
  };
78
83
 
79
- sandbox.stub(Config.Style, 'get').resolves(fakeRecord as any);
84
+ config.put(fakeRecord);
80
85
 
81
86
  const res = await handler.router.handle(request);
82
87
  o(res.status).equals(200);
@@ -102,4 +107,58 @@ o.spec('/v1/styles', () => {
102
107
 
103
108
  o(JSON.parse(body)).deepEquals(fakeStyle);
104
109
  });
110
+
111
+ o('should create raster styles', async () => {
112
+ const request = mockUrlRequest('/v1/styles/aerial.json', '', Api.header);
113
+ const tileSet = FakeData.tileSetRaster('aerial');
114
+ config.put(tileSet);
115
+ const res = await handler.router.handle(request);
116
+ o(res.status).equals(200);
117
+
118
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
119
+
120
+ o(body.version).equals(8);
121
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
122
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
123
+ `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}`,
124
+ ]);
125
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
126
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
127
+ });
128
+
129
+ o('should support parameters', async () => {
130
+ const request = mockUrlRequest('/v1/styles/aerial.json', '?tileMatrix=NZTM2000Quad&format=jpg', Api.header);
131
+ const tileSet = FakeData.tileSetRaster('aerial');
132
+ config.put(tileSet);
133
+ const res = await handler.router.handle(request);
134
+ o(res.status).equals(200);
135
+
136
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
137
+
138
+ o(body.version).equals(8);
139
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
140
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
141
+ `https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.jpeg?api=${Api.key}`,
142
+ ]);
143
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
144
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
145
+ });
146
+
147
+ o('should create raster styles from custom config', async () => {
148
+ const configId = FakeData.bundle([FakeData.tileSetRaster('aerial')]);
149
+ const request = mockUrlRequest('/v1/styles/aerial.json', `?config=${configId}`, Api.header);
150
+
151
+ const res = await handler.router.handle(request);
152
+ o(res.status).equals(200);
153
+
154
+ const body = JSON.parse(Buffer.from(res.body, 'base64').toString());
155
+
156
+ o(body.version).equals(8);
157
+ o(body.sources['basemaps-aerial'].type).deepEquals('raster');
158
+ o(body.sources['basemaps-aerial'].tiles).deepEquals([
159
+ `https://tiles.test/v1/tiles/aerial/WebMercatorQuad/{z}/{x}/{y}.webp?api=${Api.key}&config=${configId}`,
160
+ ]);
161
+ o(body.sources['basemaps-aerial'].tileSize).deepEquals(256);
162
+ o(body.layers).deepEquals([{ id: 'basemaps-aerial', type: 'raster', source: 'basemaps-aerial' }]);
163
+ });
105
164
  });
@@ -1,13 +1,21 @@
1
- import { Config } from '@basemaps/shared';
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
2
  import o from 'ospec';
3
3
  import { createSandbox } from 'sinon';
4
4
  import { handler } from '../../index.js';
5
+ import { ConfigLoader } from '../../util/config.loader.js';
5
6
  import { Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
6
7
  import { Api, mockUrlRequest } from '../../__tests__/xyz.util.js';
7
8
 
8
9
  o.spec('WMTSRouting', () => {
9
10
  const sandbox = createSandbox();
11
+ const config = new ConfigProviderMemory();
12
+
13
+ o.before(() => {
14
+ sandbox.stub(ConfigLoader, 'load').resolves(config);
15
+ });
16
+
10
17
  o.afterEach(() => {
18
+ config.objects.clear();
11
19
  sandbox.restore();
12
20
  });
13
21
 
@@ -16,25 +24,17 @@ o.spec('WMTSRouting', () => {
16
24
  imagery.set(Imagery3857.id, Imagery3857);
17
25
  imagery.set(Imagery2193.id, Imagery2193);
18
26
 
19
- const tileSetStub = sandbox.stub(Config.TileSet, 'get').returns(Promise.resolve(TileSetAerial));
20
- const imageryStub = sandbox.stub(Config.Imagery, 'getAll').returns(Promise.resolve(imagery));
21
- const providerStub = sandbox.stub(Config.Provider, 'get').returns(Promise.resolve(Provider));
27
+ config.put(TileSetAerial);
28
+ config.put(Imagery2193);
29
+ config.put(Imagery3857);
30
+ config.put(Provider);
22
31
 
23
- const req = mockUrlRequest('/v1/tiles/WMTSCapabilities.xml', `format=png&api=${Api.key}`);
32
+ const req = mockUrlRequest(
33
+ '/v1/tiles/WMTSCapabilities.xml',
34
+ `format=png&api=${Api.key}&config=s3://linz-basemaps/config.json`,
35
+ );
24
36
  const res = await handler.router.handle(req);
25
37
 
26
- o(tileSetStub.calledOnce).equals(true);
27
- o(tileSetStub.args[0][0]).equals('ts_aerial');
28
-
29
- o(providerStub.calledOnce).equals(true);
30
- o(providerStub.args[0][0]).equals('pv_linz');
31
-
32
- o(imageryStub.calledOnce).equals(true);
33
- o([...imageryStub.args[0][0].values()]).deepEquals([
34
- 'im_01FYWKATAEK2ZTJQ2PX44Y0XNT',
35
- 'im_01FYWKAJ86W9P7RWM1VB62KD0H',
36
- ]);
37
-
38
38
  o(res.status).equals(200);
39
39
  const lines = Buffer.from(res.body, 'base64').toString().split('\n');
40
40
 
@@ -46,5 +46,11 @@ o.spec('WMTSRouting', () => {
46
46
  '<ows:Title>Google Maps Compatible for the World</ows:Title>',
47
47
  '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
48
48
  ]);
49
+
50
+ const resourceURLs = lines.filter((f) => f.includes('<ResourceURL')).map((f) => f.trim());
51
+ o(resourceURLs).deepEquals([
52
+ '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/aerial/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M" />',
53
+ '<ResourceURL format="image/png" resourceType="tile" template="https://tiles.test/v1/tiles/ōtorohanga-urban-2021-0.1m/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=d01f7w7rnhdzg0p7fyrc9v9ard1&amp;config=Q5pC4UjWdtFLU1CYtLcRSmB49RekgDgMa5EGJnB2M" />',
54
+ ]);
49
55
  });
50
56
  });
@@ -1,9 +1,10 @@
1
- import { Config, ConfigProviderMemory } from '@basemaps/config';
1
+ import { ConfigProviderMemory } from '@basemaps/config';
2
2
  import { LogConfig } from '@basemaps/shared';
3
3
  import { round } from '@basemaps/test/build/rounding.js';
4
4
  import o from 'ospec';
5
5
  import sinon from 'sinon';
6
6
  import { handler } from '../../index.js';
7
+ import { ConfigLoader } from '../../util/config.loader.js';
7
8
  import { Etag } from '../../util/etag.js';
8
9
  import { FakeData } from '../../__tests__/config.data.js';
9
10
  import { Api, mockRequest } from '../../__tests__/xyz.util.js';
@@ -16,7 +17,7 @@ o.spec('/v1/tiles', () => {
16
17
 
17
18
  o.beforeEach(() => {
18
19
  LogConfig.get().level = 'silent';
19
- Config.setConfigProvider(config);
20
+ sandbox.stub(ConfigLoader, 'getDefaultConfig').resolves(config);
20
21
  config.objects.clear();
21
22
 
22
23
  for (const tileSetName of TileSetNames) config.put(FakeData.tileSetRaster(tileSetName));
@@ -25,6 +26,7 @@ o.spec('/v1/tiles', () => {
25
26
  });
26
27
 
27
28
  o.afterEach(() => {
29
+ config.objects.clear();
28
30
  sandbox.restore();
29
31
  });
30
32