@basemaps/lambda-tiler 6.29.0 → 6.32.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 (259) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/build/__tests__/config.data.d.ts +11 -0
  3. package/build/__tests__/config.data.d.ts.map +1 -0
  4. package/build/__tests__/config.data.js +112 -0
  5. package/build/__tests__/config.data.js.map +1 -0
  6. package/build/__tests__/index.test.js +5 -14
  7. package/build/__tests__/index.test.js.map +1 -0
  8. package/build/__tests__/tile.style.json.test.js +1 -0
  9. package/build/__tests__/tile.style.json.test.js.map +1 -0
  10. package/build/__tests__/wmts.capability.test.d.ts +1 -1
  11. package/build/__tests__/wmts.capability.test.d.ts.map +1 -1
  12. package/build/__tests__/wmts.capability.test.js +286 -125
  13. package/build/__tests__/wmts.capability.test.js.map +1 -0
  14. package/build/__tests__/xyz.util.d.ts +7 -11
  15. package/build/__tests__/xyz.util.d.ts.map +1 -1
  16. package/build/__tests__/xyz.util.js +14 -42
  17. package/build/__tests__/xyz.util.js.map +1 -0
  18. package/build/index.d.ts +0 -2
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +68 -41
  21. package/build/index.js.map +1 -0
  22. package/build/routes/__tests__/attribution.test.js +351 -399
  23. package/build/routes/__tests__/attribution.test.js.map +1 -0
  24. package/build/routes/__tests__/fonts.test.js +17 -3
  25. package/build/routes/__tests__/fonts.test.js.map +1 -0
  26. package/build/routes/__tests__/health.test.js +17 -13
  27. package/build/routes/__tests__/health.test.js.map +1 -0
  28. package/build/routes/__tests__/imagery.test.js +1 -0
  29. package/build/routes/__tests__/imagery.test.js.map +1 -0
  30. package/build/routes/__tests__/memory.fs.js +1 -0
  31. package/build/routes/__tests__/memory.fs.js.map +1 -0
  32. package/build/routes/__tests__/sprites.test.js +7 -0
  33. package/build/routes/__tests__/sprites.test.js.map +1 -0
  34. package/build/routes/__tests__/tile.json.test.d.ts +2 -0
  35. package/build/routes/__tests__/tile.json.test.d.ts.map +1 -0
  36. package/build/routes/__tests__/tile.json.test.js +124 -0
  37. package/build/routes/__tests__/tile.json.test.js.map +1 -0
  38. package/build/routes/__tests__/tile.style.json.test.d.ts +2 -0
  39. package/build/routes/__tests__/tile.style.json.test.d.ts.map +1 -0
  40. package/build/routes/__tests__/tile.style.json.test.js +95 -0
  41. package/build/routes/__tests__/tile.style.json.test.js.map +1 -0
  42. package/build/routes/__tests__/wmts.test.js +37 -27
  43. package/build/routes/__tests__/wmts.test.js.map +1 -0
  44. package/build/{__tests__ → routes/__tests__}/xyz.test.d.ts +0 -0
  45. package/build/routes/__tests__/xyz.test.d.ts.map +1 -0
  46. package/build/routes/__tests__/xyz.test.js +99 -0
  47. package/build/routes/__tests__/xyz.test.js.map +1 -0
  48. package/build/routes/attribution.d.ts +7 -5
  49. package/build/routes/attribution.d.ts.map +1 -1
  50. package/build/routes/attribution.js +50 -91
  51. package/build/routes/attribution.js.map +1 -0
  52. package/build/routes/fonts.d.ts +1 -1
  53. package/build/routes/fonts.d.ts.map +1 -1
  54. package/build/routes/fonts.js +33 -10
  55. package/build/routes/fonts.js.map +1 -0
  56. package/build/routes/health.d.ts +3 -3
  57. package/build/routes/health.d.ts.map +1 -1
  58. package/build/routes/health.js +16 -13
  59. package/build/routes/health.js.map +1 -0
  60. package/build/routes/imagery.d.ts +8 -1
  61. package/build/routes/imagery.d.ts.map +1 -1
  62. package/build/routes/imagery.js +17 -17
  63. package/build/routes/imagery.js.map +1 -0
  64. package/build/routes/ping.d.ts +3 -0
  65. package/build/routes/ping.d.ts.map +1 -0
  66. package/build/routes/ping.js +7 -0
  67. package/build/routes/ping.js.map +1 -0
  68. package/build/routes/sprites.d.ts.map +1 -1
  69. package/build/routes/sprites.js +22 -22
  70. package/build/routes/sprites.js.map +1 -0
  71. package/build/routes/tile.json.d.ts +7 -1
  72. package/build/routes/tile.json.d.ts.map +1 -1
  73. package/build/routes/tile.json.js +19 -22
  74. package/build/routes/tile.json.js.map +1 -0
  75. package/build/routes/tile.style.json.d.ts +6 -1
  76. package/build/routes/tile.style.json.d.ts.map +1 -1
  77. package/build/routes/tile.style.json.js +11 -13
  78. package/build/routes/tile.style.json.js.map +1 -0
  79. package/build/routes/tile.wmts.d.ts +9 -3
  80. package/build/routes/tile.wmts.d.ts.map +1 -1
  81. package/build/routes/tile.wmts.js +37 -50
  82. package/build/routes/tile.wmts.js.map +1 -0
  83. package/build/routes/tile.xyz.d.ts +14 -4
  84. package/build/routes/tile.xyz.d.ts.map +1 -1
  85. package/build/routes/tile.xyz.js +22 -17
  86. package/build/routes/tile.xyz.js.map +1 -0
  87. package/build/routes/tile.xyz.raster.d.ts +11 -0
  88. package/build/routes/tile.xyz.raster.d.ts.map +1 -0
  89. package/build/routes/tile.xyz.raster.js +90 -0
  90. package/build/routes/tile.xyz.raster.js.map +1 -0
  91. package/build/routes/tile.xyz.vector.d.ts +8 -0
  92. package/build/routes/tile.xyz.vector.d.ts.map +1 -0
  93. package/build/routes/tile.xyz.vector.js +46 -0
  94. package/build/routes/tile.xyz.vector.js.map +1 -0
  95. package/build/routes/version.d.ts +3 -0
  96. package/build/routes/version.d.ts.map +1 -0
  97. package/build/routes/version.js +9 -0
  98. package/build/routes/version.js.map +1 -0
  99. package/build/util/__test__/validate.test.d.ts +2 -0
  100. package/build/util/__test__/validate.test.d.ts.map +1 -0
  101. package/build/util/__test__/validate.test.js +66 -0
  102. package/build/util/__test__/validate.test.js.map +1 -0
  103. package/build/util/cotar.serve.d.ts +20 -0
  104. package/build/util/cotar.serve.d.ts.map +1 -0
  105. package/build/util/cotar.serve.js +41 -0
  106. package/build/util/cotar.serve.js.map +1 -0
  107. package/build/util/etag.d.ts +6 -0
  108. package/build/util/etag.d.ts.map +1 -0
  109. package/build/util/etag.js +20 -0
  110. package/build/util/etag.js.map +1 -0
  111. package/build/util/response.d.ts +4 -0
  112. package/build/util/response.d.ts.map +1 -0
  113. package/build/util/response.js +4 -0
  114. package/build/util/response.js.map +1 -0
  115. package/build/util/source.cache.d.ts +28 -0
  116. package/build/util/source.cache.d.ts.map +1 -0
  117. package/build/util/source.cache.js +53 -0
  118. package/build/util/source.cache.js.map +1 -0
  119. package/build/{source.tracer.d.ts → util/source.tracer.d.ts} +1 -0
  120. package/build/util/source.tracer.d.ts.map +1 -0
  121. package/build/{source.tracer.js → util/source.tracer.js} +4 -0
  122. package/build/util/source.tracer.js.map +1 -0
  123. package/build/util/swapping.lru.d.ts +21 -0
  124. package/build/util/swapping.lru.d.ts.map +1 -0
  125. package/build/util/swapping.lru.js +56 -0
  126. package/build/util/swapping.lru.js.map +1 -0
  127. package/build/util/validate.d.ts +46 -0
  128. package/build/util/validate.d.ts.map +1 -0
  129. package/build/util/validate.js +107 -0
  130. package/build/util/validate.js.map +1 -0
  131. package/build/wmts.capability.d.ts +27 -13
  132. package/build/wmts.capability.d.ts.map +1 -1
  133. package/build/wmts.capability.js +156 -55
  134. package/build/wmts.capability.js.map +1 -0
  135. package/dist/index.js +89 -73
  136. package/dist/node_modules/.package-lock.json +1 -1
  137. package/dist/package-lock.json +2 -2
  138. package/dist/package.json +1 -1
  139. package/package.json +10 -10
  140. package/src/__tests__/config.data.ts +120 -0
  141. package/src/__tests__/index.test.ts +4 -20
  142. package/src/__tests__/wmts.capability.test.ts +312 -139
  143. package/src/__tests__/xyz.util.ts +17 -45
  144. package/src/index.ts +75 -41
  145. package/src/routes/__tests__/attribution.test.ts +356 -403
  146. package/src/routes/__tests__/fonts.test.ts +18 -3
  147. package/src/routes/__tests__/health.test.ts +17 -13
  148. package/src/routes/__tests__/sprites.test.ts +6 -1
  149. package/src/routes/__tests__/tile.json.test.ts +145 -0
  150. package/src/routes/__tests__/tile.style.json.test.ts +105 -0
  151. package/src/routes/__tests__/wmts.test.ts +44 -34
  152. package/src/routes/__tests__/xyz.test.ts +119 -0
  153. package/src/routes/attribution.ts +59 -111
  154. package/src/routes/fonts.ts +32 -10
  155. package/src/routes/health.ts +17 -16
  156. package/src/routes/imagery.ts +18 -15
  157. package/src/routes/ping.ts +8 -0
  158. package/src/routes/sprites.ts +20 -22
  159. package/src/routes/tile.json.ts +24 -19
  160. package/src/routes/tile.style.json.ts +15 -12
  161. package/src/routes/tile.wmts.ts +41 -44
  162. package/src/routes/tile.xyz.raster.ts +106 -0
  163. package/src/routes/tile.xyz.ts +31 -16
  164. package/src/routes/tile.xyz.vector.ts +47 -0
  165. package/src/routes/version.ts +8 -0
  166. package/src/util/__test__/validate.test.ts +74 -0
  167. package/src/util/cotar.serve.ts +46 -0
  168. package/src/util/etag.ts +20 -0
  169. package/src/util/response.ts +4 -0
  170. package/src/util/source.cache.ts +71 -0
  171. package/src/{source.tracer.ts → util/source.tracer.ts} +4 -0
  172. package/src/util/swapping.lru.ts +63 -0
  173. package/src/util/validate.ts +126 -0
  174. package/src/wmts.capability.ts +170 -68
  175. package/tsconfig.tsbuildinfo +1 -1
  176. package/build/__tests__/route.test.d.ts +0 -2
  177. package/build/__tests__/route.test.d.ts.map +0 -1
  178. package/build/__tests__/route.test.js +0 -20
  179. package/build/__tests__/tiff.cache.test.d.ts +0 -2
  180. package/build/__tests__/tiff.cache.test.d.ts.map +0 -1
  181. package/build/__tests__/tiff.cache.test.js +0 -58
  182. package/build/__tests__/tile.cache.key.test.d.ts +0 -2
  183. package/build/__tests__/tile.cache.key.test.d.ts.map +0 -1
  184. package/build/__tests__/tile.cache.key.test.js +0 -48
  185. package/build/__tests__/tile.set.cache.test.d.ts +0 -2
  186. package/build/__tests__/tile.set.cache.test.d.ts.map +0 -1
  187. package/build/__tests__/tile.set.cache.test.js +0 -123
  188. package/build/__tests__/tile.set.test.d.ts +0 -2
  189. package/build/__tests__/tile.set.test.d.ts.map +0 -1
  190. package/build/__tests__/tile.set.test.js +0 -11
  191. package/build/__tests__/xyz.test.d.ts.map +0 -1
  192. package/build/__tests__/xyz.test.js +0 -306
  193. package/build/api.key.d.ts +0 -2
  194. package/build/api.key.d.ts.map +0 -1
  195. package/build/api.key.js +0 -23
  196. package/build/cli/dump.d.ts +0 -2
  197. package/build/cli/dump.d.ts.map +0 -1
  198. package/build/cli/dump.js +0 -47
  199. package/build/cli/tile.set.local.d.ts +0 -12
  200. package/build/cli/tile.set.local.d.ts.map +0 -1
  201. package/build/cli/tile.set.local.js +0 -39
  202. package/build/router.d.ts +0 -15
  203. package/build/router.d.ts.map +0 -1
  204. package/build/router.js +0 -49
  205. package/build/routes/api.d.ts +0 -5
  206. package/build/routes/api.d.ts.map +0 -1
  207. package/build/routes/api.js +0 -16
  208. package/build/routes/esri/rest.d.ts +0 -10
  209. package/build/routes/esri/rest.d.ts.map +0 -1
  210. package/build/routes/esri/rest.js +0 -87
  211. package/build/routes/response.d.ts +0 -4
  212. package/build/routes/response.d.ts.map +0 -1
  213. package/build/routes/response.js +0 -3
  214. package/build/routes/tile.d.ts +0 -3
  215. package/build/routes/tile.d.ts.map +0 -1
  216. package/build/routes/tile.etag.d.ts +0 -11
  217. package/build/routes/tile.etag.d.ts.map +0 -1
  218. package/build/routes/tile.etag.js +0 -29
  219. package/build/routes/tile.js +0 -27
  220. package/build/source.tracer.d.ts.map +0 -1
  221. package/build/tiff.cache.d.ts +0 -17
  222. package/build/tiff.cache.d.ts.map +0 -1
  223. package/build/tiff.cache.js +0 -45
  224. package/build/tile.set.cache.d.ts +0 -21
  225. package/build/tile.set.cache.d.ts.map +0 -1
  226. package/build/tile.set.cache.js +0 -100
  227. package/build/tile.set.d.ts +0 -4
  228. package/build/tile.set.d.ts.map +0 -1
  229. package/build/tile.set.js +0 -1
  230. package/build/tile.set.raster.d.ts +0 -49
  231. package/build/tile.set.raster.d.ts.map +0 -1
  232. package/build/tile.set.raster.js +0 -186
  233. package/build/tile.set.vector.d.ts +0 -25
  234. package/build/tile.set.vector.d.ts.map +0 -1
  235. package/build/tile.set.vector.js +0 -71
  236. package/build/validate.d.ts +0 -16
  237. package/build/validate.d.ts.map +0 -1
  238. package/build/validate.js +0 -31
  239. package/src/__tests__/route.test.ts +0 -24
  240. package/src/__tests__/tiff.cache.test.ts +0 -73
  241. package/src/__tests__/tile.cache.key.test.ts +0 -56
  242. package/src/__tests__/tile.set.cache.test.ts +0 -146
  243. package/src/__tests__/tile.set.test.ts +0 -12
  244. package/src/__tests__/xyz.test.ts +0 -362
  245. package/src/api.key.ts +0 -23
  246. package/src/cli/dump.ts +0 -61
  247. package/src/cli/tile.set.local.ts +0 -51
  248. package/src/router.ts +0 -58
  249. package/src/routes/api.ts +0 -19
  250. package/src/routes/esri/rest.ts +0 -90
  251. package/src/routes/response.ts +0 -4
  252. package/src/routes/tile.etag.ts +0 -36
  253. package/src/routes/tile.ts +0 -23
  254. package/src/tiff.cache.ts +0 -51
  255. package/src/tile.set.cache.ts +0 -111
  256. package/src/tile.set.raster.ts +0 -228
  257. package/src/tile.set.ts +0 -4
  258. package/src/tile.set.vector.ts +0 -79
  259. package/src/validate.ts +0 -32
