@mondaydotcomorg/monday-authorization 3.8.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +62 -2
  2. package/dist/authorization-internal-service.d.ts +12 -8
  3. package/dist/authorization-internal-service.d.ts.map +1 -1
  4. package/dist/authorization-internal-service.js +138 -26
  5. package/dist/authorization-service.d.ts +2 -2
  6. package/dist/authorization-service.d.ts.map +1 -1
  7. package/dist/esm/authorization-internal-service.d.ts +12 -8
  8. package/dist/esm/authorization-internal-service.d.ts.map +1 -1
  9. package/dist/esm/authorization-internal-service.mjs +138 -27
  10. package/dist/esm/authorization-service.d.ts +2 -2
  11. package/dist/esm/authorization-service.d.ts.map +1 -1
  12. package/dist/esm/index.d.ts +3 -2
  13. package/dist/esm/index.d.ts.map +1 -1
  14. package/dist/esm/memberships.d.ts.map +1 -1
  15. package/dist/esm/memberships.mjs +11 -2
  16. package/dist/esm/roles-service.d.ts.map +1 -1
  17. package/dist/esm/roles-service.mjs +6 -1
  18. package/dist/esm/types/fetch-options.d.ts +13 -0
  19. package/dist/esm/types/fetch-options.d.ts.map +1 -0
  20. package/dist/esm/types/fetch-options.mjs +1 -0
  21. package/dist/esm/utils/assignment-schema.d.ts +7 -7
  22. package/dist/index.d.ts +3 -2
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/memberships.d.ts.map +1 -1
  25. package/dist/memberships.js +11 -2
  26. package/dist/roles-service.d.ts.map +1 -1
  27. package/dist/roles-service.js +6 -1
  28. package/dist/types/fetch-options.d.ts +13 -0
  29. package/dist/types/fetch-options.d.ts.map +1 -0
  30. package/dist/types/fetch-options.js +1 -0
  31. package/dist/utils/assignment-schema.d.ts +7 -7
  32. package/package.json +2 -2
  33. package/src/authorization-internal-service.ts +199 -33
  34. package/src/authorization-service.ts +2 -2
  35. package/src/index.ts +4 -2
  36. package/src/memberships.ts +11 -2
  37. package/src/roles-service.ts +6 -1
  38. package/src/types/fetch-options.ts +18 -0
package/README.md CHANGED
@@ -31,7 +31,11 @@ await MondayAuthorization.init({
31
31
  serviceName: process.env.APP_NAME,
32
32
  },
33
33
  redisClient: redisClient,
34
- grantedFeatureRedisExpirationInSeconds: 10 * 60
34
+ grantedFeatureRedisExpirationInSeconds: 10 * 60,
35
+ mondayFetchOptions: {
36
+ maxRetries: 5,
37
+ retryDelayMS: 100,
38
+ },
35
39
  });
36
40
  startServer(...)
37
41
  ```
@@ -534,14 +538,70 @@ interface MembershipDeleteResponse {
534
538
  }
535
539
  ```
536
540
 
541
+ ### Retry Configuration (v4.0.0+)
542
+
543
+ Retry behavior is configurable via Ignite SDK. Configure the `authorization_retry_config` key in Ignite:
544
+
545
+ ```json
546
+ {
547
+ "maxRetries": 3,
548
+ "baseDelayMs": 20,
549
+ "exponentBase": 2,
550
+ "jitterMinMs": 0,
551
+ "jitterMaxMs": 1000,
552
+ "retryOnStatusPatterns": ["429", "5**"]
553
+ }
554
+ ```
555
+
556
+ **Configuration Options:**
557
+ - `maxRetries`: Maximum number of retry attempts (default: 3)
558
+ - `baseDelayMs`: Base delay in milliseconds for exponential backoff (default: 20)
559
+ - `exponentBase`: Base for exponential backoff calculation (default: 2)
560
+ - `jitterMinMs`: Minimum jitter value in milliseconds (default: 0)
561
+ - `jitterMaxMs`: Maximum jitter value in milliseconds (default: 1000)
562
+ - `retryOnStatusPatterns`: Array of status code patterns to retry on
563
+ - Exact codes: `"429"` (retry on 429)
564
+ - Wildcards: `"5**"` (retry on all 5xx errors), `"5*9"` (retry on 509, 519, 529, etc.)
565
+
566
+ **Retry Delay Formula:**
567
+ ```
568
+ delay = baseDelayMs * (exponentBase ^ (attemptCount - 1)) + random(jitterMinMs, jitterMaxMs)
569
+ ```
570
+
571
+ **Timeout Configuration:**
572
+ - Configure timeout via `outgoing-request-timeout-ms` Ignite key (default: 2000ms in production, 60000ms in development)
573
+ - Per-service overrides via `override-outgoing-request-timeout-ms` object keyed by `APP_NAME`
574
+
575
+ **Programmatic Configuration:**
576
+ You can also configure retry behavior programmatically via `mondayFetchOptions` in `init()`:
577
+
578
+ ```ts
579
+ import { init, AuthorizationFetchOptions } from '@mondaydotcomorg/monday-authorization';
580
+
581
+ const fetchOptions: AuthorizationFetchOptions = {
582
+ maxRetries: 5,
583
+ retryDelayMS: 100,
584
+ retryOn: (attempt, error, response, isTimeoutError) => {
585
+ // Custom retry logic
586
+ return isTimeoutError || (response?.status === 429);
587
+ },
588
+ };
589
+
590
+ await init({
591
+ mondayFetchOptions: fetchOptions,
592
+ // ... other options
593
+ });
594
+ ```
595
+
537
596
  ## Development
