@scaleway/sdk-client 2.2.0 → 2.2.2

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 (84) hide show
  1. package/dist/_virtual/_rolldown/runtime.js +2 -0
  2. package/dist/bridge.js +8 -0
  3. package/dist/helpers/__tests__/is-browser.browser.test.js +10 -0
  4. package/dist/helpers/__tests__/is-browser.node.test.js +10 -0
  5. package/dist/helpers/__tests__/json.test.js +48 -0
  6. package/dist/helpers/__tests__/marshalling.test.js +177 -0
  7. package/dist/helpers/is-browser.js +3 -1
  8. package/dist/helpers/is-response.js +3 -1
  9. package/dist/helpers/json.js +10 -4
  10. package/dist/helpers/marshalling.js +7 -5
  11. package/dist/index.js +4 -4
  12. package/dist/internal/async/__tests__/interval-retrier.test.js +121 -0
  13. package/dist/internal/async/__tests__/sleep.test.js +19 -0
  14. package/dist/internal/async/interval-retrier.d.ts +1 -1
  15. package/dist/internal/async/interval-retrier.js +33 -3
  16. package/dist/internal/async/sleep.js +3 -1
  17. package/dist/internal/interceptors/__tests__/composer.test.js +48 -0
  18. package/dist/internal/interceptors/__tests__/helpers.test.js +27 -0
  19. package/dist/internal/interceptors/composer.js +5 -3
  20. package/dist/internal/interceptors/helpers.js +4 -2
  21. package/dist/internal/interceptors/types.js +0 -0
  22. package/dist/internal/logger/__tests__/index.test.js +209 -0
  23. package/dist/internal/logger/console-logger.js +2 -0
  24. package/dist/internal/logger/index.js +5 -3
  25. package/dist/internal/logger/level-resolver.js +4 -2
  26. package/dist/internal/logger/logger.js +0 -0
  27. package/dist/internal/validations/__tests__/string-validation.test.js +98 -0
  28. package/dist/internal/validations/string-validation.js +14 -9
  29. package/dist/internals.js +9 -8
  30. package/dist/package.js +2 -1
  31. package/dist/scw/__tests__/api.test.js +19 -0
  32. package/dist/scw/__tests__/auth.test.js +57 -0
  33. package/dist/scw/__tests__/client-ini-factory.test.js +220 -0
  34. package/dist/scw/__tests__/client-ini-profile.test.js +70 -0
  35. package/dist/scw/__tests__/client-settings.test.js +51 -0
  36. package/dist/scw/__tests__/client.test.js +59 -0
  37. package/dist/scw/__tests__/custom-marshalling.test.js +168 -0
  38. package/dist/scw/api.js +2 -0
  39. package/dist/scw/auth.js +17 -6
  40. package/dist/scw/client-ini-factory.js +9 -7
  41. package/dist/scw/client-ini-profile.js +3 -1
  42. package/dist/scw/client-settings.js +3 -1
  43. package/dist/scw/client.js +4 -2
  44. package/dist/scw/constants.js +6 -4
  45. package/dist/scw/custom-marshalling.js +17 -15
  46. package/dist/scw/custom-types.js +2 -0
  47. package/dist/scw/errors/__tests__/scw-error.test.js +41 -0
  48. package/dist/scw/errors/__tests__/types.test.js +16 -0
  49. package/dist/scw/errors/error-parser.js +3 -1
  50. package/dist/scw/errors/non-standard/__tests__/index.test.js +123 -0
  51. package/dist/scw/errors/non-standard/invalid-request-mapper.js +3 -1
  52. package/dist/scw/errors/non-standard/unknown-resource-mapper.js +3 -1
  53. package/dist/scw/errors/scw-error-from-json.js +0 -0
  54. package/dist/scw/errors/scw-error.js +2 -0
  55. package/dist/scw/errors/standard/__tests__/index.test.js +329 -0
  56. package/dist/scw/errors/standard/already-exists-error.js +2 -0
  57. package/dist/scw/errors/standard/denied-authentication-error.js +2 -0
  58. package/dist/scw/errors/standard/index.js +3 -1
  59. package/dist/scw/errors/standard/invalid-arguments-error.js +2 -0
  60. package/dist/scw/errors/standard/out-of-stock-error.js +2 -0
  61. package/dist/scw/errors/standard/permissions-denied-error.js +2 -0
  62. package/dist/scw/errors/standard/precondition-failed-error.js +2 -0
  63. package/dist/scw/errors/standard/quotas-exceeded-error.js +2 -0
  64. package/dist/scw/errors/standard/resource-expired-error.js +2 -0
  65. package/dist/scw/errors/standard/resource-locked-error.js +2 -0
  66. package/dist/scw/errors/standard/resource-not-found-error.js +2 -0
  67. package/dist/scw/errors/standard/too-many-requests-error.js +2 -0
  68. package/dist/scw/errors/standard/transient-state-error.js +2 -0
  69. package/dist/scw/errors/types.js +3 -1
  70. package/dist/scw/fetch/__tests__/build-fetcher.test.js +114 -0
  71. package/dist/scw/fetch/__tests__/http-dumper.test.js +45 -0
  72. package/dist/scw/fetch/__tests__/http-interceptors.test.js +60 -0
  73. package/dist/scw/fetch/__tests__/resource-paginator.test.js +110 -0
  74. package/dist/scw/fetch/__tests__/response-parser.test.js +94 -0
  75. package/dist/scw/fetch/build-fetcher.js +6 -4
  76. package/dist/scw/fetch/http-dumper.js +4 -2
  77. package/dist/scw/fetch/http-interceptors.js +5 -3
  78. package/dist/scw/fetch/resource-paginator.js +6 -4
  79. package/dist/scw/fetch/response-parser.js +5 -3
  80. package/dist/scw/fetch/types.js +0 -0
  81. package/dist/scw/locality.js +2 -0
  82. package/dist/vendor/base64/index.d.js +0 -0
  83. package/dist/vendor/base64/index.js +2 -0
  84. package/package.json +1 -1