@@ -33,14 +33,16 @@ o.spec('/v1/fonts', () => {
33
33
  });
34
34
 
35
35
  o('should return empty list if no fonts found', async () => {
36
- const res = await fontList();
36
+ const res = await fontList(mockRequest('/v1/fonts.json'));
37
37
  o(res.status).equals(200);
38
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');
39
41
  });
40
42
 
41
43
  o('should return 404 if no assets defined', async () => {
42
44
  delete process.env[Env.AssetLocation];
43
- const res = await fontList();
45
+ const res = await fontList(mockRequest('/v1/fonts.json'));
44
46
  o(res.status).equals(404);
45
47
  });
46
48
 
@@ -50,7 +52,7 @@ o.spec('/v1/fonts', () => {
50
52
  fsa.write('memory://fonts/Roboto Thin/256-512.pbf', Buffer.from('')),
51
53
  fsa.write('memory://fonts/Roboto Black/0-255.pbf', Buffer.from('')),
52
54
  ]);
53
- const res = await fontList();
55
+ const res = await fontList(mockRequest('/v1/fonts.json'));
54
56
  o(res.status).equals(200);
55
57
  o(res.header('content-type')).equals('application/json');
56
58
  o(res.header('content-encoding')).equals(undefined);
@@ -64,8 +66,21 @@ o.spec('/v1/fonts', () => {
64
66
  o(res255.status).equals(200);
65
67
  o(res255.header('content-type')).equals('application/x-protobuf');
66
68
  o(res255.header('content-encoding')).equals(undefined);
69
+ o(res255.header('etag')).notEquals(undefined);
70
+ o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
67
71
 
68
72
  const res404 = await handler.router.handle(mockRequest('/v1/fonts/Roboto Thin/256-512.pbf'));
69
73
  o(res404.status).equals(404);
70
74
  });
