@workos-inc/node 7.29.1 → 7.30.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.
@@ -12,6 +12,8 @@ export declare class FetchHttpClient extends HttpClient implements HttpClientInt
12
12
  put<Entity = any>(path: string, entity: Entity, options: RequestOptions): Promise<HttpClientResponseInterface>;
13
13
  delete(path: string, options: RequestOptions): Promise<HttpClientResponseInterface>;
14
14
  private fetchRequest;
15
+ private fetchRequestWithRetry;
16
+ private shouldRetryRequest;
15
17
  }
16
18
  export declare class FetchHttpClientResponse extends HttpClientResponse implements HttpClientResponseInterface {
17
19
  _res: Response;
@@ -32,25 +32,45 @@ class FetchHttpClient extends http_client_1.HttpClient {
32
32
  get(path, options) {
33
33
  return __awaiter(this, void 0, void 0, function* () {
34
34
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
35
- return yield this.fetchRequest(resourceURL, 'GET', null, options.headers);
35
+ if (path.startsWith('/fga/')) {
36
+ return yield this.fetchRequestWithRetry(resourceURL, 'GET', null, options.headers);
37
+ }
38
+ else {
39
+ return yield this.fetchRequest(resourceURL, 'GET', null, options.headers);
40
+ }
36
41
  });
37
42
  }
38
43
  post(path, entity, options) {
39
44
  return __awaiter(this, void 0, void 0, function* () {
40
45
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
41
- return yield this.fetchRequest(resourceURL, 'POST', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
46
+ if (path.startsWith('/fga/')) {
47
+ return yield this.fetchRequestWithRetry(resourceURL, 'POST', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
48
+ }
49
+ else {
50
+ return yield this.fetchRequest(resourceURL, 'POST', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
51
+ }
42
52
  });
43
53
  }
44
54
  put(path, entity, options) {
45
55
  return __awaiter(this, void 0, void 0, function* () {
46
56
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
47
- return yield this.fetchRequest(resourceURL, 'PUT', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
57
+ if (path.startsWith('/fga/')) {
58
+ return yield this.fetchRequestWithRetry(resourceURL, 'PUT', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
59
+ }
60
+ else {
61
+ return yield this.fetchRequest(resourceURL, 'PUT', http_client_1.HttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
62
+ }
48
63
  });
49
64
  }
50
65
  delete(path, options) {
51
66
  return __awaiter(this, void 0, void 0, function* () {
52
67
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
53
- return yield this.fetchRequest(resourceURL, 'DELETE', null, options.headers);
68
+ if (path.startsWith('/fga/')) {
69
+ return yield this.fetchRequestWithRetry(resourceURL, 'DELETE', null, options.headers);
70
+ }
71
+ else {
72
+ return yield this.fetchRequest(resourceURL, 'DELETE', null, options.headers);
73
+ }
54
74
  });
55
75
  }
56
76
  fetchRequest(url, method, body, headers) {
@@ -80,6 +100,46 @@ class FetchHttpClient extends http_client_1.HttpClient {
80
100
  return new FetchHttpClientResponse(res);
81
101
  });
82
102
  }
103
+ fetchRequestWithRetry(url, method, body, headers) {
104
+ return __awaiter(this, void 0, void 0, function* () {
105
+ let response;
106
+ let retryAttempts = 1;
107
+ const makeRequest = () => __awaiter(this, void 0, void 0, function* () {
108
+ let requestError = null;
109
+ try {
110
+ response = yield this.fetchRequest(url, method, body, headers);
111
+ }
112
+ catch (e) {
113
+ requestError = e;
114
+ }
115
+ if (this.shouldRetryRequest(requestError, retryAttempts)) {
116
+ retryAttempts++;
117
+ yield this.sleep(retryAttempts);
118
+ return makeRequest();
119
+ }
120
+ if (requestError != null) {
121
+ throw requestError;
122
+ }
123
+ return response;
124
+ });
125
+ return makeRequest();
126
+ });
127
+ }
128
+ shouldRetryRequest(requestError, retryAttempt) {
129
+ if (retryAttempt > this.MAX_RETRY_ATTEMPTS) {
130
+ return false;
131
+ }
132
+ if (requestError != null) {
133
+ if (requestError instanceof TypeError) {
134
+ return true;
135
+ }
136
+ if (requestError instanceof http_client_1.HttpClientError &&
137
+ this.RETRY_STATUS_CODES.includes(requestError.response.status)) {
138
+ return true;
139
+ }
140
+ }
141
+ return false;
142
+ }
83
143
  }
84
144
  exports.FetchHttpClient = FetchHttpClient;
85
145
  // tslint:disable-next-line
@@ -2,6 +2,10 @@ import { HttpClientInterface, HttpClientResponseInterface, RequestHeaders, Reque
2
2
  export declare abstract class HttpClient implements HttpClientInterface {
3
3
  readonly baseURL: string;
4
4
  readonly options?: RequestInit | undefined;
5
+ readonly MAX_RETRY_ATTEMPTS = 3;
6
+ readonly BACKOFF_MULTIPLIER = 1.5;
7
+ readonly MINIMUM_SLEEP_TIME_IN_MILLISECONDS = 500;
8
+ readonly RETRY_STATUS_CODES: number[];
5
9
  constructor(baseURL: string, options?: RequestInit | undefined);
6
10
  /** The HTTP client name used for diagnostics */
7
11
  getClientName(): string;
@@ -14,6 +18,8 @@ export declare abstract class HttpClient implements HttpClientInterface {
14
18
  static getQueryString(queryObj?: Record<string, any>): string | undefined;
15
19
  static getContentTypeHeader(entity: any): RequestHeaders | undefined;
16
20
  static getBody(entity: any): BodyInit | null | undefined;
21
+ private getSleepTimeInMilliseconds;
22
+ sleep: (retryAttempt: number) => Promise<unknown>;
17
23
  }
18
24
  export declare abstract class HttpClientResponse implements HttpClientResponseInterface {
19
25
  _statusCode: number;
@@ -5,6 +5,11 @@ class HttpClient {
5
5
  constructor(baseURL, options) {
6
6
  this.baseURL = baseURL;
7
7
  this.options = options;
8
+ this.MAX_RETRY_ATTEMPTS = 3;
9
+ this.BACKOFF_MULTIPLIER = 1.5;
10
+ this.MINIMUM_SLEEP_TIME_IN_MILLISECONDS = 500;
11
+ this.RETRY_STATUS_CODES = [500, 502, 504];
12
+ this.sleep = (retryAttempt) => new Promise((resolve) => setTimeout(resolve, this.getSleepTimeInMilliseconds(retryAttempt)));
8
13
  }
9
14
  /** The HTTP client name used for diagnostics */
10
15
  getClientName() {
@@ -47,6 +52,12 @@ class HttpClient {
47
52
  }
48
53
  return JSON.stringify(entity);
49
54
  }
55
+ getSleepTimeInMilliseconds(retryAttempt) {
56
+ const sleepTime = this.MINIMUM_SLEEP_TIME_IN_MILLISECONDS *
57
+ Math.pow(this.BACKOFF_MULTIPLIER, retryAttempt);
58
+ const jitter = Math.random() + 0.5;
59
+ return sleepTime * jitter;
60
+ }
50
61
  }
51
62
  exports.HttpClient = HttpClient;
52
63
  // tslint:disable-next-line
@@ -14,6 +14,8 @@ export declare class NodeHttpClient extends HttpClient implements HttpClientInte
14
14
  put<Entity = any>(path: string, entity: Entity, options: RequestOptions): Promise<HttpClientResponseInterface>;
15
15
  delete(path: string, options: RequestOptions): Promise<HttpClientResponseInterface>;
16
16
  private nodeRequest;
17
+ private nodeRequestWithRetry;
18
+ private shouldRetryRequest;
17
19
  }
18
20
  export declare class NodeHttpClientResponse extends HttpClientResponse implements HttpClientResponseInterface {
19
21
  _res: http_.IncomingMessage;
@@ -67,25 +67,45 @@ class NodeHttpClient extends http_client_1.HttpClient {
67
67
  get(path, options) {
68
68
  return __awaiter(this, void 0, void 0, function* () {
69
69
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
70
- return yield this.nodeRequest(resourceURL, 'GET', null, options.headers);
70
+ if (path.startsWith('/fga/')) {
71
+ return yield this.nodeRequestWithRetry(resourceURL, 'GET', null, options.headers);
72
+ }
73
+ else {
74
+ return yield this.nodeRequest(resourceURL, 'GET', null, options.headers);
75
+ }
71
76
  });
72
77
  }
73
78
  post(path, entity, options) {
74
79
  return __awaiter(this, void 0, void 0, function* () {
75
80
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
76
- return yield this.nodeRequest(resourceURL, 'POST', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
81
+ if (path.startsWith('/fga/')) {
82
+ return yield this.nodeRequestWithRetry(resourceURL, 'POST', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
83
+ }
84
+ else {
85
+ return yield this.nodeRequest(resourceURL, 'POST', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
86
+ }
77
87
  });
78
88
  }
79
89
  put(path, entity, options) {
80
90
  return __awaiter(this, void 0, void 0, function* () {
81
91
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
82
- return yield this.nodeRequest(resourceURL, 'PUT', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
92
+ if (path.startsWith('/fga/')) {
93
+ return yield this.nodeRequestWithRetry(resourceURL, 'PUT', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
94
+ }
95
+ else {
96
+ return yield this.nodeRequest(resourceURL, 'PUT', NodeHttpClient.getBody(entity), Object.assign(Object.assign({}, http_client_1.HttpClient.getContentTypeHeader(entity)), options.headers));
97
+ }
83
98
  });
84
99
  }
85
100
  delete(path, options) {
86
101
  return __awaiter(this, void 0, void 0, function* () {
87
102
  const resourceURL = http_client_1.HttpClient.getResourceURL(this.baseURL, path, options.params);
88
- return yield this.nodeRequest(resourceURL, 'DELETE', null, options.headers);
103
+ if (path.startsWith('/fga/')) {
104
+ return yield this.nodeRequestWithRetry(resourceURL, 'DELETE', null, options.headers);
105
+ }
106
+ else {
107
+ return yield this.nodeRequest(resourceURL, 'DELETE', null, options.headers);
108
+ }
89
109
  });
90
110
  }
91
111
  nodeRequest(url, method, body, headers) {
@@ -126,6 +146,68 @@ class NodeHttpClient extends http_client_1.HttpClient {
126
146
  });
127
147
  });
128
148
  }
149
+ nodeRequestWithRetry(url, method, body, headers) {
150
+ var _a, _b;
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ const isSecureConnection = url.startsWith('https');
153
+ const agent = isSecureConnection ? this.httpsAgent : this.httpAgent;
154
+ const lib = isSecureConnection ? https : http;
155
+ const { 'User-Agent': userAgent } = (_a = this.options) === null || _a === void 0 ? void 0 : _a.headers;
156
+ const options = {
157
+ method,
158
+ headers: Object.assign(Object.assign(Object.assign({ Accept: 'application/json, text/plain, */*', 'Content-Type': 'application/json' }, (_b = this.options) === null || _b === void 0 ? void 0 : _b.headers), headers), { 'User-Agent': this.addClientToUserAgent(userAgent.toString()) }),
159
+ agent,
160
+ };
161
+ let retryAttempts = 1;
162
+ const makeRequest = () => __awaiter(this, void 0, void 0, function* () {
163
+ return new Promise((resolve, reject) => {
164
+ const req = lib.request(url, options, (res) => __awaiter(this, void 0, void 0, function* () {
165
+ const clientResponse = new NodeHttpClientResponse(res);
166
+ if (this.shouldRetryRequest(res, retryAttempts)) {
167
+ retryAttempts++;
168
+ yield this.sleep(retryAttempts);
169
+ return makeRequest().then(resolve).catch(reject);
170
+ }
171
+ if (res.statusCode &&
172
+ (res.statusCode < 200 || res.statusCode > 299)) {
173
+ reject(new http_client_1.HttpClientError({
174
+ message: res.statusMessage,
175
+ response: {
176
+ status: res.statusCode,
177
+ headers: res.headers,
178
+ data: yield clientResponse.toJSON(),
179
+ },
180
+ }));
181
+ }
182
+ resolve(new NodeHttpClientResponse(res));
183
+ }));
184
+ req.on('error', (err) => __awaiter(this, void 0, void 0, function* () {
185
+ if (err != null && err instanceof TypeError) {
186
+ retryAttempts++;
187
+ yield this.sleep(retryAttempts);
188
+ return makeRequest().then(resolve).catch(reject);
189
+ }
190
+ }));
191
+ if (body) {
192
+ req.setHeader('Content-Length', Buffer.byteLength(body));
193
+ req.write(body);
194
+ }
195
+ req.end();
196
+ });
197
+ });
198
+ return makeRequest();
199
+ });
200
+ }
201
+ shouldRetryRequest(response, retryAttempt) {
202
+ if (retryAttempt > this.MAX_RETRY_ATTEMPTS) {
203
+ return false;
204
+ }
205
+ if (response != null &&
206
+ this.RETRY_STATUS_CODES.includes(response.statusCode)) {
207
+ return true;
208
+ }
209
+ return false;
210
+ }
129
211
  }
130
212
  exports.NodeHttpClient = NodeHttpClient;
131
213
  // tslint:disable-next-line
@@ -16,6 +16,7 @@ const jest_fetch_mock_1 = __importDefault(require("jest-fetch-mock"));
16
16
  const test_utils_1 = require("../common/utils/test-utils");
17
17
  const workos_1 = require("../workos");
18
18
  const interfaces_1 = require("./interfaces");
19
+ const exceptions_1 = require("../common/exceptions");
19
20
  const workos = new workos_1.WorkOS('sk_test_Sz3IQjepeSWaI4cMS4ms4sMuU');
20
21
  describe('FGA', () => {
21
22
  beforeEach(() => jest_fetch_mock_1.default.resetMocks());
@@ -46,6 +47,60 @@ describe('FGA', () => {
46
47
  isImplicit: false,
47
48
  });
48
49
  }));
50
+ it('makes check request after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
51
+ (0, test_utils_1.fetchOnce)({}, {
52
+ status: 500,
53
+ });
54
+ (0, test_utils_1.fetchOnce)({
55
+ result: 'authorized',
56
+ is_implicit: false,
57
+ });
58
+ const checkResult = yield workos.fga.check({
59
+ checks: [
60
+ {
61
+ resource: {
62
+ resourceType: 'role',
63
+ resourceId: 'admin',
64
+ },
65
+ relation: 'member',
66
+ subject: {
67
+ resourceType: 'user',
68
+ resourceId: 'user_123',
69
+ },
70
+ },
71
+ ],
72
+ });
73
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/check');
74
+ expect(checkResult).toMatchObject({
75
+ result: 'authorized',
76
+ isImplicit: false,
77
+ });
78
+ }));
79
+ it('fails check request after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
80
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
81
+ message: 'Internal Server Error',
82
+ }), { status: 500 });
83
+ try {
84
+ yield workos.fga.check({
85
+ checks: [
86
+ {
87
+ resource: {
88
+ resourceType: 'role',
89
+ resourceId: 'admin',
90
+ },
91
+ relation: 'member',
92
+ subject: {
93
+ resourceType: 'user',
94
+ resourceId: 'user_123',
95
+ },
96
+ },
97
+ ],
98
+ });
99
+ }
100
+ catch (e) {
101
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
102
+ }
103
+ }), 8000);
49
104
  });
