@digitraffic/common 2022.10.25-1 → 2022.10.31-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 (283) hide show
  1. package/.editorconfig +9 -0
  2. package/.eslintignore +4 -0
  3. package/.eslintrc.json +27 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/workflows/build.yml +36 -0
  6. package/.github/workflows/eslint.yml +38 -0
  7. package/.github/workflows/mirror.yml +15 -0
  8. package/.gitignore +29 -0
  9. package/.husky/pre-commit +4 -0
  10. package/.prettierrc.json +10 -0
  11. package/dist/aws/infra/api/integration.js +52 -0
  12. package/dist/aws/infra/api/response.js +61 -0
  13. package/dist/aws/infra/api/responses.js +82 -0
  14. package/dist/aws/infra/api/static-integration.js +54 -0
  15. package/dist/aws/infra/canaries/canary-alarm.js +26 -0
  16. package/dist/aws/infra/canaries/canary-keys.js +7 -0
  17. package/dist/aws/infra/canaries/canary-parameters.js +3 -0
  18. package/dist/aws/infra/canaries/canary-role.js +46 -0
  19. package/dist/aws/infra/canaries/canary.js +32 -0
  20. package/dist/aws/infra/canaries/database-canary.js +70 -0
  21. package/dist/aws/infra/canaries/database-checker.js +103 -0
  22. package/dist/aws/infra/canaries/url-canary.js +47 -0
  23. package/dist/aws/infra/canaries/url-checker.js +252 -0
  24. package/dist/aws/infra/documentation.js +95 -0
  25. package/dist/aws/infra/scheduler.js +31 -0
  26. package/dist/aws/infra/security-rule.js +39 -0
  27. package/dist/aws/infra/sqs-integration.js +93 -0
  28. package/dist/aws/infra/sqs-queue.js +130 -0
  29. package/dist/aws/infra/stack/lambda-configs.js +105 -0
  30. package/dist/aws/infra/stack/monitoredfunction.js +143 -0
  31. package/dist/aws/infra/stack/rest_apis.js +185 -0
  32. package/dist/aws/infra/stack/stack-checking-aspect.js +174 -0
  33. package/dist/aws/infra/stack/stack.js +67 -0
  34. package/dist/aws/infra/stack/subscription.js +42 -0
  35. package/dist/aws/infra/usage-plans.js +42 -0
  36. package/dist/aws/runtime/apikey.js +13 -0
  37. package/dist/aws/runtime/digitraffic-integration-response.js +26 -0
  38. package/dist/aws/runtime/environment.js +12 -0
  39. package/dist/aws/runtime/messaging.js +31 -0
  40. package/dist/aws/runtime/s3.js +30 -0
  41. package/dist/aws/runtime/secrets/dbsecret.js +96 -0
  42. package/dist/aws/runtime/secrets/proxy-holder.js +27 -0
  43. package/dist/aws/runtime/secrets/rds-holder.js +27 -0
  44. package/dist/aws/runtime/secrets/secret-holder.js +76 -0
  45. package/dist/aws/runtime/secrets/secret.js +43 -0
  46. package/dist/aws/types/errors.js +16 -0
  47. package/dist/aws/types/lambda-response.js +33 -0
  48. package/dist/aws/types/mediatypes.js +16 -0
  49. package/dist/aws/types/model-with-reference.js +3 -0
  50. package/dist/aws/types/proxytypes.js +3 -0
  51. package/dist/aws/types/tags.js +7 -0
  52. package/dist/database/cached.js +32 -0
  53. package/dist/database/database.js +70 -0
  54. package/dist/database/last-updated.js +54 -0
  55. package/dist/database/models.js +3 -0
  56. package/dist/marine/id_utils.js +33 -0
  57. package/dist/marine/rtz.js +3 -0
  58. package/dist/test/asserter.js +45 -0
  59. package/dist/test/db-testutils.js +31 -0
  60. package/dist/test/httpserver.js +74 -0
  61. package/dist/test/secret.js +25 -0
  62. package/dist/test/secrets-manager.js +59 -0
  63. package/dist/test/testutils.js +44 -0
  64. package/dist/types/either.js +3 -0
  65. package/dist/types/input-error.js +7 -0
  66. package/dist/types/language.js +10 -0
  67. package/dist/types/traffictype.js +13 -0
  68. package/dist/types/validator.js +14 -0
  69. package/dist/utils/api-model.js +129 -0
  70. package/dist/utils/base64.js +21 -0
  71. package/dist/utils/date-utils.js +34 -0
  72. package/dist/utils/geojson-types.js +18 -0
  73. package/dist/utils/geometry.js +164 -0
  74. package/dist/utils/retry.js +50 -0
  75. package/dist/utils/slack.js +25 -0
  76. package/dist/utils/utils.js +75 -0
  77. package/jest.config.js +15 -0
  78. package/package.json +15 -13
  79. package/src/@types/geojson-validation/index.d.ts +4 -0
  80. package/src/aws/infra/api/integration.ts +73 -0
  81. package/src/aws/infra/api/response.ts +67 -0
  82. package/src/aws/infra/api/responses.ts +124 -0
  83. package/src/aws/infra/api/static-integration.ts +62 -0
  84. package/src/aws/infra/canaries/canary-alarm.ts +31 -0
  85. package/src/aws/infra/canaries/canary-keys.ts +3 -0
  86. package/{aws/infra/canaries/canary-parameters.d.ts → src/aws/infra/canaries/canary-parameters.ts} +7 -6
  87. package/src/aws/infra/canaries/canary-role.ts +47 -0
  88. package/src/aws/infra/canaries/canary.ts +46 -0
  89. package/src/aws/infra/canaries/database-canary.ts +98 -0
  90. package/src/aws/infra/canaries/database-checker.ts +155 -0
  91. package/src/aws/infra/canaries/url-canary.ts +74 -0
  92. package/src/aws/infra/canaries/url-checker.ts +366 -0
  93. package/src/aws/infra/documentation.ts +124 -0
  94. package/src/aws/infra/scheduler.ts +59 -0
  95. package/src/aws/infra/security-rule.ts +38 -0
  96. package/src/aws/infra/sqs-integration.ts +102 -0
  97. package/src/aws/infra/sqs-queue.ts +148 -0
  98. package/src/aws/infra/stack/lambda-configs.ts +207 -0
  99. package/src/aws/infra/stack/monitoredfunction.ts +342 -0
  100. package/src/aws/infra/stack/rest_apis.ts +223 -0
  101. package/src/aws/infra/stack/stack-checking-aspect.ts +279 -0
  102. package/src/aws/infra/stack/stack.ts +145 -0
  103. package/src/aws/infra/stack/subscription.ts +58 -0
  104. package/src/aws/infra/usage-plans.ts +41 -0
  105. package/src/aws/runtime/apikey.ts +9 -0
  106. package/src/aws/runtime/digitraffic-integration-response.ts +28 -0
  107. package/src/aws/runtime/environment.ts +9 -0
  108. package/src/aws/runtime/messaging.ts +26 -0
  109. package/src/aws/runtime/s3.ts +44 -0
  110. package/src/aws/runtime/secrets/dbsecret.ts +116 -0
  111. package/src/aws/runtime/secrets/proxy-holder.ts +37 -0
  112. package/src/aws/runtime/secrets/rds-holder.ts +33 -0
  113. package/src/aws/runtime/secrets/secret-holder.ts +116 -0
  114. package/src/aws/runtime/secrets/secret.ts +50 -0
  115. package/src/aws/types/errors.ts +14 -0
  116. package/src/aws/types/lambda-response.ts +43 -0
  117. package/{aws/types/mediatypes.d.ts → src/aws/types/mediatypes.ts} +4 -3
  118. package/{aws/types/model-with-reference.d.ts → src/aws/types/model-with-reference.ts} +2 -1
  119. package/src/aws/types/proxytypes.ts +27 -0
  120. package/src/aws/types/tags.ts +3 -0
  121. package/src/database/cached.ts +35 -0
  122. package/src/database/database.ts +96 -0
  123. package/src/database/last-updated.ts +59 -0
  124. package/{database/models.d.ts → src/database/models.ts} +1 -0
  125. package/src/marine/id_utils.ts +30 -0
  126. package/src/marine/rtz.ts +57 -0
  127. package/src/test/asserter.ts +48 -0
  128. package/src/test/db-testutils.ts +44 -0
  129. package/src/test/httpserver.ts +96 -0
  130. package/src/test/secret.ts +23 -0
  131. package/src/test/secrets-manager.ts +34 -0
  132. package/src/test/testutils.ts +39 -0
  133. package/src/types/either.ts +3 -0
  134. package/src/types/input-error.ts +2 -0
  135. package/src/types/language.ts +3 -0
  136. package/src/types/traffictype.ts +8 -0
  137. package/src/types/validator.ts +10 -0
  138. package/src/utils/api-model.ts +133 -0
  139. package/src/utils/base64.ts +16 -0
  140. package/src/utils/date-utils.ts +30 -0
  141. package/src/utils/geojson-types.ts +22 -0
  142. package/src/utils/geometry.ts +164 -0
  143. package/src/utils/retry.ts +49 -0
  144. package/src/utils/slack.ts +22 -0
  145. package/src/utils/utils.ts +105 -0
  146. package/test/marine/id_utils.test.ts +57 -0
  147. package/test/promise/promise.test.ts +143 -0
  148. package/test/secrets/dbsecret.test.ts +59 -0
  149. package/test/secrets/secret-holder.test.ts +143 -0
  150. package/test/secrets/secret.test.ts +49 -0
  151. package/test/test/httpserver.test.ts +128 -0
  152. package/test/utils/date-utils.test.ts +28 -0
  153. package/test/utils/geometry.test.ts +29 -0
  154. package/test/utils/utils.test.ts +64 -0
  155. package/tsconfig.eslint.json +4 -0
  156. package/tsconfig.json +22 -0
  157. package/yarn.lock +4060 -0
  158. package/aws/infra/api/integration.d.ts +0 -21
  159. package/aws/infra/api/integration.js +0 -52
  160. package/aws/infra/api/response.d.ts +0 -22
  161. package/aws/infra/api/response.js +0 -61
  162. package/aws/infra/api/responses.d.ts +0 -39
  163. package/aws/infra/api/responses.js +0 -79
  164. package/aws/infra/api/static-integration.d.ts +0 -15
  165. package/aws/infra/api/static-integration.js +0 -54
  166. package/aws/infra/canaries/canary-alarm.d.ts +0 -6
  167. package/aws/infra/canaries/canary-alarm.js +0 -26
  168. package/aws/infra/canaries/canary-parameters.js +0 -3
  169. package/aws/infra/canaries/canary-role.d.ts +0 -6
  170. package/aws/infra/canaries/canary-role.js +0 -46
  171. package/aws/infra/canaries/canary.d.ts +0 -8
  172. package/aws/infra/canaries/canary.js +0 -32
  173. package/aws/infra/canaries/database-canary.d.ts +0 -18
  174. package/aws/infra/canaries/database-canary.js +0 -55
  175. package/aws/infra/canaries/database-checker.d.ts +0 -21
  176. package/aws/infra/canaries/database-checker.js +0 -109
  177. package/aws/infra/canaries/url-canary.d.ts +0 -19
  178. package/aws/infra/canaries/url-canary.js +0 -46
  179. package/aws/infra/canaries/url-checker.d.ts +0 -46
  180. package/aws/infra/canaries/url-checker.js +0 -238
  181. package/aws/infra/documentation.d.ts +0 -56
  182. package/aws/infra/documentation.js +0 -95
  183. package/aws/infra/scheduler.d.ts +0 -12
  184. package/aws/infra/scheduler.js +0 -31
  185. package/aws/infra/security-rule.d.ts +0 -12
  186. package/aws/infra/security-rule.js +0 -39
  187. package/aws/infra/sqs-integration.d.ts +0 -7
  188. package/aws/infra/sqs-integration.js +0 -93
  189. package/aws/infra/sqs-queue.d.ts +0 -16
  190. package/aws/infra/sqs-queue.js +0 -130
  191. package/aws/infra/stack/lambda-configs.d.ts +0 -72
  192. package/aws/infra/stack/lambda-configs.js +0 -93
  193. package/aws/infra/stack/monitoredfunction.d.ts +0 -84
  194. package/aws/infra/stack/monitoredfunction.js +0 -135
  195. package/aws/infra/stack/rest_apis.d.ts +0 -41
  196. package/aws/infra/stack/rest_apis.js +0 -185
  197. package/aws/infra/stack/stack-checking-aspect.d.ts +0 -21
  198. package/aws/infra/stack/stack-checking-aspect.js +0 -174
  199. package/aws/infra/stack/stack.d.ts +0 -44
  200. package/aws/infra/stack/stack.js +0 -60
  201. package/aws/infra/stack/subscription.d.ts +0 -17
  202. package/aws/infra/stack/subscription.js +0 -41
  203. package/aws/infra/usage-plans.d.ts +0 -15
  204. package/aws/infra/usage-plans.js +0 -42
  205. package/aws/runtime/apikey.d.ts +0 -2
  206. package/aws/runtime/apikey.js +0 -13
  207. package/aws/runtime/digitraffic-integration-response.d.ts +0 -8
  208. package/aws/runtime/digitraffic-integration-response.js +0 -26
  209. package/aws/runtime/environment.d.ts +0 -1
  210. package/aws/runtime/environment.js +0 -12
  211. package/aws/runtime/messaging.d.ts +0 -10
  212. package/aws/runtime/messaging.js +0 -31
  213. package/aws/runtime/s3.d.ts +0 -2
  214. package/aws/runtime/s3.js +0 -30
  215. package/aws/runtime/secrets/dbsecret.d.ts +0 -54
  216. package/aws/runtime/secrets/dbsecret.js +0 -96
  217. package/aws/runtime/secrets/proxy-holder.d.ts +0 -9
  218. package/aws/runtime/secrets/proxy-holder.js +0 -26
  219. package/aws/runtime/secrets/rds-holder.d.ts +0 -9
  220. package/aws/runtime/secrets/rds-holder.js +0 -26
  221. package/aws/runtime/secrets/secret-holder.d.ts +0 -26
  222. package/aws/runtime/secrets/secret-holder.js +0 -73
  223. package/aws/runtime/secrets/secret.d.ts +0 -8
  224. package/aws/runtime/secrets/secret.js +0 -43
  225. package/aws/types/errors.d.ts +0 -4
  226. package/aws/types/errors.js +0 -9
  227. package/aws/types/lambda-response.d.ts +0 -12
  228. package/aws/types/lambda-response.js +0 -28
  229. package/aws/types/mediatypes.js +0 -15
  230. package/aws/types/model-with-reference.js +0 -3
  231. package/aws/types/proxytypes.d.ts +0 -26
  232. package/aws/types/proxytypes.js +0 -3
  233. package/aws/types/tags.d.ts +0 -2
  234. package/aws/types/tags.js +0 -7
  235. package/database/cached.d.ts +0 -7
  236. package/database/cached.js +0 -32
  237. package/database/database.d.ts +0 -19
  238. package/database/database.js +0 -62
  239. package/database/last-updated.d.ts +0 -16
  240. package/database/last-updated.js +0 -54
  241. package/database/models.js +0 -3
  242. package/index.d.ts +0 -1
  243. package/index.js +0 -18
  244. package/marine/id_utils.d.ts +0 -3
  245. package/marine/id_utils.js +0 -33
  246. package/marine/rtz.d.ts +0 -48
  247. package/marine/rtz.js +0 -3
  248. package/test/asserter.d.ts +0 -11
  249. package/test/asserter.js +0 -45
  250. package/test/db-testutils.d.ts +0 -2
  251. package/test/db-testutils.js +0 -31
  252. package/test/httpserver.d.ts +0 -18
  253. package/test/httpserver.js +0 -67
  254. package/test/secret.d.ts +0 -3
  255. package/test/secret.js +0 -25
  256. package/test/secrets-manager.d.ts +0 -9
  257. package/test/secrets-manager.js +0 -59
  258. package/test/testutils.d.ts +0 -12
  259. package/test/testutils.js +0 -44
  260. package/types/input-error.d.ts +0 -2
  261. package/types/input-error.js +0 -7
  262. package/types/language.d.ts +0 -5
  263. package/types/language.js +0 -10
  264. package/types/traffictype.d.ts +0 -8
  265. package/types/traffictype.js +0 -13
  266. package/types/validator.d.ts +0 -4
  267. package/types/validator.js +0 -14
  268. package/utils/api-model.d.ts +0 -87
  269. package/utils/api-model.js +0 -129
  270. package/utils/base64.d.ts +0 -12
  271. package/utils/base64.js +0 -21
  272. package/utils/date-utils.d.ts +0 -17
  273. package/utils/date-utils.js +0 -34
  274. package/utils/geojson-types.d.ts +0 -14
  275. package/utils/geojson-types.js +0 -18
  276. package/utils/geometry.d.ts +0 -36
  277. package/utils/geometry.js +0 -140
  278. package/utils/retry.d.ts +0 -13
  279. package/utils/retry.js +0 -50
  280. package/utils/slack.d.ts +0 -5
  281. package/utils/slack.js +0 -25
  282. package/utils/utils.d.ts +0 -30
  283. package/utils/utils.js +0 -64