538
597
 
539
598
  ### Local Development and Testing
540
599
 
541
600
  This package includes an `ignite-local-overrides.json` file for local development and testing only. It does **not** affect consumers of this package - they use their own Ignite configuration.
542
601
 
543
- The file enables feature flags for testing:
602
+ The file enables feature flags and retry configuration for testing:
544
603
 
545
604
  - `navigate-can-action-in-scope-to-graph`: Graph API routing for `canActionInScope` methods
605
+ - `authorization_retry_config`: Retry configuration for testing (see Retry Configuration section above)
546
606
 
547
607
  Modify this file for different local test scenarios, but remember changes only affect this package's development/testing.
@@ -1,27 +1,31 @@
1
- import { fetch, MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
- import { OnRetryCallback, RetryPolicy, RetryDelayCallback } from '@mondaydotcomorg/monday-fetch-api';
1
+ import { OnRetryCallback, Response, RetryPolicy } from '@mondaydotcomorg/monday-fetch-api';
3
2
  import { IgniteClient } from '@mondaydotcomorg/ignite-sdk';
4
3
  import { BaseRequest } from './types/general';
4
+ import type { AuthorizationFetchOptions } from './types/fetch-options';
5
5
  export declare const MAX_RETRIES = 3;
6
6
  export declare const RETRY_DELAY_MS = 20;
7
7
  export declare const logger: import("bunyan");
8
+ export declare const IGNITE_RETRY_CONFIG_KEY = "authorization_retry_config";
8
9
  export declare const onRetryCallback: OnRetryCallback;
9
10
  /**
10
11
  * Exponential backoff retry delay callback
11
- * Calculates delay as: baseDelay * 2^(attemptCount - 1)
12
+ * Calculates delay as: baseDelay * (exponentBase)^(attemptCount - 1) + jitter(min..max)
12
13
  * Example: attempt 1 -> 100ms, attempt 2 -> 200ms, attempt 3 -> 400ms
13
14
  */
14
- export declare const calcDelayDurationInMs: RetryDelayCallback;
15
+ export declare const calcDelayDurationInMs: ({ attemptCount }: {
16
+ attemptCount: number;
17
+ }) => number;
15
18
  export declare class AuthorizationInternalService {
16
- static igniteClient?: IgniteClient;
19
+ static get igniteClient(): IgniteClient | undefined;
20
+ static set igniteClient(client: IgniteClient | undefined);
17
21
  static skipAuthorization(requset: BaseRequest): void;
18
22
  static markAuthorized(request: BaseRequest): void;
19
23
  static failIfNotCoveredByAuthorization(request: BaseRequest): void;
20
- static throwOnHttpErrorIfNeeded(response: Awaited<ReturnType<typeof fetch>>, placement: string): void;
24
+ static throwOnHttpErrorIfNeeded(response: Response, placement: string): void;
21
25
  static throwOnHttpError(status: number, placement: string): never;
22
26
  static generateInternalAuthToken(accountId: number, userId: number): string;
23
- static setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
24
- static getRequestFetchOptions(): MondayFetchOptions;
27
+ static setRequestFetchOptions(customMondayFetchOptions: AuthorizationFetchOptions): void;
28
+ static getRequestFetchOptions(): AuthorizationFetchOptions;
25
29
  static setIgniteClient(client: IgniteClient): void;
26
30
  static getRequestTimeout(): number;
27
31
  static getRetriesPolicy(): RetryPolicy;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-internal-service.d.ts","sourceRoot":"","sources":["../src/authorization-internal-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,EAEL,eAAe,EACf,WAAW,EACX,kBAAkB,EACnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,MAAM,kBAA2B,CAAC;AAO/C,eAAO,MAAM,eAAe,EAAE,eAM7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,kBAEnC,CAAC;AAYF,qBAAa,4BAA4B;IACvC,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACnC,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIpD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIjD,MAAM,CAAC,+BAA+B,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMlE,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAcrG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAQjE,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIlE,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB;IAO1E,MAAM,CAAC,sBAAsB,IAAI,kBAAkB;IAInD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY;IAI3C,MAAM,CAAC,iBAAiB;IA2BxB,MAAM,CAAC,gBAAgB,IAAI,WAAW;CAUvC"}
1
+ {"version":3,"file":"authorization-internal-service.d.ts","sourceRoot":"","sources":["../src/authorization-internal-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,eAAe,EACf,QAAQ,EACR,WAAW,EAEZ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGvE,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,MAAM,kBAA2B,CAAC;AAE/C,eAAO,MAAM,uBAAuB,+BAA+B,CAAC;AA4IpE,eAAO,MAAM,eAAe,EAAE,eAkB7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,kBAAkB;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,KAAG,MAOlF,CAAC;AAIF,qBAAa,4BAA4B;IACvC,MAAM,KAAK,YAAY,IAAI,YAAY,GAAG,SAAS,CAElD;IAED,MAAM,KAAK,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,EAEvD;IACD,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIpD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIjD,MAAM,CAAC,+BAA+B,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMlE,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAc5E,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAQjE,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIlE,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,EAAE,yBAAyB;IAWjF,MAAM,CAAC,sBAAsB,IAAI,yBAAyB;IAI1D,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY;IAI3C,MAAM,CAAC,iBAAiB;IA2BxB,MAAM,CAAC,gBAAgB,IAAI,WAAW;CAqBvC"}
@@ -27,37 +27,135 @@ const INTERNAL_APP_NAME = 'internal_ms';
27
27
  const MAX_RETRIES = 3;
28
28
  const RETRY_DELAY_MS = 20;
29
29
  const logger = MondayLogger__namespace.getLogger();
30
- const defaultMondayFetchOptions = {
31
- retries: MAX_RETRIES,
32
- callback: logOnFetchFail,
30
+ const IGNITE_RETRY_CONFIG_KEY = 'authorization_retry_config';
31
+ let igniteClient;
32
+ const defaultRetryConfig = {
33
+ maxRetries: MAX_RETRIES,
34
+ baseDelayMs: RETRY_DELAY_MS,
35
+ exponentBase: 2,
36
+ jitterMinMs: 0,
37
+ jitterMaxMs: 1000,
38
+ retryOnStatusPatterns: ['429', '5**'],
33
39
  };
34
- const onRetryCallback = (attempt, error) => {
35
- if (attempt == MAX_RETRIES) {
36
- logger.error({ tag: 'authorization-service', attempt, error }, 'Authorization attempt failed');
40
+ /**
41
+ * Sanitizes retry configuration values to ensure they are within valid ranges
42
+ */
43
+ function sanitizeRetryConfig(config) {
44
+ return {
45
+ ...config,
46
+ maxRetries: Math.max(0, Math.min(5, config.maxRetries)),
47
+ exponentBase: Math.max(1, Math.min(100, config.exponentBase)),
48
+ jitterMinMs: Math.max(0, config.jitterMinMs),
49
+ };
50
+ }
51
+ function getRetryConfig() {
52
+ if (!igniteClient) {
53
+ return defaultRetryConfig;
54
+ }
55
+ try {
56
+ const config = igniteClient.configuration.getObjectValue(IGNITE_RETRY_CONFIG_KEY, defaultRetryConfig);
57
+ return sanitizeRetryConfig(config);
58
+ }
59
+ catch (error) {
60
+ logger.error({ tag: 'authorization-service', error, key: IGNITE_RETRY_CONFIG_KEY, defaultValue: defaultRetryConfig }, 'Failed to get ignite retry config, using defaults');
61
+ return defaultRetryConfig;
62
+ }
63
+ }
64
+ function randomIntInclusive(min, max) {
65
+ if (max <= min) {
66
+ return min;
67
+ }
68
+ return Math.floor(min + Math.random() * (max - min + 1));
69
+ }
70
+ function buildHttpStatusMatchers(patterns) {
71
+ const matchers = [];
72
+ const addWildcardMatcher = (wildcard) => {
73
+ // Normalized 3-char pattern with digits or '*'
74
+ matchers.push((status) => {
75
+ const s = String(status);
76
+ if (s.length !== 3) {
77
+ return false;
78
+ }
79
+ for (let i = 0; i < 3; i++) {
80
+ const w = wildcard[i];
81
+ const c = s[i];
82
+ if (w === '*') {
83
+ continue;
84
+ }
85
+ if (w !== c) {
86
+ return false;
87
+ }
88
+ }
89
+ return true;
90
+ });
91
+ };
92
+ for (const raw of patterns ?? []) {
93
+ const p = String(raw).trim();
94
+ if (!p) {
95
+ continue;
96
+ }
97
+ // Exact numeric status (e.g. "429")
98
+ if (/^\d+$/.test(p)) {
99
+ const exact = Number(p);
100
+ if (Number.isFinite(exact)) {
101
+ matchers.push((status) => status === exact);
102
+ }
103
+ continue;
104
+ }
105
+ // Wildcards for 3-digit HTTP codes:
106
+ // - "5**" => 5xx
107
+ // - "5*9" => 5?9
108
+ if (/^[0-9*]+$/.test(p) && p.includes('*')) {
109
+ const normalized = p.length < 3 ? p.padEnd(3, '*') : p;
110
+ if (normalized.length !== 3) {
111
+ logger.warn({ tag: 'authorization-service', pattern: p }, 'Invalid retry status wildcard pattern (expected a 3-character pattern like "5**" or "5*9")');
112
+ }
113
+ else {
114
+ addWildcardMatcher(normalized);
115
+ }
116
+ continue;
117
+ }
118
+ logger.warn({ tag: 'authorization-service', pattern: p }, 'Invalid retry status pattern (supported: exact code like "429" or wildcard like "5**")');
119
+ }
120
+ return matchers;
121
+ }
122
+ function shouldRetryOnResponseStatus(responseOrStatus, statusMatchers) {
123
+ const status = responseOrStatus?.status;
124
+ if (typeof status !== 'number') {
125
+ return false;
126
+ }
127
+ return statusMatchers.some(matcher => matcher(status));
128
+ }
129
+ const onRetryCallback = (attempt, error, response, isTimeoutError) => {
130
+ const effectiveMaxRetries = getRetryConfig().maxRetries;
131
+ if (attempt == effectiveMaxRetries) {
132
+ logger.error({ tag: 'authorization-service', attempt, error, response, isTimeoutError }, 'Authorization attempt failed');
37
133
  }
38
134
  else {
39
- logger.info({ tag: 'authorization-service', attempt, error }, 'Authorization attempt failed, trying again');
135
+ logger.info({ tag: 'authorization-service', attempt, error, response, isTimeoutError }, 'Authorization attempt failed, trying again');
40
136
  }
41
137
  };
42
138
  /**
43
139
  * Exponential backoff retry delay callback
44
- * Calculates delay as: baseDelay * 2^(attemptCount - 1)
140
+ * Calculates delay as: baseDelay * (exponentBase)^(attemptCount - 1) + jitter(min..max)
45
141
  * Example: attempt 1 -> 100ms, attempt 2 -> 200ms, attempt 3 -> 400ms
46
142
  */
47
143
  const calcDelayDurationInMs = ({ attemptCount }) => {
48
- return RETRY_DELAY_MS * Math.pow(2, attemptCount - 1);
144
+ const { baseDelayMs, exponentBase, jitterMinMs, jitterMaxMs } = getRetryConfig();
145
+ const jitterMin = Math.min(jitterMinMs, jitterMaxMs);
146
+ const jitterMax = Math.max(jitterMinMs, jitterMaxMs);
147
+ const expDelay = baseDelayMs * Math.pow(exponentBase, attemptCount - 1);
148
+ const jitter = randomIntInclusive(jitterMin, jitterMax);
149
+ return expDelay + jitter;
49
150
  };
50
- function logOnFetchFail(retriesLeft, error) {
51
- if (retriesLeft == 0) {
52
- logger.error({ retriesLeft, error }, `Authorization attempt failed due to ${error.message}`);
151
+ let mondayFetchOptions = defaultRetryConfig;
152
+ class AuthorizationInternalService {
153
+ static get igniteClient() {
154
+ return igniteClient;
53
155
  }
54
- else {
55
- logger.info({ retriesLeft, error }, `Authorization attempt failed due to ${error.message}, trying again`);
156
+ static set igniteClient(client) {
157
+ igniteClient = client;
56
158
  }
57
- }
58
- let mondayFetchOptions = defaultMondayFetchOptions;
59
- class AuthorizationInternalService {
60
- static igniteClient;
61
159
  static skipAuthorization(requset) {
62
160
  requset.authorizationSkipPerformed = true;
63
161
  }
@@ -85,16 +183,20 @@ class AuthorizationInternalService {
85
183
  return mondayJwt.signAuthorizationHeader({ appName: INTERNAL_APP_NAME, accountId, userId });
86
184
  }
87
185
  static setRequestFetchOptions(customMondayFetchOptions) {
186
+ const sanitizedOptions = { ...customMondayFetchOptions };
187
+ if (sanitizedOptions.maxRetries !== undefined) {
188
+ sanitizedOptions.maxRetries = Math.max(0, Math.min(5, sanitizedOptions.maxRetries));
189
+ }
88
190
  mondayFetchOptions = {
89
- ...defaultMondayFetchOptions,
90
- ...customMondayFetchOptions,
191
+ ...defaultRetryConfig,
192
+ ...sanitizedOptions,
91
193
  };
92
194
  }
93
195
  static getRequestFetchOptions() {
94
196
  return mondayFetchOptions;
95
197
  }
96
198
  static setIgniteClient(client) {
97
- this.igniteClient = client;
199
+ igniteClient = client;
98
200
  }
99
201
  static getRequestTimeout() {
100
202
  const isDevEnv = process.env.NODE_ENV === 'development';
@@ -118,17 +220,27 @@ class AuthorizationInternalService {
118
220
  }
119
221
  static getRetriesPolicy() {
120
222
  const fetchOptions = AuthorizationInternalService.getRequestFetchOptions();
121
- const retryDelayMS = calcDelayDurationInMs;
223
+ const retryConfig = getRetryConfig();
224
+ const statusMatchers = buildHttpStatusMatchers(retryConfig.retryOnStatusPatterns);
225
+ const defaultRetryOn = (_attempt, _error, response, isTimeoutError) => isTimeoutError ? true : shouldRetryOnResponseStatus(response ?? undefined, statusMatchers);
226
+ // Sanitize maxRetries from fetchOptions: clamp between 0 and 5
227
+ const rawMaxRetries = fetchOptions?.maxRetries ?? retryConfig.maxRetries;
228
+ const effectiveMaxRetries = Math.max(0, Math.min(5, rawMaxRetries));
229
+ const defaultGetTimeout = timeoutsCount => calcDelayDurationInMs({ attemptCount: timeoutsCount });
122
230
  return {
123
- useRetries: !!fetchOptions.retries,
124
- maxRetries: fetchOptions.retries ?? 0,
125
- onRetry: onRetryCallback,
126
- retryDelayMS,
231
+ useRetries: fetchOptions?.useRetries ?? true,
232
+ maxRetries: effectiveMaxRetries,
233
+ retryDelayMS: fetchOptions?.retryDelayMS ?? calcDelayDurationInMs,
234
+ onRetry: fetchOptions?.callback ?? onRetryCallback,
235
+ retryOn: fetchOptions?.retryOn ?? defaultRetryOn,
236
+ timeoutRetries: fetchOptions?.timeoutRetries ?? effectiveMaxRetries,
237
+ getTimeout: fetchOptions?.getTimeout ?? defaultGetTimeout,
127
238
  };
128
239
  }
129
240
  }
130
241
 
131
242
  exports.AuthorizationInternalService = AuthorizationInternalService;
243
+ exports.IGNITE_RETRY_CONFIG_KEY = IGNITE_RETRY_CONFIG_KEY;
132
244
  exports.MAX_RETRIES = MAX_RETRIES;
133
245
  exports.RETRY_DELAY_MS = RETRY_DELAY_MS;
134
246
  exports.calcDelayDurationInMs = calcDelayDurationInMs;
@@ -1,13 +1,13 @@
1
- import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
1
  import { IgniteClient } from '@mondaydotcomorg/ignite-sdk';
3
2
  import { Action, AuthorizationObject, AuthorizationParams, AuthorizationResource } from './types/general';
4
3
  import { ScopedAction, ScopedActionPermit, ScopedActionResponseObject, ScopeOptions } from './types/scoped-actions-contracts';
4
+ import type { AuthorizationFetchOptions } from './types/fetch-options';
5
5
  export interface AuthorizeResponse {
6
6
  isAuthorized: boolean;
7
7
  unauthorizedIds?: number[];
8
8
  unauthorizedObjects?: AuthorizationObject[];
9
9
  }
10
- export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
10
+ export declare function setRequestFetchOptions(customMondayFetchOptions: AuthorizationFetchOptions): void;
11
11
  export declare class AuthorizationService {
12
12
  private static get graphApi();
13
13
  private static _graphApi?;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAGnE,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG1G,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAY1C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB,QAElF;AAMD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,KAAK,QAAQ,GAK1B;IACD,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAW;IAEpC,OAAO,CAAC,MAAM,KAAK,WAAW,GAK7B;IACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAc;IAE1C,MAAM,CAAC,eAAe,IAAI,IAAI;IAK9B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,qBAAqB,EAAE,EAClC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;WAMjB,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA4CnB,oBAAoB;mBAUpB,oBAAoB;CAmF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,qBAAqB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAiBjH"}
1
+ {"version":3,"file":"authorization-service.d.ts","sourceRoot":"","sources":["../src/authorization-service.ts"],"names":[],"mappings":"AAGA,OAAO,EAAmB,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,MAAM,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG1G,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,0BAA0B,EAC1B,YAAY,EACb,MAAM,kCAAkC,CAAC;AAM1C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAOvE,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,OAAO,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,mBAAmB,CAAC,EAAE,mBAAmB,EAAE,CAAC;CAC7C;AAED,wBAAgB,sBAAsB,CAAC,wBAAwB,EAAE,yBAAyB,QAEzF;AAMD,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,MAAM,KAAK,QAAQ,GAK1B;IACD,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAW;IAEpC,OAAO,CAAC,MAAM,KAAK,WAAW,GAK7B;IACD,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAc;IAE1C,MAAM,CAAC,eAAe,IAAI,IAAI;IAK9B,MAAM,CAAC,WAAW,CAAC,MAAC;IACpB,MAAM,CAAC,sCAAsC,CAAC,EAAE,MAAM,CAAC;IACvD,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IAEnC;;;OAGG;WACU,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,qBAAqB,EAAE,EAClC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,iBAAiB,CAAC;WAEhB,YAAY,CACvB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,2BAA2B,EAAE,mBAAmB,EAAE,GACjD,OAAO,CAAC,iBAAiB,CAAC;IAY7B;;;OAGG;WACU,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAA;KAAO,GAC1C,OAAO,CAAC,OAAO,CAAC;mBAkBE,6BAA6B;IAclD,OAAO,CAAC,MAAM,CAAC,gBAAgB;WAIlB,gBAAgB,CAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,YAAY,GAClB,OAAO,CAAC,kBAAkB,CAAC;WAMjB,wBAAwB,CACnC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,YAAY,EAAE,GAC5B,OAAO,CAAC,0BAA0B,EAAE,CAAC;mBA4CnB,oBAAoB;mBAUpB,oBAAoB;CAmF1C;AAED,wBAAgB,cAAc,CAC5B,MAAM,KAAA,EACN,sCAAsC,GAAE,MAAiD,QAY1F;AAED,wBAAsB,eAAe,kBAMpC;AAED,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,qBAAqB,EAAE,EAAE,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAiBjH"}
@@ -1,27 +1,31 @@
1
- import { fetch, MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
- import { OnRetryCallback, RetryPolicy, RetryDelayCallback } from '@mondaydotcomorg/monday-fetch-api';
1
+ import { OnRetryCallback, Response, RetryPolicy } from '@mondaydotcomorg/monday-fetch-api';
3
2
  import { IgniteClient } from '@mondaydotcomorg/ignite-sdk';
4
3
  import { BaseRequest } from './types/general';
4
+ import type { AuthorizationFetchOptions } from './types/fetch-options';
5
5
  export declare const MAX_RETRIES = 3;
6
6
  export declare const RETRY_DELAY_MS = 20;
7
7
  export declare const logger: import("bunyan");
8
+ export declare const IGNITE_RETRY_CONFIG_KEY = "authorization_retry_config";
8
9
  export declare const onRetryCallback: OnRetryCallback;
9
10
  /**
10
11
  * Exponential backoff retry delay callback
11
- * Calculates delay as: baseDelay * 2^(attemptCount - 1)
12
+ * Calculates delay as: baseDelay * (exponentBase)^(attemptCount - 1) + jitter(min..max)
12
13
  * Example: attempt 1 -> 100ms, attempt 2 -> 200ms, attempt 3 -> 400ms
13
14
  */
14
- export declare const calcDelayDurationInMs: RetryDelayCallback;
15
+ export declare const calcDelayDurationInMs: ({ attemptCount }: {
16
+ attemptCount: number;
17
+ }) => number;
15
18
  export declare class AuthorizationInternalService {
16
- static igniteClient?: IgniteClient;
19
+ static get igniteClient(): IgniteClient | undefined;
20
+ static set igniteClient(client: IgniteClient | undefined);
17
21
  static skipAuthorization(requset: BaseRequest): void;
18
22
  static markAuthorized(request: BaseRequest): void;
19
23
  static failIfNotCoveredByAuthorization(request: BaseRequest): void;
20
- static throwOnHttpErrorIfNeeded(response: Awaited<ReturnType<typeof fetch>>, placement: string): void;
24
+ static throwOnHttpErrorIfNeeded(response: Response, placement: string): void;
21
25
  static throwOnHttpError(status: number, placement: string): never;
22
26
  static generateInternalAuthToken(accountId: number, userId: number): string;
23
- static setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
24
- static getRequestFetchOptions(): MondayFetchOptions;
27
+ static setRequestFetchOptions(customMondayFetchOptions: AuthorizationFetchOptions): void;
28
+ static getRequestFetchOptions(): AuthorizationFetchOptions;
25
29
  static setIgniteClient(client: IgniteClient): void;
26
30
  static getRequestTimeout(): number;
27
31
  static getRetriesPolicy(): RetryPolicy;
@@ -1 +1 @@
1
- {"version":3,"file":"authorization-internal-service.d.ts","sourceRoot":"","sources":["../../src/authorization-internal-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAE1E,OAAO,EAEL,eAAe,EACf,WAAW,EACX,kBAAkB,EACnB,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAG9C,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,MAAM,kBAA2B,CAAC;AAO/C,eAAO,MAAM,eAAe,EAAE,eAM7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,kBAEnC,CAAC;AAYF,qBAAa,4BAA4B;IACvC,MAAM,CAAC,YAAY,CAAC,EAAE,YAAY,CAAC;IACnC,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIpD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIjD,MAAM,CAAC,+BAA+B,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMlE,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAcrG,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAQjE,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIlE,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,EAAE,kBAAkB;IAO1E,MAAM,CAAC,sBAAsB,IAAI,kBAAkB;IAInD,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY;IAI3C,MAAM,CAAC,iBAAiB;IA2BxB,MAAM,CAAC,gBAAgB,IAAI,WAAW;CAUvC"}
1
+ {"version":3,"file":"authorization-internal-service.d.ts","sourceRoot":"","sources":["../../src/authorization-internal-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,eAAe,EACf,QAAQ,EACR,WAAW,EAEZ,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAC9C,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,uBAAuB,CAAC;AAGvE,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,MAAM,kBAA2B,CAAC;AAE/C,eAAO,MAAM,uBAAuB,+BAA+B,CAAC;AA4IpE,eAAO,MAAM,eAAe,EAAE,eAkB7B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,GAAI,kBAAkB;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,KAAG,MAOlF,CAAC;AAIF,qBAAa,4BAA4B;IACvC,MAAM,KAAK,YAAY,IAAI,YAAY,GAAG,SAAS,CAElD;IAED,MAAM,KAAK,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,EAEvD;IACD,MAAM,CAAC,iBAAiB,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIpD,MAAM,CAAC,cAAc,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAIjD,MAAM,CAAC,+BAA+B,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI;IAMlE,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAc5E,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,KAAK;IAQjE,MAAM,CAAC,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAIlE,MAAM,CAAC,sBAAsB,CAAC,wBAAwB,EAAE,yBAAyB;IAWjF,MAAM,CAAC,sBAAsB,IAAI,yBAAyB;IAI1D,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,YAAY;IAI3C,MAAM,CAAC,iBAAiB;IA2BxB,MAAM,CAAC,gBAAgB,IAAI,WAAW;CAqBvC"}
@@ -5,37 +5,135 @@ const INTERNAL_APP_NAME = 'internal_ms';
5
5
  const MAX_RETRIES = 3;
6
6
  const RETRY_DELAY_MS = 20;
7
7
  const logger = MondayLogger.getLogger();
8
- const defaultMondayFetchOptions = {
9
- retries: MAX_RETRIES,
10
- callback: logOnFetchFail,
8
+ const IGNITE_RETRY_CONFIG_KEY = 'authorization_retry_config';
9
+ let igniteClient;
10
+ const defaultRetryConfig = {
11
+ maxRetries: MAX_RETRIES,
12
+ baseDelayMs: RETRY_DELAY_MS,
13
+ exponentBase: 2,
14
+ jitterMinMs: 0,
15
+ jitterMaxMs: 1000,
16
+ retryOnStatusPatterns: ['429', '5**'],
11
17
  };
12
- const onRetryCallback = (attempt, error) => {
13
- if (attempt == MAX_RETRIES) {
14
- logger.error({ tag: 'authorization-service', attempt, error }, 'Authorization attempt failed');
18
+ /**
19
+ * Sanitizes retry configuration values to ensure they are within valid ranges
20
+ */
21
+ function sanitizeRetryConfig(config) {
22
+ return {
23
+ ...config,
24
+ maxRetries: Math.max(0, Math.min(5, config.maxRetries)),
25
+ exponentBase: Math.max(1, Math.min(100, config.exponentBase)),
26
+ jitterMinMs: Math.max(0, config.jitterMinMs),
27
+ };
28
+ }
29
+ function getRetryConfig() {
30
+ if (!igniteClient) {
31
+ return defaultRetryConfig;
32
+ }
33
+ try {
34
+ const config = igniteClient.configuration.getObjectValue(IGNITE_RETRY_CONFIG_KEY, defaultRetryConfig);
35
+ return sanitizeRetryConfig(config);
36
+ }
37
+ catch (error) {
38
+ logger.error({ tag: 'authorization-service', error, key: IGNITE_RETRY_CONFIG_KEY, defaultValue: defaultRetryConfig }, 'Failed to get ignite retry config, using defaults');
39
+ return defaultRetryConfig;
40
+ }
41
+ }
42
+ function randomIntInclusive(min, max) {
43
+ if (max <= min) {
44
+ return min;
45
+ }
46
+ return Math.floor(min + Math.random() * (max - min + 1));
47
+ }
48
+ function buildHttpStatusMatchers(patterns) {
49
+ const matchers = [];
50
+ const addWildcardMatcher = (wildcard) => {
51
+ // Normalized 3-char pattern with digits or '*'
52
+ matchers.push((status) => {
53
+ const s = String(status);
54
+ if (s.length !== 3) {
55
+ return false;
56
+ }
57
+ for (let i = 0; i < 3; i++) {
58
+ const w = wildcard[i];
59
+ const c = s[i];
60
+ if (w === '*') {
61
+ continue;
62
+ }
63
+ if (w !== c) {
64
+ return false;
65
+ }
66
+ }
67
+ return true;
68
+ });
69
+ };
70
+ for (const raw of patterns ?? []) {
71
+ const p = String(raw).trim();
72
+ if (!p) {
73
+ continue;
74
+ }
75
+ // Exact numeric status (e.g. "429")
76
+ if (/^\d+$/.test(p)) {
77
+ const exact = Number(p);
78
+ if (Number.isFinite(exact)) {
79
+ matchers.push((status) => status === exact);
80
+ }
81
+ continue;
82
+ }
83
+ // Wildcards for 3-digit HTTP codes:
84
+ // - "5**" => 5xx
85
+ // - "5*9" => 5?9
86
+ if (/^[0-9*]+$/.test(p) && p.includes('*')) {
87
+ const normalized = p.length < 3 ? p.padEnd(3, '*') : p;
88
+ if (normalized.length !== 3) {
89
+ logger.warn({ tag: 'authorization-service', pattern: p }, 'Invalid retry status wildcard pattern (expected a 3-character pattern like "5**" or "5*9")');
90
+ }
91
+ else {
92
+ addWildcardMatcher(normalized);
93
+ }
94
+ continue;
95
+ }
96
+ logger.warn({ tag: 'authorization-service', pattern: p }, 'Invalid retry status pattern (supported: exact code like "429" or wildcard like "5**")');
97
+ }
98
+ return matchers;
99
+ }
100
+ function shouldRetryOnResponseStatus(responseOrStatus, statusMatchers) {
101
+ const status = responseOrStatus?.status;
102
+ if (typeof status !== 'number') {
103
+ return false;
104
+ }
105
+ return statusMatchers.some(matcher => matcher(status));
106
+ }
107
+ const onRetryCallback = (attempt, error, response, isTimeoutError) => {
108
+ const effectiveMaxRetries = getRetryConfig().maxRetries;
109
+ if (attempt == effectiveMaxRetries) {
110
+ logger.error({ tag: 'authorization-service', attempt, error, response, isTimeoutError }, 'Authorization attempt failed');
15
111
  }
16
112
  else {
17
- logger.info({ tag: 'authorization-service', attempt, error }, 'Authorization attempt failed, trying again');
113
+ logger.info({ tag: 'authorization-service', attempt, error, response, isTimeoutError }, 'Authorization attempt failed, trying again');
18
114
  }
19
115
  };
20
116
  /**
21
117
  * Exponential backoff retry delay callback
22
- * Calculates delay as: baseDelay * 2^(attemptCount - 1)
118
+ * Calculates delay as: baseDelay * (exponentBase)^(attemptCount - 1) + jitter(min..max)
23
119
  * Example: attempt 1 -> 100ms, attempt 2 -> 200ms, attempt 3 -> 400ms
24
120
  */
25
121
  const calcDelayDurationInMs = ({ attemptCount }) => {
26
- return RETRY_DELAY_MS * Math.pow(2, attemptCount - 1);
122
+ const { baseDelayMs, exponentBase, jitterMinMs, jitterMaxMs } = getRetryConfig();
123
+ const jitterMin = Math.min(jitterMinMs, jitterMaxMs);
124
+ const jitterMax = Math.max(jitterMinMs, jitterMaxMs);
125
+ const expDelay = baseDelayMs * Math.pow(exponentBase, attemptCount - 1);
126
+ const jitter = randomIntInclusive(jitterMin, jitterMax);
127
+ return expDelay + jitter;
27
128
  };
28
- function logOnFetchFail(retriesLeft, error) {
29
- if (retriesLeft == 0) {
30
- logger.error({ retriesLeft, error }, `Authorization attempt failed due to ${error.message}`);
129
+ let mondayFetchOptions = defaultRetryConfig;
130
+ class AuthorizationInternalService {
131
+ static get igniteClient() {
132
+ return igniteClient;
31
133
  }
32
- else {
33
- logger.info({ retriesLeft, error }, `Authorization attempt failed due to ${error.message}, trying again`);
134
+ static set igniteClient(client) {
135
+ igniteClient = client;
34
136
  }
35
- }
36
- let mondayFetchOptions = defaultMondayFetchOptions;
37
- class AuthorizationInternalService {
38
- static igniteClient;
39
137
  static skipAuthorization(requset) {
40
138
  requset.authorizationSkipPerformed = true;
41
139
  }
@@ -63,16 +161,20 @@ class AuthorizationInternalService {
63
161
  return signAuthorizationHeader({ appName: INTERNAL_APP_NAME, accountId, userId });
64
162
  }
65
163
  static setRequestFetchOptions(customMondayFetchOptions) {
164
+ const sanitizedOptions = { ...customMondayFetchOptions };
165
+ if (sanitizedOptions.maxRetries !== undefined) {
166
+ sanitizedOptions.maxRetries = Math.max(0, Math.min(5, sanitizedOptions.maxRetries));
167
+ }
66
168
  mondayFetchOptions = {
67
- ...defaultMondayFetchOptions,
68
- ...customMondayFetchOptions,
169
+ ...defaultRetryConfig,
170
+ ...sanitizedOptions,
69
171
  };
70
172
  }
71
173
  static getRequestFetchOptions() {
72
174
  return mondayFetchOptions;
73
175
  }
74
176
  static setIgniteClient(client) {
75
- this.igniteClient = client;
177
+ igniteClient = client;
76
178
  }
77
179
  static getRequestTimeout() {
78
180
  const isDevEnv = process.env.NODE_ENV === 'development';
@@ -96,14 +198,23 @@ class AuthorizationInternalService {
96
198
  }
97
199
  static getRetriesPolicy() {
98
200
  const fetchOptions = AuthorizationInternalService.getRequestFetchOptions();
99
- const retryDelayMS = calcDelayDurationInMs;
201
+ const retryConfig = getRetryConfig();
202
+ const statusMatchers = buildHttpStatusMatchers(retryConfig.retryOnStatusPatterns);
203
+ const defaultRetryOn = (_attempt, _error, response, isTimeoutError) => isTimeoutError ? true : shouldRetryOnResponseStatus(response ?? undefined, statusMatchers);
204
+ // Sanitize maxRetries from fetchOptions: clamp between 0 and 5
205
+ const rawMaxRetries = fetchOptions?.maxRetries ?? retryConfig.maxRetries;
206
+ const effectiveMaxRetries = Math.max(0, Math.min(5, rawMaxRetries));
207
+ const defaultGetTimeout = timeoutsCount => calcDelayDurationInMs({ attemptCount: timeoutsCount });
100
208
  return {
101
- useRetries: !!fetchOptions.retries,
102
- maxRetries: fetchOptions.retries ?? 0,
103
- onRetry: onRetryCallback,
104
- retryDelayMS,
209
+ useRetries: fetchOptions?.useRetries ?? true,
210
+ maxRetries: effectiveMaxRetries,
211
+ retryDelayMS: fetchOptions?.retryDelayMS ?? calcDelayDurationInMs,
212
+ onRetry: fetchOptions?.callback ?? onRetryCallback,
213
+ retryOn: fetchOptions?.retryOn ?? defaultRetryOn,
214
+ timeoutRetries: fetchOptions?.timeoutRetries ?? effectiveMaxRetries,
215
+ getTimeout: fetchOptions?.getTimeout ?? defaultGetTimeout,
105
216
  };
106
217
  }
107
218
  }
108
219
 
109
- export { AuthorizationInternalService, MAX_RETRIES, RETRY_DELAY_MS, calcDelayDurationInMs, logger, onRetryCallback };
220
+ export { AuthorizationInternalService, IGNITE_RETRY_CONFIG_KEY, MAX_RETRIES, RETRY_DELAY_MS, calcDelayDurationInMs, logger, onRetryCallback };
@@ -1,13 +1,13 @@
1
- import { MondayFetchOptions } from '@mondaydotcomorg/monday-fetch';
2
1
  import { IgniteClient } from '@mondaydotcomorg/ignite-sdk';
3
2
  import { Action, AuthorizationObject, AuthorizationParams, AuthorizationResource } from './types/general';
4
3
  import { ScopedAction, ScopedActionPermit, ScopedActionResponseObject, ScopeOptions } from './types/scoped-actions-contracts';
4
+ import type { AuthorizationFetchOptions } from './types/fetch-options';
5
5
  export interface AuthorizeResponse {
6
6
  isAuthorized: boolean;
7
7
  unauthorizedIds?: number[];
8
8
  unauthorizedObjects?: AuthorizationObject[];
9
9
  }
10
- export declare function setRequestFetchOptions(customMondayFetchOptions: MondayFetchOptions): void;
10
+ export declare function setRequestFetchOptions(customMondayFetchOptions: AuthorizationFetchOptions): void;
11
11
  export declare class AuthorizationService {
12
12
  private static get graphApi();
13
13
  private static _graphApi?;