@basemaps/lambda-tiler 6.30.0 → 6.32.1

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 (276) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/build/__tests__/config.data.d.ts +6 -1
  3. package/build/__tests__/config.data.d.ts.map +1 -1
  4. package/build/__tests__/config.data.js +33 -2
  5. package/build/__tests__/config.data.js.map +1 -1
  6. package/build/__tests__/index.test.js +3 -13
  7. package/build/__tests__/index.test.js.map +1 -1
  8. package/build/__tests__/wmts.capability.test.js +88 -6
  9. package/build/__tests__/wmts.capability.test.js.map +1 -1
  10. package/build/__tests__/xyz.util.d.ts +7 -9
  11. package/build/__tests__/xyz.util.d.ts.map +1 -1
  12. package/build/__tests__/xyz.util.js +14 -20
  13. package/build/__tests__/xyz.util.js.map +1 -1
  14. package/build/index.d.ts +0 -2
  15. package/build/index.d.ts.map +1 -1
  16. package/build/index.js +57 -26
  17. package/build/index.js.map +1 -1
  18. package/build/routes/__tests__/attribution.test.js +350 -400
  19. package/build/routes/__tests__/attribution.test.js.map +1 -1
  20. package/build/routes/__tests__/fonts.test.js +11 -5
  21. package/build/routes/__tests__/fonts.test.js.map +1 -1
  22. package/build/routes/__tests__/health.test.js +16 -13
  23. package/build/routes/__tests__/health.test.js.map +1 -1
  24. package/build/routes/__tests__/sprites.test.js +6 -0
  25. package/build/routes/__tests__/sprites.test.js.map +1 -1
  26. package/build/routes/__tests__/tile.json.test.d.ts +2 -0
  27. package/build/routes/__tests__/tile.json.test.d.ts.map +1 -0
  28. package/build/routes/__tests__/tile.json.test.js +124 -0
  29. package/build/routes/__tests__/tile.json.test.js.map +1 -0
  30. package/build/routes/__tests__/tile.style.json.test.d.ts +2 -0
  31. package/build/routes/__tests__/tile.style.json.test.d.ts.map +1 -0
  32. package/build/routes/__tests__/tile.style.json.test.js +95 -0
  33. package/build/routes/__tests__/tile.style.json.test.js.map +1 -0
  34. package/build/routes/__tests__/wmts.test.js +5 -44
  35. package/build/routes/__tests__/wmts.test.js.map +1 -1
  36. package/build/{__tests__ → routes/__tests__}/xyz.test.d.ts +0 -0
  37. package/build/routes/__tests__/xyz.test.d.ts.map +1 -0
  38. package/build/routes/__tests__/xyz.test.js +99 -0
  39. package/build/routes/__tests__/xyz.test.js.map +1 -0
  40. package/build/routes/attribution.d.ts +7 -5
  41. package/build/routes/attribution.d.ts.map +1 -1
  42. package/build/routes/attribution.js +49 -91
  43. package/build/routes/attribution.js.map +1 -1
  44. package/build/routes/fonts.d.ts +1 -1
  45. package/build/routes/fonts.d.ts.map +1 -1
  46. package/build/routes/fonts.js +30 -15
  47. package/build/routes/fonts.js.map +1 -1
  48. package/build/routes/health.d.ts +3 -3
  49. package/build/routes/health.d.ts.map +1 -1
  50. package/build/routes/health.js +15 -13
  51. package/build/routes/health.js.map +1 -1
  52. package/build/routes/imagery.d.ts.map +1 -1
  53. package/build/routes/imagery.js +11 -10
  54. package/build/routes/imagery.js.map +1 -1
  55. package/build/routes/ping.d.ts +3 -0
  56. package/build/routes/ping.d.ts.map +1 -0
  57. package/build/routes/ping.js +7 -0
  58. package/build/routes/ping.js.map +1 -0
  59. package/build/routes/sprites.d.ts.map +1 -1
  60. package/build/routes/sprites.js +16 -10
  61. package/build/routes/sprites.js.map +1 -1
  62. package/build/routes/tile.json.d.ts +7 -1
  63. package/build/routes/tile.json.d.ts.map +1 -1
  64. package/build/routes/tile.json.js +18 -21
  65. package/build/routes/tile.json.js.map +1 -1
  66. package/build/routes/tile.style.json.d.ts +6 -1
  67. package/build/routes/tile.style.json.d.ts.map +1 -1
  68. package/build/routes/tile.style.json.js +10 -13
  69. package/build/routes/tile.style.json.js.map +1 -1
  70. package/build/routes/tile.wmts.d.ts +9 -3
  71. package/build/routes/tile.wmts.d.ts.map +1 -1
  72. package/build/routes/tile.wmts.js +26 -35
  73. package/build/routes/tile.wmts.js.map +1 -1
  74. package/build/routes/tile.xyz.d.ts +14 -4
  75. package/build/routes/tile.xyz.d.ts.map +1 -1
  76. package/build/routes/tile.xyz.js +21 -17
  77. package/build/routes/tile.xyz.js.map +1 -1
  78. package/build/routes/tile.xyz.raster.d.ts +11 -0
  79. package/build/routes/tile.xyz.raster.d.ts.map +1 -0
  80. package/build/routes/tile.xyz.raster.js +90 -0
  81. package/build/routes/tile.xyz.raster.js.map +1 -0
  82. package/build/routes/tile.xyz.vector.d.ts +8 -0
  83. package/build/routes/tile.xyz.vector.d.ts.map +1 -0
  84. package/build/routes/tile.xyz.vector.js +46 -0
  85. package/build/routes/tile.xyz.vector.js.map +1 -0
  86. package/build/routes/version.d.ts +3 -0
  87. package/build/routes/version.d.ts.map +1 -0
  88. package/build/routes/version.js +9 -0
  89. package/build/routes/version.js.map +1 -0
  90. package/build/util/__test__/validate.test.d.ts +2 -0
  91. package/build/util/__test__/validate.test.d.ts.map +1 -0
  92. package/build/util/__test__/validate.test.js +66 -0
  93. package/build/util/__test__/validate.test.js.map +1 -0
  94. package/build/{cotar.cache.d.ts → util/cotar.serve.d.ts} +3 -8
  95. package/build/util/cotar.serve.d.ts.map +1 -0
  96. package/build/util/cotar.serve.js +41 -0
  97. package/build/util/cotar.serve.js.map +1 -0
  98. package/build/util/etag.d.ts +6 -0
  99. package/build/util/etag.d.ts.map +1 -0
  100. package/build/util/etag.js +20 -0
  101. package/build/util/etag.js.map +1 -0
  102. package/build/util/response.d.ts +4 -0
  103. package/build/util/response.d.ts.map +1 -0
  104. package/build/util/response.js +4 -0
  105. package/build/util/response.js.map +1 -0
  106. package/build/util/source.cache.d.ts +28 -0
  107. package/build/util/source.cache.d.ts.map +1 -0
  108. package/build/util/source.cache.js +53 -0
  109. package/build/util/source.cache.js.map +1 -0
  110. package/build/{source.tracer.d.ts → util/source.tracer.d.ts} +1 -0
  111. package/build/util/source.tracer.d.ts.map +1 -0
  112. package/build/{source.tracer.js → util/source.tracer.js} +3 -0
  113. package/build/util/source.tracer.js.map +1 -0
  114. package/build/util/swapping.lru.d.ts +21 -0
  115. package/build/util/swapping.lru.d.ts.map +1 -0
  116. package/build/util/swapping.lru.js +56 -0
  117. package/build/util/swapping.lru.js.map +1 -0
  118. package/build/util/validate.d.ts +46 -0
  119. package/build/util/validate.d.ts.map +1 -0
  120. package/build/util/validate.js +107 -0
  121. package/build/util/validate.js.map +1 -0
  122. package/build/wmts.capability.d.ts +1 -1
  123. package/build/wmts.capability.d.ts.map +1 -1
  124. package/build/wmts.capability.js +24 -8
  125. package/build/wmts.capability.js.map +1 -1
  126. package/dist/index.js +78 -76
  127. package/dist/node_modules/.package-lock.json +1 -1
  128. package/dist/package-lock.json +2 -2
  129. package/dist/package.json +1 -1
  130. package/package.json +10 -10
  131. package/src/__tests__/config.data.ts +41 -3
  132. package/src/__tests__/index.test.ts +3 -19
  133. package/src/__tests__/wmts.capability.test.ts +109 -6
  134. package/src/__tests__/xyz.util.ts +18 -21
  135. package/src/index.ts +66 -29
  136. package/src/routes/__tests__/attribution.test.ts +356 -403
  137. package/src/routes/__tests__/fonts.test.ts +11 -5
  138. package/src/routes/__tests__/health.test.ts +17 -13
  139. package/src/routes/__tests__/sprites.test.ts +6 -1
  140. package/src/routes/__tests__/tile.json.test.ts +145 -0
  141. package/src/routes/__tests__/tile.style.json.test.ts +105 -0
  142. package/src/routes/__tests__/wmts.test.ts +5 -55
  143. package/src/routes/__tests__/xyz.test.ts +119 -0
  144. package/src/routes/attribution.ts +59 -111
  145. package/src/routes/fonts.ts +29 -15
  146. package/src/routes/health.ts +17 -16
  147. package/src/routes/imagery.ts +10 -9
  148. package/src/routes/ping.ts +8 -0
  149. package/src/routes/sprites.ts +16 -10
  150. package/src/routes/tile.json.ts +24 -18
  151. package/src/routes/tile.style.json.ts +15 -12
  152. package/src/routes/tile.wmts.ts +30 -31
  153. package/src/routes/tile.xyz.raster.ts +106 -0
  154. package/src/routes/tile.xyz.ts +31 -16
  155. package/src/routes/tile.xyz.vector.ts +47 -0
  156. package/src/routes/version.ts +8 -0
  157. package/src/util/__test__/validate.test.ts +74 -0
  158. package/src/util/cotar.serve.ts +46 -0
  159. package/src/util/etag.ts +20 -0
  160. package/src/util/response.ts +4 -0
  161. package/src/util/source.cache.ts +71 -0
  162. package/src/{source.tracer.ts → util/source.tracer.ts} +4 -0
  163. package/src/util/swapping.lru.ts +63 -0
  164. package/src/util/validate.ts +126 -0
  165. package/src/wmts.capability.ts +30 -13
  166. package/tsconfig.tsbuildinfo +1 -1
  167. package/build/__tests__/route.test.d.ts +0 -2
  168. package/build/__tests__/route.test.d.ts.map +0 -1
  169. package/build/__tests__/route.test.js +0 -21
  170. package/build/__tests__/route.test.js.map +0 -1
  171. package/build/__tests__/tiff.cache.test.d.ts +0 -2
  172. package/build/__tests__/tiff.cache.test.d.ts.map +0 -1
  173. package/build/__tests__/tiff.cache.test.js +0 -59
  174. package/build/__tests__/tiff.cache.test.js.map +0 -1
  175. package/build/__tests__/tile.cache.key.test.d.ts +0 -2
  176. package/build/__tests__/tile.cache.key.test.d.ts.map +0 -1
  177. package/build/__tests__/tile.cache.key.test.js +0 -49
  178. package/build/__tests__/tile.cache.key.test.js.map +0 -1
  179. package/build/__tests__/tile.set.cache.test.d.ts +0 -2
  180. package/build/__tests__/tile.set.cache.test.d.ts.map +0 -1
  181. package/build/__tests__/tile.set.cache.test.js +0 -72
  182. package/build/__tests__/tile.set.cache.test.js.map +0 -1
  183. package/build/__tests__/tile.set.test.d.ts +0 -2
  184. package/build/__tests__/tile.set.test.d.ts.map +0 -1
  185. package/build/__tests__/tile.set.test.js +0 -12
  186. package/build/__tests__/tile.set.test.js.map +0 -1
  187. package/build/__tests__/xyz.test.d.ts.map +0 -1
  188. package/build/__tests__/xyz.test.js +0 -275
  189. package/build/__tests__/xyz.test.js.map +0 -1
  190. package/build/api.key.d.ts +0 -2
  191. package/build/api.key.d.ts.map +0 -1
  192. package/build/api.key.js +0 -24
  193. package/build/api.key.js.map +0 -1
  194. package/build/cli/dump.d.ts +0 -2
  195. package/build/cli/dump.d.ts.map +0 -1
  196. package/build/cli/dump.js +0 -48
  197. package/build/cli/dump.js.map +0 -1
  198. package/build/cli/tile.set.local.d.ts +0 -12
  199. package/build/cli/tile.set.local.d.ts.map +0 -1
  200. package/build/cli/tile.set.local.js +0 -40
  201. package/build/cli/tile.set.local.js.map +0 -1
  202. package/build/cotar.cache.d.ts.map +0 -1
  203. package/build/cotar.cache.js +0 -50
  204. package/build/cotar.cache.js.map +0 -1
  205. package/build/router.d.ts +0 -15
  206. package/build/router.d.ts.map +0 -1
  207. package/build/router.js +0 -50
  208. package/build/router.js.map +0 -1
  209. package/build/routes/api.d.ts +0 -4
  210. package/build/routes/api.d.ts.map +0 -1
  211. package/build/routes/api.js +0 -14
  212. package/build/routes/api.js.map +0 -1
  213. package/build/routes/esri/rest.d.ts +0 -10
  214. package/build/routes/esri/rest.d.ts.map +0 -1
  215. package/build/routes/esri/rest.js +0 -88
  216. package/build/routes/esri/rest.js.map +0 -1
  217. package/build/routes/response.d.ts +0 -4
  218. package/build/routes/response.d.ts.map +0 -1
  219. package/build/routes/response.js +0 -4
  220. package/build/routes/response.js.map +0 -1
  221. package/build/routes/tile.d.ts +0 -3
  222. package/build/routes/tile.d.ts.map +0 -1
  223. package/build/routes/tile.etag.d.ts +0 -11
  224. package/build/routes/tile.etag.d.ts.map +0 -1
  225. package/build/routes/tile.etag.js +0 -30
  226. package/build/routes/tile.etag.js.map +0 -1
  227. package/build/routes/tile.js +0 -28
  228. package/build/routes/tile.js.map +0 -1
  229. package/build/source.tracer.d.ts.map +0 -1
  230. package/build/source.tracer.js.map +0 -1
  231. package/build/tiff.cache.d.ts +0 -17
  232. package/build/tiff.cache.d.ts.map +0 -1
  233. package/build/tiff.cache.js +0 -46
  234. package/build/tiff.cache.js.map +0 -1
  235. package/build/tile.set.cache.d.ts +0 -20
  236. package/build/tile.set.cache.d.ts.map +0 -1
  237. package/build/tile.set.cache.js +0 -72
  238. package/build/tile.set.cache.js.map +0 -1
  239. package/build/tile.set.d.ts +0 -4
  240. package/build/tile.set.d.ts.map +0 -1
  241. package/build/tile.set.js +0 -2
  242. package/build/tile.set.js.map +0 -1
  243. package/build/tile.set.raster.d.ts +0 -49
  244. package/build/tile.set.raster.d.ts.map +0 -1
  245. package/build/tile.set.raster.js +0 -189
  246. package/build/tile.set.raster.js.map +0 -1
  247. package/build/tile.set.vector.d.ts +0 -18
  248. package/build/tile.set.vector.d.ts.map +0 -1
  249. package/build/tile.set.vector.js +0 -54
  250. package/build/tile.set.vector.js.map +0 -1
  251. package/build/validate.d.ts +0 -16
  252. package/build/validate.d.ts.map +0 -1
  253. package/build/validate.js +0 -32
  254. package/build/validate.js.map +0 -1
  255. package/src/__tests__/route.test.ts +0 -24
  256. package/src/__tests__/tiff.cache.test.ts +0 -73
  257. package/src/__tests__/tile.cache.key.test.ts +0 -56
  258. package/src/__tests__/tile.set.cache.test.ts +0 -80
  259. package/src/__tests__/tile.set.test.ts +0 -12
  260. package/src/__tests__/xyz.test.ts +0 -318
  261. package/src/api.key.ts +0 -23
  262. package/src/cli/dump.ts +0 -61
  263. package/src/cli/tile.set.local.ts +0 -51
  264. package/src/cotar.cache.ts +0 -54
  265. package/src/router.ts +0 -58
  266. package/src/routes/api.ts +0 -15
  267. package/src/routes/esri/rest.ts +0 -90
  268. package/src/routes/response.ts +0 -4
  269. package/src/routes/tile.etag.ts +0 -36
  270. package/src/routes/tile.ts +0 -23
  271. package/src/tiff.cache.ts +0 -51
  272. package/src/tile.set.cache.ts +0 -84
  273. package/src/tile.set.raster.ts +0 -230
  274. package/src/tile.set.ts +0 -4
  275. package/src/tile.set.vector.ts +0 -61
  276. package/src/validate.ts +0 -32
