@digitraffic/common 2023.5.31-1 → 2023.6.19-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.
@@ -16,7 +16,7 @@ export interface DbConfiguration {
16
16
  readonly customParameterGroup: boolean;
17
17
  readonly securityGroupId: string;
18
18
  /** If this is not specified, import default vpc */
19
- readonly vpcId?: string;
19
+ readonly vpc?: IVpc;
20
20
  readonly proxy: {
21
21
  readonly name?: string;
22
22
  readonly securityGroupId: string;
@@ -83,10 +83,8 @@ class DbStack extends aws_cdk_lib_1.Stack {
83
83
  const instanceName = isc.environmentName + "-db";
84
84
  const securityGroup = aws_ec2_1.SecurityGroup.fromSecurityGroupId(this, "securitygroup", configuration.securityGroupId);
85
85
  const parameterGroup = this.createParamaterGroup(configuration);
86
- const vpc = configuration.vpcId
87
- ? aws_ec2_1.Vpc.fromLookup(this, "vpc", {
88
- vpcId: configuration.vpcId,
89
- })
86
+ const vpc = configuration.vpc
87
+ ? configuration.vpc
90
88
  : (0, import_util_1.importVpc)(this, isc.environmentName);
91
89
  const parameters = this.createClusterParameters(configuration, instanceName, vpc, securityGroup, parameterGroup);
92
90
  // create cluster from the snapshot or from the scratch
@@ -26,7 +26,7 @@ export interface CustomParams {
26
26
  /** do not log your apikey! */
27
27
  customApiKey?: never;
28
28
  [key: `custom${Capitalize<string>}Count`]: number;
29
- [key: `custom${Capitalize<string>}`]: string | number | boolean | Date | null | undefined;
29
+ [key: `custom${Capitalize<string>}`]: string | number | bigint | boolean | Date | null | undefined;
30
30
  }
31
31
  /**
32
32
  * Digitraffic logging object.
@@ -0,0 +1,3 @@
1
+ export declare class AsyncTimeoutError extends Error {
2
+ constructor();
3
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AsyncTimeoutError = void 0;
4
+ class AsyncTimeoutError extends Error {
5
+ constructor() {
6
+ super("Async operation timed out");
7
+ }
8
+ }
9
+ exports.AsyncTimeoutError = AsyncTimeoutError;
10
+ //# sourceMappingURL=async-timeout-error.js.map
@@ -0,0 +1,4 @@
1
+ export declare class HttpError extends Error {
2
+ statusCode: number;
3
+ constructor(statusCode: number, message: string);
4
+ }
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpError = void 0;
4
+ class HttpError extends Error {
5
+ constructor(statusCode, message) {
6
+ super(message);
7
+ this.statusCode = statusCode;
8
+ }
9
+ }
10
+ exports.HttpError = HttpError;
11
+ //# sourceMappingURL=http-error.js.map
@@ -2,6 +2,8 @@
2
2
  * Constant for the 1970-01-01T00:00:00Z epoch Date.
3
3
  */
4
4
  export declare const EPOCH: Date;
5
+ export declare const UTC = "UTC";
6
+ export declare const MYSQL_DATETIME_FORMAT = "yyyy-MM-dd HH:mm";
5
7
  /**
6
8
  * Counts difference in milliseconds between dates.
7
9
  * @param start
@@ -19,3 +21,7 @@ export declare function countDiffInSeconds(start: Date, end: Date): number;
19
21
  * @param isoString to convert
20
22
  */
21
23
  export declare function dateFromIsoString(isoString: string): Date;
24
+ /**
25
+ * Formats a date in UTC in the given format, regardless of system time zone
26
+ */
27
+ export declare function dateToUTCString(date: Date, format: string): string;
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.dateFromIsoString = exports.countDiffInSeconds = exports.countDiffMs = exports.EPOCH = void 0;
3
+ exports.dateToUTCString = exports.dateFromIsoString = exports.countDiffInSeconds = exports.countDiffMs = exports.MYSQL_DATETIME_FORMAT = exports.UTC = exports.EPOCH = void 0;
4
+ const date_fns_tz_1 = require("date-fns-tz");
4
5
  /**
5
6
  * Constant for the 1970-01-01T00:00:00Z epoch Date.
6
7
  */
7
8
  exports.EPOCH = new Date(Date.UTC(1970, 0, 1));
9
+ exports.UTC = "UTC";
10
+ exports.MYSQL_DATETIME_FORMAT = "yyyy-MM-dd HH:mm";
8
11
  /**
9
12
  * Counts difference in milliseconds between dates.
10
13
  * @param start
@@ -38,4 +41,11 @@ exports.dateFromIsoString = dateFromIsoString;
38
41
  function isValidDate(d) {
39
42
  return d instanceof Date && !isNaN(d.getTime());
40
43
  }
44
+ /**
45
+ * Formats a date in UTC in the given format, regardless of system time zone
46
+ */
47
+ function dateToUTCString(date, format) {
48
+ return (0, date_fns_tz_1.formatInTimeZone)(date, exports.UTC, format);
49
+ }
50
+ exports.dateToUTCString = dateToUTCString;
41
51
  //# sourceMappingURL=date-utils.js.map
@@ -3,11 +3,31 @@ export declare enum RetryLogError {
3
3
  LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS = 1,
4
4
  NO_LOGGING = 2
5
5
  }
6
+ export type TimeoutFn = (retryCount: number) => number;
7
+ export type RetryPredicate = (error: unknown) => boolean;
8
+ /**
9
+ * Utility timeout functions for "retry" function.
10
+ */
11
+ export declare const timeoutFunctions: {
12
+ noTimeout: (retryCount: number) => number;
13
+ exponentialTimeout: (retryCount: number) => number;
14
+ };
15
+ /**
16
+ * Utility retry predicates for "retry" function.
17
+ */
18
+ export declare const retryPredicates: {
19
+ retryBasedOnStatusCode: (error: unknown) => boolean;
20
+ alwaysRetry: (error: unknown) => boolean;
21
+ };
22
+ export declare let retryCount: number;
6
23
  /**
7
24
  * Utility function for retrying async functions.
8
25
  * @param asyncFn Function
9
26
  * @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.
10
27
  * @param logError Logging options
28
+ * @param timeoutBetweenRetries A function that returns the timeout between retries in milliseconds. Default is a function returning 0. The function is called with the current retry count.
29
+ * @param retryPredicate A function that returns true if the error should be retried. Default is a function that always returns true. The function is called with the error object.
11
30
  * @return Promise return value
12
31
  */
13
- export declare function retry<T>(asyncFn: () => Promise<T>, retries?: number, logError?: RetryLogError): Promise<T>;
32
+ export declare function retry<T>(asyncFn: () => Promise<T>, retries?: number, logError?: RetryLogError, timeoutBetweenRetries?: TimeoutFn, retryPredicate?: RetryPredicate): Promise<T>;
33
+ export declare function retryRequest<T>(request: (...args: unknown[]) => Promise<T>, ...args: unknown[]): Promise<T>;
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.retry = exports.RetryLogError = void 0;
3
+ exports.retryRequest = exports.retry = exports.retryCount = exports.retryPredicates = exports.timeoutFunctions = exports.RetryLogError = void 0;
4
+ const http_error_1 = require("../types/http-error");
5
+ const async_timeout_error_1 = require("../types/async-timeout-error");
6
+ const dt_logger_default_1 = require("../aws/runtime/dt-logger-default");
4
7
  var RetryLogError;
5
8
  (function (RetryLogError) {
6
9
  RetryLogError[RetryLogError["LOG_ALL_AS_ERRORS"] = 0] = "LOG_ALL_AS_ERRORS";
@@ -8,43 +11,136 @@ var RetryLogError;
8
11
  RetryLogError[RetryLogError["NO_LOGGING"] = 2] = "NO_LOGGING";
9
12
  })(RetryLogError = exports.RetryLogError || (exports.RetryLogError = {}));
10
13
  /**
11
- * Utility function for retrying async functions.
12
- * @param asyncFn Function
13
- * @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.
14
- * @param logError Logging options
15
- * @return Promise return value
14
+ * Utility timeout functions for "retry" function.
15
+ */
16
+ exports.timeoutFunctions = (function () {
17
+ return {
18
+ noTimeout: (retryCount) => {
19
+ return 0;
20
+ },
21
+ exponentialTimeout: (retryCount) => {
22
+ return 2 ** retryCount * 1000;
23
+ },
24
+ };
25
+ })();
26
+ /**
27
+ * Utility retry predicates for "retry" function.
16
28
  */
17
- async function retry(asyncFn, retries = 3, logError = RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS) {
29
+ exports.retryPredicates = (function () {
30
+ const retryStatusCodes = new Set([
31
+ // service might return 403 for no apparent reason
32
+ 403,
33
+ // Opensearch responds 429, if you make too many requests too fast
34
+ 429,
35
+ ]);
36
+ return {
37
+ retryBasedOnStatusCode: (error) => {
38
+ if (error instanceof http_error_1.HttpError) {
39
+ return retryStatusCodes.has(error.statusCode);
40
+ }
41
+ return false;
42
+ },
43
+ alwaysRetry: (error) => {
44
+ return true;
45
+ },
46
+ };
47
+ })();
48
+ function readPossibleErrorMessage(error) {
49
+ if (error instanceof Error) {
50
+ return error.message;
51
+ }
52
+ return "Something else than an Error object was thrown";
53
+ }
54
+ // Tämä muuttuja on testejä varten määritelty täällä.
55
+ exports.retryCount = 0;
56
+ async function retryRecursive(asyncFn, retries, retryCountInj, logError, timeoutBetweenRetries, retryPredicate) {
57
+ const asyncFnTimeout = 30 * 60 * 1000; // 30 minutes
18
58
  if (!isFinite(retries)) {
19
- throw new Error('Only finite numbers are supported');
59
+ throw new Error("Only finite numbers are supported");
20
60
  }
21
61
  if (retries > 100) {
22
- throw new Error('Exceeded the maximum retry count of 100');
62
+ throw new Error("Exceeded the maximum retry count of 100");
23
63
  }
24
64
  try {
25
- return await asyncFn();
65
+ // NOTE, a Promise cannot be cancelled. So if the asyncFn calls multiple async/await paris and the first one takes 31 minutes to complete,
66
+ // then the rest of async/await pairs will be called even though AysncTimeoutError is allready thrown.
67
+ const result = await Promise.race([
68
+ asyncFn(),
69
+ new Promise((_, reject) => setTimeout(() => reject(new async_timeout_error_1.AsyncTimeoutError()), asyncFnTimeout)),
70
+ ]);
71
+ return result;
26
72
  }
27
73
  catch (error) {
28
74
  const remainingRetries = retries - 1;
29
- const errorMessage = 'method=retry error';
30
75
  if (logError === RetryLogError.LOG_ALL_AS_ERRORS) {
31
- console.error(errorMessage, error);
76
+ dt_logger_default_1.logger.error({
77
+ message: readPossibleErrorMessage(error),
78
+ method: "retry.retryRecursive",
79
+ });
32
80
  }
33
81
  else if (logError === RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS) {
34
82
  if (remainingRetries < 0) {
35
- console.error(errorMessage, error);
83
+ dt_logger_default_1.logger.error({
84
+ message: readPossibleErrorMessage(error),
85
+ method: "retry.retryRecursive",
86
+ });
36
87
  }
37
88
  else {
38
- console.warn(errorMessage, error);
89
+ dt_logger_default_1.logger.warn({
90
+ message: readPossibleErrorMessage(error),
91
+ method: "retry.retryRecursive",
92
+ });
39
93
  }
40
94
  }
41
95
  if (remainingRetries < 0) {
42
- console.warn('method=retry no retries left');
43
- throw new Error('No retries left');
96
+ dt_logger_default_1.logger.warn({
97
+ message: "No retries left",
98
+ method: "retry.retryRecursive",
99
+ });
100
+ throw new Error("No retries left");
101
+ }
102
+ dt_logger_default_1.logger.warn({
103
+ message: `Retrying with remaining retries ${remainingRetries}`,
104
+ method: "retry.retryRecursive",
105
+ });
106
+ if (retryPredicate(error)) {
107
+ retryCountInj++;
108
+ exports.retryCount = retryCountInj;
109
+ const milliseconds = timeoutBetweenRetries(retryCountInj);
110
+ if (milliseconds > 0) {
111
+ await new Promise((resolve) => setTimeout(resolve, milliseconds));
112
+ }
113
+ return retryRecursive(asyncFn, remainingRetries, retryCountInj, logError, timeoutBetweenRetries, retryPredicate);
114
+ }
115
+ else {
116
+ throw new Error("Retry predicate failed");
44
117
  }
45
- console.warn('method=retry invocation failed, retrying with remaining retries %d', remainingRetries);
46
- return retry(asyncFn, remainingRetries, logError);
47
118
  }
48
119
  }
120
+ /**
121
+ * Utility function for retrying async functions.
122
+ * @param asyncFn Function
123
+ * @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.
124
+ * @param logError Logging options
125
+ * @param timeoutBetweenRetries A function that returns the timeout between retries in milliseconds. Default is a function returning 0. The function is called with the current retry count.
126
+ * @param retryPredicate A function that returns true if the error should be retried. Default is a function that always returns true. The function is called with the error object.
127
+ * @return Promise return value
128
+ */
129
+ async function retry(asyncFn, retries = 3, logError = RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS, timeoutBetweenRetries = exports.timeoutFunctions.noTimeout, retryPredicate = exports.retryPredicates.alwaysRetry) {
130
+ exports.retryCount = 0;
131
+ dt_logger_default_1.logger.debug({
132
+ message: `Retrying with ${retries} retries`,
133
+ method: "retry.retry",
134
+ });
135
+ return retryRecursive(asyncFn, retries, 0, logError, timeoutBetweenRetries, retryPredicate);
136
+ }
49
137
  exports.retry = retry;
138
+ function wrapArgsToFn(fn, ...args) {
139
+ return async () => await fn(...args);
140
+ }
141
+ async function retryRequest(request, ...args) {
142
+ const asyncFn = wrapArgsToFn(request, ...args);
143
+ return retry(asyncFn, 5, RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS, exports.timeoutFunctions.exponentialTimeout, exports.retryPredicates.retryBasedOnStatusCode);
144
+ }
145
+ exports.retryRequest = retryRequest;
50
146
  //# sourceMappingURL=retry.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitraffic/common",
3
- "version": "2023.5.31-1",
3
+ "version": "2023.6.19-1",
4
4
  "description": "",
5
5
  "repository": {
6
6
  "type": "git",
@@ -24,9 +24,10 @@
24
24
  "axios": "^1.2.6",
25
25
  "change-case": "^4.1.2",
26
26
  "constructs": "^10.2.17",
27
+ "date-fns-tz": "~2.0.0",
28
+ "date-fns": "~2.29.3",
27
29
  "etag": "^1.8.1",
28
30
  "geojson-validation": "^1.0.2",
29
- "moment": "^2.29.4",
30
31
  "node-ttl": "^0.2.0",
31
32
  "pg-native": "^3.0.1",
32
33
  "pg-promise": "^11.0.0"
@@ -48,6 +49,8 @@
48
49
  "axios": "^1.3.6",
49
50
  "change-case": "^4.1.2",
50
51
  "constructs": "10.2.17",
52
+ "date-fns-tz": "~2.0.0",
53
+ "date-fns": "~2.29.3",
51
54
  "eslint": "~8.40.0",
52
55
  "eslint-config-prettier": "^8.8.0",
53
56
  "eslint-plugin-deprecation": "~1.4.1",
@@ -58,7 +61,6 @@
58
61
  "jest-junit": "^16.0.0",
59
62
  "lint-staged": "^13.2.2",
60
63
  "lodash": "^4.17.21",
61
- "moment": "^2.29.4",
62
64
  "node-ttl": "^0.2.0",
63
65
  "pg-native": "^3.0.1",
64
66
  "pg-promise": "^11.4.3",
@@ -81,7 +83,7 @@
81
83
  "build": "tsc",
82
84
  "lint": "eslint --cache .",
83
85
  "eslint-report": "eslint . --format html",
84
- "ci:eslint-report": "eslint . --format html -o report.html",
86
+ "ci:eslint-report": "eslint . --format html -o report.html || true",
85
87
  "clean": "rimraf dist output",
86
88
  "test": "jest --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest",
87
89
  "test:watch": "jest --detectOpenHandles --forceExit --coverage --coverageDirectory=output/coverage/jest --watch"
@@ -37,7 +37,7 @@ export interface DbConfiguration {
37
37
  readonly customParameterGroup: boolean;
38
38
  readonly securityGroupId: string;
39
39
  /** If this is not specified, import default vpc */
40
- readonly vpcId?: string;
40
+ readonly vpc?: IVpc;
41
41
 
42
42
  readonly proxy: {
43
43
  readonly name?: string;
@@ -185,10 +185,8 @@ export class DbStack extends Stack {
185
185
  configuration.securityGroupId
186
186
  );
187
187
  const parameterGroup = this.createParamaterGroup(configuration);
188
- const vpc = configuration.vpcId
189
- ? Vpc.fromLookup(this, "vpc", {
190
- vpcId: configuration.vpcId,
191
- })
188
+ const vpc = configuration.vpc
189
+ ? configuration.vpc
192
190
  : importVpc(this, isc.environmentName);
193
191
 
194
192
  const parameters = this.createClusterParameters(
@@ -37,6 +37,7 @@ export interface CustomParams {
37
37
  [key: `custom${Capitalize<string>}`]:
38
38
  | string
39
39
  | number
40
+ | bigint
40
41
  | boolean
41
42
  | Date
42
43
  | null
@@ -0,0 +1,5 @@
1
+ export class AsyncTimeoutError extends Error {
2
+ constructor() {
3
+ super("Async operation timed out");
4
+ }
5
+ }
@@ -0,0 +1,8 @@
1
+ export class HttpError extends Error {
2
+ statusCode: number;
3
+
4
+ constructor(statusCode: number, message: string) {
5
+ super(message);
6
+ this.statusCode = statusCode;
7
+ }
8
+ }
@@ -1,8 +1,14 @@
1
+ import { formatInTimeZone } from "date-fns-tz";
2
+
1
3
  /**
2
4
  * Constant for the 1970-01-01T00:00:00Z epoch Date.
3
5
  */
4
6
  export const EPOCH = new Date(Date.UTC(1970, 0, 1));
5
7
 
8
+ export const UTC = "UTC";
9
+
10
+ export const MYSQL_DATETIME_FORMAT = "yyyy-MM-dd HH:mm";
11
+
6
12
  /**
7
13
  * Counts difference in milliseconds between dates.
8
14
  * @param start
@@ -38,3 +44,10 @@ export function dateFromIsoString(isoString: string): Date {
38
44
  function isValidDate(d: unknown) {
39
45
  return d instanceof Date && !isNaN(d.getTime());
40
46
  }
47
+
48
+ /**
49
+ * Formats a date in UTC in the given format, regardless of system time zone
50
+ */
51
+ export function dateToUTCString(date: Date, format: string): string {
52
+ return formatInTimeZone(date, UTC, format);
53
+ }
@@ -1,49 +1,198 @@
1
+ import { HttpError } from "../types/http-error";
2
+ import { AsyncTimeoutError } from "../types/async-timeout-error";
3
+ import { logger } from "../aws/runtime/dt-logger-default";
4
+
1
5
  export enum RetryLogError {
2
6
  LOG_ALL_AS_ERRORS,
3
7
  LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS,
4
- NO_LOGGING
8
+ NO_LOGGING,
5
9
  }
6
10
 
11
+ export type TimeoutFn = (retryCount: number) => number;
12
+ export type RetryPredicate = (error: unknown) => boolean;
13
+
7
14
  /**
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
15
+ * Utility timeout functions for "retry" function.
13
16
  */
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
+ export const timeoutFunctions = (function () {
18
+ return {
19
+ noTimeout: (retryCount: number): number => {
20
+ return 0;
21
+ },
22
+ exponentialTimeout: (retryCount: number): number => {
23
+ return 2 ** retryCount * 1000;
24
+ },
25
+ };
26
+ })();
17
27
 
28
+ /**
29
+ * Utility retry predicates for "retry" function.
30
+ */
31
+ export const retryPredicates = (function () {
32
+ const retryStatusCodes = new Set([
33
+ // service might return 403 for no apparent reason
34
+ 403,
35
+ // Opensearch responds 429, if you make too many requests too fast
36
+ 429,
37
+ ]);
38
+ return {
39
+ retryBasedOnStatusCode: (error: unknown): boolean => {
40
+ if (error instanceof HttpError) {
41
+ return retryStatusCodes.has(error.statusCode);
42
+ }
43
+ return false;
44
+ },
45
+ alwaysRetry: (error: unknown): boolean => {
46
+ return true;
47
+ },
48
+ };
49
+ })();
50
+
51
+ function readPossibleErrorMessage(error: unknown): string {
52
+ if (error instanceof Error) {
53
+ return error.message;
54
+ }
55
+ return "Something else than an Error object was thrown";
56
+ }
57
+
58
+ // Tämä muuttuja on testejä varten määritelty täällä.
59
+ export let retryCount = 0;
60
+
61
+ async function retryRecursive<T>(
62
+ asyncFn: () => Promise<T>,
63
+ retries: number,
64
+ retryCountInj: number,
65
+ logError: RetryLogError,
66
+ timeoutBetweenRetries: TimeoutFn,
67
+ retryPredicate: RetryPredicate
68
+ ): Promise<T> {
69
+ const asyncFnTimeout = 30 * 60 * 1000; // 30 minutes
18
70
  if (!isFinite(retries)) {
19
- throw new Error('Only finite numbers are supported');
71
+ throw new Error("Only finite numbers are supported");
20
72
  }
21
73
  if (retries > 100) {
22
- throw new Error('Exceeded the maximum retry count of 100');
74
+ throw new Error("Exceeded the maximum retry count of 100");
23
75
  }
24
76
  try {
25
- return await asyncFn();
77
+ // NOTE, a Promise cannot be cancelled. So if the asyncFn calls multiple async/await paris and the first one takes 31 minutes to complete,
78
+ // then the rest of async/await pairs will be called even though AysncTimeoutError is allready thrown.
79
+ const result: T = await Promise.race([
80
+ asyncFn(),
81
+ new Promise<never>((_, reject) =>
82
+ setTimeout(
83
+ () => reject(new AsyncTimeoutError()),
84
+ asyncFnTimeout
85
+ )
86
+ ),
87
+ ]);
88
+ return result;
26
89
  } catch (error) {
27
90
  const remainingRetries = retries - 1;
28
91
 
29
- const errorMessage = 'method=retry error';
30
92
  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) {
93
+ logger.error({
94
+ message: readPossibleErrorMessage(error),
95
+ method: "retry.retryRecursive",
96
+ });
97
+ } else if (
98
+ logError === RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS
99
+ ) {
33
100
  if (remainingRetries < 0) {
34
- console.error(errorMessage, error);
101
+ logger.error({
102
+ message: readPossibleErrorMessage(error),
103
+ method: "retry.retryRecursive",
104
+ });
35
105
  } else {
36
- console.warn(errorMessage, error);
106
+ logger.warn({
107
+ message: readPossibleErrorMessage(error),
108
+ method: "retry.retryRecursive",
109
+ });
37
110
  }
38
111
  }
39
112
 
40
113
  if (remainingRetries < 0) {
41
- console.warn('method=retry no retries left');
42
- throw new Error('No retries left');
114
+ logger.warn({
115
+ message: "No retries left",
116
+ method: "retry.retryRecursive",
117
+ });
118
+ throw new Error("No retries left");
119
+ }
120
+ logger.warn({
121
+ message: `Retrying with remaining retries ${remainingRetries}`,
122
+ method: "retry.retryRecursive",
123
+ });
124
+ if (retryPredicate(error)) {
125
+ retryCountInj++;
126
+ retryCount = retryCountInj;
127
+ const milliseconds = timeoutBetweenRetries(retryCountInj);
128
+ if (milliseconds > 0) {
129
+ await new Promise((resolve) =>
130
+ setTimeout(resolve, milliseconds)
131
+ );
132
+ }
133
+ return retryRecursive(
134
+ asyncFn,
135
+ remainingRetries,
136
+ retryCountInj,
137
+ logError,
138
+ timeoutBetweenRetries,
139
+ retryPredicate
140
+ );
141
+ } else {
142
+ throw new Error("Retry predicate failed");
43
143
  }
44
- console.warn('method=retry invocation failed, retrying with remaining retries %d', remainingRetries);
45
- return retry(asyncFn,
46
- remainingRetries,
47
- logError);
48
144
  }
49
145
  }
146
+
147
+ /**
148
+ * Utility function for retrying async functions.
149
+ * @param asyncFn Function
150
+ * @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.
151
+ * @param logError Logging options
152
+ * @param timeoutBetweenRetries A function that returns the timeout between retries in milliseconds. Default is a function returning 0. The function is called with the current retry count.
153
+ * @param retryPredicate A function that returns true if the error should be retried. Default is a function that always returns true. The function is called with the error object.
154
+ * @return Promise return value
155
+ */
156
+ export async function retry<T>(
157
+ asyncFn: () => Promise<T>,
158
+ retries = 3,
159
+ logError = RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS,
160
+ timeoutBetweenRetries: TimeoutFn = timeoutFunctions.noTimeout,
161
+ retryPredicate: RetryPredicate = retryPredicates.alwaysRetry
162
+ ): Promise<T> {
163
+ retryCount = 0;
164
+
165
+ logger.debug({
166
+ message: `Retrying with ${retries} retries`,
167
+ method: "retry.retry",
168
+ });
169
+ return retryRecursive(
170
+ asyncFn,
171
+ retries,
172
+ 0,
173
+ logError,
174
+ timeoutBetweenRetries,
175
+ retryPredicate
176
+ );
177
+ }
178
+
179
+ function wrapArgsToFn<T>(
180
+ fn: (...args: unknown[]) => Promise<T>,
181
+ ...args: unknown[]
182
+ ): () => Promise<T> {
183
+ return async () => await fn(...args);
184
+ }
185
+
186
+ export async function retryRequest<T>(
187
+ request: (...args: unknown[]) => Promise<T>,
188
+ ...args: unknown[]
189
+ ): Promise<T> {
190
+ const asyncFn = wrapArgsToFn(request, ...args);
191
+ return retry(
192
+ asyncFn,
193
+ 5,
194
+ RetryLogError.LOG_LAST_RETRY_AS_ERROR_OTHERS_AS_WARNS,
195
+ timeoutFunctions.exponentialTimeout,
196
+ retryPredicates.retryBasedOnStatusCode
197
+ );
198
+ }