50
105
  describe('createResource', () => {
51
106
  it('creates resource', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -65,6 +120,42 @@ describe('FGA', () => {
65
120
  resourceId: 'admin',
66
121
  });
67
122
  }));
123
+ it('creates resource after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
124
+ (0, test_utils_1.fetchOnce)({}, {
125
+ status: 502,
126
+ });
127
+ (0, test_utils_1.fetchOnce)({
128
+ resource_type: 'role',
129
+ resource_id: 'admin',
130
+ });
131
+ const resource = yield workos.fga.createResource({
132
+ resource: {
133
+ resourceType: 'role',
134
+ resourceId: 'admin',
135
+ },
136
+ });
137
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources');
138
+ expect(resource).toMatchObject({
139
+ resourceType: 'role',
140
+ resourceId: 'admin',
141
+ });
142
+ }));
143
+ it('fails to creates resource after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
144
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
145
+ message: 'Internal Server Error',
146
+ }), { status: 500 });
147
+ try {
148
+ yield workos.fga.createResource({
149
+ resource: {
150
+ resourceType: 'role',
151
+ resourceId: 'admin',
152
+ },
153
+ });
154
+ }
155
+ catch (e) {
156
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
157
+ }
158
+ }), 8000);
68
159
  it('creates resource with metadata', () => __awaiter(void 0, void 0, void 0, function* () {
69
160
  (0, test_utils_1.fetchOnce)({
70
161
  resource_type: 'role',
@@ -108,6 +199,38 @@ describe('FGA', () => {
108
199
  resourceId: 'admin',
109
200
  });
110
201
  }));
202
+ it('gets resource after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
203
+ (0, test_utils_1.fetchOnce)({}, {
204
+ status: 504,
205
+ });
206
+ (0, test_utils_1.fetchOnce)({
207
+ resource_type: 'role',
208
+ resource_id: 'admin',
209
+ });
210
+ const resource = yield workos.fga.getResource({
211
+ resourceType: 'role',
212
+ resourceId: 'admin',
213
+ });
214
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources/role/admin');
215
+ expect(resource).toMatchObject({
216
+ resourceType: 'role',
217
+ resourceId: 'admin',
218
+ });
219
+ }));
220
+ it('fails to get resource after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
221
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
222
+ message: 'Internal Server Error',
223
+ }), { status: 500 });
224
+ try {
225
+ yield workos.fga.getResource({
226
+ resourceType: 'role',
227
+ resourceId: 'admin',
228
+ });
229
+ }
230
+ catch (e) {
231
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
232
+ }
233
+ }), 8000);
111
234
  });