@@ -1,4 +1,4 @@
1
- import { ConfigImagery, ConfigLayer, ConfigProvider, TileSetType } from '@basemaps/config';
1
+ import { ConfigTileSet, TileSetType } from '@basemaps/config';
2
2
  import {
3
3
  AttributionCollection,
4
4
  AttributionItem,
@@ -7,32 +7,20 @@ import {
7
7
  GoogleTms,
8
8
  NamedBounds,
9
9
  Stac,
10
- StacCollection,
11
10
  StacExtent,
12
11
  TileMatrixSet,
13
12
  } from '@basemaps/geo';
14
- import {
15
- CompositeError,
16
- Config,
17
- extractYearRangeFromName,
18
- fsa,
19
- Projection,
20
- setNameAndProjection,
21
- tileAttributionFromPath,
22
- titleizeImageryName,
23
- } from '@basemaps/shared';
13
+ import { Config, extractYearRangeFromName, Projection, titleizeImageryName } from '@basemaps/shared';
24
14
  import { BBox, MultiPolygon, multiPolygonToWgs84, Pair, union, Wgs84 } from '@linzjs/geojson';
25
15
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
26
- import { createHash } from 'crypto';
27
- import { Router } from '../router.js';
28
- import { TileSets } from '../tile.set.cache.js';
29
- import { TileSetRaster } from '../tile.set.raster.js';
16
+
17
+ import { Etag } from '../util/etag.js';
18
+ import { NotFound, NotModified } from '../util/response.js';
19
+ import { Validate } from '../util/validate.js';
30
20
 
31
21
  /** Amount to pad imagery bounds to avoid fragmenting polygons */
32
22
  const SmoothPadding = 1 + 1e-10; // about 1/100th of a millimeter at equator
33
23
 
34
- const NotFound = new LambdaHttpResponse(404, 'Not Found');
35
-
36
24
  const Precision = 10 ** 8;
37
25
 
38
26
  /**
@@ -74,90 +62,38 @@ function createCoordinates(bbox: BBox, files: NamedBounds[], proj: Projection):
74
62
  return multiPolygonToWgs84(coordinates, roundToWgs84);
75
63
  }
76
64
 
77
- async function readStac(uri: string): Promise<StacCollection | null> {
78
- try {
79
- return await fsa.readJson<StacCollection>(uri);
80
- } catch (err) {
81
- if (CompositeError.isCompositeError(err) && err.code < 500) {
82
- return null;
83
- }
84
- throw err;
85
- }
86
- }
87
-
88
- export function createAttributionCollection(
89
- tileSet: TileSetRaster,
90
- stac: StacCollection | null | undefined,
91
- imagery: ConfigImagery,
92
- layer: ConfigLayer,
93
- host: ConfigProvider,
94
- extent: StacExtent,
95
- ): AttributionCollection {
96
- const tileMatrix = tileSet.tileMatrix;
97
- return {
98
- stac_version: Stac.Version,
99
- license: stac?.license ?? Stac.License,
100
- id: imagery.id,
101
- providers: stac?.providers ?? [
102
- { name: host.serviceProvider.name, url: host.serviceProvider.site, roles: ['host'] },
103
- ],
104
- title: stac?.title ?? titleizeImageryName(imagery.name),
105
- description: stac?.description ?? 'No description',
106
- extent,
107
- links: [],
108
- summaries: {
109
- 'linz:zoom': {
110
- min: TileMatrixSet.convertZoomLevel(layer.minZoom ? layer.minZoom : 0, GoogleTms, tileMatrix, true),
111
- max: TileMatrixSet.convertZoomLevel(layer.maxZoom ? layer.maxZoom : 32, GoogleTms, tileMatrix, true),
112
- },
113
- 'linz:priority': [1000 + tileSet.tileSet.layers.indexOf(layer)],
114
- },
115
- };
116
- }
117
-
118
65
  /**
119
66
  * Build a Single File STAC for the given TileSet.
120
67
  *
121
68
  * For now this is the minimal set required for attribution. This can be embellished later with
122
69
  * links and assets for a more comprehensive STAC file.
123
70
  */
124
- async function tileSetAttribution(tileSet: TileSetRaster): Promise<AttributionStac | null> {
125
- const proj = Projection.get(tileSet.tileMatrix);
126
- const stacFiles = new Map<string, Promise<StacCollection | null>>();
71
+ async function tileSetAttribution(
72
+ req: LambdaHttpRequest,
73
+ tileSet: ConfigTileSet,
74
+ tileMatrix: TileMatrixSet,
75
+ ): Promise<AttributionStac | null> {
76
+ const proj = Projection.get(tileMatrix);
127
77
  const cols: AttributionCollection[] = [];
128
78
  const items: AttributionItem[] = [];
129
79
 
130
- // read all stac files in parallel
131
- for (const layer of tileSet.tileSet.layers) {
132
- const imgId = layer[proj.epsg.code];
133
- if (imgId == null) continue;
134
- const im = tileSet.imagery.get(imgId);
135
- if (im == null) continue;
136
- if (stacFiles.get(im.uri) == null) {
137
- stacFiles.set(im.uri, readStac(fsa.join(im.uri, 'collection.json')));
138
- }
139
- }
80
+ const imagery = await Config.getAllImagery(tileSet.layers, [tileMatrix.projection]);
140
81
 
141
82
  const host = await Config.Provider.get(Config.Provider.id('linz'));
142
83
  if (host == null) return null;
143
84
 
144
- for (const layer of tileSet.tileSet.layers) {
85
+ for (const layer of tileSet.layers) {
145
86
  const imgId = layer[proj.epsg.code];
146
87
  if (imgId == null) continue;
147
- const im = tileSet.imagery.get(imgId);
88
+ const im = imagery.get(imgId);
148
89
  if (im == null) continue;
149
- const stac = await stacFiles.get(im.uri);
150
90
 
151
91
  const bbox = proj.boundsToWgs84BoundingBox(im.bounds).map(roundNumber) as BBox;
152
92
 
153
- let interval: [string, string][] | undefined = stac?.extent.temporal.interval;
154
- if (interval == null) {
155
- const years = extractYearRangeFromName(im.name);
156
- if (years[0] === -1) {
157
- throw new Error('Missing date in imagery name: ' + im.name);
158
- }
159
- interval = [years.map((y) => `${y}-01-01T00:00:00Z`) as [string, string]];
160
- }
93
+ const years = extractYearRangeFromName(im.name);
94
+ if (years[0] === -1) throw new Error('Missing date in imagery name: ' + im.name);
95
+ const interval = [years.map((y) => `${y}-01-01T00:00:00Z`) as [string, string]];
96
+
161
97
  const extent: StacExtent = { spatial: { bbox: [bbox] }, temporal: { interval } };
162
98
 
163
99
  items.push({
@@ -168,68 +104,80 @@ async function tileSetAttribution(tileSet: TileSetRaster): Promise<AttributionSt
168
104
  assets: {},
169
105
  links: [],
170
106
  bbox,
171
- geometry: {
172
- type: 'MultiPolygon',
173
- coordinates: createCoordinates(bbox, im.files, proj),
174
- },
107
+ geometry: { type: 'MultiPolygon', coordinates: createCoordinates(bbox, im.files, proj) },
175
108
  properties: {
176
- title: titleizeImageryName(im.name),
109
+ title: im.title ?? titleizeImageryName(im.name),
110
+ category: im.category,
177
111
  datetime: null,
178
112
  start_datetime: interval[0][0],
179
113
  end_datetime: interval[0][1],
180
114
  },
181
115
  });
182
116
 
183
- cols.push(createAttributionCollection(tileSet, stac, im, layer, host, extent));
117
+ const zoomMin = TileMatrixSet.convertZoomLevel(layer.minZoom ? layer.minZoom : 0, GoogleTms, tileMatrix, true);
118
+ const zoomMax = TileMatrixSet.convertZoomLevel(layer.maxZoom ? layer.maxZoom : 32, GoogleTms, tileMatrix, true);
119
+ cols.push({
120
+ stac_version: Stac.Version,
121
+ license: Stac.License,
122
+ id: im.id,
123
+ providers: [{ name: host.serviceProvider.name, url: host.serviceProvider.site, roles: ['host'] }],
124
+ title: im.title ?? titleizeImageryName(im.name),
125
+ description: 'No description',
126
+ extent,
127
+ links: [],
128
+ summaries: {
129
+ 'linz:category': im.category,
130
+ 'linz:zoom': { min: zoomMin, max: zoomMax },
131
+ 'linz:priority': [1000 + tileSet.layers.indexOf(layer)],
132
+ },
133
+ });
184
134
  }
185
135
  return {
186
136
  id: tileSet.id,
187
137
  type: 'FeatureCollection',
188
138
  stac_version: Stac.Version,
189
139
  stac_extensions: ['single-file-stac'],
190
- title: tileSet.title,
191
- description: tileSet.description,
140
+ title: tileSet.title ?? 'No title',
141
+ description: tileSet.description ?? 'No Description',
192
142
  features: items,
193
143
  collections: cols,
194
144
  links: [],
195
145
  };
196
146
  }
197
147
 
148
+ export interface TileAttributionGet {
149
+ Params: {
150
+ tileSet: string;
151
+ tileMatrix: string;
152
+ };
153
+ }
154
+
198
155
  /**
199
156
  * Create a LambdaHttpResponse for a attribution request
200
157
  */
201
- export async function attribution(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
202
- const action = Router.action(req);
203
- const data = tileAttributionFromPath(action.rest);
204
- if (data == null) return NotFound;
205
- setNameAndProjection(req, data);
158
+ export async function tileAttributionGet(req: LambdaHttpRequest<TileAttributionGet>): Promise<LambdaHttpResponse> {
159
+ const tileMatrix = Validate.getTileMatrixSet(req.params.tileMatrix);
160
+ if (tileMatrix == null) throw new LambdaHttpResponse(404, 'Tile Matrix not found');
206
161
 
207
162
  req.timer.start('tileset:load');
208
- const tileSet = await TileSets.get(data.name, data.tileMatrix);
163
+ const tileSet = await Config.TileSet.get(Config.TileSet.id(req.params.tileSet));
209
164
  req.timer.end('tileset:load');
210
- if (tileSet == null || tileSet.type === TileSetType.Vector) return NotFound;
165
+ if (tileSet == null || tileSet.type === TileSetType.Vector) return NotFound();
211
166
 
212
- const cacheKey = createHash('sha256').update(JSON.stringify(tileSet.tileSet)).digest('base64');
213
-
214
- const ifNoneMatch = req.header(HttpHeader.IfNoneMatch);
215
- if (ifNoneMatch != null && ifNoneMatch.indexOf(cacheKey) > -1) {
216
- req.set('cache', { key: cacheKey, hit: true, match: ifNoneMatch });
217
- return new LambdaHttpResponse(304, 'Not modified');
218
- }
167
+ const cacheKey = Etag.key(tileSet);
168
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
219
169
 
220
170
  req.timer.start('stac:load');
221
- const attributions = await tileSetAttribution(tileSet);
171
+ const attributions = await tileSetAttribution(req, tileSet, tileMatrix);
222
172
  req.timer.end('stac:load');
223
173
 
224
- if (attributions == null) return NotFound;
174
+ if (attributions == null) return NotFound();
225
175
 
226
176
  const response = new LambdaHttpResponse(200, 'ok');
227
177
 
228
178
  response.header(HttpHeader.ETag, cacheKey);
229
- // Keep fresh for one day; otherwise use cache but refresh cache for next time
230
- response.header(HttpHeader.CacheControl, 'public, max-age=86400, stale-while-revalidate=604800');
231
-
179
+ // Keep fresh for 7 days; otherwise use cache but refresh cache for next time
180
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
232
181
  response.json(attributions);
233
-
234
182
  return response;
235
183
  }
@@ -1,9 +1,10 @@
1
1
  import { Env } from '@basemaps/shared';
2
2
  import { fsa } from '@chunkd/fs';
3
- import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
3
+ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import path from 'path';
5
- import { serveFromCotar } from '../cotar.cache.js';
6
- import { NotFound } from './response.js';
5
+ import { isGzip, serveFromCotar } from '../util/cotar.serve.js';
6
+ import { Etag } from '../util/etag.js';
7
+ import { NotFound, NotModified } from '../util/response.js';
7
8
 
8
9
  interface FontGet {
9
10
  Params: { fontStack: string; range: string };
@@ -11,20 +12,27 @@ interface FontGet {
11
12
 
12
13
  export async function fontGet(req: LambdaHttpRequest<FontGet>): Promise<LambdaHttpResponse> {
13
14
  const assetLocation = Env.get(Env.AssetLocation);
14
- if (assetLocation == null) return NotFound;
15
+ if (assetLocation == null) return NotFound();
15
16
 
16
- const fontStack = decodeURIComponent(req.params.fontStack);
17
- const targetFile = path.join('fonts', fontStack, req.params.range) + '.pbf';
18
-
19
- if (assetLocation.endsWith('.tar.co')) return serveFromCotar(assetLocation, targetFile, 'application/x-protobuf');
17
+ const targetFile = path.join('fonts', req.params.fontStack, req.params.range) + '.pbf';
18
+ if (assetLocation.endsWith('.tar.co')) {
19
+ return serveFromCotar(req, assetLocation, targetFile, 'application/x-protobuf');
20
+ }
20
21
 
21
22
  try {
22
23
  const filePath = fsa.join(assetLocation, targetFile);
23
24
  const buf = await fsa.read(filePath);
24
25
 
25
- return LambdaHttpResponse.ok().buffer(buf, 'application/x-protobuf');
26
+ const cacheKey = Etag.key(buf);
27
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
28
+
29
+ const response = LambdaHttpResponse.ok().buffer(buf, 'application/x-protobuf');
30
+ response.header(HttpHeader.ETag, cacheKey);
31
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
32
+ if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
33
+ return response;
26
34
  } catch (e: any) {
27
- if (e.code === 404) return NotFound;
35
+ if (e.code === 404) return NotFound();
28
36
  throw e;
29
37
  }
30
38
  }
@@ -44,19 +52,25 @@ export async function getFonts(fontPath: string): Promise<string[]> {
44
52
  return [...fonts].sort();
45
53
  }
46
54
 
47
- export async function fontList(): Promise<LambdaHttpResponse> {
55
+ export async function fontList(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
48
56
  const assetLocation = Env.get(Env.AssetLocation);
49
- if (assetLocation == null) return NotFound;
57
+ if (assetLocation == null) return NotFound();
50
58
 
51
- if (assetLocation.endsWith('.tar.co')) return serveFromCotar(assetLocation, 'fonts.json', 'application/json');
59
+ if (assetLocation.endsWith('.tar.co')) return serveFromCotar(req, assetLocation, 'fonts.json', 'application/json');
52
60
 
53
61
  try {
54
62
  const filePath = fsa.join(assetLocation, '/fonts');
55
63
  const fonts = await getFonts(filePath);
56
64
 
57
- return LambdaHttpResponse.ok().buffer(JSON.stringify(fonts), 'application/json');
65
+ const cacheKey = Etag.key(fonts);
66
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
67
+
68
+ const response = LambdaHttpResponse.ok().buffer(JSON.stringify(fonts), 'application/json');
69
+ response.header(HttpHeader.ETag, cacheKey);
70
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
71
+ return response;
58
72
  } catch (e: any) {
59
- if (e.code === 404) return NotFound;
73
+ if (e.code === 404) return NotFound();
60
74
  throw e;
61
75
  }
62
76
  }
@@ -1,27 +1,29 @@
1
- import { GoogleTms, Nztm2000QuadTms, ImageFormat } from '@basemaps/geo';
2
- import { TileDataXyz, TileType } from '@basemaps/shared';
1
+ import { Config, ConfigTileSetRaster } from '@basemaps/config';
2
+ import { GoogleTms, ImageFormat, Nztm2000QuadTms } from '@basemaps/geo';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
4
  import * as fs from 'fs';
5
5
  import * as path from 'path';
6
6
  import PixelMatch from 'pixelmatch';
7
7
  import Sharp from 'sharp';
8
8
  import url from 'url';
9
- import { TileSets } from '../tile.set.cache.js';
9
+ import { TileXyz } from '../util/validate.js';
10
+ import { TileXyzRaster } from './tile.xyz.raster.js';
10
11
 
11
- interface TestTile extends TileDataXyz {
12
+ interface TestTile extends TileXyz {
12
13
  buf?: Buffer;
13
14
  }
14
15
 
15
16
  export const TestTiles: TestTile[] = [
16
- { type: TileType.Tile, name: 'health', tileMatrix: GoogleTms, ext: ImageFormat.Png, x: 252, y: 156, z: 8 },
17
- { type: TileType.Tile, name: 'health', tileMatrix: Nztm2000QuadTms, ext: ImageFormat.Png, x: 30, y: 33, z: 6 },
17
+ { tileSet: 'health', tileMatrix: GoogleTms, tileType: ImageFormat.Png, tile: { x: 252, y: 156, z: 8 } },
18
+ { tileSet: 'health', tileMatrix: Nztm2000QuadTms, tileType: ImageFormat.Png, tile: { x: 30, y: 33, z: 6 } },
18
19
  ];
19
20
  const TileSize = 256;
20
21
 
21
22
  export async function getTestBuffer(test: TestTile): Promise<Buffer> {
22
23
  if (Buffer.isBuffer(test.buf)) return test.buf;
24
+ const tile = test.tile;
23
25
 
24
- const expectedFile = `static/expected_tile_${test.tileMatrix.identifier}_${test.x}_${test.y}_z${test.z}.${test.ext}`;
26
+ const expectedFile = `static/expected_tile_${test.tileMatrix.identifier}_${tile.x}_${tile.y}_z${tile.z}.${test.tileType}`;
25
27
  // Initiate test img buffer if not defined
26
28
  try {
27
29
  return await fs.promises.readFile(expectedFile);
@@ -33,7 +35,9 @@ export async function getTestBuffer(test: TestTile): Promise<Buffer> {
33
35
  }
34
36
 
35
37
  export async function updateExpectedTile(test: TestTile, newTileData: Buffer, difference: Buffer): Promise<void> {
36
- const expectedFileName = `static/expected_tile_${test.tileMatrix.identifier}_${test.x}_${test.y}_z${test.z}.${test.ext}`;
38
+ const tile = test.tile;
39
+
40
+ const expectedFileName = `static/expected_tile_${test.tileMatrix.identifier}_${tile.x}_${tile.y}_z${tile.z}.${test.tileType}`;
37
41
  await fs.promises.writeFile(expectedFileName, newTileData);
38
42
  const imgPng = await Sharp(difference, { raw: { width: TileSize, height: TileSize, channels: 4 } })
39
43
  .png()
@@ -48,12 +52,12 @@ export async function updateExpectedTile(test: TestTile, newTileData: Buffer, di
48
52
  *
49
53
  * @throws LambdaHttpResponse for failure health test
50
54
  */
51
- export async function Health(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
55
+ export async function healthGet(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
56
+ const tileSet = await Config.TileSet.get(Config.TileSet.id('health'));
57
+ if (tileSet == null) throw new LambdaHttpResponse(500, 'TileSet: "health" not found');
52
58
  for (const test of TestTiles) {
53
- const tileSet = await TileSets.get('health', test.tileMatrix);
54
- if (tileSet == null) throw new LambdaHttpResponse(500, 'TileSet: "health" not found');
55
59
  // Get the parse response tile to raw buffer
56
- const response = await tileSet.tile(req, test);
60
+ const response = await TileXyzRaster.tile(req, tileSet as ConfigTileSetRaster, test);
57
61
  if (response.status !== 200) return new LambdaHttpResponse(500, response.statusDescription);
58
62
  if (!Buffer.isBuffer(response._body)) throw new LambdaHttpResponse(500, 'Not a Buffer response content.');
59
63
  const resImgBuffer = await Sharp(response._body).raw().toBuffer();
@@ -68,10 +72,7 @@ export async function Health(req: LambdaHttpRequest): Promise<LambdaHttpResponse
68
72
  if (missMatchedPixels) {
69
73
  /** Uncomment this to overwite the expected files */
70
74
  // await updateExpectedTile(test, response._body as Buffer, outputBuffer);
71
- req.log.error(
72
- { missMatchedPixels, projection: test.tileMatrix.identifier, xyz: { x: test.x, y: test.y, z: test.z } },
73
- 'Health:MissMatch',
74
- );
75
+ req.log.error({ missMatchedPixels, projection: test.tileMatrix.identifier, xyz: test.tile }, 'Health:MissMatch');
75
76
  return new LambdaHttpResponse(500, 'TileSet does not match.');
76
77
  }
77
78
  }
@@ -1,11 +1,11 @@
1
1
  import { Config } from '@basemaps/config';
2
2
  import { fsa } from '@basemaps/shared';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
- import { createHash } from 'crypto';
5
4
  import { promisify } from 'util';
6
5
  import { gzip } from 'zlib';
7
- import { NotModified } from './response.js';
8
- import { TileEtag } from './tile.etag.js';
6
+ import { isGzip } from '../util/cotar.serve.js';
7
+ import { Etag } from '../util/etag.js';
8
+ import { NotFound, NotModified } from '../util/response.js';
9
9
 
10
10
  const gzipP = promisify(gzip);
11
11
 
@@ -32,27 +32,28 @@ interface ImageryGet {
32
32
  */
33
33
  export async function imageryGet(req: LambdaHttpRequest<ImageryGet>): Promise<LambdaHttpResponse> {
34
34
  const requestedFile = req.params.fileName;
35
- if (!isAllowedFile(requestedFile)) return new LambdaHttpResponse(404, 'Not found');
35
+ if (!isAllowedFile(requestedFile)) return NotFound();
36
36
 
37
37
  const imagery = await Config.Imagery.get(Config.Imagery.id(req.params.imageryId));
38
- if (imagery == null) return new LambdaHttpResponse(404, 'Not found');
38
+ if (imagery == null) return NotFound();
39
39
 
40
40
  const targetPath = fsa.join(imagery.uri, requestedFile);
41
41
 
42
42
  try {
43
43
  const buf = await fsa.read(targetPath);
44
- const cacheKey = createHash('sha256').update(buf).digest('base64');
45
44
 
46
- if (TileEtag.isNotModified(req, cacheKey)) return NotModified;
45
+ const cacheKey = Etag.key(buf);
46
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
47
47
 
48
48
  const response = new LambdaHttpResponse(200, 'ok');
49
49
  response.header(HttpHeader.ETag, cacheKey);
50
50
  response.header(HttpHeader.ContentEncoding, 'gzip');
51
- response.buffer(await gzipP(buf), 'application/json');
51
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
52
+ response.buffer(isGzip(buf) ? buf : await gzipP(buf), 'application/json');
52
53
  req.set('bytes', buf.byteLength);
53
54
  return response;
54
55
  } catch (e) {
55
56
  req.log.warn({ targetPath }, 'ImageryMetadata:Failed');
56
- return new LambdaHttpResponse(404, 'Not found');
57
+ return NotFound();
57
58
  }
58
59
  }
@@ -0,0 +1,8 @@
1
+ import { LambdaHttpResponse, HttpHeader } from '@linzjs/lambda';
2
+
3
+ const OkResponse = new LambdaHttpResponse(200, 'ok');
4
+ OkResponse.header(HttpHeader.CacheControl, 'no-store');
5
+
6
+ export async function pingGet(): Promise<LambdaHttpResponse> {
7
+ return OkResponse;
8
+ }
@@ -1,9 +1,10 @@
1
1
  import { Env } from '@basemaps/shared';
2
2
  import { fsa } from '@chunkd/fs';
3
3
  import path from 'path';
4
- import { LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
- import { NotFound } from './response.js';
6
- import { isGzip, serveFromCotar } from '../cotar.cache.js';
4
+ import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
+ import { NotFound, NotModified } from '../util/response.js';
6
+ import { isGzip, serveFromCotar } from '../util/cotar.serve.js';
7
+ import { Etag } from '../util/etag.js';
7
8
 
8
9
  interface SpriteGet {
9
10
  Params: {
@@ -17,25 +18,30 @@ Extensions.set('.json', 'application/json');
17
18
 
18
19
  export async function spriteGet(req: LambdaHttpRequest<SpriteGet>): Promise<LambdaHttpResponse> {
19
20
  const assetLocation = Env.get(Env.AssetLocation);
20
- if (assetLocation == null) return NotFound;
21
+ if (assetLocation == null) return NotFound();
21
22
 
22
23
  const extension = path.extname(req.params.spriteName);
23
24
  const mimeType = Extensions.get(extension);
24
- if (mimeType == null) return NotFound;
25
+ if (mimeType == null) return NotFound();
25
26
 
26
27
  const targetFile = fsa.join('sprites', req.params.spriteName);
27
- if (assetLocation.endsWith('.tar.co')) return serveFromCotar(assetLocation, targetFile, mimeType);
28
+ if (assetLocation.endsWith('.tar.co')) return serveFromCotar(req, assetLocation, targetFile, mimeType);
28
29
 
29
30
  try {
30
31
  const filePath = fsa.join(assetLocation, targetFile);
31
32
  req.set('target', filePath);
32
33
 
33
34
  const buf = await fsa.read(filePath);
34
- const res = LambdaHttpResponse.ok().buffer(buf, mimeType);
35
- if (isGzip(buf)) res.header('content-encoding', 'gzip');
36
- return res;
35
+ const cacheKey = Etag.key(buf);
36
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
37
+
38
+ const response = LambdaHttpResponse.ok().buffer(buf, mimeType);
39
+ response.header(HttpHeader.ETag, cacheKey);
40
+ response.header(HttpHeader.CacheControl, 'public, max-age=604800, stale-while-revalidate=86400');
41
+ if (isGzip(buf)) response.header(HttpHeader.ContentEncoding, 'gzip');
42
+ return response;
37
43
  } catch (e: any) {
38
- if (e.code === 404) return NotFound;
44
+ if (e.code === 404) return NotFound();
39
45
  throw e;
40
46
  }
41
47
  }
@@ -1,35 +1,41 @@
1
1
  import { GoogleTms, TileJson, TileMatrixSet } from '@basemaps/geo';
2
- import { Env, extractTileMatrixSet } from '@basemaps/shared';
2
+ import { Config, Env } from '@basemaps/shared';
3
3
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
4
- import { Router } from '../router.js';
5
- import { TileSets } from '../tile.set.cache.js';
6
- import { NotFound } from './response.js';
4
+ import { NotFound } from '../util/response.js';
5
+ import { Validate } from '../util/validate.js';
6
+
7
+ export interface TileJsonGet {
8
+ Params: {
9
+ tileSet: string;
10
+ tileMatrix: string;
11
+ };
12
+ }
7
13
 
8
- export async function tileJson(req: LambdaHttpRequest): Promise<LambdaHttpResponse> {
9
- const { version, rest, name } = Router.action(req);
10
- if (rest.length !== 3) return NotFound;
14
+ export async function tileJsonGet(req: LambdaHttpRequest<TileJsonGet>): Promise<LambdaHttpResponse> {
15
+ const tileMatrix = Validate.getTileMatrixSet(req.params.tileMatrix);
16
+ if (tileMatrix == null) return NotFound();
11
17
 
12
- const tileMatrix = extractTileMatrixSet(rest[1]);
13
- if (tileMatrix == null) return NotFound;
18
+ const apiKey = Validate.apiKey(req);
14
19
 
15
20
  req.timer.start('tileset:load');
16
- const tileSet = await TileSets.get(rest[0], tileMatrix);
21
+ const tileSet = await Config.TileSet.get(Config.TileSet.id(req.params.tileSet));
17
22
  req.timer.end('tileset:load');
18
- if (tileSet == null) return NotFound;
23
+ if (tileSet == null) return NotFound();
24
+
25
+ const format = Validate.getRequestedFormats(req) ?? [tileSet.format];
19
26
 
20
- const apiKey = Router.apiKey(req);
21
27
  const host = Env.get(Env.PublicUrlBase) ?? '';
22
28
 
23
29
  const tileUrl =
24
- [host, version, name, tileSet.fullName, tileMatrix.identifier, '{z}', '{x}', '{y}'].join('/') +
25
- `.${tileSet.format}?api=${apiKey}`;
30
+ [host, 'v1', 'tiles', tileSet.name, tileMatrix.identifier, '{z}', '{x}', '{y}'].join('/') +
31
+ `.${format[0]}?api=${apiKey}`;
26
32
 
27
33
  const tileJson: TileJson = { tiles: [tileUrl], tilejson: '3.0.0' };
28
- const maxZoom = TileMatrixSet.convertZoomLevel(tileSet.tileSet.maxZoom ?? 30, GoogleTms, tileMatrix, true);
29
- const minZoom = TileMatrixSet.convertZoomLevel(tileSet.tileSet.minZoom ?? 0, GoogleTms, tileMatrix, true);
34
+ const maxZoom = TileMatrixSet.convertZoomLevel(tileSet.maxZoom ?? 30, GoogleTms, tileMatrix, true);
35
+ const minZoom = TileMatrixSet.convertZoomLevel(tileSet.minZoom ?? 0, GoogleTms, tileMatrix, true);
30
36
 
31
- if (tileSet.tileSet.maxZoom) tileJson.maxzoom = maxZoom;
32
- if (tileSet.tileSet.minZoom) tileJson.minzoom = minZoom;
37
+ if (tileSet.maxZoom) tileJson.maxzoom = maxZoom;
38
+ if (tileSet.minZoom) tileJson.minzoom = minZoom;
33
39
 
34
40
  const json = JSON.stringify(tileJson);
35
41
  const data = Buffer.from(json);
@@ -2,11 +2,10 @@ import { Sources, StyleJson } from '@basemaps/config';
2
2
  import { Config, Env } from '@basemaps/shared';
3
3
  import { fsa } from '@chunkd/fs';
4
4
  import { HttpHeader, LambdaHttpRequest, LambdaHttpResponse } from '@linzjs/lambda';
5
- import { createHash } from 'crypto';
6
5
  import { URL } from 'url';
7
- import { Router } from '../router.js';
8
- import { NotFound, NotModified } from './response.js';
9
- import { TileEtag } from './tile.etag.js';
6
+ import { NotFound, NotModified } from '../util/response.js';
7
+ import { Validate } from '../util/validate.js';
8
+ import { Etag } from '../util/etag.js';
10
9
 
11
10
  /**
12
11
  * Convert relative URLS into a full hostname url
@@ -54,23 +53,27 @@ export function convertStyleJson(style: StyleJson, apiKey: string): StyleJson {
54
53
  } as StyleJson;
55
54
  }
56
55
 
57
- export async function styleJson(req: LambdaHttpRequest, fileName: string): Promise<LambdaHttpResponse> {
58
- const apiKey = Router.apiKey(req);
59
- if (apiKey == null) return new LambdaHttpResponse(400, 'Invalid API Key.');
60
- const styleName = fileName.split('.json')[0];
56
+ export interface StyleGet {
57
+ Params: {
58
+ styleName: string;
59
+ };
60
+ }
61
+
62
+ export async function styleJsonGet(req: LambdaHttpRequest<StyleGet>): Promise<LambdaHttpResponse> {
63
+ const apiKey = Validate.apiKey(req);
64
+ const styleName = req.params.styleName;
61
65
 
62
66
  // Get style Config from db
63
67
  const dbId = Config.Style.id(styleName);
64
68
  const styleConfig = await Config.Style.get(dbId);
65
- if (styleConfig == null) return NotFound;
69
+ if (styleConfig == null) return NotFound();
66
70
 
67
71
  // Prepare sources and add linz source
68
72
  const style = convertStyleJson(styleConfig.style, apiKey);
69
73
  const data = Buffer.from(JSON.stringify(style));
70
74
 
71
- const cacheKey = createHash('sha256').update(data).digest('base64');
72
-
73
- if (TileEtag.isNotModified(req, cacheKey)) return NotModified;
75
+ const cacheKey = Etag.key(data);
76
+ if (Etag.isNotModified(req, cacheKey)) return NotModified();
74
77
 
75
78
  const response = new LambdaHttpResponse(200, 'ok');
76
79
  response.header(HttpHeader.ETag, cacheKey);