@@ -0,0 +1,164 @@
1
+ /**
2
+ * GeoJSON functions and tools
3
+ */
4
+ import { Feature, FeatureCollection, Geometry, Position } from "geojson";
5
+ import * as geoJsonValidator from "geojson-validation";
6
+
7
+ export const SRID_WGS84 = 4326;
8
+
9
+ /**
10
+ * Creates WKT geometry from GeoJSON geometry
11
+ * @param geometry GeoJson geometry to convert to WKT
12
+ */
13
+ export function createGeometry(geometry: Geometry): string {
14
+ if (geometry.type === "LineString") {
15
+ const coordinates = linestring(geometry.coordinates);
16
+
17
+ return `LINESTRING(${coordinates})`;
18
+ } else if (geometry.type === "Point") {
19
+ const coordinates = coordinatePair(geometry.coordinates);
20
+
21
+ return `POINT(${coordinates})`;
22
+ } else if (geometry.type === "Polygon") {
23
+ const coordinates = polygon(geometry.coordinates);
24
+
25
+ return `POLYGON(${coordinates})`;
26
+ } else if (geometry.type === "MultiPolygon") {
27
+ const coordinates = multiPolygon(geometry.coordinates);
28
+
29
+ return `MULTIPOLYGON(${coordinates})`;
30
+ }
31
+
32
+ console.error("unsupported locationType=%s", geometry.type);
33
+ return "POLYGON EMPTY";
34
+ }
35
+
36
+ function linestring(coordinates: Position[]): string {
37
+ return coordinates.map((c: Position) => coordinatePair(c)).join(",");
38
+ }
39
+
40
+ function polygon(coordinates: Position[][]): string {
41
+ const list = coordinates.map((c: Position[]) => linestring(c)).join(",");
42
+ return `(${list})`;
43
+ }
44
+
45
+ function multiPolygon(coordinates: Position[][][]): string {
46
+ const list = coordinates.map((c: Position[][]) => polygon(c)).join(",");
47
+ return `(${list})`;
48
+ }
49
+
50
+ function coordinatePair(coordinate: Position): string {
51
+ return `${coordinate[0]} ${coordinate[1]}`;
52
+ }
53
+
54
+ /**
55
+ * Create a GeoJSON FeatureCollection from a list of GeoJSON features with a 'last updated' property
56
+ * @param features List of Features
57
+ * @param lastUpdated Last updated date
58
+ */
59
+ export function createFeatureCollection(
60
+ features: Feature[],
61
+ lastUpdated: Date | null
62
+ ): FeatureCollection {
63
+ return {
64
+ type: "FeatureCollection",
65
+ lastUpdated: lastUpdated,
66
+ features: features,
67
+ } as FeatureCollection;
68
+ }
69
+
70
+ export function isValidGeoJson<T>(json: T): boolean {
71
+ return geoJsonValidator.valid(json);
72
+ }
73
+
74
+ export function isFeatureCollection<T>(json: T): boolean {
75
+ return geoJsonValidator.isFeatureCollection(json);
76
+ }
77
+
78
+ const DEGREES_TO_RADIANS = 0.017453292519943295; // = Math.PI / 180
79
+ const EARTH_RADIUS_KM = 6371;
80
+
81
+ /**
82
+ * Returns the distance between this and given GeoJSON point in kilometers. Doesn't take in account altitude.
83
+ * Based on the following Stack Overflow question:
84
+ * http://stackoverflow.com/questions/27928/calculate-distance-between-two-latitude-longitude-points-haversine-formula,
85
+ * which is based on https://en.wikipedia.org/wiki/Haversine_formula (error rate: ~0.55%).
86
+ */
87
+ function distanceBetweenWGS84PointsInKm(
88
+ fromXLon: number,
89
+ fromYLat: number,
90
+ toXLon: number,
91
+ toYLat: number
92
+ ): number {
93
+ const diffLat = toRadians(toYLat - fromYLat);
94
+ const diffLon = toRadians(toXLon - fromXLon);
95
+
96
+ const a =
97
+ Math.sin(diffLat / 2) * Math.sin(diffLat / 2) +
98
+ Math.cos(toRadians(fromYLat)) *
99
+ Math.cos(toRadians(toYLat)) *
100
+ Math.sin(diffLon / 2) *
101
+ Math.sin(diffLon / 2);
102
+ const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
103
+ return EARTH_RADIUS_KM * c;
104
+ }
105
+
106
+ /**
107
+ * Calculates distance between two GeoJSON points (WGS84)
108
+ * @param pos1
109
+ * @param pos2
110
+ */
111
+ export function distanceBetweenPositionsInKm(pos1: Position, pos2: Position) {
112
+ return distanceBetweenWGS84PointsInKm(pos1[0], pos1[1], pos2[0], pos2[1]);
113
+ }
114
+
115
+ export function areDistinctPositions(previous: Position, next: Position) {
116
+ return previous[0] !== next[0] || previous[1] !== next[1];
117
+ }
118
+
119
+ /**
120
+ * Calculates distance between two GeoJSON points (WGS84)
121
+ * @param pos1
122
+ * @param pos2
123
+ */
124
+ export function distanceBetweenPositionsInM(pos1: Position, pos2: Position) {
125
+ return distanceBetweenPositionsInKm(pos1, pos2) * 1000; // km -> m
126
+ }
127
+
128
+ export function createGmlLineString(geometry: Geometry, srsName = "EPSG:4326") {
129
+ const posList = createPosList(geometry);
130
+
131
+ return {
132
+ srsName,
133
+ posList,
134
+ };
135
+ }
136
+
137
+ function createPosList(geometry: Geometry) {
138
+ if (geometry.type === "Point") {
139
+ return positionToList(geometry.coordinates);
140
+ } else if (geometry.type === "LineString") {
141
+ return lineStringToList(geometry.coordinates);
142
+ } else if (geometry.type === "Polygon") {
143
+ return polygonToList(geometry.coordinates);
144
+ }
145
+
146
+ throw new Error("unknown geometry type " + JSON.stringify(geometry));
147
+ }
148
+
149
+ function polygonToList(positions: Position[][], precision = 8) {
150
+ return positions.map((p) => lineStringToList(p, precision)).join(" ");
151
+ }
152
+
153
+ function lineStringToList(positions: Position[], precision = 8) {
154
+ return positions.map((p) => positionToList(p, precision)).join(" ");
155
+ }
156
+
157
+ export function positionToList(position: Position, precision = 8) {
158
+ return position.map((n) => n.toPrecision(precision)).join(" ");
159
+ }
160
+
161
+ // Converts numeric degrees to radians
162
+ function toRadians(angdeg: number) {
163
+ return angdeg * DEGREES_TO_RADIANS;
164
+ }
@@ -0,0 +1,49 @@
1
+ export enum RetryLogError {
2
+ LOG_ALL_AS_ERRORS,
3
+ LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS,
4
+ NO_LOGGING
5
+ }
6
+
7
+ /**
8
+ * Utility function for retrying async functions.
9
+ * @param asyncFn Function
10
+ * @param retries Amount of retries, default is 3. If set to <= 0, no retries will be done. Using non-finite numbers will throw an error. The maximum allowed retry count is 100.
11
+ * @param logError Logging options
12
+ * @return Promise return value
13
+ */
14
+ export async function retry<T>(asyncFn: () => Promise<T>,
15
+ retries = 3,
16
+ logError = RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS): Promise<T> {
17
+
18
+ if (!isFinite(retries)) {
19
+ throw new Error('Only finite numbers are supported');
20
+ }
21
+ if (retries > 100) {
22
+ throw new Error('Exceeded the maximum retry count of 100');
23
+ }
24
+ try {
25
+ return await asyncFn();
26
+ } catch (error) {
27
+ const remainingRetries = retries - 1;
28
+
29
+ const errorMessage = 'method=retry error';
30
+ if (logError === RetryLogError.LOG_ALL_AS_ERRORS) {
31
+ console.error(errorMessage, error);
32
+ } else if (logError === RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS) {
33
+ if (remainingRetries < 0) {
34
+ console.error(errorMessage, error);
35
+ } else {
36
+ console.warn(errorMessage, error);
37
+ }
38
+ }
39
+
40
+ if (remainingRetries < 0) {
41
+ console.warn('method=retry no retries left');
42
+ throw new Error('No retries left');
43
+ }
44
+ console.warn('method=retry invocation failed, retrying with remaining retries %d', remainingRetries);
45
+ return retry(asyncFn,
46
+ remainingRetries,
47
+ logError);
48
+ }
49
+ }
@@ -0,0 +1,22 @@
1
+ import axios from "axios";
2
+
3
+ export class SlackApi {
4
+
5
+ private readonly url: string;
6
+
7
+ constructor(url :string) {
8
+ this.url = url;
9
+ }
10
+
11
+ async notify(text: string) {
12
+ try {
13
+ console.info('method=notify Notifying Slack');
14
+ await axios.post(this.url, {
15
+ text,
16
+ });
17
+ } catch (error) {
18
+ console.error('method=notify Slack notify failed!');
19
+ }
20
+ }
21
+
22
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Check if arrays have only elements that also exists also in other array.
3
+ * Individual element count doesn't matter.
4
+ * Function works only for primitive types and for other it just checks the reference to object.
5
+ *
6
+ * Some examples
7
+ * bothArraysHasSameValues( [a, b], [b, a] ) => true
8
+ * bothArraysHasSameValues( [a, a], [a, a, a] ) => true
9
+ * bothArraysHasSameValues( [a, b], [a] ) => false
10
+ *
11
+ * Object references:
12
+ * const o1 = { a: 1, b: 2};
13
+ * const o2 = { a: 1, b: 2};
14
+ * // Arrays has references to same objects
15
+ * bothArraysHasSameValues([o1], [o1])) => true
16
+ * Arrays have references to different objects
17
+ * bothArraysHasSameValues([o1], [o2])) => false
18
+ *
19
+ * @param a first array to compare
20
+ * @param b second array to compare
21
+ */
22
+ import { Either } from "../types/either";
23
+
24
+ export function bothArraysHasSameValues(
25
+ a: null | undefined | unknown[],
26
+ b: null | undefined | unknown[]
27
+ ): boolean {
28
+ if ((a && !b) || (!a && b)) {
29
+ return false;
30
+ } else if (!a && !b) {
31
+ return true;
32
+ }
33
+ const aSet = new Set(a);
34
+ const bSet = new Set(b);
35
+ if (aSet.size !== bSet.size) {
36
+ return false;
37
+ }
38
+ return Array.from(aSet).every((value) => bSet.has(value));
39
+ }
40
+
41
+ /**
42
+ * Returns the last item on the array. If the array is empty, throws an error!
43
+ */
44
+ export function getLast<T>(array: T[], sortFunction?: (a: T) => number): T {
45
+ return getFirstOrLast(false, array, sortFunction);
46
+ }
47
+
48
+ /**
49
+ * Returns the first item on the array. If the array is empty, throws an error!
50
+ */
51
+ export function getFirst<T>(array: T[], sortFunction?: (a: T) => number): T {
52
+ return getFirstOrLast(true, array, sortFunction);
53
+ }
54
+
55
+ function getFirstOrLast<T>(
56
+ getFirst: boolean,
57
+ array: T[],
58
+ sortFunction?: (a: T) => number
59
+ ): T {
60
+ if (array.length == 0) {
61
+ throw new Error(
62
+ `can't get ${getFirst ? "first" : "last"} from empty array!`
63
+ );
64
+ }
65
+
66
+ const index = getFirst ? 0 : array.length - 1;
67
+
68
+ if (sortFunction) {
69
+ return array.sort(sortFunction)[index];
70
+ }
71
+
72
+ return array[index];
73
+ }
74
+
75
+ /**
76
+ * Gets environment variable. Throws error if variable is not found.
77
+ *
78
+ * @param key Environment key
79
+ * @return string
80
+ */
81
+ export function getEnvVariable(key: string): string {
82
+ const either = getEnvVariableSafe(key);
83
+ if (either.result === "error") {
84
+ throw new Error(either.message);
85
+ }
86
+ return either.value;
87
+ }
88
+
89
+ /**
90
+ * Gets environment variable. Safe version returns object with either ok or error status.
91
+ * Easier to use for recovery than catching an error.
92
+ *
93
+ * @param key Environment key
94
+ * @return Either<string>
95
+ */
96
+ export function getEnvVariableSafe(key: string): Either<string> {
97
+ const value = process.env[key];
98
+ if (value === undefined) {
99
+ return {
100
+ result: "error",
101
+ message: `Error: environment variable "${key}" is undefined.`,
102
+ };
103
+ }
104
+ return { result: "ok", value };
105
+ }
@@ -0,0 +1,57 @@
1
+ import * as IdUtils from '../../src/marine/id_utils';
2
+ import {getRandomNumber} from "../../src/test/testutils";
3
+
4
+ describe('IdUtils tests', () => {
5
+
6
+ test('isValidLOCODE - success', () => {
7
+ expect(IdUtils.isValidLOCODE('FILOL')).toBe(true);
8
+ });
9
+
10
+ test('isValidLOCODE - fail with non-finnish prefix', () => {
11
+ expect(IdUtils.isValidLOCODE('SEABS')).toBe(false);
12
+ });
13
+
14
+ test('isValidLOCODE - fail with numbers', () => {
15
+ expect(IdUtils.isValidLOCODE('FIAA1')).toBe(false);
16
+ });
17
+
18
+ test('isValidIMO - successful checksum - ship TRANSMAR', () => {
19
+ expect(IdUtils.isValidIMO(9167332)).toBe(true);
20
+ });
21
+
22
+ test('isValidIMO - successful checksum - ship ANNIKA B', () => {
23
+ expect(IdUtils.isValidIMO(9213715)).toBe(true);
24
+ });
25
+
26
+ test('isValidIMO - successful checksum - ship X PRESS ELBE', () => {
27
+ expect(IdUtils.isValidIMO(9483669)).toBe(true);
28
+ });
29
+
30
+ test('isValidIMO - successful checksum - ship SILVERFORS', () => {
31
+ expect(IdUtils.isValidIMO(8322765)).toBe(true);
32
+ });
33
+
34
+ test('isValidIMO - invalid checksum', () => {
35
+ expect(IdUtils.isValidIMO(8322766)).toBe(false);
36
+ });
37
+
38
+ test('isValidIMO - fail with number smaller than 1000000', () => {
39
+ expect(IdUtils.isValidIMO(getRandomNumber(0, 1000000 - 1))).toBe(false);
40
+ });
41
+
42
+ test('isValidIMO - fail with number larger than 9999999', () => {
43
+ expect(IdUtils.isValidIMO(getRandomNumber(9999999 + 1, 99999999))).toBe(false);
44
+ });
45
+
46
+ test('isValidMMSI - success', () => {
47
+ expect(IdUtils.isValidMMSI(230927000)).toBe(true);
48
+ });
49
+
50
+ test('isValidMMSI - fail with number smaller than 100000000', () => {
51
+ expect(IdUtils.isValidMMSI(getRandomNumber(0, 100000000 - 1))).toBe(false);
52
+ });
53
+
54
+ test('isValidMMSI - fail with number larger than 999999999', () => {
55
+ expect(IdUtils.isValidMMSI(getRandomNumber(999999999 + 1, 9999999999))).toBe(false);
56
+ });
57
+ });
@@ -0,0 +1,143 @@
1
+ import {getRandomInteger} from "../../src/test/testutils";
2
+ import {retry, RetryLogError} from "../../src/utils/retry";
3
+
4
+ describe('Promise utils tests', () => {
5
+
6
+ test('retry - no retries', async () => {
7
+ const fn = jest.fn().mockResolvedValue(1);
8
+
9
+ const ret = await retry(fn, 0, RetryLogError.NO_LOGGING);
10
+
11
+ expect(ret).toBe(1);
12
+ expect(fn.mock.calls.length).toBe(1);
13
+ });
14
+
15
+ test('retry - error with n+1 retries', async () => {
16
+ const fn = jest.fn().mockRejectedValue('error');
17
+ const retries = getRandomInteger(1, 10);
18
+
19
+ try {
20
+ await retry(fn, retries, RetryLogError.NO_LOGGING);
21
+ } catch {
22
+ // ignore
23
+ } finally {
24
+ expect(fn.mock.calls.length).toBe(retries + 1);
25
+ }
26
+ });
27
+
28
+ test('retry - no error with n+1 retries', async () => {
29
+ const fn = jest.fn().mockResolvedValue(1);
30
+ const retries = getRandomInteger(1, 10);
31
+
32
+ const ret = await retry(fn, retries, RetryLogError.NO_LOGGING);
33
+
34
+ expect(ret).toBe(1);
35
+ expect(fn.mock.calls.length).toBe(1);
36
+ });
37
+
38
+ test('retry - errors with no error logging', async () => {
39
+ const fn = jest.fn().mockRejectedValue('error');
40
+ const consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation();
41
+
42
+ try {
43
+ await retry(fn, getRandomInteger(0, 10), RetryLogError.NO_LOGGING);
44
+ } catch {
45
+ // ignore
46
+ } finally {
47
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(0);
48
+ consoleErrorSpy.mockRestore();
49
+ }
50
+ });
51
+
52
+ test('retry - no retries with error logging', async () => {
53
+ const fn = jest.fn().mockRejectedValue('error');
54
+ const consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation();
55
+
56
+ try {
57
+ await retry(fn, 0, RetryLogError.LOG_ALL_AS_ERRORS);
58
+ } catch {
59
+ // ignore
60
+ } finally {
61
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1);
62
+ consoleErrorSpy.mockRestore();
63
+ }
64
+ });
65
+
66
+ test('retry - retries with error logging', async () => {
67
+ const fn = jest.fn().mockRejectedValue('error');
68
+ const retries = getRandomInteger(1, 10);
69
+ const consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation();
70
+
71
+ try {
72
+ await retry(fn, retries, RetryLogError.LOG_ALL_AS_ERRORS);
73
+ } catch {
74
+ // ignore
75
+ } finally {
76
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(retries + 1);
77
+ consoleErrorSpy.mockRestore();
78
+ }
79
+ });
80
+
81
+ test('retry - exceeded retry count throws error', async () => {
82
+ const fn = jest.fn().mockRejectedValue('error');
83
+
84
+ await expect(() => retry(fn, 3, RetryLogError.LOG_ALL_AS_ERRORS)).rejects.toThrow();
85
+ });
86
+
87
+ test('retry - defaults', async () => {
88
+ const fn = jest.fn().mockRejectedValue('error');
89
+ const consoleErrorSpy = jest.spyOn(global.console, 'error').mockImplementation();
90
+
91
+ try {
92
+ await retry(fn);
93
+ } catch {
94
+ // ignore
95
+ } finally {
96
+ expect(fn.mock.calls.length).toBe(3 + 1);
97
+ expect(consoleErrorSpy).toHaveBeenCalledTimes(1); // last retry
98
+ }
99
+ });
100
+
101
+ test('retry - NaN throws error', async () => {
102
+ const fn = jest.fn();
103
+
104
+ await expect(() => retry(fn, NaN, RetryLogError.NO_LOGGING)).rejects.toThrow();
105
+ });
106
+
107
+ test('retry - Infinity throws error', async () => {
108
+ const fn = jest.fn();
109
+
110
+ await expect(() => retry(fn, Infinity, RetryLogError.NO_LOGGING)).rejects.toThrow();
111
+ });
112
+
113
+ test('retry - exceeded maximum retry count throws error', async () => {
114
+ const fn = jest.fn();
115
+
116
+ await expect(() => retry(fn, getRandomInteger(101, 1000000), RetryLogError.NO_LOGGING)).rejects.toThrow();
117
+ });
118
+
119
+ test('retry - use without mocks without retry', async () => {
120
+ const val = 1;
121
+ const fn = () => Promise.resolve(val);
122
+
123
+ const ret = await retry(fn);
124
+
125
+ expect(ret).toBe(val);
126
+ });
127
+
128
+ test('retry - use without mocks with retry', async () => {
129
+ let i = 0;
130
+ const val = 1;
131
+ const fn = () => {
132
+ if (i < 3) {
133
+ i++;
134
+ throw new Error('not yet');
135
+ }
136
+ return Promise.resolve(val);
137
+ };
138
+
139
+ const ret = await retry(fn);
140
+
141
+ expect(ret).toBe(val);
142
+ });
143
+ });
@@ -0,0 +1,59 @@
1
+ import {mockSecret, stubSecretsManager} from "../../src/test/secrets-manager";
2
+
3
+ import * as sinon from 'sinon';
4
+
5
+ const SECRET_ID = "test_secret";
6
+ const SECRET_WITH_PREFIX = {
7
+ "prefix.value" : "value",
8
+ "prefix.name" : "name",
9
+ "wrong.value" : "value",
10
+ };
11
+ const SECRET_EMPTY = {};
12
+
13
+ stubSecretsManager();
14
+
15
+ import {GenericSecret, withSecret, withSecretAndPrefix} from "../../src/aws/runtime/secrets/secret";
16
+
17
+ describe('secret - test', () => {
18
+ afterEach(() => {
19
+ sinon.restore();
20
+ });
21
+
22
+ test('getSecret - no secret', async () => {
23
+ mockSecret(null);
24
+
25
+ await expect(async () => {
26
+ await withSecret(SECRET_ID, () => {
27
+ // do nothing
28
+ });
29
+ }).rejects.toThrowError("No secret found!");
30
+ });
31
+
32
+ test('getSecret - empty secret', async () => {
33
+ mockSecret(SECRET_EMPTY);
34
+
35
+ await withSecret(SECRET_ID, (secret: GenericSecret) => {
36
+ expect(secret).toEqual(SECRET_EMPTY);
37
+ });
38
+ });
39
+
40
+ test('getSecret - no prefix', async () => {
41
+ mockSecret(SECRET_WITH_PREFIX);
42
+
43
+ await withSecret(SECRET_ID, (secret: GenericSecret) => {
44
+ expect(secret).toEqual(SECRET_WITH_PREFIX);
45
+ });
46
+ });
47
+
48
+ test('getSecret - with prefix', async () => {
49
+ mockSecret(SECRET_WITH_PREFIX);
50
+
51
+ await withSecretAndPrefix(SECRET_ID, 'prefix', (secret: GenericSecret) => {
52
+ expect(secret).toEqual({
53
+ value: "value",
54
+ name: "name",
55
+ });
56
+ });
57
+
58
+ });
59
+ });