112
235
  describe('listResources', () => {
113
236
  it('lists resources', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -140,6 +263,53 @@ describe('FGA', () => {
140
263
  },
141
264
  ]);
142
265
  }));
266
+ it('lists resources after two retries', () => __awaiter(void 0, void 0, void 0, function* () {
267
+ (0, test_utils_1.fetchOnce)({}, {
268
+ status: 502,
269
+ });
270
+ (0, test_utils_1.fetchOnce)({}, {
271
+ status: 500,
272
+ });
273
+ (0, test_utils_1.fetchOnce)({
274
+ data: [
275
+ {
276
+ resource_type: 'role',
277
+ resource_id: 'admin',
278
+ },
279
+ {
280
+ resource_type: 'role',
281
+ resource_id: 'manager',
282
+ },
283
+ ],
284
+ list_metadata: {
285
+ before: null,
286
+ after: null,
287
+ },
288
+ });
289
+ const { data: resources } = yield workos.fga.listResources();
290
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources');
291
+ expect(resources).toMatchObject([
292
+ {
293
+ resourceType: 'role',
294
+ resourceId: 'admin',
295
+ },
296
+ {
297
+ resourceType: 'role',
298
+ resourceId: 'manager',
299
+ },
300
+ ]);
301
+ }));
302
+ it('fails to list resources after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
303
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
304
+ message: 'Internal Server Error',
305
+ }), { status: 500 });
306
+ try {
307
+ yield workos.fga.listResources();
308
+ }
309
+ catch (e) {
310
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
311
+ }
312
+ }), 8000);
143
313
  it('sends correct params when filtering', () => __awaiter(void 0, void 0, void 0, function* () {
144
314
  (0, test_utils_1.fetchOnce)({
145
315
  data: [
@@ -177,6 +347,32 @@ describe('FGA', () => {
177
347
  expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources/role/admin');
178
348
  expect(response).toBeUndefined();
179
349
  }));
350
+ it('should delete resource after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
351
+ (0, test_utils_1.fetchOnce)({}, {
352
+ status: 500,
353
+ });
354
+ (0, test_utils_1.fetchOnce)();
355
+ const response = yield workos.fga.deleteResource({
356
+ resourceType: 'role',
357
+ resourceId: 'admin',
358
+ });
359
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources/role/admin');
360
+ expect(response).toBeUndefined();
361
+ }));
362
+ it('fails to delete resource after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
363
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
364
+ message: 'Internal Server Error',
365
+ }), { status: 500 });
366
+ try {
367
+ yield workos.fga.deleteResource({
368
+ resourceType: 'role',
369
+ resourceId: 'admin',
370
+ });
371
+ }
372
+ catch (e) {
373
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
374
+ }
375
+ }), 8000);
180
376
  });
