@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.
- 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,60 @@
|
|
|
1
|
+
import { ConsoleLogger } from "../../../internal/logger/console-logger.js";
|
|
2
|
+
import { setLogger } from "../../../internal/logger/index.js";
|
|
3
|
+
import { logRequest, logResponse, obfuscateInterceptor } from "../http-interceptors.js";
|
|
4
|
+
import { afterAll, beforeEach, describe, expect, it } from "vitest";
|
|
5
|
+
//#region src/scw/fetch/__tests__/http-interceptors.test.ts
|
|
6
|
+
var latestMessage = "";
|
|
7
|
+
var makeCallbackConsole = (onMessage) => ({
|
|
8
|
+
...console,
|
|
9
|
+
debug: onMessage,
|
|
10
|
+
error: onMessage,
|
|
11
|
+
info: onMessage,
|
|
12
|
+
warn: onMessage
|
|
13
|
+
});
|
|
14
|
+
var enableDummyLogger = (logLevel) => {
|
|
15
|
+
setLogger(new ConsoleLogger(logLevel, "", makeCallbackConsole((obj) => {
|
|
16
|
+
latestMessage = obj;
|
|
17
|
+
})));
|
|
18
|
+
};
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
latestMessage = "";
|
|
21
|
+
});
|
|
22
|
+
afterAll(() => setLogger(new ConsoleLogger("silent")));
|
|
23
|
+
describe(`logRequest`, () => {
|
|
24
|
+
const request = new Request("https://api.scaleway.com");
|
|
25
|
+
it(`logs nothing if the level isn't high enough`, async () => {
|
|
26
|
+
enableDummyLogger("warn");
|
|
27
|
+
await logRequest("1")({ request });
|
|
28
|
+
expect(latestMessage).toBe("");
|
|
29
|
+
});
|
|
30
|
+
it(`logs something if the level is high enough`, async () => {
|
|
31
|
+
enableDummyLogger("debug");
|
|
32
|
+
await logRequest("1")({ request });
|
|
33
|
+
expect(latestMessage.startsWith("--------------- Scaleway SDK REQUEST 1")).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe(`logResponse`, () => {
|
|
37
|
+
const response = new Response("output");
|
|
38
|
+
it(`logs nothing if the level isn't high enough`, async () => {
|
|
39
|
+
enableDummyLogger("warn");
|
|
40
|
+
await logResponse("1")({ response });
|
|
41
|
+
expect(latestMessage).toBe("");
|
|
42
|
+
});
|
|
43
|
+
it(`logs something if the level is high enough`, async () => {
|
|
44
|
+
enableDummyLogger("debug");
|
|
45
|
+
await logResponse("1")({ response });
|
|
46
|
+
expect(latestMessage.startsWith("--------------- Scaleway SDK RESPONSE 1")).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
describe("obfuscateInterceptor", () => {
|
|
50
|
+
const prependInterceptor = (preprendValue) => ([name, value]) => [name, `${preprendValue}${value}`];
|
|
51
|
+
it("changes the request headers", async () => {
|
|
52
|
+
const obfRequest = await obfuscateInterceptor(prependInterceptor("obj-"))({ request: new Request("https://api.scaleway.com", { headers: { "X-Random-Key": "value" } }) });
|
|
53
|
+
expect(obfRequest).toBeInstanceOf(Request);
|
|
54
|
+
expect(obfRequest.headers.get("X-Random-Key")).toBe("obj-value");
|
|
55
|
+
});
|
|
56
|
+
it("clones the request without altering the headers", async () => {
|
|
57
|
+
expect((await obfuscateInterceptor(prependInterceptor("obj-"))({ request: new Request("https://api.scaleway.com", { headers: { "X-Random-Key": "value" } }) })).clone().headers.get("X-Random-Key")).toBe("obj-value");
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
//#endregion
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { enrichForPagination, fetchAll, fetchPaginated } from "../resource-paginator.js";
|
|
2
|
+
import { describe, expect, it, vi } from "vitest";
|
|
3
|
+
//#region src/scw/fetch/__tests__/resource-paginator.test.ts
|
|
4
|
+
var fetchPages = (input = [], delay = 0) => {
|
|
5
|
+
const totalCount = input.flat().length;
|
|
6
|
+
const pages = [...input];
|
|
7
|
+
return () => {
|
|
8
|
+
const page = {
|
|
9
|
+
items: pages.shift() ?? [],
|
|
10
|
+
totalCount
|
|
11
|
+
};
|
|
12
|
+
return new Promise((resolve) => {
|
|
13
|
+
setTimeout(() => resolve(page), delay);
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
describe("fetchPaginated", () => {
|
|
18
|
+
it("iterate page by page", async () => {
|
|
19
|
+
const input = [
|
|
20
|
+
[{ name: "Alice" }, { name: "Bob" }],
|
|
21
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
22
|
+
[{ name: "David" }]
|
|
23
|
+
];
|
|
24
|
+
for await (const page of fetchPaginated("items", fetchPages(input), {})) expect(page).toStrictEqual(input.shift());
|
|
25
|
+
});
|
|
26
|
+
it("iterate starting from the desired page", async () => {
|
|
27
|
+
const input = [
|
|
28
|
+
[{ name: "Alice" }, { name: "Bob" }],
|
|
29
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
30
|
+
[{ name: "David" }]
|
|
31
|
+
];
|
|
32
|
+
let readIndex = 2;
|
|
33
|
+
for await (const page of fetchPaginated("items", fetchPages(input), { page: readIndex })) {
|
|
34
|
+
expect(page).toStrictEqual(input[readIndex - 1]);
|
|
35
|
+
readIndex += 1;
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
it("iterate without over iteration - 2 pages", async () => {
|
|
39
|
+
const twoPages = [[{ name: "Alice" }, { name: "Bob" }], [{ name: "Claire" }]];
|
|
40
|
+
const twoPagesFetcher = vi.fn(fetchPages(twoPages));
|
|
41
|
+
for await (const page of fetchPaginated("items", twoPagesFetcher, {})) expect(page).toStrictEqual(twoPages.shift());
|
|
42
|
+
expect(twoPagesFetcher).toHaveBeenCalledTimes(2);
|
|
43
|
+
});
|
|
44
|
+
it("iterate without over iteration - 1 page", async () => {
|
|
45
|
+
const onePage = [[{ name: "Alice" }, { name: "Bob" }], []];
|
|
46
|
+
const onePagesFetcher = vi.fn(fetchPages(onePage));
|
|
47
|
+
for await (const page of fetchPaginated("items", onePagesFetcher, {})) expect(page).toStrictEqual(onePage.shift());
|
|
48
|
+
expect(onePage).toStrictEqual([[]]);
|
|
49
|
+
expect(onePagesFetcher).toHaveBeenCalledTimes(1);
|
|
50
|
+
});
|
|
51
|
+
it("iterate without over iteration - 1 result", async () => {
|
|
52
|
+
const singleResult = [[{ name: "Alice" }]];
|
|
53
|
+
const singleResultFetcher = vi.fn(fetchPages(singleResult));
|
|
54
|
+
for await (const page of fetchPaginated("items", singleResultFetcher, {})) expect(page).toStrictEqual(singleResult.shift());
|
|
55
|
+
expect(singleResultFetcher).toHaveBeenCalledTimes(1);
|
|
56
|
+
});
|
|
57
|
+
it("returns empty array on empty result", async () => {
|
|
58
|
+
const iterator = fetchPaginated("items", fetchPages([]), {});
|
|
59
|
+
const empty = await iterator.next();
|
|
60
|
+
expect(empty.value).toEqual([]);
|
|
61
|
+
expect(empty.done).toBe(false);
|
|
62
|
+
const ended = await iterator.next();
|
|
63
|
+
expect(ended.value).toEqual(void 0);
|
|
64
|
+
expect(ended.done).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
it("throws when wrong key is provided", async () => {
|
|
67
|
+
await expect(fetchPaginated("x", fetchPages([]), {}).next()).rejects.toThrow(`Property x is not a list in paginated result`);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe("fetchAll", () => {
|
|
71
|
+
it("iterate pages in paralel", async () => {
|
|
72
|
+
const input = [
|
|
73
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
74
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
75
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
76
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
77
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
78
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
79
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
80
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
81
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
82
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
83
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
84
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
85
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
86
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
87
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
88
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
89
|
+
[{ name: "Julia" }, { name: "Thomas" }],
|
|
90
|
+
[{ name: "Julia" }, { name: "Thomas" }]
|
|
91
|
+
];
|
|
92
|
+
const delay = 50;
|
|
93
|
+
const tolerance = 50;
|
|
94
|
+
const startTime = Date.now();
|
|
95
|
+
expect(await fetchAll("items", fetchPages(input, 50), {})).toStrictEqual(input.flat());
|
|
96
|
+
expect(Date.now() - startTime).toBeLessThanOrEqual(2 * delay + tolerance);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
describe("enrichForPagination", () => {
|
|
100
|
+
const input = [[{ name: "Rémy" }, { name: "Jaime" }], [{ name: "Vincent" }]];
|
|
101
|
+
it("can fetch all items", () => expect(enrichForPagination("items", fetchPages(input), {}).all()).resolves.toStrictEqual(input.flat()));
|
|
102
|
+
it("can fetch items page by page", async () => {
|
|
103
|
+
let readIndex = 1;
|
|
104
|
+
for await (const page of enrichForPagination("items", fetchPages(input), { page: readIndex })) {
|
|
105
|
+
expect(page).toStrictEqual(input[readIndex - 1]);
|
|
106
|
+
readIndex += 1;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
//#endregion
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { isJSONObject } from "../../../helpers/json.js";
|
|
2
|
+
import { ScalewayError } from "../../errors/scw-error.js";
|
|
3
|
+
import { fixLegacyTotalCount, responseParser } from "../response-parser.js";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
//#region src/scw/fetch/__tests__/response-parser.test.ts
|
|
6
|
+
var SIMPLE_REQ_BODY = { "what-is-life": 42 };
|
|
7
|
+
var convertObjToBuffer = (obj) => Buffer.from(JSON.stringify(obj));
|
|
8
|
+
var unmarshalJSON = (obj) => {
|
|
9
|
+
if (!isJSONObject(obj)) throw new Error(`couldn't unwrap response value`);
|
|
10
|
+
return obj;
|
|
11
|
+
};
|
|
12
|
+
var makeResponse = (value, status = 200, contentType = void 0) => new Response(value !== null ? new Uint8Array(convertObjToBuffer(value)) : value, {
|
|
13
|
+
headers: contentType ? { "Content-Type": contentType } : void 0,
|
|
14
|
+
status
|
|
15
|
+
});
|
|
16
|
+
var makeJSONResponse = (value = SIMPLE_REQ_BODY, status = 200) => makeResponse(value, status, "application/json");
|
|
17
|
+
var makeTextResponse = (value, status = 200) => new Response(value, {
|
|
18
|
+
headers: { "Content-Type": "plain/text" },
|
|
19
|
+
status
|
|
20
|
+
});
|
|
21
|
+
describe(`responseParser`, () => {
|
|
22
|
+
const parseJson = responseParser(unmarshalJSON, "json");
|
|
23
|
+
const parseAsIs = responseParser((response) => response, "json");
|
|
24
|
+
const parseBlob = responseParser((response) => response, "blob");
|
|
25
|
+
it(`triggers a type error for non 'Response' object`, () => expect(parseJson("not-a-response")).rejects.toThrow(/* @__PURE__ */ new TypeError("Invalid response object")));
|
|
26
|
+
it(`triggers an error for invalid status code`, async () => {
|
|
27
|
+
return expect(parseJson(new Response(new Uint8Array(convertObjToBuffer(SIMPLE_REQ_BODY)), {
|
|
28
|
+
headers: { "Content-Type": "application/json" },
|
|
29
|
+
status: 500
|
|
30
|
+
}))).rejects.toThrow();
|
|
31
|
+
});
|
|
32
|
+
it(`triggers an error with string payload`, async () => {
|
|
33
|
+
return expect(parseJson(new Response(new Uint8Array(convertObjToBuffer("random-error")), {
|
|
34
|
+
headers: { "Content-Type": "application/json" },
|
|
35
|
+
status: 500
|
|
36
|
+
}))).rejects.toThrow(new ScalewayError(500, "random-error"));
|
|
37
|
+
});
|
|
38
|
+
it(`triggers an error with unknown payload`, async () => {
|
|
39
|
+
return expect(parseJson(new Response(new Uint8Array(convertObjToBuffer(null)), {
|
|
40
|
+
headers: { "Content-Type": "application/json" },
|
|
41
|
+
status: 500
|
|
42
|
+
}))).rejects.toThrow(new ScalewayError(500, "cannot read error response body"));
|
|
43
|
+
});
|
|
44
|
+
it(`triggers an error with server response message when parsing fails`, async () => {
|
|
45
|
+
return expect(parseJson(new Response("text error content", {
|
|
46
|
+
headers: { "Content-Type": "application/json" },
|
|
47
|
+
status: 500
|
|
48
|
+
}))).rejects.toThrow(new ScalewayError(500, "text error content"));
|
|
49
|
+
});
|
|
50
|
+
it(`triggers an error for unsuccessful unmarshalling`, async () => {
|
|
51
|
+
const validResponse = makeJSONResponse();
|
|
52
|
+
const emptyContentTypeResponse = makeResponse("some-text", 200, "");
|
|
53
|
+
await expect(responseParser(() => {
|
|
54
|
+
throw new Error(`couldn't unwrap response value`);
|
|
55
|
+
}, "json")(validResponse.clone())).rejects.toThrow(new ScalewayError(validResponse.status, `could not parse 'application/json' response: couldn't unwrap response value`));
|
|
56
|
+
await expect(responseParser(() => {
|
|
57
|
+
throw "not-of-error-type";
|
|
58
|
+
}, "text")(validResponse.clone())).rejects.toThrow(new ScalewayError(validResponse.status, `could not parse 'application/json' response`));
|
|
59
|
+
await expect(responseParser(() => {
|
|
60
|
+
throw "not-of-error-type";
|
|
61
|
+
}, "blob")(emptyContentTypeResponse)).rejects.toThrow(new ScalewayError(emptyContentTypeResponse.status, `could not parse '' response`));
|
|
62
|
+
});
|
|
63
|
+
it(`returns the response as text for unknown content type`, async () => expect(parseAsIs(makeTextResponse("text-body"))).resolves.toBe("text-body"));
|
|
64
|
+
it(`returns the proper object for a valid JSON object`, async () => expect(parseJson(makeJSONResponse())).resolves.toMatchObject(SIMPLE_REQ_BODY));
|
|
65
|
+
it(`returns the proper type for a Blob responseType`, async () => expect(parseBlob(makeTextResponse("hello world")).then((obj) => typeof obj)).resolves.toBe("object"));
|
|
66
|
+
it(`returns undefined for a 204 status code, even if content-type is json`, async () => expect(parseAsIs(makeJSONResponse(null, 204))).resolves.toBeUndefined());
|
|
67
|
+
});
|
|
68
|
+
describe("fixLegacyTotalCount", () => {
|
|
69
|
+
it("appends total_count if headers contains the key", () => {
|
|
70
|
+
expect(fixLegacyTotalCount({ my_key: "anything" }, new Headers({ "x-total-count": "3" }))).toStrictEqual({
|
|
71
|
+
my_key: "anything",
|
|
72
|
+
total_count: 3
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
it("does nothing if expected key is empty", () => {
|
|
76
|
+
expect(fixLegacyTotalCount({ my_key: "anything" }, new Headers({}))).toStrictEqual({ my_key: "anything" });
|
|
77
|
+
});
|
|
78
|
+
it("does nothing if expected key is not of valid value", () => {
|
|
79
|
+
expect(fixLegacyTotalCount({ my_key: "anything" }, new Headers({ "x-total-count": "true" }))).toStrictEqual({ my_key: "anything" });
|
|
80
|
+
});
|
|
81
|
+
it("does nothing if total_count already exists", () => {
|
|
82
|
+
expect(fixLegacyTotalCount({
|
|
83
|
+
my_key: "anything",
|
|
84
|
+
total_count: 5
|
|
85
|
+
}, new Headers({ "x-total-count": "3" }))).toStrictEqual({
|
|
86
|
+
my_key: "anything",
|
|
87
|
+
total_count: 5
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
it("does nothing if input is not a Record", () => {
|
|
91
|
+
expect(fixLegacyTotalCount("my-value", new Headers({ "x-total-count": "3" }))).toStrictEqual("my-value");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
//#endregion
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { obfuscateAuthHeadersEntry } from "../auth.js";
|
|
2
1
|
import { isBrowser } from "../../helpers/is-browser.js";
|
|
2
|
+
import { obfuscateAuthHeadersEntry } from "../auth.js";
|
|
3
3
|
import { composeRequestInterceptors, composeResponseErrorInterceptors, composeResponseInterceptors } from "../../internal/interceptors/composer.js";
|
|
4
4
|
import { logRequest, logResponse, obfuscateInterceptor } from "./http-interceptors.js";
|
|
5
5
|
import { responseParser } from "./response-parser.js";
|
|
6
|
+
//#region src/scw/fetch/build-fetcher.ts
|
|
6
7
|
/**
|
|
7
8
|
* Builds Request from {@link ScwRequest} & {@link Settings}.
|
|
8
9
|
*
|
|
@@ -12,7 +13,7 @@ import { responseParser } from "./response-parser.js";
|
|
|
12
13
|
*
|
|
13
14
|
* @internal
|
|
14
15
|
*/
|
|
15
|
-
|
|
16
|
+
var buildRequest = (request, settings) => {
|
|
16
17
|
let { path } = request;
|
|
17
18
|
if (request.urlParams instanceof URLSearchParams) path = path.concat(`?${request.urlParams.toString()}`);
|
|
18
19
|
return new Request(`${settings.apiURL}${path}`, {
|
|
@@ -35,7 +36,7 @@ var asIs = (response) => response;
|
|
|
35
36
|
*
|
|
36
37
|
* @internal
|
|
37
38
|
*/
|
|
38
|
-
|
|
39
|
+
var buildFetcher = (settings, httpClient) => {
|
|
39
40
|
let requestNumber = 0;
|
|
40
41
|
const prepareRequest = (requestId) => composeRequestInterceptors([...settings.interceptors.map((obj) => obj.request).filter((obj) => obj), logRequest(requestId, obfuscateInterceptor(obfuscateAuthHeadersEntry))]);
|
|
41
42
|
const prepareResponse = (requestId) => composeResponseInterceptors([...settings.interceptors.map((obj) => obj.response).filter((obj) => obj), logResponse(requestId)]);
|
|
@@ -53,4 +54,5 @@ const buildFetcher = (settings, httpClient) => {
|
|
|
53
54
|
}
|
|
54
55
|
};
|
|
55
56
|
};
|
|
56
|
-
|
|
57
|
+
//#endregion
|
|
58
|
+
export { buildFetcher, buildRequest };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/scw/fetch/http-dumper.ts
|
|
1
2
|
/**
|
|
2
3
|
* Converts a string to PascalCase.
|
|
3
4
|
*
|
|
@@ -33,7 +34,7 @@ var serializeHeaders = (headers) => Array.from(headers.entries(), serializeHeade
|
|
|
33
34
|
*
|
|
34
35
|
* @internal
|
|
35
36
|
*/
|
|
36
|
-
|
|
37
|
+
var dumpRequest = async (request) => [
|
|
37
38
|
`${request.method.toUpperCase()}: ${request.url}`,
|
|
38
39
|
...serializeHeaders(request.headers),
|
|
39
40
|
await request.clone().text()
|
|
@@ -46,9 +47,10 @@ const dumpRequest = async (request) => [
|
|
|
46
47
|
*
|
|
47
48
|
* @internal
|
|
48
49
|
*/
|
|
49
|
-
|
|
50
|
+
var dumpResponse = async (response) => [
|
|
50
51
|
`HTTP ${response.status} ${response.ok ? "OK" : "NOK"}`,
|
|
51
52
|
...serializeHeaders(response.headers),
|
|
52
53
|
await response.clone().text()
|
|
53
54
|
].join("\r\n");
|
|
55
|
+
//#endregion
|
|
54
56
|
export { dumpRequest, dumpResponse };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LevelResolver, shouldLog } from "../../internal/logger/level-resolver.js";
|
|
2
2
|
import { getLogger } from "../../internal/logger/index.js";
|
|
3
3
|
import { dumpRequest, dumpResponse } from "./http-dumper.js";
|
|
4
|
+
//#region src/scw/fetch/http-interceptors.ts
|
|
4
5
|
/**
|
|
5
6
|
* HTTP Request with obfuscated secrets.
|
|
6
7
|
*
|
|
@@ -27,7 +28,7 @@ var ObfuscatedRequest = class ObfuscatedRequest extends Request {
|
|
|
27
28
|
*
|
|
28
29
|
* @internal
|
|
29
30
|
*/
|
|
30
|
-
|
|
31
|
+
var obfuscateInterceptor = (obfuscate) => ({ request }) => new ObfuscatedRequest(request, obfuscate);
|
|
31
32
|
var identity = ({ request }) => request;
|
|
32
33
|
/**
|
|
33
34
|
* Creates an interceptor to log the requests.
|
|
@@ -38,7 +39,7 @@ var identity = ({ request }) => request;
|
|
|
38
39
|
*
|
|
39
40
|
* @internal
|
|
40
41
|
*/
|
|
41
|
-
|
|
42
|
+
var logRequest = (identifier, obfuscate = identity) => async ({ request }) => {
|
|
42
43
|
if (shouldLog(LevelResolver[getLogger().logLevel], "debug")) getLogger().debug(`--------------- Scaleway SDK REQUEST ${identifier} ---------------
|
|
43
44
|
${await dumpRequest(await obfuscate({ request }))}
|
|
44
45
|
---------------------------------------------------------`);
|
|
@@ -52,10 +53,11 @@ ${await dumpRequest(await obfuscate({ request }))}
|
|
|
52
53
|
*
|
|
53
54
|
* @internal
|
|
54
55
|
*/
|
|
55
|
-
|
|
56
|
+
var logResponse = (identifier) => async ({ response }) => {
|
|
56
57
|
if (shouldLog(LevelResolver[getLogger().logLevel], "debug")) getLogger().debug(`--------------- Scaleway SDK RESPONSE ${identifier} ---------------
|
|
57
58
|
${await dumpResponse(response)}
|
|
58
59
|
---------------------------------------------------------`);
|
|
59
60
|
return response;
|
|
60
61
|
};
|
|
62
|
+
//#endregion
|
|
61
63
|
export { logRequest, logResponse, obfuscateInterceptor };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
//#region src/scw/fetch/resource-paginator.ts
|
|
2
|
+
var extract = (key) => (result) => result[key];
|
|
2
3
|
function* pages(key, fetcher, request, firstPage) {
|
|
3
4
|
if (!Array.isArray(firstPage[key])) throw new Error(`Property ${key} is not a list in paginated result`);
|
|
4
5
|
const getList = extract(key);
|
|
@@ -39,7 +40,7 @@ async function* fetchPaginated(key, fetcher, request, initial = fetcher(request)
|
|
|
39
40
|
* @param initial - The first page
|
|
40
41
|
* @returns A resources array Promise
|
|
41
42
|
*/
|
|
42
|
-
|
|
43
|
+
var fetchAll = async (key, fetcher, request, initial = fetcher(request)) => (await Promise.all(Array.from(pages(key, fetcher, request, await initial)))).flat();
|
|
43
44
|
/**
|
|
44
45
|
* Enriches a listing method with helpers.
|
|
45
46
|
*
|
|
@@ -50,11 +51,12 @@ const fetchAll = async (key, fetcher, request, initial = fetcher(request)) => (a
|
|
|
50
51
|
*
|
|
51
52
|
* @internal
|
|
52
53
|
*/
|
|
53
|
-
|
|
54
|
+
var enrichForPagination = (key, fetcher, request) => {
|
|
54
55
|
const firstPage = fetcher(request);
|
|
55
56
|
return Object.assign(firstPage, {
|
|
56
57
|
all: () => fetchAll(key, fetcher, request, firstPage),
|
|
57
58
|
[Symbol.asyncIterator]: () => fetchPaginated(key, fetcher, request, firstPage)
|
|
58
59
|
});
|
|
59
60
|
};
|
|
60
|
-
|
|
61
|
+
//#endregion
|
|
62
|
+
export { enrichForPagination, extract, fetchAll, fetchPaginated };
|
|
@@ -2,6 +2,7 @@ import { isJSONObject } from "../../helpers/json.js";
|
|
|
2
2
|
import { isResponse } from "../../helpers/is-response.js";
|
|
3
3
|
import { ScalewayError } from "../errors/scw-error.js";
|
|
4
4
|
import { parseScalewayError } from "../errors/error-parser.js";
|
|
5
|
+
//#region src/scw/fetch/response-parser.ts
|
|
5
6
|
var X_TOTAL_COUNT_HEADER_KEY = "x-total-count";
|
|
6
7
|
var TOTAL_COUNT_RES_KEY = "total_count";
|
|
7
8
|
/**
|
|
@@ -9,7 +10,7 @@ var TOTAL_COUNT_RES_KEY = "total_count";
|
|
|
9
10
|
*
|
|
10
11
|
* @internal
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
var fixLegacyTotalCount = (obj, headers) => {
|
|
13
14
|
const headerVal = headers.get(X_TOTAL_COUNT_HEADER_KEY);
|
|
14
15
|
if (!headerVal) return obj;
|
|
15
16
|
const totalCount = parseInt(headerVal, 10);
|
|
@@ -34,7 +35,7 @@ const fixLegacyTotalCount = (obj, headers) => {
|
|
|
34
35
|
*
|
|
35
36
|
* @internal
|
|
36
37
|
*/
|
|
37
|
-
|
|
38
|
+
var responseParser = (unmarshaller, responseType) => async (response) => {
|
|
38
39
|
if (!isResponse(response)) throw new TypeError("Invalid response object");
|
|
39
40
|
if (response.ok) {
|
|
40
41
|
if (response.status === 204) return unmarshaller(void 0);
|
|
@@ -51,4 +52,5 @@ const responseParser = (unmarshaller, responseType) => async (response) => {
|
|
|
51
52
|
if (isJSONObject(error)) throw parseScalewayError(response.status, error);
|
|
52
53
|
throw new ScalewayError(response.status, typeof error === "string" ? error : "cannot read error response body");
|
|
53
54
|
};
|
|
54
|
-
|
|
55
|
+
//#endregion
|
|
56
|
+
export { fixLegacyTotalCount, responseParser };
|
|
File without changes
|
package/dist/scw/locality.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
//#region src/scw/locality.ts
|
|
1
2
|
function toApiLocality(legacy) {
|
|
2
3
|
if (!legacy) return { type: "unspecified" };
|
|
3
4
|
const { zones, regions } = legacy;
|
|
@@ -11,4 +12,5 @@ function toApiLocality(legacy) {
|
|
|
11
12
|
};
|
|
12
13
|
return { type: "global" };
|
|
13
14
|
}
|
|
15
|
+
//#endregion
|
|
14
16
|
export { toApiLocality };
|
|
File without changes
|