@@ -0,0 +1,329 @@
1
+ import { InvalidArgumentsError } from "../invalid-arguments-error.js";
2
+ import { QuotasExceededError } from "../quotas-exceeded-error.js";
3
+ import { ResourceNotFoundError } from "../resource-not-found-error.js";
4
+ import { AlreadyExistsError } from "../already-exists-error.js";
5
+ import { DeniedAuthenticationError } from "../denied-authentication-error.js";
6
+ import { OutOfStockError } from "../out-of-stock-error.js";
7
+ import { PermissionsDeniedError } from "../permissions-denied-error.js";
8
+ import { PreconditionFailedError } from "../precondition-failed-error.js";
9
+ import { ResourceExpiredError } from "../resource-expired-error.js";
10
+ import { ResourceLockedError } from "../resource-locked-error.js";
11
+ import { TooManyRequestsError } from "../too-many-requests-error.js";
12
+ import { TransientStateError } from "../transient-state-error.js";
13
+ import { parseScalewayError } from "../../error-parser.js";
14
+ import { describe, expect, it } from "vitest";
15
+ //#region src/scw/errors/standard/__tests__/index.test.ts
16
+ describe("Empty input", () => {
17
+ it(`uses the http status`, () => {
18
+ expect(parseScalewayError(404, {}).message).toBe("http error 404");
19
+ });
20
+ });
21
+ describe("Unrecognied error", () => {
22
+ it(`parses message`, () => {
23
+ expect(parseScalewayError(409, {
24
+ message: "Group is in use. You cannot delete it.",
25
+ type: "conflict"
26
+ }).message).toBe("http error 409: Group is in use. You cannot delete it.");
27
+ });
28
+ });
29
+ describe("InvalidArgumentsError", () => {
30
+ const body = {
31
+ details: [
32
+ {
33
+ argument_name: "size",
34
+ help_message: "must be lower than 50GB",
35
+ reason: "constraint"
36
+ },
37
+ {
38
+ argument_name: "id",
39
+ reason: "required"
40
+ },
41
+ {
42
+ argument_name: "name",
43
+ reason: "unknown"
44
+ },
45
+ {
46
+ argument_name: "size",
47
+ reason: "format"
48
+ },
49
+ {
50
+ argument_name: 42,
51
+ reason: "format"
52
+ }
53
+ ],
54
+ type: "invalid_arguments"
55
+ };
56
+ it(`parses a valid input`, () => {
57
+ expect(parseScalewayError(400, body).toString()).toBe("InvalidArgumentsError: invalid argument(s): size does not respect constraint, must be lower than 50GB; id is required; name is invalid for unexpected reason; size is wrongly formatted");
58
+ });
59
+ it(`doesn't parse invalid details object`, () => {
60
+ expect(InvalidArgumentsError.fromJSON(400, {
61
+ ...body,
62
+ details: {}
63
+ })).toBeNull();
64
+ });
65
+ });
66
+ describe("QuotasExceededError", () => {
67
+ const body = {
68
+ details: [
69
+ {
70
+ current: 10,
71
+ project_id: "b7a1cf21-1e90-464c-a9f6-375c4d1f4b64",
72
+ quota: 10,
73
+ resource: "instance_volume"
74
+ },
75
+ {
76
+ current: 80,
77
+ organization_id: "72dd6741-d748-42e7-ba65-acd07447d3e9",
78
+ quota: 100,
79
+ resource: "instance_volumes_l_ssd_total_gb"
80
+ },
81
+ {
82
+ current: 70,
83
+ quota: 100,
84
+ resource: "instance_volumes_l_ssd_total_gb"
85
+ },
86
+ {
87
+ current: "70",
88
+ quota: "100",
89
+ resource: "invalid_input"
90
+ }
91
+ ],
92
+ type: "quotas_exceeded"
93
+ };
94
+ expect(parseScalewayError(403, body).toString()).toContain("quota(s) exceeded: Quotas reached: You have reached the maximum number of instance_volume authorized by your Organization. Access the quotas page from your Organization dashboard to manage quotas. for project 'b7a1cf21-1e90-464c-a9f6-375c4d1f4b64'; Quotas reached: You have reached the maximum number of instance_volumes_l_ssd_total_gb authorized by your Organization. Access the quotas page from your Organization dashboard to manage quotas. for organization '72dd6741-d748-42e7-ba65-acd07447d3e9'; Quotas reached: You have reached the maximum number of instance_volumes_l_ssd_total_gb authorized by your Organization. Access the quotas page from your Organization dashboard to manage quotas.");
95
+ it(`doesn't parse invalid details object`, () => {
96
+ expect(QuotasExceededError.fromJSON(403, {
97
+ ...body,
98
+ details: {}
99
+ })).toBeNull();
100
+ });
101
+ });
102
+ describe("ResourceNotFoundError", () => {
103
+ const body = {
104
+ resource: "instance_server",
105
+ resource_id: "f974feac-abae-4365-b988-8ec7d1cec10d",
106
+ type: "not_found"
107
+ };
108
+ it(`parses a valid input`, () => {
109
+ expect(parseScalewayError(404, body).toString()).toBe("ResourceNotFoundError: resource instance_server with ID f974feac-abae-4365-b988-8ec7d1cec10d is not found");
110
+ });
111
+ it(`doesn't parse invalid input`, () => {
112
+ expect(ResourceNotFoundError.fromJSON(404, {
113
+ ...body,
114
+ resource_id: 0
115
+ })).toBeNull();
116
+ });
117
+ });
118
+ describe("ResourceLockedError", () => {
119
+ const body = {
120
+ resource: "instance_server",
121
+ resource_id: "f974feac-abae-4365-b988-8ec7d1cec10d",
122
+ type: "locked"
123
+ };
124
+ it(`parses a valid input`, () => {
125
+ expect(parseScalewayError(423, body).toString()).toBe("ResourceLockedError: resource instance_server with ID f974feac-abae-4365-b988-8ec7d1cec10d is locked");
126
+ });
127
+ it(`doesn't parse invalid input`, () => {
128
+ expect(ResourceLockedError.fromJSON(423, {
129
+ ...body,
130
+ resource_id: 0
131
+ })).toBeNull();
132
+ });
133
+ });
134
+ describe("OutOfStockError", () => {
135
+ const body = {
136
+ resource: "kubernetes_cluster",
137
+ type: "out_of_stock"
138
+ };
139
+ it(`parses a valid input`, () => {
140
+ expect(parseScalewayError(503, body).toString()).toBe("OutOfStockError: resource kubernetes_cluster is out of stock");
141
+ });
142
+ it(`doesn't parse invalid input`, () => {
143
+ expect(OutOfStockError.fromJSON(503, {
144
+ ...body,
145
+ resource: 0
146
+ })).toBeNull();
147
+ });
148
+ });
149
+ describe("ResourceExpiredError", () => {
150
+ const body = {
151
+ expired_since: "2017-01-15T01:30:15.01Z",
152
+ resource: "account_token",
153
+ resource_id: "SCWQPD3J3PTKJFJKFQJG",
154
+ type: "expired"
155
+ };
156
+ it(`parses a valid input`, () => {
157
+ expect(parseScalewayError(410, body).toString()).toBe("ResourceExpiredError: resource account_token with ID SCWQPD3J3PTKJFJKFQJG expired since 2017-01-15T01:30:15.010Z");
158
+ });
159
+ it(`doesn't parse invalid input`, () => {
160
+ expect(ResourceExpiredError.fromJSON(410, {
161
+ ...body,
162
+ expired_since: 0
163
+ })).toBeNull();
164
+ });
165
+ });
166
+ describe("PermissionsDeniedError", () => {
167
+ const body = {
168
+ details: [{
169
+ action: "read",
170
+ resource: "instance_volume"
171
+ }, {
172
+ action: "read",
173
+ wrong_key: "instance_volume"
174
+ }],
175
+ type: "permissions_denied"
176
+ };
177
+ it(`parses a valid input`, () => {
178
+ expect(parseScalewayError(503, body).toString()).toBe("PermissionsDeniedError: insufficient permissions: read instance_volume");
179
+ });
180
+ it(`doesn't parse invalid details object`, () => {
181
+ expect(PermissionsDeniedError.fromJSON(503, {
182
+ ...body,
183
+ details: {}
184
+ })).toBeNull();
185
+ });
186
+ });
187
+ describe("DeniedAuthenticationError", () => {
188
+ it(`parses a body for invalid argument `, () => {
189
+ expect(parseScalewayError(401, {
190
+ method: "jwt",
191
+ reason: "invalid_argument",
192
+ type: "denied_authentication"
193
+ }).toString()).toBe("DeniedAuthenticationError: denied authentication: invalid jwt format or empty value");
194
+ });
195
+ it(`parses a body for not found method`, () => {
196
+ expect(parseScalewayError(401, {
197
+ method: "api_key",
198
+ reason: "not_found",
199
+ type: "denied_authentication"
200
+ }).toString()).toBe("DeniedAuthenticationError: denied authentication: api_key does not exist");
201
+ });
202
+ it(`parses a body for expired method`, () => {
203
+ expect(parseScalewayError(401, {
204
+ method: "jwt",
205
+ reason: "expired",
206
+ type: "denied_authentication"
207
+ }).toString()).toBe("DeniedAuthenticationError: denied authentication: jwt is expired");
208
+ });
209
+ it(`parses a body for unknown reason`, () => {
210
+ expect(parseScalewayError(401, {
211
+ method: "unknown_method",
212
+ reason: "unknown_reason",
213
+ type: "denied_authentication"
214
+ }).toString()).toBe("DeniedAuthenticationError: denied authentication: unknown reason for unknown_method");
215
+ });
216
+ it(`doesn't parse invalid input`, () => {
217
+ expect(DeniedAuthenticationError.fromJSON(401, { method: "jwt" })).toBeNull();
218
+ });
219
+ });
220
+ describe("PreconditionFailedError", () => {
221
+ it(`parses a valid input for precondition unknown_precondition`, () => {
222
+ expect(parseScalewayError(412, {
223
+ help_message: "",
224
+ precondition: "unknown_precondition",
225
+ type: "precondition_failed"
226
+ }).toString()).toBe("PreconditionFailedError: precondition failed: unknown_precondition");
227
+ });
228
+ it(`parses a valid input for precondition precondition_failed`, () => {
229
+ expect(parseScalewayError(412, {
230
+ help_message: "All servers must be removed from the private network before deleting it.",
231
+ precondition: "resource_still_in_use",
232
+ type: "precondition_failed"
233
+ }).toString()).toBe("PreconditionFailedError: precondition failed: resource_still_in_use, All servers must be removed from the private network before deleting it.");
234
+ });
235
+ it(`parses a valid input for precondition attribute_must_be_set`, () => {
236
+ expect(parseScalewayError(412, {
237
+ help_message: "",
238
+ precondition: "attribute_must_be_set",
239
+ type: "precondition_failed"
240
+ }).toString()).toBe("PreconditionFailedError: precondition failed: attribute_must_be_set");
241
+ });
242
+ it(`parses a valid input for unknown precondition`, () => {
243
+ expect(parseScalewayError(412, {
244
+ help_message: "",
245
+ precondition: "wrong_precondition_key",
246
+ type: "precondition_failed"
247
+ }).toString()).toBe("PreconditionFailedError: precondition failed: wrong_precondition_key");
248
+ });
249
+ it(`doesn't parse invalid input`, () => {
250
+ expect(PreconditionFailedError.fromJSON(412, {
251
+ precondition: 0,
252
+ type: "precondition_failed"
253
+ })).toBeNull();
254
+ });
255
+ });
256
+ describe("TransientStateError", () => {
257
+ const body = {
258
+ current_state: "starting",
259
+ resource: "instance_server",
260
+ resource_id: "f974feac-abae-4365-b988-8ec7d1cec10d",
261
+ type: "transient_state"
262
+ };
263
+ it(`parses a valid input`, () => {
264
+ expect(parseScalewayError(409, body).toString()).toBe("TransientStateError: resource instance_server with ID f974feac-abae-4365-b988-8ec7d1cec10d is in a transient state: starting");
265
+ });
266
+ it(`doesn't parse invalid input`, () => {
267
+ expect(TransientStateError.fromJSON(409, {
268
+ ...body,
269
+ resource_id: 0
270
+ })).toBeNull();
271
+ });
272
+ });
273
+ describe("AlreadyExistsError", () => {
274
+ const body = {
275
+ help_message: "The customer level NAME already exists",
276
+ resource: "NAME",
277
+ resource_id: "unknown",
278
+ type: "already_exists"
279
+ };
280
+ it(`parses a valid input`, () => {
281
+ expect(parseScalewayError(409, body).toString()).toBe("AlreadyExistsError: resource NAME with ID unknown already exists: The customer level NAME already exists");
282
+ });
283
+ it(`doesn't parse invalid input`, () => {
284
+ expect(AlreadyExistsError.fromJSON(409, {
285
+ ...body,
286
+ resource_id: 0
287
+ })).toBeNull();
288
+ });
289
+ });
290
+ describe("TooManyRequestsError", () => {
291
+ it(`parses a valid input with limit and reset_seconds`, () => {
292
+ expect(parseScalewayError(429, {
293
+ help_message: "You are limited to 3 requests per hour to reset your password",
294
+ limit: {
295
+ quota: 3,
296
+ window_seconds: 60
297
+ },
298
+ reset_seconds: 13,
299
+ type: "too_many_requests"
300
+ }).toString()).toBe("TooManyRequestsError: too many requests (quota is 3 for 60s, resets in 13s): You are limited to 3 requests per hour to reset your password");
301
+ });
302
+ it(`parses a valid input with limit but no window`, () => {
303
+ expect(parseScalewayError(429, {
304
+ help_message: "You are limited to 5 requests per day",
305
+ limit: { quota: 5 },
306
+ type: "too_many_requests"
307
+ }).toString()).toBe("TooManyRequestsError: too many requests (quota is 5): You are limited to 5 requests per day");
308
+ });
309
+ it(`parses a valid input with empty help message`, () => {
310
+ expect(parseScalewayError(429, {
311
+ help_message: "",
312
+ type: "too_many_requests"
313
+ }).toString()).toBe("TooManyRequestsError: too many requests");
314
+ });
315
+ it(`parses a valid input with only reset_at`, () => {
316
+ expect(parseScalewayError(429, {
317
+ help_message: "",
318
+ reset_at: "2022-05-23T15:00:00.01Z",
319
+ type: "too_many_requests"
320
+ }).toString()).toBe("TooManyRequestsError: too many requests (resets at 2022-05-23T15:00:00.010Z)");
321
+ });
322
+ it(`doesn't parse invalid input`, () => {
323
+ expect(TooManyRequestsError.fromJSON(429, {
324
+ reset_at: "2022-05-23T15:00:00.01Z",
325
+ type: "too_many_requests"
326
+ })).toBeNull();
327
+ });
328
+ });
329
+ //#endregion
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/already-exists-error.ts
2
3
  /**
3
4
  * AlreadyExists error is used when a resource already exists.
4
5
  *
@@ -19,4 +20,5 @@ var AlreadyExistsError = class AlreadyExistsError extends ScalewayError {
19
20
  return new AlreadyExistsError(status, obj, obj.resource, obj.resource_id, obj.help_message);
20
21
  }
21
22
  };
23
+ //#endregion
22
24
  export { AlreadyExistsError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/denied-authentication-error.ts
2
3
  /**
3
4
  * Build the default message for {@link DeniedAuthenticationError}.
4
5
  *
@@ -43,4 +44,5 @@ var DeniedAuthenticationError = class DeniedAuthenticationError extends Scaleway
43
44
  return new DeniedAuthenticationError(status, obj, obj.method, obj.reason);
44
45
  }
45
46
  };
47
+ //#endregion
46
48
  export { DeniedAuthenticationError };
@@ -12,6 +12,7 @@ import { ResourceExpiredError } from "./resource-expired-error.js";
12
12
  import { ResourceLockedError } from "./resource-locked-error.js";
13
13
  import { TooManyRequestsError } from "./too-many-requests-error.js";
14
14
  import { TransientStateError } from "./transient-state-error.js";
15
+ //#region src/scw/errors/standard/index.ts
15
16
  var standard_exports = /* @__PURE__ */ __exportAll({
16
17
  AlreadyExistsError: () => AlreadyExistsError,
17
18
  DeniedAuthenticationError: () => DeniedAuthenticationError,
@@ -27,4 +28,5 @@ var standard_exports = /* @__PURE__ */ __exportAll({
27
28
  TooManyRequestsError: () => TooManyRequestsError,
28
29
  TransientStateError: () => TransientStateError
29
30
  });
30
- export { standard_exports };
31
+ //#endregion
32
+ export { AlreadyExistsError, DeniedAuthenticationError, InvalidArgumentsError, OutOfStockError, PermissionsDeniedError, PreconditionFailedError, QuotasExceededError, ResourceExpiredError, ResourceLockedError, ResourceNotFoundError, ScalewayError, TooManyRequestsError, TransientStateError, standard_exports };
@@ -1,5 +1,6 @@
1
1
  import { isJSONObject } from "../../../helpers/json.js";
2
2
  import { ScalewayError } from "../scw-error.js";
3
+ //#region src/scw/errors/standard/invalid-arguments-error.ts
3
4
  /**
4
5
  * Build the default message for {@link InvalidArgumentsError}.
5
6
  *
@@ -52,4 +53,5 @@ var InvalidArgumentsError = class InvalidArgumentsError extends ScalewayError {
52
53
  }) : list, []));
53
54
  }
54
55
  };
56
+ //#endregion
55
57
  export { InvalidArgumentsError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/out-of-stock-error.ts
2
3
  /**
3
4
  * OutOfStock error happens when stocks are empty for the resource.
4
5
  *
@@ -17,4 +18,5 @@ var OutOfStockError = class OutOfStockError extends ScalewayError {
17
18
  return new OutOfStockError(status, obj, obj.resource);
18
19
  }
19
20
  };
21
+ //#endregion
20
22
  export { OutOfStockError };
@@ -1,5 +1,6 @@
1
1
  import { isJSONObject } from "../../../helpers/json.js";
2
2
  import { ScalewayError } from "../scw-error.js";
3
+ //#region src/scw/errors/standard/permissions-denied-error.ts
3
4
  /**
4
5
  * Build the default message for {@link PermissionsDeniedError}.
5
6
  *
@@ -30,4 +31,5 @@ var PermissionsDeniedError = class PermissionsDeniedError extends ScalewayError
30
31
  }) : list, []));
31
32
  }
32
33
  };
34
+ //#endregion
33
35
  export { PermissionsDeniedError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/precondition-failed-error.ts
2
3
  /**
3
4
  * Build the default message for {@link PreconditionFailedError}.
4
5
  *
@@ -32,4 +33,5 @@ var PreconditionFailedError = class PreconditionFailedError extends ScalewayErro
32
33
  return new PreconditionFailedError(status, obj, obj.precondition, obj.help_message);
33
34
  }
34
35
  };
36
+ //#endregion
35
37
  export { PreconditionFailedError };
@@ -1,5 +1,6 @@
1
1
  import { isJSONObject } from "../../../helpers/json.js";
2
2
  import { ScalewayError } from "../scw-error.js";
3
+ //#region src/scw/errors/standard/quotas-exceeded-error.ts
3
4
  /**
4
5
  * Build the default message for {@link QuotasExceededError}.
5
6
  *
@@ -45,4 +46,5 @@ var QuotasExceededError = class QuotasExceededError extends ScalewayError {
45
46
  }) : list, []));
46
47
  }
47
48
  };
49
+ //#endregion
48
50
  export { QuotasExceededError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/resource-expired-error.ts
2
3
  /**
3
4
  * ResourceExpired error happens when trying to access a resource that has expired.
4
5
  *
@@ -19,4 +20,5 @@ var ResourceExpiredError = class ResourceExpiredError extends ScalewayError {
19
20
  return new ResourceExpiredError(status, obj, obj.resource, obj.resource_id, new Date(obj.expired_since));
20
21
  }
21
22
  };
23
+ //#endregion
22
24
  export { ResourceExpiredError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/resource-locked-error.ts
2
3
  /**
3
4
  * ResourceLocked error happens when a resource is locked by trust and safety.
4
5
  *
@@ -18,4 +19,5 @@ var ResourceLockedError = class ResourceLockedError extends ScalewayError {
18
19
  return new ResourceLockedError(status, obj, obj.resource, obj.resource_id);
19
20
  }
20
21
  };
22
+ //#endregion
21
23
  export { ResourceLockedError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/resource-not-found-error.ts
2
3
  /**
3
4
  * ResourceNotFound error happens when getting a resource that does not exist anymore.
4
5
  *
@@ -18,4 +19,5 @@ var ResourceNotFoundError = class ResourceNotFoundError extends ScalewayError {
18
19
  return new ResourceNotFoundError(status, obj, obj.resource, obj.resource_id);
19
20
  }
20
21
  };
22
+ //#endregion
21
23
  export { ResourceNotFoundError };
@@ -1,5 +1,6 @@
1
1
  import { isJSONObject } from "../../../helpers/json.js";
2
2
  import { ScalewayError } from "../scw-error.js";
3
+ //#region src/scw/errors/standard/too-many-requests-error.ts
3
4
  /**
4
5
  * Build the default message for {@link TooManyRequestsError}.
5
6
  *
@@ -42,4 +43,5 @@ var TooManyRequestsError = class TooManyRequestsError extends ScalewayError {
42
43
  return new TooManyRequestsError(status, obj, obj.help_message, limit, typeof obj.reset_seconds === "number" ? obj.reset_seconds : void 0, typeof obj.reset_at === "string" ? new Date(obj.reset_at) : void 0);
43
44
  }
44
45
  };
46
+ //#endregion
45
47
  export { TooManyRequestsError };
@@ -1,4 +1,5 @@
1
1
  import { ScalewayError } from "../scw-error.js";
2
+ //#region src/scw/errors/standard/transient-state-error.ts
2
3
  /**
3
4
  * TransientState error happens when trying to perform an action on a resource in a transient state.
4
5
  *
@@ -19,4 +20,5 @@ var TransientStateError = class TransientStateError extends ScalewayError {
19
20
  return new TransientStateError(status, obj, obj.resource, obj.resource_id, obj.current_state);
20
21
  }
21
22
  };
23
+ //#endregion
22
24
  export { TransientStateError };
@@ -1,4 +1,5 @@
1
1
  import { isJSONObject } from "../../helpers/json.js";
2
+ //#region src/scw/errors/types.ts
2
3
  /**
3
4
  * Verifies the object is a record of string to string[].
4
5
  *
@@ -7,9 +8,10 @@ import { isJSONObject } from "../../helpers/json.js";
7
8
  *
8
9
  * @internal
9
10
  */
10
- const isRecordOfStringArray = (obj) => {
11
+ var isRecordOfStringArray = (obj) => {
11
12
  if (!isJSONObject(obj)) return false;
12
13
  for (const elt of Object.values(obj)) if (!Array.isArray(elt) || Object.values(elt).find((x) => typeof x !== "string") !== void 0) return false;
13
14
  return true;
14
15
  };
16
+ //#endregion
15
17
  export { isRecordOfStringArray };
@@ -0,0 +1,114 @@
1
+ import { isBrowser } from "../../../helpers/is-browser.js";
2
+ import { addHeaderInterceptor } from "../../../internal/interceptors/helpers.js";
3
+ import { buildFetcher, buildRequest } from "../build-fetcher.js";
4
+ import { afterAll, describe, expect, it, vi } from "vitest";
5
+ //#region src/scw/fetch/__tests__/build-fetcher.test.ts
6
+ var DEFAULT_SETTINGS = {
7
+ apiURL: "https://api.scaleway.com",
8
+ defaultRegion: "fr-par",
9
+ defaultZone: "fr-par-1",
10
+ httpClient: global.fetch,
11
+ interceptors: [],
12
+ requestInterceptors: [],
13
+ responseInterceptors: [],
14
+ userAgent: "scaleway-sdk-js/v1.0.0"
15
+ };
16
+ var SCW_POST_REQUEST = {
17
+ method: "POST",
18
+ path: "/undefined"
19
+ };
20
+ describe(`buildRequest`, () => {
21
+ it(`has the specified method & url`, () => {
22
+ const fReq = buildRequest(SCW_POST_REQUEST, DEFAULT_SETTINGS);
23
+ expect(fReq.method).toBe(SCW_POST_REQUEST.method);
24
+ expect(fReq.url).toBe(`${DEFAULT_SETTINGS.apiURL}${SCW_POST_REQUEST.path}`);
25
+ });
26
+ it(`has the default header "Accept: 'application/json'"`, () => {
27
+ expect(buildRequest(SCW_POST_REQUEST, DEFAULT_SETTINGS).headers.get("accept")).toBe(`application/json`);
28
+ });
29
+ if (!isBrowser()) it(`has the default header "User-Agent: 'scaleway-sdk-js/v1.0.0'"`, () => {
30
+ expect(buildRequest(SCW_POST_REQUEST, DEFAULT_SETTINGS).headers.get("User-Agent")).toBe(DEFAULT_SETTINGS.userAgent);
31
+ });
32
+ else it(`has NOT the default header "User-Agent: 'scaleway-sdk-js/v1.0.0'"`, () => {
33
+ expect(buildRequest(SCW_POST_REQUEST, DEFAULT_SETTINGS).headers.get("User-Agent")).toBeNull();
34
+ });
35
+ it(`has the custom headers`, () => {
36
+ const mReq = {
37
+ ...SCW_POST_REQUEST,
38
+ headers: { randomName: "random-value" }
39
+ };
40
+ expect(buildRequest(mReq, DEFAULT_SETTINGS).headers.get("randomName")).toBe(mReq.headers?.randomName);
41
+ });
42
+ it("has the url params", () => {
43
+ expect(buildRequest({
44
+ ...SCW_POST_REQUEST,
45
+ urlParams: new URLSearchParams([["param1", "value1"], ["param2", "value2"]])
46
+ }, DEFAULT_SETTINGS).url).toBe("https://api.scaleway.com/undefined?param1=value1&param2=value2");
47
+ });
48
+ });
49
+ describe(`buildFetcher (mock)`, () => {
50
+ afterAll(() => {
51
+ vi.restoreAllMocks();
52
+ });
53
+ vi.spyOn(global, "fetch");
54
+ const mockedFetch = vi.mocked(fetch);
55
+ const fetcher = buildFetcher(DEFAULT_SETTINGS, global.fetch);
56
+ it(`gets a response without error for a simple request with unmarshaller`, async () => {
57
+ mockedFetch.mockResolvedValue(new Response(JSON.stringify({}), { headers: { "Content-Type": "application/json" } }));
58
+ return expect(fetcher({
59
+ method: "POST",
60
+ path: "/undefined"
61
+ }, () => "dummy-output")).resolves.toStrictEqual("dummy-output");
62
+ });
63
+ it("gets modified response", () => {
64
+ mockedFetch.mockResolvedValue(new Response(JSON.stringify({}), { headers: { "Content-Type": "application/json" } }));
65
+ return expect(buildFetcher({
66
+ ...DEFAULT_SETTINGS,
67
+ interceptors: [{ response: () => new Response(JSON.stringify(42)) }]
68
+ }, global.fetch)({
69
+ method: "POST",
70
+ path: "/undefined"
71
+ })).resolves.toStrictEqual("42");
72
+ });
73
+ it(`gets a response without error for a simple request without unmarshaller`, async () => {
74
+ mockedFetch.mockResolvedValue(new Response(JSON.stringify({ any_parameter: "any-value" }), { headers: { "Content-Type": "application/json" } }));
75
+ return expect(fetcher({
76
+ method: "POST",
77
+ path: "/undefined"
78
+ })).resolves.toMatchObject({ any_parameter: "any-value" });
79
+ });
80
+ it("gets a response with response error interceptor despite the error", () => {
81
+ mockedFetch.mockRejectedValue(/* @__PURE__ */ new TypeError(""));
82
+ return expect(buildFetcher({
83
+ ...DEFAULT_SETTINGS,
84
+ interceptors: [{ responseError: () => Promise.resolve(42) }]
85
+ }, global.fetch)({
86
+ method: "GET",
87
+ path: "/will-trigger-an-error"
88
+ })).resolves.toBe(42);
89
+ });
90
+ it("gets the unmarshalled value of what responseError returns", () => {
91
+ mockedFetch.mockRejectedValue(/* @__PURE__ */ new TypeError(""));
92
+ return expect(buildFetcher({
93
+ ...DEFAULT_SETTINGS,
94
+ interceptors: [{ responseError: () => Promise.resolve(42) }]
95
+ }, global.fetch)({
96
+ method: "GET",
97
+ path: "/will-trigger-an-error"
98
+ }, (data) => `${typeof data === "number" ? data : ""}-dummy-output`)).resolves.toBe("42-dummy-output");
99
+ });
100
+ it("gets modified request in response error", () => {
101
+ mockedFetch.mockRejectedValue(/* @__PURE__ */ new TypeError(""));
102
+ return expect(buildFetcher({
103
+ ...DEFAULT_SETTINGS,
104
+ interceptors: [{
105
+ request: addHeaderInterceptor("random-header", "42"),
106
+ responseError: ({ request }) => request.headers.get("random-header")
107
+ }]
108
+ }, global.fetch)({
109
+ method: "GET",
110
+ path: "/will-trigger-an-error"
111
+ })).resolves.toBe("42");
112
+ });
113
+ });
114
+ //#endregion
@@ -0,0 +1,45 @@
1
+ import { isBrowser } from "../../../helpers/is-browser.js";
2
+ import { dumpRequest, dumpResponse } from "../http-dumper.js";
3
+ import { buildRequest } from "../build-fetcher.js";
4
+ import { describe, expect, it } from "vitest";
5
+ //#region src/scw/fetch/__tests__/http-dumper.test.ts
6
+ var DEFAULT_SETTINGS = {
7
+ apiURL: "https://api.scaleway.com",
8
+ defaultRegion: "fr-par",
9
+ defaultZone: "fr-par-1",
10
+ httpClient: fetch,
11
+ interceptors: [],
12
+ requestInterceptors: [],
13
+ responseInterceptors: [],
14
+ userAgent: "scaleway-sdk-js/v1.0.0"
15
+ };
16
+ describe(`dumpRequest`, () => {
17
+ it(`returns a readable string`, () => {
18
+ const userAgentHeader = !isBrowser() ? `User-Agent: scaleway-sdk-js/v1.0.0\r\n` : "";
19
+ return expect(dumpRequest(buildRequest({
20
+ body: JSON.stringify({ myProp: "random-value" }),
21
+ method: "POST",
22
+ path: "/random/path"
23
+ }, DEFAULT_SETTINGS))).resolves.toBe(`POST: https://api.scaleway.com/random/path\r\nAccept: application/json\r\nContent-Type: text/plain;charset=UTF-8\r\n${userAgentHeader}{"myProp":"random-value"}`);
24
+ });
25
+ });
26
+ var convertObjToBuffer = (obj) => Buffer.from(JSON.stringify(obj));
27
+ var makeFetchResponse = (value, contentType, status) => new Response(new Uint8Array(convertObjToBuffer(value)), {
28
+ headers: { "Content-Type": contentType },
29
+ status
30
+ });
31
+ describe("dumpResponse", () => {
32
+ it("returns a readable string for ok response", async () => {
33
+ const input = { randomKey: 42 };
34
+ const res = makeFetchResponse(input, "application/json", 200);
35
+ const expectedDump = `HTTP 200 OK\r\nContent-Type: application/json\r\n${JSON.stringify(input)}`;
36
+ return expect(dumpResponse(res)).resolves.toBe(expectedDump);
37
+ });
38
+ it("returns a readable string for nok response", async () => {
39
+ const input = "Raw error message";
40
+ const res = makeFetchResponse(input, "text/plain", 400);
41
+ const expectedDump = `HTTP 400 NOK\r\nContent-Type: text/plain\r\n${JSON.stringify(input)}`;
42
+ return expect(dumpResponse(res)).resolves.toBe(expectedDump);
43
+ });
44
+ });
45
+ //#endregion