@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
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { composeRequestInterceptors, composeResponseErrorInterceptors } from "../composer.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/internal/interceptors/__tests__/composer.test.ts
|
|
4
|
+
describe("composeRequestInterceptors", () => {
|
|
5
|
+
it("modifies the request header", async () => {
|
|
6
|
+
return expect(composeRequestInterceptors([({ request }) => {
|
|
7
|
+
const clone = request.clone();
|
|
8
|
+
clone.headers.set("new-header", "42");
|
|
9
|
+
return clone;
|
|
10
|
+
}])(new Request("https://api.scaleway.com")).then((obj) => obj.headers.get("new-header"))).resolves.toBe("42");
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("composeResponseErrorInterceptors", () => {
|
|
14
|
+
it("passes the error to all interceptors if they all throw", () => {
|
|
15
|
+
class NumberError extends Error {
|
|
16
|
+
counter;
|
|
17
|
+
constructor(obj) {
|
|
18
|
+
super();
|
|
19
|
+
this.counter = obj;
|
|
20
|
+
Object.setPrototypeOf(this, NumberError.prototype);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return expect(composeResponseErrorInterceptors([({ error }) => {
|
|
24
|
+
throw error instanceof NumberError ? new NumberError(error.counter + 1) : error;
|
|
25
|
+
}, ({ error }) => {
|
|
26
|
+
throw error instanceof NumberError ? new NumberError(error.counter + 2) : error;
|
|
27
|
+
}])(new Request("https://api.scaleway.com"), new NumberError(42))).rejects.toThrow(new NumberError(45));
|
|
28
|
+
});
|
|
29
|
+
it("stops at the second interceptor (amongst three) if it resolves", () => {
|
|
30
|
+
return expect(composeResponseErrorInterceptors([
|
|
31
|
+
({ error }) => {
|
|
32
|
+
throw error;
|
|
33
|
+
},
|
|
34
|
+
() => Promise.resolve(42),
|
|
35
|
+
({ error }) => {
|
|
36
|
+
throw error;
|
|
37
|
+
}
|
|
38
|
+
])(new Request("https://api.scaleway.com"), /* @__PURE__ */ new TypeError(""))).resolves.toBe(42);
|
|
39
|
+
});
|
|
40
|
+
it("throws the last processed error", () => {
|
|
41
|
+
return expect(composeResponseErrorInterceptors([({ error }) => {
|
|
42
|
+
throw error;
|
|
43
|
+
}, () => {
|
|
44
|
+
throw new TypeError("second error");
|
|
45
|
+
}])(new Request("https://api.scaleway.com"), /* @__PURE__ */ new TypeError("first error"))).rejects.toThrow(/* @__PURE__ */ new TypeError("second error"));
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//#endregion
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { addAsyncHeaderInterceptor, addHeaderInterceptor } from "../helpers.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/internal/interceptors/__tests__/helpers.test.ts
|
|
4
|
+
describe("addHeaderInterceptor", () => {
|
|
5
|
+
it("insertsnothing if value is undefined", async () => {
|
|
6
|
+
const request = new Request("https://api.scaleway.com/my/path");
|
|
7
|
+
expect((await addHeaderInterceptor("my-key", void 0)({ request })).headers.has("my-key")).toBe(false);
|
|
8
|
+
});
|
|
9
|
+
it("inserts 1 key/value in the request", async () => {
|
|
10
|
+
const request = new Request("https://api.scaleway.com/my/path");
|
|
11
|
+
expect((await addHeaderInterceptor("my-key", "my-value")({ request })).headers.get("my-key")).toBe("my-value");
|
|
12
|
+
});
|
|
13
|
+
it(`doesn't modify the input request`, async () => {
|
|
14
|
+
const request = new Request("https://api.scaleway.com/my/path");
|
|
15
|
+
const originalHeaders = request.headers;
|
|
16
|
+
const updatedReq = await addHeaderInterceptor("my-key", "my-value")({ request });
|
|
17
|
+
expect(request.headers).toBe(originalHeaders);
|
|
18
|
+
expect(updatedReq.headers.get("my-key")).toBe("my-value");
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
describe("addAsyncHeaderInterceptor", () => {
|
|
22
|
+
it("inserts 1 key/value in the request", async () => {
|
|
23
|
+
const request = new Request("https://api.scaleway.com/my/path");
|
|
24
|
+
expect((await addAsyncHeaderInterceptor("my-key", async () => Promise.resolve("my-value"))({ request })).headers.get("my-key")).toBe("my-value");
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
//#endregion
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/internal/interceptors/composer.ts
|
|
1
2
|
/**
|
|
2
3
|
* Composes request interceptors.
|
|
3
4
|
*
|
|
@@ -6,7 +7,7 @@
|
|
|
6
7
|
*
|
|
7
8
|
* @internal
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
var composeRequestInterceptors = (interceptors) => async (request) => interceptors.reduce(async (asyncResult, interceptor) => interceptor({ request: await asyncResult }), Promise.resolve(request));
|
|
10
11
|
/**
|
|
11
12
|
* Composes response interceptors.
|
|
12
13
|
*
|
|
@@ -15,13 +16,13 @@ const composeRequestInterceptors = (interceptors) => async (request) => intercep
|
|
|
15
16
|
*
|
|
16
17
|
* @internal
|
|
17
18
|
*/
|
|
18
|
-
|
|
19
|
+
var composeResponseInterceptors = (interceptors) => async (response) => interceptors.reduce(async (asyncResult, interceptor) => interceptor({ response: await asyncResult }), Promise.resolve(response));
|
|
19
20
|
/**
|
|
20
21
|
* Compose response error interceptors.
|
|
21
22
|
*
|
|
22
23
|
* @internal
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
var composeResponseErrorInterceptors = (interceptors) => async (request, error) => {
|
|
25
26
|
let prevError = error;
|
|
26
27
|
for (const interceptor of interceptors) try {
|
|
27
28
|
return await interceptor({
|
|
@@ -33,4 +34,5 @@ const composeResponseErrorInterceptors = (interceptors) => async (request, error
|
|
|
33
34
|
}
|
|
34
35
|
throw prevError;
|
|
35
36
|
};
|
|
37
|
+
//#endregion
|
|
36
38
|
export { composeRequestInterceptors, composeResponseErrorInterceptors, composeResponseInterceptors };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/internal/interceptors/helpers.ts
|
|
1
2
|
/**
|
|
2
3
|
* Adds an header to a request through an interceptor.
|
|
3
4
|
*
|
|
@@ -7,7 +8,7 @@
|
|
|
7
8
|
*
|
|
8
9
|
* @internal
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var addHeaderInterceptor = (key, value) => ({ request }) => {
|
|
11
12
|
const clone = request.clone();
|
|
12
13
|
if (value !== void 0) clone.headers.append(key, value);
|
|
13
14
|
return clone;
|
|
@@ -21,5 +22,6 @@ const addHeaderInterceptor = (key, value) => ({ request }) => {
|
|
|
21
22
|
*
|
|
22
23
|
* @internal
|
|
23
24
|
*/
|
|
24
|
-
|
|
25
|
+
var addAsyncHeaderInterceptor = (key, getter) => async (request) => addHeaderInterceptor(key, await getter())(request);
|
|
26
|
+
//#endregion
|
|
25
27
|
export { addAsyncHeaderInterceptor, addHeaderInterceptor };
|
|
File without changes
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { ConsoleLogger } from "../console-logger.js";
|
|
2
|
+
import { enableConsoleLogger, getLogger, setLogger } from "../index.js";
|
|
3
|
+
import { describe, expect, it, vi } from "vitest";
|
|
4
|
+
//#region src/internal/logger/__tests__/index.test.ts
|
|
5
|
+
var makeCallbackConsole = (onMessage) => ({
|
|
6
|
+
...console,
|
|
7
|
+
debug: onMessage,
|
|
8
|
+
error: onMessage,
|
|
9
|
+
info: onMessage,
|
|
10
|
+
warn: onMessage
|
|
11
|
+
});
|
|
12
|
+
var allLogLevels = [
|
|
13
|
+
"debug",
|
|
14
|
+
"info",
|
|
15
|
+
"warn",
|
|
16
|
+
"error"
|
|
17
|
+
];
|
|
18
|
+
var IS_LEVEL_ENOUGH_CASES = [
|
|
19
|
+
{
|
|
20
|
+
base: "debug",
|
|
21
|
+
tests: [
|
|
22
|
+
{
|
|
23
|
+
expected: true,
|
|
24
|
+
target: "debug"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
expected: true,
|
|
28
|
+
target: "info"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
expected: true,
|
|
32
|
+
target: "warn"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
expected: true,
|
|
36
|
+
target: "error"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
base: "info",
|
|
42
|
+
tests: [
|
|
43
|
+
{
|
|
44
|
+
expected: false,
|
|
45
|
+
target: "debug"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
expected: true,
|
|
49
|
+
target: "info"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
expected: true,
|
|
53
|
+
target: "warn"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
expected: true,
|
|
57
|
+
target: "error"
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
base: "warn",
|
|
63
|
+
tests: [
|
|
64
|
+
{
|
|
65
|
+
expected: false,
|
|
66
|
+
target: "debug"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
expected: false,
|
|
70
|
+
target: "info"
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
expected: true,
|
|
74
|
+
target: "warn"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
expected: true,
|
|
78
|
+
target: "error"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
base: "error",
|
|
84
|
+
tests: [
|
|
85
|
+
{
|
|
86
|
+
expected: false,
|
|
87
|
+
target: "debug"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
expected: false,
|
|
91
|
+
target: "info"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
expected: false,
|
|
95
|
+
target: "warn"
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
expected: true,
|
|
99
|
+
target: "error"
|
|
100
|
+
}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
base: "unknown",
|
|
105
|
+
tests: [
|
|
106
|
+
{
|
|
107
|
+
expected: false,
|
|
108
|
+
target: "debug"
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
expected: false,
|
|
112
|
+
target: "info"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
expected: false,
|
|
116
|
+
target: "warn"
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
expected: false,
|
|
120
|
+
target: "error"
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
}
|
|
124
|
+
];
|
|
125
|
+
describe("ConsoleLogger", () => {
|
|
126
|
+
it("initializes with the asked level", () => {
|
|
127
|
+
expect(new ConsoleLogger("info").logLevel).toBe("info");
|
|
128
|
+
});
|
|
129
|
+
it("returns the proper level boolean", () => {
|
|
130
|
+
const log = vi.fn().mockImplementation(() => {});
|
|
131
|
+
const out = makeCallbackConsole(log);
|
|
132
|
+
for (const test of IS_LEVEL_ENOUGH_CASES) {
|
|
133
|
+
const logger = new ConsoleLogger(test.base, "", out);
|
|
134
|
+
setLogger(logger);
|
|
135
|
+
for (const elt of test.tests) {
|
|
136
|
+
log.mockReset();
|
|
137
|
+
logger[elt.target](elt.target);
|
|
138
|
+
if (elt.expected) expect(log).toHaveBeenCalledWith(elt.target);
|
|
139
|
+
else expect(log).not.toHaveBeenCalledWith(elt.target);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
it("prints the indicated output", () => {
|
|
144
|
+
const prefix = "test:";
|
|
145
|
+
let latestMessage = "";
|
|
146
|
+
const logger = new ConsoleLogger("debug", prefix, makeCallbackConsole((message) => {
|
|
147
|
+
latestMessage = message;
|
|
148
|
+
}));
|
|
149
|
+
for (const method of allLogLevels) {
|
|
150
|
+
const randomStr = (Math.random() + 1).toString(36).substring(7);
|
|
151
|
+
logger[method](randomStr);
|
|
152
|
+
expect(latestMessage).toBe(`${prefix} ${randomStr}`);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
it("prints no output (as loglevel is silent)", () => {
|
|
156
|
+
let latestMessage = "";
|
|
157
|
+
const logger = new ConsoleLogger("silent", "test:", makeCallbackConsole((message) => {
|
|
158
|
+
latestMessage = message;
|
|
159
|
+
}));
|
|
160
|
+
for (const method of allLogLevels) {
|
|
161
|
+
const randomStr = (Math.random() + 1).toString(36).substring(7);
|
|
162
|
+
logger[method](randomStr);
|
|
163
|
+
expect(latestMessage).toBe("");
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
describe("setLogger", () => {
|
|
168
|
+
it(`changes the logger object when called'`, () => {
|
|
169
|
+
const initialLogger = getLogger();
|
|
170
|
+
setLogger(new ConsoleLogger("info"));
|
|
171
|
+
expect(initialLogger).not.toBe(getLogger());
|
|
172
|
+
});
|
|
173
|
+
it(`changes the logLevel when updated`, () => {
|
|
174
|
+
setLogger(new ConsoleLogger("warn"));
|
|
175
|
+
const { logLevel } = getLogger();
|
|
176
|
+
setLogger(new ConsoleLogger("debug"));
|
|
177
|
+
expect(getLogger().logLevel).not.toBe(logLevel);
|
|
178
|
+
expect(getLogger().logLevel).toBe("debug");
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("enableConsoleLogger", () => {
|
|
182
|
+
it(`initializes console logger with warn level by default`, () => {
|
|
183
|
+
setLogger(new ConsoleLogger("silent"));
|
|
184
|
+
expect(getLogger().logLevel).toBe("silent");
|
|
185
|
+
enableConsoleLogger();
|
|
186
|
+
expect(getLogger().logLevel).toBe("warn");
|
|
187
|
+
});
|
|
188
|
+
it(`initializes console logger with given level`, () => {
|
|
189
|
+
enableConsoleLogger("silent");
|
|
190
|
+
expect(getLogger().logLevel).toBe("silent");
|
|
191
|
+
enableConsoleLogger("debug");
|
|
192
|
+
expect(getLogger().logLevel).toBe("debug");
|
|
193
|
+
enableConsoleLogger("info");
|
|
194
|
+
expect(getLogger().logLevel).toBe("info");
|
|
195
|
+
enableConsoleLogger("warn");
|
|
196
|
+
expect(getLogger().logLevel).toBe("warn");
|
|
197
|
+
enableConsoleLogger("error");
|
|
198
|
+
expect(getLogger().logLevel).toBe("error");
|
|
199
|
+
});
|
|
200
|
+
it(`replaces custom logger with new console logger`, () => {
|
|
201
|
+
const customLogger = new ConsoleLogger("error");
|
|
202
|
+
setLogger(customLogger);
|
|
203
|
+
expect(getLogger().logLevel).toBe(customLogger.logLevel);
|
|
204
|
+
enableConsoleLogger("warn");
|
|
205
|
+
expect(getLogger().logLevel).toBe("warn");
|
|
206
|
+
expect(getLogger()).not.toBe(customLogger);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
//#endregion
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { LevelResolver, shouldLog } from "./level-resolver.js";
|
|
2
|
+
//#region src/internal/logger/console-logger.ts
|
|
2
3
|
/**
|
|
3
4
|
* A Logger using console output.
|
|
4
5
|
*
|
|
@@ -26,4 +27,5 @@ var ConsoleLogger = class {
|
|
|
26
27
|
info = this.makeMethod("info");
|
|
27
28
|
warn = this.makeMethod("warn");
|
|
28
29
|
};
|
|
30
|
+
//#endregion
|
|
29
31
|
export { ConsoleLogger };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ConsoleLogger } from "./console-logger.js";
|
|
2
|
+
//#region src/internal/logger/index.ts
|
|
2
3
|
var sdkLogger = new ConsoleLogger("silent");
|
|
3
4
|
/**
|
|
4
5
|
* Sets a logger to be used within the SDK.
|
|
@@ -7,7 +8,7 @@ var sdkLogger = new ConsoleLogger("silent");
|
|
|
7
8
|
*
|
|
8
9
|
* @public
|
|
9
10
|
*/
|
|
10
|
-
|
|
11
|
+
var setLogger = (logger) => {
|
|
11
12
|
sdkLogger = logger;
|
|
12
13
|
};
|
|
13
14
|
/**
|
|
@@ -18,11 +19,12 @@ const setLogger = (logger) => {
|
|
|
18
19
|
*
|
|
19
20
|
* @public
|
|
20
21
|
*/
|
|
21
|
-
|
|
22
|
+
var enableConsoleLogger = (logLevel = "warn", prefix = "scaleway-sdk-js:") => setLogger(new ConsoleLogger(logLevel, prefix));
|
|
22
23
|
/**
|
|
23
24
|
* Returns the active SDK logger.
|
|
24
25
|
*
|
|
25
26
|
* @internal
|
|
26
27
|
*/
|
|
27
|
-
|
|
28
|
+
var getLogger = () => sdkLogger;
|
|
29
|
+
//#endregion
|
|
28
30
|
export { enableConsoleLogger, getLogger, setLogger };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/internal/logger/level-resolver.ts
|
|
2
|
+
var LevelResolver = /* @__PURE__ */ function(LevelResolver) {
|
|
2
3
|
LevelResolver[LevelResolver["silent"] = 0] = "silent";
|
|
3
4
|
LevelResolver[LevelResolver["error"] = 1] = "error";
|
|
4
5
|
LevelResolver[LevelResolver["warn"] = 2] = "warn";
|
|
@@ -6,5 +7,6 @@ let LevelResolver = /* @__PURE__ */ function(LevelResolver) {
|
|
|
6
7
|
LevelResolver[LevelResolver["debug"] = 4] = "debug";
|
|
7
8
|
return LevelResolver;
|
|
8
9
|
}({});
|
|
9
|
-
|
|
10
|
+
var shouldLog = (currentLevel, level) => LevelResolver[level] <= currentLevel;
|
|
11
|
+
//#endregion
|
|
10
12
|
export { LevelResolver, shouldLog };
|
|
File without changes
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { isAccessKey, isEmail, isOrganizationId, isProjectId, isRegion, isSecretKey, isURL, isUUID, isZone } from "../string-validation.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/internal/validations/__tests__/string-validation.test.ts
|
|
4
|
+
var validUUIDs = ["00000000-0000-0000-0000-000000000000", "123e4567-e89b-12d3-a456-426614174000"];
|
|
5
|
+
var invalidUUIDs = [
|
|
6
|
+
"",
|
|
7
|
+
"1",
|
|
8
|
+
"23e4567-e89b-12d3-a456-426614174000"
|
|
9
|
+
];
|
|
10
|
+
var validators = [
|
|
11
|
+
{
|
|
12
|
+
invalids: invalidUUIDs,
|
|
13
|
+
name: "UUID",
|
|
14
|
+
validator: isUUID,
|
|
15
|
+
valids: validUUIDs
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
invalids: [
|
|
19
|
+
"",
|
|
20
|
+
"SCW012345678",
|
|
21
|
+
"WCS01234567890123456"
|
|
22
|
+
],
|
|
23
|
+
name: "AccessKey",
|
|
24
|
+
validator: isAccessKey,
|
|
25
|
+
valids: ["SCW01234567890123456"]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
invalids: invalidUUIDs,
|
|
29
|
+
name: "SecretKey",
|
|
30
|
+
validator: isSecretKey,
|
|
31
|
+
valids: validUUIDs
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
invalids: invalidUUIDs,
|
|
35
|
+
name: "Organization",
|
|
36
|
+
validator: isOrganizationId,
|
|
37
|
+
valids: validUUIDs
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
invalids: invalidUUIDs,
|
|
41
|
+
name: "Project",
|
|
42
|
+
validator: isProjectId,
|
|
43
|
+
valids: validUUIDs
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
invalids: [
|
|
47
|
+
"",
|
|
48
|
+
"f-par",
|
|
49
|
+
"fr",
|
|
50
|
+
"-par",
|
|
51
|
+
"fr-pa"
|
|
52
|
+
],
|
|
53
|
+
name: "Region",
|
|
54
|
+
validator: isRegion,
|
|
55
|
+
valids: [
|
|
56
|
+
"fr-par",
|
|
57
|
+
"nl-ams",
|
|
58
|
+
"pl-waw"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
invalids: [
|
|
63
|
+
"",
|
|
64
|
+
"fr-par",
|
|
65
|
+
"fr"
|
|
66
|
+
],
|
|
67
|
+
name: "Zone",
|
|
68
|
+
validator: isZone,
|
|
69
|
+
valids: [
|
|
70
|
+
"fr-par-1",
|
|
71
|
+
"fr-par-2",
|
|
72
|
+
"nl-ams-1",
|
|
73
|
+
"pl-waw-1",
|
|
74
|
+
"pl-waw-9"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
invalids: ["", "api.scaleway.com"],
|
|
79
|
+
name: "URL",
|
|
80
|
+
validator: isURL,
|
|
81
|
+
valids: ["https://api.scaleway.com"]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
invalids: ["", "@example.com"],
|
|
85
|
+
name: "Email",
|
|
86
|
+
validator: isEmail,
|
|
87
|
+
valids: ["john@example.com"]
|
|
88
|
+
}
|
|
89
|
+
];
|
|
90
|
+
for (const component of validators) describe(component.validator.name, () => {
|
|
91
|
+
it(`validates correct ${component.name} format`, () => {
|
|
92
|
+
for (const str of component.valids) expect(component.validator(str)).toStrictEqual(true);
|
|
93
|
+
});
|
|
94
|
+
it(`doesn't validate incorrect ${component.name} format`, () => {
|
|
95
|
+
for (const str of component.invalids) expect(component.validator(str)).toStrictEqual(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
//#endregion
|
|
@@ -1,23 +1,25 @@
|
|
|
1
|
+
//#region src/internal/validations/string-validation.ts
|
|
1
2
|
var isAccessKeyRegex = /^SCW[A-Z0-9]{17}$/i;
|
|
3
|
+
var isEmailRegex = /^.+@.+$/i;
|
|
2
4
|
var isRegionRegex = /^[a-z]{2}-[a-z]{3}$/i;
|
|
3
5
|
var isUUIDRegex = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/i;
|
|
4
6
|
var isZoneRegex = /^[a-z]{2}-[a-z]{3}-[1-9]$/i;
|
|
5
7
|
/** Returns true if the given string has a valid UUID format. */
|
|
6
|
-
|
|
8
|
+
var isUUID = (str) => isUUIDRegex.test(str);
|
|
7
9
|
/** Returns true if the given string has a valid Scaleway access key format. */
|
|
8
|
-
|
|
10
|
+
var isAccessKey = (str) => isAccessKeyRegex.test(str);
|
|
9
11
|
/** Returns true if the given string has a valid Scaleway secret key format. */
|
|
10
|
-
|
|
12
|
+
var isSecretKey = (str) => isUUID(str);
|
|
11
13
|
/** Returns true if the given string has a valid Scaleway organization ID format. */
|
|
12
|
-
|
|
14
|
+
var isOrganizationId = (str) => isUUID(str);
|
|
13
15
|
/** Returns true if the given string has a valid Scaleway project ID format. */
|
|
14
|
-
|
|
16
|
+
var isProjectId = (str) => isUUID(str);
|
|
15
17
|
/** Returns true if the given string has a valid region format. */
|
|
16
|
-
|
|
18
|
+
var isRegion = (str) => isRegionRegex.test(str);
|
|
17
19
|
/** Returns true if the given string has a valid zone format. */
|
|
18
|
-
|
|
20
|
+
var isZone = (str) => isZoneRegex.test(str);
|
|
19
21
|
/** Returns true if the given string has a valid URL format and starts by `http(s):`. */
|
|
20
|
-
|
|
22
|
+
var isURL = (str) => {
|
|
21
23
|
let url;
|
|
22
24
|
try {
|
|
23
25
|
url = new URL(str);
|
|
@@ -26,4 +28,7 @@ const isURL = (str) => {
|
|
|
26
28
|
}
|
|
27
29
|
return url.protocol === "http:" || url.protocol === "https:";
|
|
28
30
|
};
|
|
29
|
-
|
|
31
|
+
/** Returns true if the given string has an email format. */
|
|
32
|
+
var isEmail = (str) => isEmailRegex.test(str);
|
|
33
|
+
//#endregion
|
|
34
|
+
export { isAccessKey, isEmail, isOrganizationId, isProjectId, isRegion, isSecretKey, isURL, isUUID, isZone };
|
package/dist/internals.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import "./helpers/json.js";
|
|
2
|
-
import "./helpers/marshalling.js";
|
|
3
|
-
import "./internal/async/interval-retrier.js";
|
|
4
|
-
import "./
|
|
5
|
-
import "./scw/
|
|
6
|
-
import "./scw/
|
|
7
|
-
import "./
|
|
8
|
-
import "./scw/
|
|
1
|
+
import { isJSONObject } from "./helpers/json.js";
|
|
2
|
+
import { resolveOneOf, unmarshalArrayOfObject, unmarshalDate, unmarshalMapOfObject, urlParams, validatePathParam } from "./helpers/marshalling.js";
|
|
3
|
+
import { createExponentialBackoffStrategy, tryAtIntervals, waitForResource } from "./internal/async/interval-retrier.js";
|
|
4
|
+
import { API } from "./scw/api.js";
|
|
5
|
+
import { marshalBlobToScwFile, marshalDecimal, marshalMoney, marshalScwFile, marshalTimeSeries, unmarshalAnyRes, unmarshalDecimal, unmarshalMoney, unmarshalScwFile, unmarshalServiceInfo, unmarshalTimeSeries, unmarshalTimeSeriesPoint } from "./scw/custom-marshalling.js";
|
|
6
|
+
import { enrichForPagination } from "./scw/fetch/resource-paginator.js";
|
|
7
|
+
import { addAsyncHeaderInterceptor } from "./internal/interceptors/helpers.js";
|
|
8
|
+
import { authenticateWithSessionToken } from "./scw/auth.js";
|
|
9
|
+
export { API, addAsyncHeaderInterceptor, authenticateWithSessionToken, createExponentialBackoffStrategy, enrichForPagination, isJSONObject, marshalBlobToScwFile, marshalDecimal, marshalMoney, marshalScwFile, marshalTimeSeries, resolveOneOf, tryAtIntervals, unmarshalAnyRes, unmarshalArrayOfObject, unmarshalDate, unmarshalDecimal, unmarshalMapOfObject, unmarshalMoney, unmarshalScwFile, unmarshalServiceInfo, unmarshalTimeSeries, unmarshalTimeSeriesPoint, urlParams, validatePathParam, waitForResource };
|
package/dist/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var package_default = {
|
|
2
2
|
name: "@scaleway/sdk-client",
|
|
3
|
-
version: "2.2.
|
|
3
|
+
version: "2.2.2",
|
|
4
4
|
license: "Apache-2.0",
|
|
5
5
|
description: "Scaleway SDK Client",
|
|
6
6
|
keywords: [
|
|
@@ -29,4 +29,5 @@ var package_default = {
|
|
|
29
29
|
"default": "./dist/index.js"
|
|
30
30
|
} }
|
|
31
31
|
};
|
|
32
|
+
//#endregion
|
|
32
33
|
export { package_default as default };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { API } from "../api.js";
|
|
2
|
+
import { createClient } from "../client.js";
|
|
3
|
+
import { describe, expect, it } from "vitest";
|
|
4
|
+
//#region src/scw/__tests__/api.test.ts
|
|
5
|
+
var CustomAPI = class extends API {
|
|
6
|
+
getBaseURL = () => {
|
|
7
|
+
if (!this.client.settings.apiURL) throw new Error("API URL is missing");
|
|
8
|
+
return this.client.settings.apiURL;
|
|
9
|
+
};
|
|
10
|
+
};
|
|
11
|
+
describe("API", () => {
|
|
12
|
+
it("binds methods properly", () => {
|
|
13
|
+
const unboundMethod = new CustomAPI(createClient()).getBaseURL;
|
|
14
|
+
expect(() => {
|
|
15
|
+
unboundMethod();
|
|
16
|
+
}).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
//#endregion
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { authenticateWithSecrets, authenticateWithSessionToken, obfuscateAuthHeadersEntry, obfuscateToken, obfuscateUUID } from "../auth.js";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
//#region src/scw/__tests__/auth.test.ts
|
|
4
|
+
describe("obfuscateToken", () => {
|
|
5
|
+
it("hides anything after 5 characters", () => {
|
|
6
|
+
expect(obfuscateToken("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c")).toBe("eyJhbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
|
7
|
+
expect(obfuscateToken("")).toBe("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
describe("obfuscateUUID", () => {
|
|
11
|
+
it("hides anything after 8 characters", () => {
|
|
12
|
+
expect(obfuscateUUID("db31db7b-473d-488e-bd2e-1b77ee426910")).toBe("db31db7b-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
13
|
+
expect(obfuscateUUID("")).toBe("-xxxx-xxxx-xxxx-xxxxxxxxxxxx");
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe("obfuscateAuthHeadersEntry", () => {
|
|
17
|
+
it("obfuscates an auth token", () => {
|
|
18
|
+
expect(obfuscateAuthHeadersEntry(["x-auth-token", "db31db7b-473d-488e-bd2e-1b77ee426910"])).toStrictEqual(["x-auth-token", "db31db7b-xxxx-xxxx-xxxx-xxxxxxxxxxxx"]);
|
|
19
|
+
});
|
|
20
|
+
it("obfuscates a session token", () => {
|
|
21
|
+
expect(obfuscateAuthHeadersEntry(["x-session-token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"])).toStrictEqual(["x-session-token", "eyJhbxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"]);
|
|
22
|
+
});
|
|
23
|
+
it(`doesn't obfuscate unknown key`, () => {
|
|
24
|
+
expect(obfuscateAuthHeadersEntry(["x-unknown-key", "random-value"])).toStrictEqual(["x-unknown-key", "random-value"]);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
describe("authenticateWithSessionToken", () => {
|
|
28
|
+
it("retrieves the token and updates the request header", async () => {
|
|
29
|
+
const dummyToken = "dummy";
|
|
30
|
+
const sourceReq = new Request("https://api.scaleway.com/my/path");
|
|
31
|
+
const updatedReq = await authenticateWithSessionToken(() => Promise.resolve(dummyToken))({ request: sourceReq });
|
|
32
|
+
const expectedReq = sourceReq.clone();
|
|
33
|
+
expectedReq.headers.append("x-session-token", "dummy");
|
|
34
|
+
expect(updatedReq).toMatchObject(expectedReq);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
describe("authenticateWithSecrets", () => {
|
|
38
|
+
const sourceReq = new Request("https://api.scaleway.com/my/path");
|
|
39
|
+
it("updates the request header with the valid secrets", async () => {
|
|
40
|
+
const validSecrets = {
|
|
41
|
+
accessKey: "SCW01234567890123456",
|
|
42
|
+
secretKey: "e4b83996-4c60-449a-98d2-38f5de7b4e6b"
|
|
43
|
+
};
|
|
44
|
+
const updatedReq = await authenticateWithSecrets(validSecrets)({ request: sourceReq });
|
|
45
|
+
const expectedReq = sourceReq.clone();
|
|
46
|
+
expectedReq.headers.append("x-auth-token", validSecrets.secretKey);
|
|
47
|
+
expect(updatedReq).toMatchObject(expectedReq);
|
|
48
|
+
});
|
|
49
|
+
it("throws an exception for invalid secrets", () => {
|
|
50
|
+
const invalidSecrets = {
|
|
51
|
+
accessKey: "",
|
|
52
|
+
secretKey: ""
|
|
53
|
+
};
|
|
54
|
+
expect(() => authenticateWithSecrets(invalidSecrets)({ request: sourceReq })).toThrow();
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
//#endregion
|