@scaleway/sdk-client 2.2.1 → 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.
- package/dist/_virtual/_rolldown/runtime.js +2 -0
- package/dist/bridge.js +8 -0
- package/dist/helpers/__tests__/is-browser.browser.test.js +10 -0
- package/dist/helpers/__tests__/is-browser.node.test.js +10 -0
- package/dist/helpers/__tests__/json.test.js +48 -0
- package/dist/helpers/__tests__/marshalling.test.js +177 -0
- package/dist/helpers/is-browser.js +3 -1
- package/dist/helpers/is-response.js +3 -1
- package/dist/helpers/json.js +10 -4
- package/dist/helpers/marshalling.js +7 -5
- package/dist/index.js +4 -4
- package/dist/internal/async/__tests__/interval-retrier.test.js +121 -0
- package/dist/internal/async/__tests__/sleep.test.js +19 -0
- package/dist/internal/async/interval-retrier.d.ts +1 -1
- package/dist/internal/async/interval-retrier.js +33 -3
- package/dist/internal/async/sleep.js +3 -1
- package/dist/internal/interceptors/__tests__/composer.test.js +48 -0
- package/dist/internal/interceptors/__tests__/helpers.test.js +27 -0
- package/dist/internal/interceptors/composer.js +5 -3
- package/dist/internal/interceptors/helpers.js +4 -2
- package/dist/internal/interceptors/types.js +0 -0
- package/dist/internal/logger/__tests__/index.test.js +209 -0
- package/dist/internal/logger/console-logger.js +2 -0
- package/dist/internal/logger/index.js +5 -3
- package/dist/internal/logger/level-resolver.js +4 -2
- package/dist/internal/logger/logger.js +0 -0
- package/dist/internal/validations/__tests__/string-validation.test.js +98 -0
- package/dist/internal/validations/string-validation.js +14 -9
- package/dist/internals.js +9 -8
- package/dist/package.js +2 -1
- package/dist/scw/__tests__/api.test.js +19 -0
- package/dist/scw/__tests__/auth.test.js +57 -0
- package/dist/scw/__tests__/client-ini-factory.test.js +220 -0
- package/dist/scw/__tests__/client-ini-profile.test.js +70 -0
- package/dist/scw/__tests__/client-settings.test.js +51 -0
- package/dist/scw/__tests__/client.test.js +59 -0
- package/dist/scw/__tests__/custom-marshalling.test.js +168 -0
- package/dist/scw/api.js +2 -0
- package/dist/scw/auth.js +17 -6
- package/dist/scw/client-ini-factory.js +9 -7
- package/dist/scw/client-ini-profile.js +3 -1
- package/dist/scw/client-settings.js +3 -1
- package/dist/scw/client.js +4 -2
- package/dist/scw/constants.js +6 -4
- package/dist/scw/custom-marshalling.js +17 -15
- package/dist/scw/custom-types.js +2 -0
- package/dist/scw/errors/__tests__/scw-error.test.js +41 -0
- package/dist/scw/errors/__tests__/types.test.js +16 -0
- package/dist/scw/errors/error-parser.js +3 -1
- package/dist/scw/errors/non-standard/__tests__/index.test.js +123 -0
- package/dist/scw/errors/non-standard/invalid-request-mapper.js +3 -1
- package/dist/scw/errors/non-standard/unknown-resource-mapper.js +3 -1
- package/dist/scw/errors/scw-error-from-json.js +0 -0
- package/dist/scw/errors/scw-error.js +2 -0
- package/dist/scw/errors/standard/__tests__/index.test.js +329 -0
- package/dist/scw/errors/standard/already-exists-error.js +2 -0
- package/dist/scw/errors/standard/denied-authentication-error.js +2 -0
- package/dist/scw/errors/standard/index.js +3 -1
- package/dist/scw/errors/standard/invalid-arguments-error.js +2 -0
- package/dist/scw/errors/standard/out-of-stock-error.js +2 -0
- package/dist/scw/errors/standard/permissions-denied-error.js +2 -0
- package/dist/scw/errors/standard/precondition-failed-error.js +2 -0
- package/dist/scw/errors/standard/quotas-exceeded-error.js +2 -0
- package/dist/scw/errors/standard/resource-expired-error.js +2 -0
- package/dist/scw/errors/standard/resource-locked-error.js +2 -0
- package/dist/scw/errors/standard/resource-not-found-error.js +2 -0
- package/dist/scw/errors/standard/too-many-requests-error.js +2 -0
- package/dist/scw/errors/standard/transient-state-error.js +2 -0
- package/dist/scw/errors/types.js +3 -1
- package/dist/scw/fetch/__tests__/build-fetcher.test.js +114 -0
- package/dist/scw/fetch/__tests__/http-dumper.test.js +45 -0
- package/dist/scw/fetch/__tests__/http-interceptors.test.js +60 -0
- package/dist/scw/fetch/__tests__/resource-paginator.test.js +110 -0
- package/dist/scw/fetch/__tests__/response-parser.test.js +94 -0
- package/dist/scw/fetch/build-fetcher.js +6 -4
- package/dist/scw/fetch/http-dumper.js +4 -2
- package/dist/scw/fetch/http-interceptors.js +5 -3
- package/dist/scw/fetch/resource-paginator.js +6 -4
- package/dist/scw/fetch/response-parser.js +5 -3
- package/dist/scw/fetch/types.js +0 -0
- package/dist/scw/locality.js +2 -0
- package/dist/vendor/base64/index.d.js +0 -0
- package/dist/vendor/base64/index.js +2 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { hasAuthenticationSecrets } from "./client-ini-profile.js";
|
|
2
2
|
import { authenticateWithSecrets } from "./auth.js";
|
|
3
|
+
//#region src/scw/client-ini-factory.ts
|
|
3
4
|
/**
|
|
4
5
|
* Instantiates the SDK from a configuration {@link Profile}.
|
|
5
6
|
*
|
|
@@ -10,7 +11,7 @@ import { authenticateWithSecrets } from "./auth.js";
|
|
|
10
11
|
*
|
|
11
12
|
* @public
|
|
12
13
|
*/
|
|
13
|
-
|
|
14
|
+
var withProfile = (profile) => (settings) => {
|
|
14
15
|
const newSettings = { ...settings };
|
|
15
16
|
if (profile.apiURL) newSettings.apiURL = profile.apiURL;
|
|
16
17
|
if (profile.defaultOrganizationId) newSettings.defaultOrganizationId = profile.defaultOrganizationId;
|
|
@@ -30,7 +31,7 @@ const withProfile = (profile) => (settings) => {
|
|
|
30
31
|
*
|
|
31
32
|
* @public
|
|
32
33
|
*/
|
|
33
|
-
|
|
34
|
+
var withHTTPClient = (httpClient) => (settings) => ({
|
|
34
35
|
...settings,
|
|
35
36
|
httpClient
|
|
36
37
|
});
|
|
@@ -44,7 +45,7 @@ const withHTTPClient = (httpClient) => (settings) => ({
|
|
|
44
45
|
*
|
|
45
46
|
* @public
|
|
46
47
|
*/
|
|
47
|
-
|
|
48
|
+
var withDefaultPageSize = (defaultPageSize) => (settings) => ({
|
|
48
49
|
...settings,
|
|
49
50
|
defaultPageSize
|
|
50
51
|
});
|
|
@@ -58,7 +59,7 @@ const withDefaultPageSize = (defaultPageSize) => (settings) => ({
|
|
|
58
59
|
*
|
|
59
60
|
* @public
|
|
60
61
|
*/
|
|
61
|
-
|
|
62
|
+
var withUserAgent = (userAgent) => (settings) => ({
|
|
62
63
|
...settings,
|
|
63
64
|
userAgent
|
|
64
65
|
});
|
|
@@ -72,7 +73,7 @@ const withUserAgent = (userAgent) => (settings) => ({
|
|
|
72
73
|
*
|
|
73
74
|
* @public
|
|
74
75
|
*/
|
|
75
|
-
|
|
76
|
+
var withUserAgentSuffix = (userAgent) => (settings) => ({
|
|
76
77
|
...settings,
|
|
77
78
|
userAgent: settings.userAgent ? `${settings.userAgent} ${userAgent}` : userAgent
|
|
78
79
|
});
|
|
@@ -118,14 +119,14 @@ const withUserAgentSuffix = (userAgent) => (settings) => ({
|
|
|
118
119
|
*
|
|
119
120
|
* @public
|
|
120
121
|
*/
|
|
121
|
-
|
|
122
|
+
var withAdditionalInterceptors = (interceptors) => (settings) => ({
|
|
122
123
|
...settings,
|
|
123
124
|
interceptors: settings.interceptors.concat(interceptors)
|
|
124
125
|
});
|
|
125
126
|
/**
|
|
126
127
|
* Instantiates the SDK with legacy interceptors.
|
|
127
128
|
*/
|
|
128
|
-
|
|
129
|
+
var withLegacyInterceptors = () => (settings) => {
|
|
129
130
|
if (!settings.requestInterceptors && !settings.responseInterceptors) return settings;
|
|
130
131
|
const allInterceptors = settings.interceptors.concat((settings.requestInterceptors ?? []).map((obj) => ({ request: obj })), (settings.responseInterceptors ?? []).map((obj) => ({ response: obj })));
|
|
131
132
|
return {
|
|
@@ -133,4 +134,5 @@ const withLegacyInterceptors = () => (settings) => {
|
|
|
133
134
|
interceptors: allInterceptors
|
|
134
135
|
};
|
|
135
136
|
};
|
|
137
|
+
//#endregion
|
|
136
138
|
export { withAdditionalInterceptors, withDefaultPageSize, withHTTPClient, withLegacyInterceptors, withProfile, withUserAgent, withUserAgentSuffix };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAccessKey, isSecretKey } from "../internal/validations/string-validation.js";
|
|
2
|
+
//#region src/scw/client-ini-profile.ts
|
|
2
3
|
/**
|
|
3
4
|
* Verifies that the payload contains both the accessKey and the secretKey.
|
|
4
5
|
*
|
|
@@ -7,7 +8,7 @@ import { isAccessKey, isSecretKey } from "../internal/validations/string-validat
|
|
|
7
8
|
*
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var hasAuthenticationSecrets = (obj) => typeof obj.accessKey === "string" && obj.accessKey !== "" && typeof obj.secretKey === "string" && obj.secretKey !== "";
|
|
11
12
|
/**
|
|
12
13
|
* Asserts the format of secrets.
|
|
13
14
|
*
|
|
@@ -24,4 +25,5 @@ function assertValidAuthenticationSecrets(obj) {
|
|
|
24
25
|
if (!isAccessKey(obj.accessKey)) throw new Error(`Invalid access key format '${obj.accessKey}', expected SCWXXXXXXXXXXXXXXXXX format. See https://www.scaleway.com/en/docs/identity-and-access-management/iam/how-to/create-api-keys/`);
|
|
25
26
|
if (!isSecretKey(obj.secretKey)) throw new Error(`Invalid secret key format '${obj.secretKey}', expected a UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. See https://www.scaleway.com/en/docs/identity-and-access-management/iam/how-to/create-api-keys/`);
|
|
26
27
|
}
|
|
28
|
+
//#endregion
|
|
27
29
|
export { assertValidAuthenticationSecrets, hasAuthenticationSecrets };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isOrganizationId, isProjectId, isRegion, isURL, isZone } from "../internal/validations/string-validation.js";
|
|
2
|
+
//#region src/scw/client-settings.ts
|
|
2
3
|
/**
|
|
3
4
|
* Validates the content of a {@link Settings} object.
|
|
4
5
|
*
|
|
@@ -7,7 +8,7 @@ import { isOrganizationId, isProjectId, isRegion, isURL, isZone } from "../inter
|
|
|
7
8
|
*
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var assertValidSettings = (obj) => {
|
|
11
12
|
if (obj.defaultOrganizationId !== void 0) {
|
|
12
13
|
if (typeof obj.defaultOrganizationId !== "string" || obj.defaultOrganizationId.length === 0) throw new Error("Default organization ID cannot be empty");
|
|
13
14
|
if (!isOrganizationId(obj.defaultOrganizationId)) throw new Error(`Invalid organization ID format '${obj.defaultOrganizationId}', expected a UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`);
|
|
@@ -24,4 +25,5 @@ const assertValidSettings = (obj) => {
|
|
|
24
25
|
if (obj.defaultPageSize !== void 0 && (typeof obj.defaultPageSize !== "number" || Number.isNaN(obj.defaultPageSize) || obj.defaultPageSize <= 0)) throw new Error(`Invalid defaultPageSize ${obj.defaultPageSize}: it should be a number above 0`);
|
|
25
26
|
if (typeof obj.userAgent !== "string") throw new Error(`Invalid User-Agent`);
|
|
26
27
|
};
|
|
28
|
+
//#endregion
|
|
27
29
|
export { assertValidSettings };
|
package/dist/scw/client.js
CHANGED
|
@@ -3,6 +3,7 @@ import { userAgent, version } from "./constants.js";
|
|
|
3
3
|
import { withAdditionalInterceptors, withLegacyInterceptors, withProfile } from "./client-ini-factory.js";
|
|
4
4
|
import { assertValidSettings } from "./client-settings.js";
|
|
5
5
|
import { buildFetcher } from "./fetch/build-fetcher.js";
|
|
6
|
+
//#region src/scw/client.ts
|
|
6
7
|
/** Default {@link Settings} values. */
|
|
7
8
|
var DEFAULT_SETTINGS = {
|
|
8
9
|
apiURL: "https://api.scaleway.com",
|
|
@@ -34,7 +35,7 @@ var DEFAULT_SETTINGS = {
|
|
|
34
35
|
*
|
|
35
36
|
* @public
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
var createAdvancedClient = (...configs) => {
|
|
38
39
|
const settings = configs.concat([withLegacyInterceptors()]).reduce((currentSettings, config) => config(currentSettings), DEFAULT_SETTINGS);
|
|
39
40
|
assertValidSettings(settings);
|
|
40
41
|
getLogger().info(`init Scaleway SDK version ${version}`);
|
|
@@ -78,5 +79,6 @@ const createAdvancedClient = (...configs) => {
|
|
|
78
79
|
*
|
|
79
80
|
* @public
|
|
80
81
|
*/
|
|
81
|
-
|
|
82
|
+
var createClient = (settings = {}) => createAdvancedClient(withProfile(settings), withAdditionalInterceptors(settings.interceptors ?? []));
|
|
83
|
+
//#endregion
|
|
82
84
|
export { createAdvancedClient, createClient };
|
package/dist/scw/constants.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import package_default from "../package.js";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
//#region src/scw/constants.ts
|
|
3
|
+
var { version } = package_default;
|
|
4
|
+
var userAgent = `scaleway-sdk-js/${version}`;
|
|
5
|
+
var SESSION_HEADER_KEY = "x-session-token";
|
|
6
|
+
var AUTH_HEADER_KEY = "x-auth-token";
|
|
7
|
+
//#endregion
|
|
6
8
|
export { AUTH_HEADER_KEY, SESSION_HEADER_KEY, userAgent, version };
|
|
@@ -2,12 +2,13 @@ import { camelizeKeys, isJSONObject } from "../helpers/json.js";
|
|
|
2
2
|
import { unmarshalArrayOfObject, unmarshalDate } from "../helpers/marshalling.js";
|
|
3
3
|
import { fromByteArray } from "../vendor/base64/index.js";
|
|
4
4
|
import { Decimal } from "./custom-types.js";
|
|
5
|
+
//#region src/scw/custom-marshalling.ts
|
|
5
6
|
/**
|
|
6
7
|
* Unmarshals {@link Money}
|
|
7
8
|
*
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var unmarshalMoney = (data) => {
|
|
11
12
|
if (!isJSONObject(data)) throw new TypeError(`Unmarshalling the type 'Money' failed as data isn't a dictionary.`);
|
|
12
13
|
return {
|
|
13
14
|
currencyCode: data.currency_code,
|
|
@@ -20,7 +21,7 @@ const unmarshalMoney = (data) => {
|
|
|
20
21
|
*
|
|
21
22
|
* @internal
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
var unmarshalServiceInfo = (data) => {
|
|
24
25
|
if (!isJSONObject(data)) throw new TypeError(`Unmarshalling the type 'ServiceInfo' failed as data isn't a dictionary.`);
|
|
25
26
|
return {
|
|
26
27
|
description: data.description,
|
|
@@ -34,7 +35,7 @@ const unmarshalServiceInfo = (data) => {
|
|
|
34
35
|
*
|
|
35
36
|
* @internal
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
var unmarshalScwFile = (data) => {
|
|
38
39
|
if (!isJSONObject(data)) throw new TypeError(`Unmarshalling the type 'ScwFile' failed as data isn't a dictionary.`);
|
|
39
40
|
return {
|
|
40
41
|
content: data.content,
|
|
@@ -51,7 +52,7 @@ const unmarshalScwFile = (data) => {
|
|
|
51
52
|
*
|
|
52
53
|
* @internal
|
|
53
54
|
*/
|
|
54
|
-
|
|
55
|
+
var unmarshalTimeSeriesPoint = (data) => {
|
|
55
56
|
if (!Array.isArray(data)) throw new TypeError(`Unmarshalling the type 'TimeSeriesPoint' failed as data isn't an array.`);
|
|
56
57
|
return {
|
|
57
58
|
timestamp: unmarshalDate(data[0]),
|
|
@@ -63,7 +64,7 @@ const unmarshalTimeSeriesPoint = (data) => {
|
|
|
63
64
|
*
|
|
64
65
|
* @internal
|
|
65
66
|
*/
|
|
66
|
-
|
|
67
|
+
var unmarshalTimeSeries = (data) => {
|
|
67
68
|
if (!isJSONObject(data)) throw new TypeError(`Unmarshalling the type 'TimeSeries' failed as data isn't a dictionary.`);
|
|
68
69
|
return {
|
|
69
70
|
metadata: data.metadata,
|
|
@@ -76,7 +77,7 @@ const unmarshalTimeSeries = (data) => {
|
|
|
76
77
|
*
|
|
77
78
|
* @internal
|
|
78
79
|
*/
|
|
79
|
-
|
|
80
|
+
var unmarshalDecimal = (data) => {
|
|
80
81
|
if (!(typeof data === "object")) throw new TypeError(`Unmarshalling the type 'Decimal' failed as data isn't an object.`);
|
|
81
82
|
if (data === null) return null;
|
|
82
83
|
if (!("value" in data)) throw new TypeError(`Unmarshalling the type 'Decimal' failed as data object does not have a 'value' key.`);
|
|
@@ -88,7 +89,7 @@ const unmarshalDecimal = (data) => {
|
|
|
88
89
|
*
|
|
89
90
|
* @internal
|
|
90
91
|
*/
|
|
91
|
-
|
|
92
|
+
var marshalScwFile = (obj) => ({
|
|
92
93
|
content: obj.content,
|
|
93
94
|
content_type: obj.contentType,
|
|
94
95
|
name: obj.name
|
|
@@ -98,7 +99,7 @@ const marshalScwFile = (obj) => ({
|
|
|
98
99
|
*
|
|
99
100
|
* @internal
|
|
100
101
|
*/
|
|
101
|
-
|
|
102
|
+
var marshalBlobToScwFile = async (blob) => ({
|
|
102
103
|
content: fromByteArray(new Uint8Array(await blob.arrayBuffer())),
|
|
103
104
|
content_type: blob.type,
|
|
104
105
|
name: "file"
|
|
@@ -108,7 +109,7 @@ const marshalBlobToScwFile = async (blob) => ({
|
|
|
108
109
|
*
|
|
109
110
|
* @internal
|
|
110
111
|
*/
|
|
111
|
-
|
|
112
|
+
var marshalMoney = (obj) => ({
|
|
112
113
|
currency_code: obj.currencyCode,
|
|
113
114
|
nanos: obj.nanos,
|
|
114
115
|
units: obj.units
|
|
@@ -118,7 +119,7 @@ const marshalMoney = (obj) => ({
|
|
|
118
119
|
*
|
|
119
120
|
* @internal
|
|
120
121
|
*/
|
|
121
|
-
|
|
122
|
+
var marshalTimeSeriesPoint = (obj) => ({
|
|
122
123
|
timestamp: obj.timestamp?.toISOString(),
|
|
123
124
|
value: obj.value
|
|
124
125
|
});
|
|
@@ -127,7 +128,7 @@ const marshalTimeSeriesPoint = (obj) => ({
|
|
|
127
128
|
*
|
|
128
129
|
* @internal
|
|
129
130
|
*/
|
|
130
|
-
|
|
131
|
+
var marshalTimeSeries = (obj) => ({
|
|
131
132
|
metadata: obj.metadata,
|
|
132
133
|
name: obj.name,
|
|
133
134
|
points: obj.points.map((elt) => marshalTimeSeriesPoint(elt))
|
|
@@ -137,7 +138,7 @@ const marshalTimeSeries = (obj) => ({
|
|
|
137
138
|
*
|
|
138
139
|
* @internal
|
|
139
140
|
*/
|
|
140
|
-
|
|
141
|
+
var marshalDecimal = (obj) => ({ value: obj.toString() });
|
|
141
142
|
/**
|
|
142
143
|
* Unmarshals record to convert iso dates from string to Dates.
|
|
143
144
|
*
|
|
@@ -147,7 +148,7 @@ const marshalDecimal = (obj) => ({ value: obj.toString() });
|
|
|
147
148
|
*
|
|
148
149
|
* @internal
|
|
149
150
|
*/
|
|
150
|
-
|
|
151
|
+
var unmarshalDates = (obj, keys) => {
|
|
151
152
|
if (Array.isArray(obj)) return obj.map((v) => unmarshalDates(v, keys));
|
|
152
153
|
if (obj && typeof obj === "object") {
|
|
153
154
|
const result = {};
|
|
@@ -169,8 +170,9 @@ const unmarshalDates = (obj, keys) => {
|
|
|
169
170
|
*
|
|
170
171
|
* @internal
|
|
171
172
|
*/
|
|
172
|
-
|
|
173
|
+
var unmarshalAnyRes = (obj, ignoreKeys = [], dateKeys) => {
|
|
173
174
|
if (!isJSONObject(obj)) throw new TypeError(`Data isn't a dictionary.`);
|
|
174
175
|
return camelizeKeys(dateKeys && dateKeys.length > 0 ? unmarshalDates(obj, dateKeys) : obj, ignoreKeys);
|
|
175
176
|
};
|
|
176
|
-
|
|
177
|
+
//#endregion
|
|
178
|
+
export { marshalBlobToScwFile, marshalDecimal, marshalMoney, marshalScwFile, marshalTimeSeries, marshalTimeSeriesPoint, unmarshalAnyRes, unmarshalDates, unmarshalDecimal, unmarshalMoney, unmarshalScwFile, unmarshalServiceInfo, unmarshalTimeSeries, unmarshalTimeSeriesPoint };
|
package/dist/scw/custom-types.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/scw/custom-types.ts
|
|
1
2
|
/** A representation of a decimal value, such as 2.5.
|
|
2
3
|
* Comparable to language-native decimal formats, such as Java's BigDecimal or Python's decimal.Decimal.
|
|
3
4
|
* Lookup protobuf google.type.Decimal for details */
|
|
@@ -9,4 +10,5 @@ var Decimal = class {
|
|
|
9
10
|
toString = () => this.str;
|
|
10
11
|
marshal = () => ({ value: this.str });
|
|
11
12
|
};
|
|
13
|
+
//#endregion
|
|
12
14
|
export { Decimal };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ScalewayError } from "../scw-error.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/scw/errors/__tests__/scw-error.test.ts
|
|
4
|
+
describe("ScalewayError", () => {
|
|
5
|
+
it("initializes from JSON", () => {
|
|
6
|
+
expect(ScalewayError.fromJSON(400, {})?.message).toBe("http error 400");
|
|
7
|
+
});
|
|
8
|
+
it("returns proper name, even if class name has changed", () => {
|
|
9
|
+
class AltError extends ScalewayError {}
|
|
10
|
+
expect(new AltError(200, "").name).toBe("ScalewayError");
|
|
11
|
+
});
|
|
12
|
+
it(`returns only the status code for empty body`, () => {
|
|
13
|
+
expect(new ScalewayError(400, {}).message).toBe(`http error 400`);
|
|
14
|
+
});
|
|
15
|
+
it(`handles a string body`, () => {
|
|
16
|
+
expect(new ScalewayError(400, "unknown error").message).toBe(`http error 400: unknown error`);
|
|
17
|
+
});
|
|
18
|
+
it(`handles a non-JSON object`, () => {
|
|
19
|
+
expect(new ScalewayError(400, []).message).toBe(`http error 400`);
|
|
20
|
+
});
|
|
21
|
+
it(`handles a resource field`, () => {
|
|
22
|
+
expect(new ScalewayError(400, { resource: "registry" }).message).toBe(`http error 400: resource registry`);
|
|
23
|
+
});
|
|
24
|
+
it(`handles a message field`, () => {
|
|
25
|
+
expect(new ScalewayError(400, { message: "plain text message" }).message).toBe(`http error 400: plain text message`);
|
|
26
|
+
expect(new ScalewayError(400, { message: "plain text message" }).rawMessage).toBe(`plain text message`);
|
|
27
|
+
});
|
|
28
|
+
it(`handles 1 field with 1 message`, () => {
|
|
29
|
+
expect(new ScalewayError(400, { fields: { project_id: ["value is required"] } }).message).toBe(`http error 400: project_id (value is required)`);
|
|
30
|
+
});
|
|
31
|
+
it(`handles 1 field with 2 messages`, () => {
|
|
32
|
+
expect(new ScalewayError(400, { fields: { project_id: ["value is required", "incorrect value"] } }).message).toBe(`http error 400: project_id (value is required, incorrect value)`);
|
|
33
|
+
});
|
|
34
|
+
it(`handles 2 fields`, () => {
|
|
35
|
+
expect(new ScalewayError(400, { fields: {
|
|
36
|
+
organization_id: ["project_id is already specified"],
|
|
37
|
+
project_id: ["value is required"]
|
|
38
|
+
} }).message).toBe(`http error 400: organization_id (project_id is already specified), project_id (value is required)`);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { isRecordOfStringArray } from "../types.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/scw/errors/__tests__/types.test.ts
|
|
4
|
+
describe("isRecordOfStringArray", () => {
|
|
5
|
+
it("validates a proper object", () => {
|
|
6
|
+
expect(isRecordOfStringArray({ myValue: ["reason 1", "reason 2"] })).toBeTruthy();
|
|
7
|
+
});
|
|
8
|
+
it("refuses object with proper structure but non-string values", () => {
|
|
9
|
+
expect(isRecordOfStringArray({ myValue: [42] })).toBeFalsy();
|
|
10
|
+
});
|
|
11
|
+
it("refuses non-JSONObjects", () => {
|
|
12
|
+
expect(isRecordOfStringArray(["reason 1"])).toBeFalsy();
|
|
13
|
+
expect(isRecordOfStringArray("reason 1")).toBeFalsy();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
//#endregion
|
|
@@ -13,6 +13,7 @@ import { ResourceExpiredError } from "./standard/resource-expired-error.js";
|
|
|
13
13
|
import { ResourceLockedError } from "./standard/resource-locked-error.js";
|
|
14
14
|
import { TooManyRequestsError } from "./standard/too-many-requests-error.js";
|
|
15
15
|
import { TransientStateError } from "./standard/transient-state-error.js";
|
|
16
|
+
//#region src/scw/errors/error-parser.ts
|
|
16
17
|
/**
|
|
17
18
|
* Unmarshals a standard error from raw body.
|
|
18
19
|
*
|
|
@@ -92,7 +93,8 @@ var unmarshalNonStandardError = (type, status, body) => {
|
|
|
92
93
|
*
|
|
93
94
|
* @internal
|
|
94
95
|
*/
|
|
95
|
-
|
|
96
|
+
var parseScalewayError = (status, body) => {
|
|
96
97
|
return typeof body.type === "string" && (unmarshalStandardError(body.type, status, body) ?? unmarshalNonStandardError(body.type, status, body)) || new ScalewayError(status, body);
|
|
97
98
|
};
|
|
99
|
+
//#endregion
|
|
98
100
|
export { parseScalewayError };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { ScalewayError } from "../../scw-error.js";
|
|
2
|
+
import { InvalidArgumentsError } from "../../standard/invalid-arguments-error.js";
|
|
3
|
+
import { QuotasExceededError } from "../../standard/quotas-exceeded-error.js";
|
|
4
|
+
import { ResourceNotFoundError } from "../../standard/resource-not-found-error.js";
|
|
5
|
+
import { parseScalewayError } from "../../error-parser.js";
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
//#region src/scw/errors/non-standard/__tests__/index.test.ts
|
|
8
|
+
describe("InvalidRequestError", () => {
|
|
9
|
+
it(`transforms to InvalidArgumentsError when there are fields`, () => {
|
|
10
|
+
const error = parseScalewayError(400, {
|
|
11
|
+
fields: {
|
|
12
|
+
"volumes.5.id": ["92 is not a valid UUID."],
|
|
13
|
+
"volumes.5.name": ["required key not provided"]
|
|
14
|
+
},
|
|
15
|
+
message: "Validation Error",
|
|
16
|
+
type: "invalid_request_error"
|
|
17
|
+
});
|
|
18
|
+
expect(error).toBeInstanceOf(InvalidArgumentsError);
|
|
19
|
+
expect(error instanceof Error ? error.details : void 0).toEqual([{
|
|
20
|
+
argumentName: "volumes.5.id",
|
|
21
|
+
helpMessage: "92 is not a valid UUID.",
|
|
22
|
+
reason: "constraint"
|
|
23
|
+
}, {
|
|
24
|
+
argumentName: "volumes.5.name",
|
|
25
|
+
helpMessage: "required key not provided",
|
|
26
|
+
reason: "constraint"
|
|
27
|
+
}]);
|
|
28
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("invalid argument(s): volumes.5.id does not respect constraint, 92 is not a valid UUID.; volumes.5.name does not respect constraint, required key not provided");
|
|
29
|
+
});
|
|
30
|
+
it(`transforms to QuotasExceededError when message includes a specific text`, () => {
|
|
31
|
+
const error = parseScalewayError(400, {
|
|
32
|
+
message: "Quota exceeded for this resource.",
|
|
33
|
+
resource: "compute_snapshots_type_b_ssd_available",
|
|
34
|
+
type: "invalid_request_error"
|
|
35
|
+
});
|
|
36
|
+
expect(error).toBeInstanceOf(QuotasExceededError);
|
|
37
|
+
expect(error instanceof Error ? error.list : void 0).toEqual([{
|
|
38
|
+
current: 0,
|
|
39
|
+
quota: 0,
|
|
40
|
+
resource: "compute_snapshots_type_b_ssd_available"
|
|
41
|
+
}]);
|
|
42
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("quota(s) exceeded: Quotas reached: You have reached the maximum number of compute_snapshots_type_b_ssd_available authorized by your Organization. Access the quotas page from your Organization dashboard to manage quotas.");
|
|
43
|
+
});
|
|
44
|
+
it(`transforms to QuotasExceededError when message includes a specific text, no resource case`, () => {
|
|
45
|
+
const error = parseScalewayError(400, {
|
|
46
|
+
message: "Quota exceeded for this resource.",
|
|
47
|
+
type: "invalid_request_error"
|
|
48
|
+
});
|
|
49
|
+
expect(error).toBeInstanceOf(QuotasExceededError);
|
|
50
|
+
expect(error instanceof Error ? error.list : void 0).toEqual([{
|
|
51
|
+
current: 0,
|
|
52
|
+
quota: 0,
|
|
53
|
+
resource: ""
|
|
54
|
+
}]);
|
|
55
|
+
});
|
|
56
|
+
it(`fallbacks to ScalewayError when there is only a message`, () => {
|
|
57
|
+
const error = parseScalewayError(400, {
|
|
58
|
+
message: "server should be running",
|
|
59
|
+
type: "invalid_request_error"
|
|
60
|
+
});
|
|
61
|
+
expect(error).toBeInstanceOf(ScalewayError);
|
|
62
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("http error 400: server should be running");
|
|
63
|
+
});
|
|
64
|
+
it(`fallbacks on ScalewayError without a message`, () => {
|
|
65
|
+
const error = parseScalewayError(400, { type: "invalid_request_error" });
|
|
66
|
+
expect(error).toBeInstanceOf(ScalewayError);
|
|
67
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("http error 400");
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("UnknownResourceError", () => {
|
|
71
|
+
it(`transforms to ResourceNotFoundError with only the resource ID`, () => {
|
|
72
|
+
const error = parseScalewayError(404, {
|
|
73
|
+
message: `"11111111-1111-4111-8111-111111111142" not found`,
|
|
74
|
+
type: "unknown_resource"
|
|
75
|
+
});
|
|
76
|
+
expect(error).toBeInstanceOf(ResourceNotFoundError);
|
|
77
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("resource with ID 11111111-1111-4111-8111-111111111142 is not found");
|
|
78
|
+
expect(error instanceof Error ? error.resource : void 0).toBe("");
|
|
79
|
+
expect(error instanceof Error ? error.resourceId : void 0).toBe("11111111-1111-4111-8111-111111111142");
|
|
80
|
+
});
|
|
81
|
+
it(`transforms to ResourceNotFoundError with the resource Name and ID`, () => {
|
|
82
|
+
const error = parseScalewayError(404, {
|
|
83
|
+
message: `Security group "11111111-1111-4111-8111-111111111112" not found`,
|
|
84
|
+
type: "unknown_resource"
|
|
85
|
+
});
|
|
86
|
+
expect(error).toBeInstanceOf(ResourceNotFoundError);
|
|
87
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("resource security_group with ID 11111111-1111-4111-8111-111111111112 is not found");
|
|
88
|
+
expect(error instanceof Error ? error.resource : void 0).toBe("security_group");
|
|
89
|
+
expect(error instanceof Error ? error.resourceId : void 0).toBe("11111111-1111-4111-8111-111111111112");
|
|
90
|
+
});
|
|
91
|
+
it("transforms to ResourceNotFoundError with the not found resource / single quote", () => {
|
|
92
|
+
const error = parseScalewayError(404, {
|
|
93
|
+
message: `Volume '11111111-1111-4111-8111-111111111111' not found`,
|
|
94
|
+
type: "unknown_resource"
|
|
95
|
+
});
|
|
96
|
+
expect(error).toBeInstanceOf(ResourceNotFoundError);
|
|
97
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("resource volume with ID 11111111-1111-4111-8111-111111111111 is not found");
|
|
98
|
+
expect(error instanceof Error ? error.resource : void 0).toBe("volume");
|
|
99
|
+
expect(error instanceof Error ? error.resourceId : void 0).toBe("11111111-1111-4111-8111-111111111111");
|
|
100
|
+
});
|
|
101
|
+
it(`transforms to ScalewayError when the message can't be analyzed`, () => {
|
|
102
|
+
const error = parseScalewayError(404, {
|
|
103
|
+
message: "uncommon message",
|
|
104
|
+
type: "unknown_resource"
|
|
105
|
+
});
|
|
106
|
+
expect(error).toBeInstanceOf(ScalewayError);
|
|
107
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("http error 404: uncommon message");
|
|
108
|
+
});
|
|
109
|
+
it("do not transform to ResourceNotFoundError for invalid resource ID", () => {
|
|
110
|
+
const error = parseScalewayError(404, {
|
|
111
|
+
message: `Volume 'not-an-uuid' not found`,
|
|
112
|
+
type: "unknown_resource"
|
|
113
|
+
});
|
|
114
|
+
expect(error).not.toBeInstanceOf(ResourceNotFoundError);
|
|
115
|
+
expect(error instanceof Error ? error.message : String(error)).toBe(`http error 404: Volume 'not-an-uuid' not found`);
|
|
116
|
+
});
|
|
117
|
+
it("fallbacks to ScalewayError without a message", () => {
|
|
118
|
+
const error = parseScalewayError(404, { type: "unknown_resource" });
|
|
119
|
+
expect(error).toBeInstanceOf(ScalewayError);
|
|
120
|
+
expect(error instanceof Error ? error.message : String(error)).toBe("http error 404");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
//#endregion
|
|
@@ -2,12 +2,13 @@ import { isRecordOfStringArray } from "../types.js";
|
|
|
2
2
|
import { ScalewayError } from "../scw-error.js";
|
|
3
3
|
import { InvalidArgumentsError } from "../standard/invalid-arguments-error.js";
|
|
4
4
|
import { QuotasExceededError } from "../standard/quotas-exceeded-error.js";
|
|
5
|
+
//#region src/scw/errors/non-standard/invalid-request-mapper.ts
|
|
5
6
|
/**
|
|
6
7
|
* InvalidRequest error is only returned by the instance API.
|
|
7
8
|
*
|
|
8
9
|
* @public
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var mapInvalidRequestFromJSON = (status, obj) => {
|
|
11
12
|
if (typeof obj.message === "string" && obj.message.toLowerCase().includes("quota exceeded for this resource")) return new QuotasExceededError(status, obj, [{
|
|
12
13
|
current: 0,
|
|
13
14
|
quota: 0,
|
|
@@ -22,4 +23,5 @@ const mapInvalidRequestFromJSON = (status, obj) => {
|
|
|
22
23
|
}))));
|
|
23
24
|
return new ScalewayError(status, obj);
|
|
24
25
|
};
|
|
26
|
+
//#endregion
|
|
25
27
|
export { mapInvalidRequestFromJSON };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { isUUID } from "../../../internal/validations/string-validation.js";
|
|
2
2
|
import { ScalewayError } from "../scw-error.js";
|
|
3
3
|
import { ResourceNotFoundError } from "../standard/resource-not-found-error.js";
|
|
4
|
+
//#region src/scw/errors/non-standard/unknown-resource-mapper.ts
|
|
4
5
|
/**
|
|
5
6
|
* UnknownResource error is only returned by the instance API.
|
|
6
7
|
*
|
|
7
8
|
* @public
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
var mapUnknownResourceFromJSON = (status, obj) => {
|
|
10
11
|
const messageParts = typeof obj.message === "string" ? obj.message.split(/"|'/) : [];
|
|
11
12
|
if (messageParts.length === 3 && isUUID(messageParts[1])) return new ResourceNotFoundError(status, obj, messageParts[0].trim().toLowerCase().split(" ").join("_"), messageParts[1]);
|
|
12
13
|
return new ScalewayError(status, obj);
|
|
13
14
|
};
|
|
15
|
+
//#endregion
|
|
14
16
|
export { mapUnknownResourceFromJSON };
|
|
File without changes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isJSONObject } from "../../helpers/json.js";
|
|
2
2
|
import { isRecordOfStringArray } from "./types.js";
|
|
3
|
+
//#region src/scw/errors/scw-error.ts
|
|
3
4
|
/**
|
|
4
5
|
* Builds the default message for {@link ScalewayError}.
|
|
5
6
|
*
|
|
@@ -43,4 +44,5 @@ var ScalewayError = class ScalewayError extends Error {
|
|
|
43
44
|
return `${this.name}: ${this.message}`;
|
|
44
45
|
}
|
|
45
46
|
};
|
|
47
|
+
//#endregion
|
|
46
48
|
export { ScalewayError };
|