75
+
76
+ o('should get the correct utf8 font', async () => {
77
+ await fsa.write('memory://fonts/🦄 🌈/0-255.pbf', Buffer.from(''));
78
+
79
+ const res255 = await handler.router.handle(mockRequest('/v1/fonts/🦄 🌈/0-255.pbf'));
80
+ o(res255.status).equals(200);
81
+ o(res255.header('content-type')).equals('application/x-protobuf');
82
+ o(res255.header('content-encoding')).equals(undefined);
83
+ o(res255.header('etag')).notEquals(undefined);
84
+ o(res255.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
85
+ });
71
86
  });
@@ -1,12 +1,13 @@
1
- import { GoogleTms } from '@basemaps/geo';
1
+ import { Config, 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 { TileSets } from '../../tile.set.cache.js';
8
- import { TileSetRaster } from '../../tile.set.raster.js';
9
- import { getTestBuffer, Health, TestTiles } from '../health.js';
7
+ import { FakeData } from '../../__tests__/config.data.js';
8
+
9
+ import { getTestBuffer, healthGet, TestTiles } from '../health.js';
10
+ import { TileXyzRaster } from '../tile.xyz.raster.js';
10
11
 
11
12
  const ctx: LambdaHttpRequest = new LambdaAlbRequest(
12
13
  {
@@ -20,13 +21,16 @@ const ctx: LambdaHttpRequest = new LambdaAlbRequest(
20
21
  LogConfig.get(),
21
22
  );
22
23
 
23
- o.spec('health', async () => {
24
+ o.spec('/v1/health', async () => {
24
25
  o.specTimeout(1000);
25
26
  const sandbox = sinon.createSandbox();
27
+ const config = new ConfigProviderMemory();
26
28
 
27
- const tileSet = new TileSetRaster('health', GoogleTms);
29
+ const fakeTileSet = FakeData.tileSetRaster('health');
28
30
  o.beforeEach(() => {
29
- sandbox.stub(TileSets, 'get').resolves(tileSet);
31
+ config.objects.clear();
32
+ Config.setConfigProvider(config);
33
+ config.put(fakeTileSet);
30
34
  });
31
35
 
32
36
  o.afterEach(() => {
@@ -36,10 +40,10 @@ o.spec('health', async () => {
36
40
  o('Should return bad response', async () => {
37
41
  // Given ... a bad get tile response
38
42
  const BadResponse = new LambdaHttpResponse(500, 'Can not get Tile Set.');
39
- sandbox.stub(tileSet, 'tile').resolves(BadResponse);
43
+ sandbox.stub(TileXyzRaster, 'tile').resolves(BadResponse);
40
44
 
41
45
  // When ...
42
- const res = await Health(ctx);
46
+ const res = await healthGet(ctx);
43
47
 
44
48
  // Then ...
45
49
  o(res.status).equals(500);
@@ -59,12 +63,12 @@ o.spec('health', async () => {
59
63
 
60
64
  o('Should give a 200 response', async () => {
61
65
  // Given ... a series good get tile response
62
- const callback = sandbox.stub(tileSet, 'tile');
66
+ const callback = sandbox.stub(TileXyzRaster, 'tile');
63
67
  callback.onCall(0).resolves(Response1);
64
68
  callback.onCall(1).resolves(Response2);
65
69
 
66
70
  // When ...
67
- const res = await Health(ctx);
71
+ const res = await healthGet(ctx);
68
72
 
69
73
  // Then ...
70
74
  o(res.status).equals(200);
@@ -73,12 +77,12 @@ o.spec('health', async () => {
73
77
 
74
78
  o('Should return mis-match tile response', async () => {
75
79
  // Given ... a bad get tile response for second get tile
76
- const callback = sandbox.stub(tileSet, 'tile');
80
+ const callback = sandbox.stub(TileXyzRaster, 'tile');
77
81
  callback.onCall(0).resolves(Response1);
78
82
  callback.onCall(1).resolves(Response1);
79
83
 
80
84
  // When ...
81
- const res = await Health(ctx);
85
+ const res = await healthGet(ctx);
82
86
 
83
87
  // Then ...
84
88
  o(res.status).equals(500);
@@ -36,6 +36,8 @@ o.spec('/v1/sprites', () => {
36
36
  o(res.status).equals(200);
37
37
  o(res.header('content-type')).equals('application/json');
38
38
  o(res.header('content-encoding')).equals(undefined);
39
+ o(res.header('etag')).notEquals(undefined);
40
+ o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
39
41
 
40
42
  o(JSON.parse(Buffer.from(res.body, 'base64').toString())).deepEquals({ test: true });
41
43
  });
@@ -48,6 +50,8 @@ o.spec('/v1/sprites', () => {
48
50
  const res = await handler.router.handle(mockRequest('/v1/sprites/topographic@2x.png'));
49
51
  o(res.status).equals(200);
50
52
  o(res.header('content-type')).equals('image/png');
53
+ o(res.header('etag')).notEquals(undefined);
54
+ o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
51
55
  });
52
56
 
53
57
  o('should detect gziped files and set content-encoding', async () => {
@@ -58,7 +62,8 @@ o.spec('/v1/sprites', () => {
58
62
  o(res.status).equals(200);
59
63
  o(res.header('content-type')).equals('application/json');
60
64
  o(res.header('content-encoding')).equals('gzip');
61
-
65
+ o(res.header('etag')).notEquals(undefined);
66
+ o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
62
67
  o(JSON.parse(gunzipSync(Buffer.from(res.body, 'base64')).toString())).deepEquals({ test: true });
63
68
  });
64
69
  });
@@ -0,0 +1,145 @@
1
+ import { Config, ConfigProviderMemory } from '@basemaps/config';
2
+ import { Env } from '@basemaps/shared';
3
+ import o from 'ospec';
4
+ import { handler } from '../../index.js';
5
+ import { CoSources } from '../../util/source.cache.js';
6
+ import { FakeData } from '../../__tests__/config.data.js';
7
+ import { Api, mockRequest, mockUrlRequest } from '../../__tests__/xyz.util.js';
8
+
9
+ o.spec('/v1/tiles/:tileSet/:tileMatrix/tile.json', () => {
10
+ const config = new ConfigProviderMemory();
11
+ o.before(() => {
12
+ process.env[Env.PublicUrlBase] = 'https://tiles.test';
13
+ Config.setConfigProvider(config);
14
+ });
15
+
16
+ o.beforeEach(() => {
17
+ config.objects.clear();
18
+ CoSources.cache.clear();
19
+ });
20
+
21
+ o('should 404 if invalid url is given', async () => {
22
+ const request = mockRequest('/v1/tiles/tile.json', 'get', Api.header);
23
+
24
+ const res = await handler.router.handle(request);
25
+ o(res.status).equals(404);
26
+ });
27
+
28
+ o('should support utf8 tilesets', async () => {
29
+ const request = mockUrlRequest('/v1/tiles/🦄 🌈/NZTM2000Quad/tile.json', `api=${Api.key}`);
30
+ o(request.path).equals('/v1/tiles/%F0%9F%A6%84%20%F0%9F%8C%88/NZTM2000Quad/tile.json');
31
+
32
+ const fakeTileSet = FakeData.tileSetRaster('🦄 🌈');
33
+ config.put(fakeTileSet);
34
+
35
+ const res = await handler.router.handle(request);
36
+ o(res.status).equals(200);
37
+ });
38
+
39
+ o('should serve tile json for tile_set', async () => {
40
+ const request = mockRequest('/v1/tiles/aerial/NZTM2000Quad/tile.json', 'get', Api.header);
41
+ const fakeTileSet = FakeData.tileSetRaster('aerial');
42
+ config.put(fakeTileSet);
43
+
44
+ const res = await handler.router.handle(request);
45
+ o(res.status).equals(200);
46
+ o(res.header('cache-control')).equals('no-store');
47
+
48
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
49
+ o(JSON.parse(body)).deepEquals({
50
+ tiles: [`https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.webp?api=${Api.key}`],
51
+ tilejson: '3.0.0',
52
+ });
53
+ });
54
+
55
+ o('should use the correct format', async () => {
56
+ const request = mockRequest('/v1/tiles/aerial/NZTM2000Quad/tile.json', 'get', Api.header);
57
+ request.query.set('format', 'jpeg');
58
+
59
+ const fakeTileSet = FakeData.tileSetRaster('aerial');
60
+ config.put(fakeTileSet);
61
+
62
+ const res = await handler.router.handle(request);
63
+ o(res.status).equals(200);
64
+ o(res.header('cache-control')).equals('no-store');
65
+
66
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
67
+ o(JSON.parse(body)).deepEquals({
68
+ tiles: [`https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.jpeg?api=${Api.key}`],
69
+ tilejson: '3.0.0',
70
+ });
71
+ });
72
+
73
+ o('should use the correct format when multiple set', async () => {
74
+ const request = mockRequest('/v1/tiles/aerial/NZTM2000Quad/tile.json', 'get', Api.header);
75
+ request.query.append('format', 'png');
76
+ request.query.append('format', 'jpeg');
77
+
78
+ const fakeTileSet = FakeData.tileSetRaster('aerial');
79
+ config.put(fakeTileSet);
80
+
81
+ const res = await handler.router.handle(request);
82
+ o(res.status).equals(200);
83
+ o(res.header('cache-control')).equals('no-store');
84
+
85
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
86
+ o(JSON.parse(body)).deepEquals({
87
+ tiles: [`https://tiles.test/v1/tiles/aerial/NZTM2000Quad/{z}/{x}/{y}.png?api=${Api.key}`],
88
+ tilejson: '3.0.0',
89
+ });
90
+ });
91
+
92
+ o('should serve vector tiles', async () => {
93
+ const request = mockRequest('/v1/tiles/topographic/EPSG:3857/tile.json', 'get', Api.header);
94
+ const fakeTileSet = FakeData.tileSetVector('topographic');
95
+ config.put(fakeTileSet);
96
+
97
+ const res = await handler.router.handle(request);
98
+ o(res.status).equals(200);
99
+
100
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
101
+ o(JSON.parse(body)).deepEquals({
102
+ tiles: [`https://tiles.test/v1/tiles/topographic/WebMercatorQuad/{z}/{x}/{y}.pbf?api=${Api.key}`],
103
+ tilejson: '3.0.0',
104
+ });
105
+ });
106
+
107
+ o('should serve vector tiles with min/max zoom', async () => {
108
+ const fakeTileSet = FakeData.tileSetVector('fake-vector');
109
+ fakeTileSet.maxZoom = 15;
110
+ fakeTileSet.minZoom = 3;
111
+ config.put(fakeTileSet);
112
+ const request = mockRequest('/v1/tiles/fake-vector/WebMercatorQuad/tile.json', 'get', Api.header);
113
+
114
+ const res = await handler.router.handle(request);
115
+ o(res.status).equals(200);
116
+
117
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
118
+ o(JSON.parse(body)).deepEquals({
119
+ tiles: [`https://tiles.test/v1/tiles/fake-vector/WebMercatorQuad/{z}/{x}/{y}.pbf?api=${Api.key}`],
120
+ maxzoom: 15,
121
+ minzoom: 3,
122
+ tilejson: '3.0.0',
123
+ });
124
+ });
125
+
126
+ o('should serve convert zoom to tile matrix', async () => {
127
+ const fakeTileSet = FakeData.tileSetVector('fake-vector');
128
+ fakeTileSet.maxZoom = 15;
129
+ fakeTileSet.minZoom = 1;
130
+ config.put(fakeTileSet);
131
+
132
+ const request = mockRequest('/v1/tiles/fake-vector/NZTM2000Quad/tile.json', 'get', Api.header);
133
+
134
+ const res = await handler.router.handle(request);
135
+ o(res.status).equals(200);
136
+
137
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
138
+ o(JSON.parse(body)).deepEquals({
139
+ tiles: [`https://tiles.test/v1/tiles/fake-vector/NZTM2000Quad/{z}/{x}/{y}.pbf?api=${Api.key}`],
140
+ maxzoom: 13,
141
+ minzoom: 0,
142
+ tilejson: '3.0.0',
143
+ });
144
+ });
145
+ });
@@ -0,0 +1,105 @@
1
+ import { Config, StyleJson } from '@basemaps/config';
2
+ import { Env } from '@basemaps/shared';
3
+ import o from 'ospec';
4
+ import { createSandbox } from 'sinon';
5
+ import { handler } from '../../index.js';
6
+ import { Api, mockRequest } from '../../__tests__/xyz.util.js';
7
+
8
+ o.spec('/v1/styles', () => {
9
+ const host = 'https://tiles.test';
10
+ const sandbox = createSandbox();
11
+
12
+ o.before(() => {
13
+ process.env[Env.PublicUrlBase] = host;
14
+ });
15
+ // o.beforeEach(() => {});
16
+ o.afterEach(() => sandbox.restore());
17
+ o('should not found style json', async () => {
18
+ const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', Api.header);
19
+
20
+ sandbox.stub(Config.Style, 'get').resolves(null);
21
+
22
+ const res = await handler.router.handle(request);
23
+ o(res.status).equals(404);
24
+ });
25
+
26
+ o('should serve style json', async () => {
27
+ const request = mockRequest('/v1/tiles/topographic/Google/style/topographic.json', 'get', Api.header);
28
+
29
+ const fakeStyle: StyleJson = {
30
+ version: 8,
31
+ id: 'test',
32
+ name: 'topographic',
33
+ sources: {
34
+ basemaps_vector: {
35
+ type: 'vector',
36
+ url: `/vector`,
37
+ },
38
+ basemaps_raster: {
39
+ type: 'raster',
40
+ tiles: [`/raster`],
41
+ },
42
+ basemaps_raster_encode: {
43
+ type: 'raster',
44
+ tiles: [`/raster/{z}/{x}/{y}.webp`], // Shouldn't encode the {}
45
+ },
46
+ test_vector: {
47
+ type: 'vector',
48
+ url: 'vector.url.co.nz',
49
+ },
50
+ test_raster: {
51
+ type: 'raster',
52
+ tiles: ['raster.url.co.nz'],
53
+ },
54
+ },
55
+ layers: [
56
+ {
57
+ layout: {
58
+ visibility: 'visible',
59
+ },
60
+ paint: {
61
+ 'background-color': 'rgba(206, 229, 242, 1)',
62
+ },
63
+ id: 'Background',
64
+ type: 'background',
65
+ minzoom: 0,
66
+ },
67
+ ],
68
+ glyphs: '/glyphs',
69
+ sprite: '/sprite',
70
+ metadata: { id: 'test' },
71
+ };
72
+
73
+ const fakeRecord = {
74
+ id: 'st_topographic_production',
75
+ name: 'topographic',
76
+ style: fakeStyle,
77
+ };
78
+
79
+ sandbox.stub(Config.Style, 'get').resolves(fakeRecord as any);
80
+
81
+ const res = await handler.router.handle(request);
82
+ o(res.status).equals(200);
83
+ o(res.header('content-type')).equals('application/json');
84
+ o(res.header('cache-control')).equals('no-store');
85
+
86
+ const body = Buffer.from(res.body ?? '', 'base64').toString();
87
+ fakeStyle.sources.basemaps_vector = {
88
+ type: 'vector',
89
+ url: `${host}/vector?api=${Api.key}`,
90
+ };
91
+ fakeStyle.sources.basemaps_raster = {
92
+ type: 'raster',
93
+ tiles: [`${host}/raster?api=${Api.key}`],
94
+ };
95
+ fakeStyle.sources.basemaps_raster_encode = {
96
+ type: 'raster',
97
+ tiles: [`${host}/raster/{z}/{x}/{y}.webp?api=${Api.key}`],
98
+ };
99
+
100
+ fakeStyle.sprite = `${host}/sprite`;
101
+ fakeStyle.glyphs = `${host}/glyphs`;
102
+
103
+ o(JSON.parse(body)).deepEquals(fakeStyle);
104
+ });
105
+ });
@@ -1,40 +1,50 @@
1
- import { ImageFormat } from '@basemaps/geo';
2
- import { LogConfig } from '@basemaps/shared';
3
- import { LambdaHttpRequest, LambdaUrlRequest } from '@linzjs/lambda';
4
- import { Context } from 'aws-lambda';
1
+ import { Config } from '@basemaps/shared';
5
2
  import o from 'ospec';
6
- import { getImageFormats } from '../tile.wmts.js';
7
-
8
- o.spec('GetImageFormats', () => {
9
- function newRequest(path: string, query: string): LambdaHttpRequest {
10
- return new LambdaUrlRequest(
11
- {
12
- requestContext: { http: { method: 'GET' } },
13
- headers: {},
14
- rawPath: path,
15
- rawQueryString: query,
16
- isBase64Encoded: false,
17
- } as any,
18
- {} as Context,
19
- LogConfig.get(),
20
- );
21
- }
22
-
23
- o('should parse all formats', () => {
24
- const req = newRequest('/v1/blank', 'format=png&format=jpeg');
25
- const formats = getImageFormats(req);
26
- o(formats).deepEquals([ImageFormat.Png, ImageFormat.Jpeg]);
27
- });
3
+ import { createSandbox } from 'sinon';
4
+ import { handler } from '../../index.js';
5
+ import { Imagery2193, Imagery3857, Provider, TileSetAerial } from '../../__tests__/config.data.js';
6
+ import { Api, mockUrlRequest } from '../../__tests__/xyz.util.js';
28
7
 
29
- o('should ignore bad formats', () => {
30
- const req = newRequest('/v1/blank', 'format=fake&format=mvt');
31
- const formats = getImageFormats(req);
32
- o(formats).equals(undefined);
8
+ o.spec('WMTSRouting', () => {
9
+ const sandbox = createSandbox();
10
+ o.afterEach(() => {
11
+ sandbox.restore();
33
12
  });
34
13
 
35
- o('should de-dupe formats', () => {
36
- const req = newRequest('/v1/blank', 'format=png&format=jpeg&format=png&format=jpeg&format=png&format=jpeg');
37
- const formats = getImageFormats(req);
38
- o(formats).deepEquals([ImageFormat.Png, ImageFormat.Jpeg]);
14
+ o('should default to the aerial layer', async () => {
15
+ const imagery = new Map();
16
+ imagery.set(Imagery3857.id, Imagery3857);
17
+ imagery.set(Imagery2193.id, Imagery2193);
18
+
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));
22
+
23
+ const req = mockUrlRequest('/v1/tiles/WMTSCapabilities.xml', `format=png&api=${Api.key}`);
24
+ const res = await handler.router.handle(req);
25
+
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
+ o(res.status).equals(200);
39
+ const lines = Buffer.from(res.body, 'base64').toString().split('\n');
40
+
41
+ const titles = lines.filter((f) => f.startsWith(' <ows:Title>')).map((f) => f.trim());
42
+
43
+ o(titles).deepEquals([
44
+ '<ows:Title>Aerial Imagery</ows:Title>',
45
+ '<ows:Title>Ōtorohanga 0.1m Urban Aerial Photos (2021)</ows:Title>',
46
+ '<ows:Title>Google Maps Compatible for the World</ows:Title>',
47
+ '<ows:Title>LINZ NZTM2000 Map Tile Grid V2</ows:Title>',
48
+ ]);
39
49
  });
40
50
  });
@@ -0,0 +1,119 @@
1
+ import { Config, ConfigProviderMemory } from '@basemaps/config';
2
+ import { LogConfig } from '@basemaps/shared';
3
+ import { round } from '@basemaps/test/build/rounding.js';
4
+ import o from 'ospec';
5
+ import sinon from 'sinon';
6
+ import { handler } from '../../index.js';
7
+ import { Etag } from '../../util/etag.js';
8
+ import { FakeData } from '../../__tests__/config.data.js';
9
+ import { Api, mockRequest } from '../../__tests__/xyz.util.js';
10
+
11
+ const sandbox = sinon.createSandbox();
12
+
13
+ const TileSetNames = ['aerial', 'aerial:ōtorohanga_urban_2021_0-1m_RGB', '01FYWKAJ86W9P7RWM1VB62KD0H'];
14
+ o.spec('/v1/tiles', () => {
15
+ const config = new ConfigProviderMemory();
16
+
17
+ o.beforeEach(() => {
18
+ LogConfig.get().level = 'silent';
19
+ Config.setConfigProvider(config);
20
+ config.objects.clear();
21
+
22
+ for (const tileSetName of TileSetNames) config.put(FakeData.tileSetRaster(tileSetName));
23
+
24
+ sandbox.stub(Etag, 'key').returns('fakeEtag');
25
+ });
26
+
27
+ o.afterEach(() => {
28
+ sandbox.restore();
29
+ });
30
+
31
+ o('should export handler', async () => {
32
+ const base = await import('../../index.js');
33
+ o(typeof base.handler).equals('function');
34
+ });
35
+
36
+ TileSetNames.forEach((tileSetName) => {
37
+ o(`should generate a tile/v1/tiles/${tileSetName}/global-mercator/0/0/0.png`, async () => {
38
+ const request = mockRequest(`/v1/tiles/${tileSetName}/global-mercator/0/0/0.png`, 'get', Api.header);
39
+ const res = await handler.router.handle(request);
40
+ o(res.status).equals(200);
41
+ o(res.header('content-type')).equals('image/png');
42
+ o(res.header('eTaG')).equals('fakeEtag');
43
+
44
+ // Validate the session information has been set correctly
45
+ o(request.logContext['tileSet']).equals(tileSetName);
46
+ o(request.logContext['xyz']).deepEquals({ x: 0, y: 0, z: 0 });
47
+ o(round(request.logContext['location'])).deepEquals({ lat: 0, lon: 0 });
48
+ });
49
+ });
50
+
51
+ o('should generate a tile 0,0,0 for webp', async () => {
52
+ const request = mockRequest('/v1/tiles/aerial/3857/0/0/0.webp', 'get', Api.header);
53
+ const res = await handler.router.handle(request);
54
+ o(res.status).equals(200);
55
+ o(res.header('content-type')).equals('image/webp');
56
+ o(res.header('eTaG')).equals('fakeEtag');
57
+ // o(res.body).equals(rasterMockBuffer.toString('base64'));
58
+
59
+ // Validate the session information has been set correctly
60
+ o(request.logContext['xyz']).deepEquals({ x: 0, y: 0, z: 0 });
61
+ o(round(request.logContext['location'])).deepEquals({ lat: 0, lon: 0 });
62
+ });
63
+
64
+ ['png', 'webp', 'jpeg', 'avif'].forEach((fmt) => {
65
+ o(`should 200 with empty ${fmt} if a tile is out of bounds`, async () => {
66
+ // tiler.tile = async () => [];
67
+ const res = await handler.router.handle(
68
+ mockRequest(`/v1/tiles/aerial/global-mercator/0/0/0.${fmt}`, 'get', Api.header),
69
+ );
70
+ o(res.status).equals(200);
71
+ o(res.header('content-type')).equals(`image/${fmt}`);
72
+ o(res.header('etag')).notEquals(undefined);
73
+ o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
74
+ // o(rasterMock.calls.length).equals(1);
75
+ });
76
+ });
77
+
78
+ o('should 304 if a tile is not modified', async () => {
79
+ const key = 'fakeEtag';
80
+ const request = mockRequest('/v1/tiles/aerial/global-mercator/0/0/0.png', 'get', {
81
+ 'if-none-match': key,
82
+ ...Api.header,
83
+ });
84
+ const res = await handler.router.handle(request);
85
+ o(res.status).equals(304);
86
+ o(res.header('eTaG')).equals(undefined);
87
+
88
+ o(request.logContext['cache']).deepEquals({ match: key, hit: true });
89
+ });
90
+
91
+ o('should 404 if a tile is outside of the range', async () => {
92
+ const res = await handler.router.handle(
93
+ mockRequest('/v1/tiles/aerial/global-mercator/25/0/0.png', 'get', Api.header),
94
+ );
95
+ o(res.status).equals(404);
96
+
97
+ const resB = await handler.router.handle(mockRequest('/v1/tiles/aerial/2193/17/0/0.png', 'get', Api.header));
98
+ o(resB.status).equals(404);
99
+ });
100
+
101
+ o('should support utf8 tilesets', async () => {
102
+ const fakeTileSet = FakeData.tileSetRaster('🦄 🌈');
103
+ config.put(fakeTileSet);
104
+ const req = mockRequest('/v1/tiles/🦄 🌈/global-mercator/0/0/0.png', 'get', Api.header);
105
+ o(req.path).equals('/v1/tiles/%F0%9F%A6%84%20%F0%9F%8C%88/global-mercator/0/0/0.png');
106
+ const res = await handler.router.handle(req);
107
+ o(res.status).equals(200);
108
+ o(res.header('content-type')).equals('image/png');
109
+ o(res.header('etag')).notEquals(undefined);
110
+ o(res.header('cache-control')).equals('public, max-age=604800, stale-while-revalidate=86400');
111
+ });
112
+
113
+ ['/favicon.ico', '/index.html', '/foo/bar'].forEach((path) => {
114
+ o('should 404 on invalid paths: ' + path, async () => {
115
+ const res = await handler.router.handle(mockRequest(path, 'get', Api.header));
116
+ o(res.status).equals(404);
117
+ });
118
+ });
119
+ });