181
377
  describe('batchWriteResources', () => {
182
378
  it('batch create resources', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -244,6 +440,110 @@ describe('FGA', () => {
244
440
  },
245
441
  ]);
246
442
  }));
443
+ it('batch create resources after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
444
+ (0, test_utils_1.fetchOnce)({}, {
445
+ status: 500,
446
+ });
447
+ (0, test_utils_1.fetchOnce)({
448
+ data: [
449
+ {
450
+ resource_type: 'role',
451
+ resource_id: 'admin',
452
+ meta: {
453
+ description: 'The admin role',
454
+ },
455
+ },
456
+ {
457
+ resource_type: 'role',
458
+ resource_id: 'manager',
459
+ },
460
+ {
461
+ resource_type: 'role',
462
+ resource_id: 'employee',
463
+ },
464
+ ],
465
+ });
466
+ const createdResources = yield workos.fga.batchWriteResources({
467
+ op: interfaces_1.ResourceOp.Create,
468
+ resources: [
469
+ {
470
+ resource: {
471
+ resourceType: 'role',
472
+ resourceId: 'admin',
473
+ },
474
+ meta: {
475
+ description: 'The admin role',
476
+ },
477
+ },
478
+ {
479
+ resource: {
480
+ resourceType: 'role',
481
+ resourceId: 'manager',
482
+ },
483
+ },
484
+ {
485
+ resource: {
486
+ resourceType: 'role',
487
+ resourceId: 'employee',
488
+ },
489
+ },
490
+ ],
491
+ });
492
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/resources/batch');
493
+ expect(createdResources).toMatchObject([
494
+ {
495
+ resourceType: 'role',
496
+ resourceId: 'admin',
497
+ meta: {
498
+ description: 'The admin role',
499
+ },
500
+ },
501
+ {
502
+ resourceType: 'role',
503
+ resourceId: 'manager',
504
+ },
505
+ {
506
+ resourceType: 'role',
507
+ resourceId: 'employee',
508
+ },
509
+ ]);
510
+ }));
511
+ it('fails to batch create resources after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
512
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
513
+ message: 'Internal Server Error',
514
+ }), { status: 500 });
515
+ try {
516
+ yield workos.fga.batchWriteResources({
517
+ op: interfaces_1.ResourceOp.Create,
518
+ resources: [
519
+ {
520
+ resource: {
521
+ resourceType: 'role',
522
+ resourceId: 'admin',
523
+ },
524
+ meta: {
525
+ description: 'The admin role',
526
+ },
527
+ },
528
+ {
529
+ resource: {
530
+ resourceType: 'role',
531
+ resourceId: 'manager',
532
+ },
533
+ },
534
+ {
535
+ resource: {
536
+ resourceType: 'role',
537
+ resourceId: 'employee',
538
+ },
539
+ },
540
+ ],
541
+ });
542
+ }
543
+ catch (e) {
544
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
545
+ }
546
+ }), 8000);
247
547
  it('batch delete resources', () => __awaiter(void 0, void 0, void 0, function* () {
248
548
  (0, test_utils_1.fetchOnce)({
249
549
  data: [
@@ -365,6 +665,62 @@ describe('FGA', () => {
365
665
  warrantToken: 'some_token',
366
666
  });
367
667
  }));
668
+ it('should create warrant after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
669
+ (0, test_utils_1.fetchOnce)({}, {
670
+ status: 500,
671
+ });
672
+ (0, test_utils_1.fetchOnce)({
673
+ warrant_token: 'some_token',
674
+ });
675
+ const warrantToken = yield workos.fga.writeWarrant({
676
+ op: interfaces_1.WarrantOp.Create,
677
+ resource: {
678
+ resourceType: 'role',
679
+ resourceId: 'admin',
680
+ },
681
+ relation: 'member',
682
+ subject: {
683
+ resourceType: 'user',
684
+ resourceId: 'user_123',
685
+ },
686
+ });
687
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/warrants');
688
+ expect((0, test_utils_1.fetchBody)()).toEqual({
689
+ op: 'create',
690
+ resource_type: 'role',
691
+ resource_id: 'admin',
692
+ relation: 'member',
693
+ subject: {
694
+ resource_type: 'user',
695
+ resource_id: 'user_123',
696
+ },
697
+ });
698
+ expect(warrantToken).toMatchObject({
699
+ warrantToken: 'some_token',
700
+ });
701
+ }));
702
+ it('fails to create warrant after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
703
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
704
+ message: 'Internal Server Error',
705
+ }), { status: 500 });
706
+ try {
707
+ yield workos.fga.writeWarrant({
708
+ op: interfaces_1.WarrantOp.Create,
709
+ resource: {
710
+ resourceType: 'role',
711
+ resourceId: 'admin',
712
+ },
713
+ relation: 'member',
714
+ subject: {
715
+ resourceType: 'user',
716
+ resourceId: 'user_123',
717
+ },
718
+ });
719
+ }
720
+ catch (e) {
721
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
722
+ }
723
+ }), 8000);
368
724
  it('should delete warrant with delete op', () => __awaiter(void 0, void 0, void 0, function* () {
369
725
  (0, test_utils_1.fetchOnce)({
370
726
  warrant_token: 'some_token',
@@ -475,6 +831,133 @@ describe('FGA', () => {
475
831
  warrantToken: 'some_token',
476
832
  });
477
833
  }));
834
+ it('should batch write warrants after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
835
+ (0, test_utils_1.fetchOnce)({}, {
836
+ status: 500,
837
+ });
838
+ (0, test_utils_1.fetchOnce)({
839
+ warrant_token: 'some_token',
840
+ });
841
+ const warrantToken = yield workos.fga.batchWriteWarrants([
842
+ {
843
+ resource: {
844
+ resourceType: 'role',
845
+ resourceId: 'admin',
846
+ },
847
+ relation: 'member',
848
+ subject: {
849
+ resourceType: 'user',
850
+ resourceId: 'user_123',
851
+ },
852
+ },
853
+ {
854
+ op: interfaces_1.WarrantOp.Create,
855
+ resource: {
856
+ resourceType: 'role',
857
+ resourceId: 'admin',
858
+ },
859
+ relation: 'member',
860
+ subject: {
861
+ resourceType: 'user',
862
+ resourceId: 'user_124',
863
+ },
864
+ },
865
+ {
866
+ op: interfaces_1.WarrantOp.Delete,
867
+ resource: {
868
+ resourceType: 'role',
869
+ resourceId: 'admin',
870
+ },
871
+ relation: 'member',
872
+ subject: {
873
+ resourceType: 'user',
874
+ resourceId: 'user_125',
875
+ },
876
+ },
877
+ ]);
878
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/warrants');
879
+ expect((0, test_utils_1.fetchBody)()).toEqual([
880
+ {
881
+ resource_type: 'role',
882
+ resource_id: 'admin',
883
+ relation: 'member',
884
+ subject: {
885
+ resource_type: 'user',
886
+ resource_id: 'user_123',
887
+ },
888
+ },
889
+ {
890
+ op: 'create',
891
+ resource_type: 'role',
892
+ resource_id: 'admin',
893
+ relation: 'member',
894
+ subject: {
895
+ resource_type: 'user',
896
+ resource_id: 'user_124',
897
+ },
898
+ },
899
+ {
900
+ op: 'delete',
901
+ resource_type: 'role',
902
+ resource_id: 'admin',
903
+ relation: 'member',
904
+ subject: {
905
+ resource_type: 'user',
906
+ resource_id: 'user_125',
907
+ },
908
+ },
909
+ ]);
910
+ expect(warrantToken).toMatchObject({
911
+ warrantToken: 'some_token',
912
+ });
913
+ }));
914
+ it('fails to batch write warrants after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
915
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
916
+ message: 'Internal Server Error',
917
+ }), { status: 500 });
918
+ try {
919
+ yield workos.fga.batchWriteWarrants([
920
+ {
921
+ resource: {
922
+ resourceType: 'role',
923
+ resourceId: 'admin',
924
+ },
925
+ relation: 'member',
926
+ subject: {
927
+ resourceType: 'user',
928
+ resourceId: 'user_123',
929
+ },
930
+ },
931
+ {
932
+ op: interfaces_1.WarrantOp.Create,
933
+ resource: {
934
+ resourceType: 'role',
935
+ resourceId: 'admin',
936
+ },
937
+ relation: 'member',
938
+ subject: {
939
+ resourceType: 'user',
940
+ resourceId: 'user_124',
941
+ },
942
+ },
943
+ {
944
+ op: interfaces_1.WarrantOp.Delete,
945
+ resource: {
946
+ resourceType: 'role',
947
+ resourceId: 'admin',
948
+ },
949
+ relation: 'member',
950
+ subject: {
951
+ resourceType: 'user',
952
+ resourceId: 'user_125',
953
+ },
954
+ },
955
+ ]);
956
+ }
957
+ catch (e) {
958
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
959
+ }
960
+ }), 8000);
478
961
  });
