@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.
- package/.editorconfig +9 -0
- package/.eslintignore +4 -0
- package/.eslintrc.json +27 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/workflows/build.yml +36 -0
- package/.github/workflows/eslint.yml +38 -0
- package/.github/workflows/mirror.yml +15 -0
- package/.gitignore +29 -0
- package/.husky/pre-commit +4 -0
- package/.prettierrc.json +10 -0
- package/dist/aws/infra/api/integration.js +52 -0
- package/dist/aws/infra/api/response.js +61 -0
- package/dist/aws/infra/api/responses.js +82 -0
- package/dist/aws/infra/api/static-integration.js +54 -0
- package/dist/aws/infra/canaries/canary-alarm.js +26 -0
- package/dist/aws/infra/canaries/canary-keys.js +7 -0
- package/dist/aws/infra/canaries/canary-parameters.js +3 -0
- package/dist/aws/infra/canaries/canary-role.js +46 -0
- package/dist/aws/infra/canaries/canary.js +32 -0
- package/dist/aws/infra/canaries/database-canary.js +70 -0
- package/dist/aws/infra/canaries/database-checker.js +103 -0
- package/dist/aws/infra/canaries/url-canary.js +47 -0
- package/dist/aws/infra/canaries/url-checker.js +252 -0
- package/dist/aws/infra/documentation.js +95 -0
- package/dist/aws/infra/scheduler.js +31 -0
- package/dist/aws/infra/security-rule.js +39 -0
- package/dist/aws/infra/sqs-integration.js +93 -0
- package/dist/aws/infra/sqs-queue.js +130 -0
- package/dist/aws/infra/stack/lambda-configs.js +105 -0
- package/dist/aws/infra/stack/monitoredfunction.js +143 -0
- package/dist/aws/infra/stack/rest_apis.js +185 -0
- package/dist/aws/infra/stack/stack-checking-aspect.js +174 -0
- package/dist/aws/infra/stack/stack.js +67 -0
- package/dist/aws/infra/stack/subscription.js +42 -0
- package/dist/aws/infra/usage-plans.js +42 -0
- package/dist/aws/runtime/apikey.js +13 -0
- package/dist/aws/runtime/digitraffic-integration-response.js +26 -0
- package/dist/aws/runtime/environment.js +12 -0
- package/dist/aws/runtime/messaging.js +31 -0
- package/dist/aws/runtime/s3.js +30 -0
- package/dist/aws/runtime/secrets/dbsecret.js +96 -0
- package/dist/aws/runtime/secrets/proxy-holder.js +27 -0
- package/dist/aws/runtime/secrets/rds-holder.js +27 -0
- package/dist/aws/runtime/secrets/secret-holder.js +76 -0
- package/dist/aws/runtime/secrets/secret.js +43 -0
- package/dist/aws/types/errors.js +16 -0
- package/dist/aws/types/lambda-response.js +33 -0
- package/dist/aws/types/mediatypes.js +16 -0
- package/dist/aws/types/model-with-reference.js +3 -0
- package/dist/aws/types/proxytypes.js +3 -0
- package/dist/aws/types/tags.js +7 -0
- package/dist/database/cached.js +32 -0
- package/dist/database/database.js +70 -0
- package/dist/database/last-updated.js +54 -0
- package/dist/database/models.js +3 -0
- package/dist/marine/id_utils.js +33 -0
- package/dist/marine/rtz.js +3 -0
- package/dist/test/asserter.js +45 -0
- package/dist/test/db-testutils.js +31 -0
- package/dist/test/httpserver.js +74 -0
- package/dist/test/secret.js +25 -0
- package/dist/test/secrets-manager.js +59 -0
- package/dist/test/testutils.js +44 -0
- package/dist/types/either.js +3 -0
- package/dist/types/input-error.js +7 -0
- package/dist/types/language.js +10 -0
- package/dist/types/traffictype.js +13 -0
- package/dist/types/validator.js +14 -0
- package/dist/utils/api-model.js +129 -0
- package/dist/utils/base64.js +21 -0
- package/dist/utils/date-utils.js +34 -0
- package/dist/utils/geojson-types.js +18 -0
- package/dist/utils/geometry.js +164 -0
- package/dist/utils/retry.js +50 -0
- package/dist/utils/slack.js +25 -0
- package/dist/utils/utils.js +75 -0
- package/jest.config.js +15 -0
- package/package.json +15 -13
- package/src/@types/geojson-validation/index.d.ts +4 -0
- package/src/aws/infra/api/integration.ts +73 -0
- package/src/aws/infra/api/response.ts +67 -0
- package/src/aws/infra/api/responses.ts +124 -0
- package/src/aws/infra/api/static-integration.ts +62 -0
- package/src/aws/infra/canaries/canary-alarm.ts +31 -0
- package/src/aws/infra/canaries/canary-keys.ts +3 -0
- package/{aws/infra/canaries/canary-parameters.d.ts → src/aws/infra/canaries/canary-parameters.ts} +7 -6
- package/src/aws/infra/canaries/canary-role.ts +47 -0
- package/src/aws/infra/canaries/canary.ts +46 -0
- package/src/aws/infra/canaries/database-canary.ts +98 -0
- package/src/aws/infra/canaries/database-checker.ts +155 -0
- package/src/aws/infra/canaries/url-canary.ts +74 -0
- package/src/aws/infra/canaries/url-checker.ts +366 -0
- package/src/aws/infra/documentation.ts +124 -0
- package/src/aws/infra/scheduler.ts +59 -0
- package/src/aws/infra/security-rule.ts +38 -0
- package/src/aws/infra/sqs-integration.ts +102 -0
- package/src/aws/infra/sqs-queue.ts +148 -0
- package/src/aws/infra/stack/lambda-configs.ts +207 -0
- package/src/aws/infra/stack/monitoredfunction.ts +342 -0
- package/src/aws/infra/stack/rest_apis.ts +223 -0
- package/src/aws/infra/stack/stack-checking-aspect.ts +279 -0
- package/src/aws/infra/stack/stack.ts +145 -0
- package/src/aws/infra/stack/subscription.ts +58 -0
- package/src/aws/infra/usage-plans.ts +41 -0
- package/src/aws/runtime/apikey.ts +9 -0
- package/src/aws/runtime/digitraffic-integration-response.ts +28 -0
- package/src/aws/runtime/environment.ts +9 -0
- package/src/aws/runtime/messaging.ts +26 -0
- package/src/aws/runtime/s3.ts +44 -0
- package/src/aws/runtime/secrets/dbsecret.ts +116 -0
- package/src/aws/runtime/secrets/proxy-holder.ts +37 -0
- package/src/aws/runtime/secrets/rds-holder.ts +33 -0
- package/src/aws/runtime/secrets/secret-holder.ts +116 -0
- package/src/aws/runtime/secrets/secret.ts +50 -0
- package/src/aws/types/errors.ts +14 -0
- package/src/aws/types/lambda-response.ts +43 -0
- package/{aws/types/mediatypes.d.ts → src/aws/types/mediatypes.ts} +4 -3
- package/{aws/types/model-with-reference.d.ts → src/aws/types/model-with-reference.ts} +2 -1
- package/src/aws/types/proxytypes.ts +27 -0
- package/src/aws/types/tags.ts +3 -0
- package/src/database/cached.ts +35 -0
- package/src/database/database.ts +96 -0
- package/src/database/last-updated.ts +59 -0
- package/{database/models.d.ts → src/database/models.ts} +1 -0
- package/src/marine/id_utils.ts +30 -0
- package/src/marine/rtz.ts +57 -0
- package/src/test/asserter.ts +48 -0
- package/src/test/db-testutils.ts +44 -0
- package/src/test/httpserver.ts +96 -0
- package/src/test/secret.ts +23 -0
- package/src/test/secrets-manager.ts +34 -0
- package/src/test/testutils.ts +39 -0
- package/src/types/either.ts +3 -0
- package/src/types/input-error.ts +2 -0
- package/src/types/language.ts +3 -0
- package/src/types/traffictype.ts +8 -0
- package/src/types/validator.ts +10 -0
- package/src/utils/api-model.ts +133 -0
- package/src/utils/base64.ts +16 -0
- package/src/utils/date-utils.ts +30 -0
- package/src/utils/geojson-types.ts +22 -0
- package/src/utils/geometry.ts +164 -0
- package/src/utils/retry.ts +49 -0
- package/src/utils/slack.ts +22 -0
- package/src/utils/utils.ts +105 -0
- package/test/marine/id_utils.test.ts +57 -0
- package/test/promise/promise.test.ts +143 -0
- package/test/secrets/dbsecret.test.ts +59 -0
- package/test/secrets/secret-holder.test.ts +143 -0
- package/test/secrets/secret.test.ts +49 -0
- package/test/test/httpserver.test.ts +128 -0
- package/test/utils/date-utils.test.ts +28 -0
- package/test/utils/geometry.test.ts +29 -0
- package/test/utils/utils.test.ts +64 -0
- package/tsconfig.eslint.json +4 -0
- package/tsconfig.json +22 -0
- package/yarn.lock +4060 -0
- package/aws/infra/api/integration.d.ts +0 -21
- package/aws/infra/api/integration.js +0 -52
- package/aws/infra/api/response.d.ts +0 -22
- package/aws/infra/api/response.js +0 -61
- package/aws/infra/api/responses.d.ts +0 -39
- package/aws/infra/api/responses.js +0 -79
- package/aws/infra/api/static-integration.d.ts +0 -15
- package/aws/infra/api/static-integration.js +0 -54
- package/aws/infra/canaries/canary-alarm.d.ts +0 -6
- package/aws/infra/canaries/canary-alarm.js +0 -26
- package/aws/infra/canaries/canary-parameters.js +0 -3
- package/aws/infra/canaries/canary-role.d.ts +0 -6
- package/aws/infra/canaries/canary-role.js +0 -46
- package/aws/infra/canaries/canary.d.ts +0 -8
- package/aws/infra/canaries/canary.js +0 -32
- package/aws/infra/canaries/database-canary.d.ts +0 -18
- package/aws/infra/canaries/database-canary.js +0 -55
- package/aws/infra/canaries/database-checker.d.ts +0 -21
- package/aws/infra/canaries/database-checker.js +0 -109
- package/aws/infra/canaries/url-canary.d.ts +0 -19
- package/aws/infra/canaries/url-canary.js +0 -46
- package/aws/infra/canaries/url-checker.d.ts +0 -46
- package/aws/infra/canaries/url-checker.js +0 -238
- package/aws/infra/documentation.d.ts +0 -56
- package/aws/infra/documentation.js +0 -95
- package/aws/infra/scheduler.d.ts +0 -12
- package/aws/infra/scheduler.js +0 -31
- package/aws/infra/security-rule.d.ts +0 -12
- package/aws/infra/security-rule.js +0 -39
- package/aws/infra/sqs-integration.d.ts +0 -7
- package/aws/infra/sqs-integration.js +0 -93
- package/aws/infra/sqs-queue.d.ts +0 -16
- package/aws/infra/sqs-queue.js +0 -130
- package/aws/infra/stack/lambda-configs.d.ts +0 -72
- package/aws/infra/stack/lambda-configs.js +0 -93
- package/aws/infra/stack/monitoredfunction.d.ts +0 -84
- package/aws/infra/stack/monitoredfunction.js +0 -135
- package/aws/infra/stack/rest_apis.d.ts +0 -41
- package/aws/infra/stack/rest_apis.js +0 -185
- package/aws/infra/stack/stack-checking-aspect.d.ts +0 -21
- package/aws/infra/stack/stack-checking-aspect.js +0 -174
- package/aws/infra/stack/stack.d.ts +0 -44
- package/aws/infra/stack/stack.js +0 -60
- package/aws/infra/stack/subscription.d.ts +0 -17
- package/aws/infra/stack/subscription.js +0 -41
- package/aws/infra/usage-plans.d.ts +0 -15
- package/aws/infra/usage-plans.js +0 -42
- package/aws/runtime/apikey.d.ts +0 -2
- package/aws/runtime/apikey.js +0 -13
- package/aws/runtime/digitraffic-integration-response.d.ts +0 -8
- package/aws/runtime/digitraffic-integration-response.js +0 -26
- package/aws/runtime/environment.d.ts +0 -1
- package/aws/runtime/environment.js +0 -12
- package/aws/runtime/messaging.d.ts +0 -10
- package/aws/runtime/messaging.js +0 -31
- package/aws/runtime/s3.d.ts +0 -2
- package/aws/runtime/s3.js +0 -30
- package/aws/runtime/secrets/dbsecret.d.ts +0 -54
- package/aws/runtime/secrets/dbsecret.js +0 -96
- package/aws/runtime/secrets/proxy-holder.d.ts +0 -9
- package/aws/runtime/secrets/proxy-holder.js +0 -26
- package/aws/runtime/secrets/rds-holder.d.ts +0 -9
- package/aws/runtime/secrets/rds-holder.js +0 -26
- package/aws/runtime/secrets/secret-holder.d.ts +0 -26
- package/aws/runtime/secrets/secret-holder.js +0 -73
- package/aws/runtime/secrets/secret.d.ts +0 -8
- package/aws/runtime/secrets/secret.js +0 -43
- package/aws/types/errors.d.ts +0 -4
- package/aws/types/errors.js +0 -9
- package/aws/types/lambda-response.d.ts +0 -12
- package/aws/types/lambda-response.js +0 -28
- package/aws/types/mediatypes.js +0 -15
- package/aws/types/model-with-reference.js +0 -3
- package/aws/types/proxytypes.d.ts +0 -26
- package/aws/types/proxytypes.js +0 -3
- package/aws/types/tags.d.ts +0 -2
- package/aws/types/tags.js +0 -7
- package/database/cached.d.ts +0 -7
- package/database/cached.js +0 -32
- package/database/database.d.ts +0 -19
- package/database/database.js +0 -62
- package/database/last-updated.d.ts +0 -16
- package/database/last-updated.js +0 -54
- package/database/models.js +0 -3
- package/index.d.ts +0 -1
- package/index.js +0 -18
- package/marine/id_utils.d.ts +0 -3
- package/marine/id_utils.js +0 -33
- package/marine/rtz.d.ts +0 -48
- package/marine/rtz.js +0 -3
- package/test/asserter.d.ts +0 -11
- package/test/asserter.js +0 -45
- package/test/db-testutils.d.ts +0 -2
- package/test/db-testutils.js +0 -31
- package/test/httpserver.d.ts +0 -18
- package/test/httpserver.js +0 -67
- package/test/secret.d.ts +0 -3
- package/test/secret.js +0 -25
- package/test/secrets-manager.d.ts +0 -9
- package/test/secrets-manager.js +0 -59
- package/test/testutils.d.ts +0 -12
- package/test/testutils.js +0 -44
- package/types/input-error.d.ts +0 -2
- package/types/input-error.js +0 -7
- package/types/language.d.ts +0 -5
- package/types/language.js +0 -10
- package/types/traffictype.d.ts +0 -8
- package/types/traffictype.js +0 -13
- package/types/validator.d.ts +0 -4
- package/types/validator.js +0 -14
- package/utils/api-model.d.ts +0 -87
- package/utils/api-model.js +0 -129
- package/utils/base64.d.ts +0 -12
- package/utils/base64.js +0 -21
- package/utils/date-utils.d.ts +0 -17
- package/utils/date-utils.js +0 -34
- package/utils/geojson-types.d.ts +0 -14
- package/utils/geojson-types.js +0 -18
- package/utils/geometry.d.ts +0 -36
- package/utils/geometry.js +0 -140
- package/utils/retry.d.ts +0 -13
- package/utils/retry.js +0 -50
- package/utils/slack.d.ts +0 -5
- package/utils/slack.js +0 -25
- package/utils/utils.d.ts +0 -30
- 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
|
+
});
|