479
962
  describe('listWarrants', () => {
480
963
  it('should list warrants', () => __awaiter(void 0, void 0, void 0, function* () {
@@ -529,6 +1012,72 @@ describe('FGA', () => {
529
1012
  },
530
1013
  ]);
531
1014
  }));
1015
+ it('should list warrants after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
1016
+ (0, test_utils_1.fetchOnce)({}, {
1017
+ status: 500,
1018
+ });
1019
+ (0, test_utils_1.fetchOnce)({
1020
+ data: [
1021
+ {
1022
+ resource_type: 'role',
1023
+ resource_id: 'admin',
1024
+ relation: 'member',
1025
+ subject: {
1026
+ resource_type: 'user',
1027
+ resource_id: 'user_123',
1028
+ },
1029
+ },
1030
+ {
1031
+ resource_type: 'role',
1032
+ resource_id: 'admin',
1033
+ relation: 'member',
1034
+ subject: {
1035
+ resource_type: 'user',
1036
+ resource_id: 'user_124',
1037
+ },
1038
+ policy: 'region == "us"',
1039
+ },
1040
+ ],
1041
+ list_metadata: {
1042
+ before: null,
1043
+ after: null,
1044
+ },
1045
+ });
1046
+ const { data: warrants } = yield workos.fga.listWarrants();
1047
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/warrants');
1048
+ expect(warrants).toMatchObject([
1049
+ {
1050
+ resourceType: 'role',
1051
+ resourceId: 'admin',
1052
+ relation: 'member',
1053
+ subject: {
1054
+ resourceType: 'user',
1055
+ resourceId: 'user_123',
1056
+ },
1057
+ },
1058
+ {
1059
+ resourceType: 'role',
1060
+ resourceId: 'admin',
1061
+ relation: 'member',
1062
+ subject: {
1063
+ resourceType: 'user',
1064
+ resourceId: 'user_124',
1065
+ },
1066
+ policy: 'region == "us"',
1067
+ },
1068
+ ]);
1069
+ }));
1070
+ it('fails to list warrants after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
1071
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
1072
+ message: 'Internal Server Error',
1073
+ }), { status: 500 });
1074
+ try {
1075
+ yield workos.fga.listWarrants();
1076
+ }
1077
+ catch (e) {
1078
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
1079
+ }
1080
+ }), 8000);
532
1081
  it('sends correct params when filtering', () => __awaiter(void 0, void 0, void 0, function* () {
533
1082
  (0, test_utils_1.fetchOnce)({
534
1083
  data: [
@@ -604,6 +1153,66 @@ describe('FGA', () => {
604
1153
  },
605
1154
  ]);
606
1155
  }));
1156
+ it('makes query request after one retry', () => __awaiter(void 0, void 0, void 0, function* () {
1157
+ (0, test_utils_1.fetchOnce)({}, {
1158
+ status: 500,
1159
+ });
1160
+ (0, test_utils_1.fetchOnce)({
1161
+ data: [
1162
+ {
1163
+ resource_type: 'role',
1164
+ resource_id: 'admin',
1165
+ warrant: {
1166
+ resource_type: 'role',
1167
+ resource_id: 'admin',
1168
+ relation: 'member',
1169
+ subject: {
1170
+ resource_type: 'user',
1171
+ resource_id: 'user_123',
1172
+ },
1173
+ },
1174
+ is_implicit: false,
1175
+ },
1176
+ ],
1177
+ list_metadata: {
1178
+ before: null,
1179
+ after: null,
1180
+ },
1181
+ });
1182
+ const { data: queryResults } = yield workos.fga.query({
1183
+ q: 'select role where user:user_123 is member',
1184
+ });
1185
+ expect((0, test_utils_1.fetchURL)()).toContain('/fga/v1/query');
1186
+ expect(queryResults).toMatchObject([
1187
+ {
1188
+ resourceType: 'role',
1189
+ resourceId: 'admin',
1190
+ warrant: {
1191
+ resourceType: 'role',
1192
+ resourceId: 'admin',
1193
+ relation: 'member',
1194
+ subject: {
1195
+ resourceType: 'user',
1196
+ resourceId: 'user_123',
1197
+ },
1198
+ },
1199
+ isImplicit: false,
1200
+ },
1201
+ ]);
1202
+ }));
1203
+ it('fails to make query after max retries', () => __awaiter(void 0, void 0, void 0, function* () {
1204
+ jest_fetch_mock_1.default.mockResponse(JSON.stringify({
1205
+ message: 'Internal Server Error',
1206
+ }), { status: 500 });
1207
+ try {
1208
+ yield workos.fga.query({
1209
+ q: 'select role where user:user_123 is member',
1210
+ });
1211
+ }
1212
+ catch (e) {
1213
+ expect(e).toBeInstanceOf(exceptions_1.GenericServerException);
1214
+ }
1215
+ }), 8000);
607
1216
  it('sends correct params and options', () => __awaiter(void 0, void 0, void 0, function* () {
608
1217
  (0, test_utils_1.fetchOnce)({
609
1218
  data: [
package/lib/workos.js CHANGED
@@ -27,7 +27,7 @@ const bad_request_exception_1 = require("./common/exceptions/bad-request.excepti
27
27
  const http_client_1 = require("./common/net/http-client");
28
28
  const subtle_crypto_provider_1 = require("./common/crypto/subtle-crypto-provider");
29
29
  const fetch_client_1 = require("./common/net/fetch-client");
30
- const VERSION = '7.29.1';
30
+ const VERSION = '7.30.0';
31
31
  const DEFAULT_HOSTNAME = 'api.workos.com';
32
32
  const HEADER_AUTHORIZATION = 'Authorization';
33
33
  const HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "7.29.1",
2
+ "version": "7.30.0",
3
3
  "name": "@workos-inc/node",
4
4
  "author": "WorkOS",
5
5
  "description": "A Node wrapper for the WorkOS API",