@scaleway/sdk 0.0.2-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +28 -0
  3. package/dist/api/function/manual/FunctionAPI.js +436 -0
  4. package/dist/api/function/manual/WaitForFunctionAPI.js +27 -0
  5. package/dist/api/function/manual/types.js +1 -0
  6. package/dist/api/registry/manual/RegistryAPI.js +260 -0
  7. package/dist/api/registry/manual/WaitForRegistryAPI.js +27 -0
  8. package/dist/api/registry/manual/types.js +1 -0
  9. package/dist/helpers/API.js +12 -0
  10. package/dist/helpers/camelize.js +36 -0
  11. package/dist/helpers/forRegions.js +15 -0
  12. package/dist/helpers/is-browser.js +3 -0
  13. package/dist/helpers/json.js +6 -0
  14. package/dist/index.cjs +9449 -0
  15. package/dist/index.d.ts +1126 -0
  16. package/dist/index.js +13 -0
  17. package/dist/internal/async/interval-retrier.js +89 -0
  18. package/dist/internal/async/sleep.js +13 -0
  19. package/dist/internal/auth.js +68 -0
  20. package/dist/internal/interceptors/interceptor.js +11 -0
  21. package/dist/internal/interceptors/request.js +29 -0
  22. package/dist/internal/logger/console-logger.js +31 -0
  23. package/dist/internal/logger/index.js +38 -0
  24. package/dist/internal/logger/level-resolver.js +16 -0
  25. package/dist/internal/tools/string-validation.js +39 -0
  26. package/dist/internals.js +4 -0
  27. package/dist/node_modules/@scaleway/random-name/dist/index.js +254 -0
  28. package/dist/scw/client-ini-factory.js +24 -0
  29. package/dist/scw/client-ini-profile.js +30 -0
  30. package/dist/scw/client-settings.js +49 -0
  31. package/dist/scw/client.js +96 -0
  32. package/dist/scw/constants.js +4 -0
  33. package/dist/scw/errors/error-parser.js +121 -0
  34. package/dist/scw/errors/non-standard/invalid-request-mapper.js +34 -0
  35. package/dist/scw/errors/non-standard/unknown-resource-mapper.js +26 -0
  36. package/dist/scw/errors/scw-error.js +64 -0
  37. package/dist/scw/errors/standard/already-exists-error.js +26 -0
  38. package/dist/scw/errors/standard/denied-authentication-error.js +58 -0
  39. package/dist/scw/errors/standard/index.js +12 -0
  40. package/dist/scw/errors/standard/invalid-arguments-error.js +75 -0
  41. package/dist/scw/errors/standard/out-of-stock-error.js +24 -0
  42. package/dist/scw/errors/standard/permissions-denied-error.js +50 -0
  43. package/dist/scw/errors/standard/precondition-failed-error.js +62 -0
  44. package/dist/scw/errors/standard/quotas-exceeded-error.js +61 -0
  45. package/dist/scw/errors/standard/resource-expired-error.js +26 -0
  46. package/dist/scw/errors/standard/resource-locked-error.js +25 -0
  47. package/dist/scw/errors/standard/resource-not-found-error.js +25 -0
  48. package/dist/scw/errors/standard/transient-state-error.js +26 -0
  49. package/dist/scw/errors/types.js +26 -0
  50. package/dist/scw/fetch/build-fetcher.js +66 -0
  51. package/dist/scw/fetch/http-dumper.js +57 -0
  52. package/dist/scw/fetch/http-interceptors.js +80 -0
  53. package/dist/scw/fetch/resource-paginator.js +41 -0
  54. package/dist/scw/fetch/response-parser.js +46 -0
  55. package/dist/scw/marshalling.js +103 -0
  56. package/package.json +27 -0
@@ -0,0 +1,25 @@
1
+ import { ScalewayError } from '../scw-error.js';
2
+
3
+ /**
4
+ * ResourceNotFound error happens when getting a resource that does not exist anymore.
5
+ *
6
+ * @public
7
+ */
8
+
9
+ class ResourceNotFoundError extends ScalewayError {
10
+ constructor(status, body, resource, resourceId) {
11
+ super(status, body, `scaleway-sdk-js: resource ${resource} with ID ${resourceId} is not found`);
12
+ this.status = status;
13
+ this.body = body;
14
+ this.resource = resource;
15
+ this.resourceId = resourceId;
16
+ }
17
+
18
+ static fromJSON(status, obj) {
19
+ if (typeof obj.resource !== 'string' || typeof obj.resource_id !== 'string') return null;
20
+ return new ResourceNotFoundError(status, obj, obj.resource, obj.resource_id);
21
+ }
22
+
23
+ }
24
+
25
+ export { ResourceNotFoundError };
@@ -0,0 +1,26 @@
1
+ import { ScalewayError } from '../scw-error.js';
2
+
3
+ /**
4
+ * TransientState error happens when trying to perform an action on a resource in a transient state.
5
+ *
6
+ * @public
7
+ */
8
+
9
+ class TransientStateError extends ScalewayError {
10
+ constructor(status, body, resource, resourceId, currentState) {
11
+ super(status, body, `scaleway-sdk-js: resource ${resource} with ID ${resourceId} is in a transient state: ${currentState}`);
12
+ this.status = status;
13
+ this.body = body;
14
+ this.resource = resource;
15
+ this.resourceId = resourceId;
16
+ this.currentState = currentState;
17
+ }
18
+
19
+ static fromJSON(status, obj) {
20
+ if (typeof obj.resource !== 'string' || typeof obj.resource_id !== 'string' || typeof obj.current_state !== 'string') return null;
21
+ return new TransientStateError(status, obj, obj.resource, obj.resource_id, obj.current_state);
22
+ }
23
+
24
+ }
25
+
26
+ export { TransientStateError };
@@ -0,0 +1,26 @@
1
+ import { isJSONObject } from '../../helpers/json.js';
2
+
3
+ /**
4
+ * Verifies the object is a record of string to string[].
5
+ *
6
+ * @param obj - The object
7
+ * @returns Whether the object is of the expected type
8
+ *
9
+ * @internal
10
+ */
11
+
12
+ const isRecordOfStringArray = obj => {
13
+ if (!isJSONObject(obj)) {
14
+ return false;
15
+ }
16
+
17
+ for (const elt of Object.values(obj)) {
18
+ if (!Array.isArray(elt) || Object.values(elt).find(x => typeof x !== 'string') !== undefined) {
19
+ return false;
20
+ }
21
+ }
22
+
23
+ return true;
24
+ };
25
+
26
+ export { isRecordOfStringArray };
@@ -0,0 +1,66 @@
1
+ import { isBrowser } from '../../helpers/is-browser.js';
2
+ import { obfuscateAuthHeadersEntry } from '../../internal/auth.js';
3
+ import { composeInterceptors } from '../../internal/interceptors/interceptor.js';
4
+ import { logRequest, logResponse, obfuscateInterceptor } from './http-interceptors.js';
5
+ import { responseParser } from './response-parser.js';
6
+
7
+ /**
8
+ * Builds Request from {@link ScwRequest} & {@link Settings}.
9
+ *
10
+ * @param request - A scaleway request
11
+ * @param settings - The settings
12
+ * @returns A fetch Request
13
+ *
14
+ * @internal
15
+ */
16
+ const buildRequest = (request, settings) => {
17
+ let {
18
+ path
19
+ } = request;
20
+
21
+ if (request.urlParams instanceof URLSearchParams) {
22
+ path = path.concat(`?${request.urlParams.toString()}`);
23
+ }
24
+
25
+ return new Request(`${settings.apiURL}${path}`, {
26
+ body: request.body,
27
+ headers: {
28
+ Accept: 'application/json',
29
+ ...(!isBrowser() ? {
30
+ 'User-Agent': settings.userAgent
31
+ } : {}),
32
+ ...request.headers
33
+ },
34
+ method: request.method
35
+ });
36
+ };
37
+
38
+ const asIs = response => response;
39
+
40
+ /**
41
+ * Builds a resource fetcher.
42
+ *
43
+ * @param settings - The {@link Settings} object
44
+ * @param httpClient - The HTTP client that should be used to call the API
45
+ * @returns The fetcher
46
+ *
47
+ * @internal
48
+ */
49
+ const buildFetcher = (settings, httpClient) => {
50
+ let requestNumber = 0;
51
+
52
+ const prepareRequest = requestId => composeInterceptors([...settings.requestInterceptors, logRequest(requestId, obfuscateInterceptor(obfuscateAuthHeadersEntry))]);
53
+
54
+ const prepareResponse = requestId => composeInterceptors([...settings.responseInterceptors, logResponse(requestId)]);
55
+
56
+ return async function (request, unwrapper) {
57
+ if (unwrapper === void 0) {
58
+ unwrapper = asIs;
59
+ }
60
+
61
+ const requestId = `${requestNumber += 1}`;
62
+ return Promise.resolve(buildRequest(request, settings)).then(prepareRequest(requestId)).then(httpClient).then(prepareResponse(requestId)).then(responseParser(unwrapper));
63
+ };
64
+ };
65
+
66
+ export { buildFetcher, buildRequest };
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Converts a string to PascalCase.
3
+ *
4
+ * @param str - The input string
5
+ * @returns The string in PascalCase
6
+ *
7
+ * @internal
8
+ */
9
+ const toPascalCase = str => str.replace(/\w+/g, word => `${word[0].toUpperCase()}${word.slice(1).toLowerCase()}`);
10
+ /**
11
+ * Converts a Headers entry to string.
12
+ *
13
+ * @param entry - The header entry as a string tuple
14
+ * @returns A serialized string
15
+ *
16
+ * @internal
17
+ */
18
+
19
+
20
+ const serializeHeadersEntry = _ref => {
21
+ let [name, value] = _ref;
22
+ return `${toPascalCase(name)}: ${value}`;
23
+ };
24
+ /**
25
+ * Converts Headers to safe to log strings (with obfuscated auth secrets).
26
+ *
27
+ * @param headers - The Headers
28
+ * @returns Serialized headers strings
29
+ *
30
+ * @internal
31
+ */
32
+
33
+
34
+ const serializeHeaders = headers => Array.from(headers.entries(), serializeHeadersEntry);
35
+ /**
36
+ * Dumps a Request into a readable string.
37
+ *
38
+ * @param request - The request
39
+ * @returns The readable string
40
+ *
41
+ * @internal
42
+ */
43
+
44
+
45
+ const dumpRequest = async request => [`${request.method.toUpperCase()}: ${request.url}`, ...serializeHeaders(request.headers), await request.clone().text()].join('\r\n');
46
+ /**
47
+ * Dumps a Response into a readable string.
48
+ *
49
+ * @param response - The response
50
+ * @returns The readable string
51
+ *
52
+ * @internal
53
+ */
54
+
55
+ const dumpResponse = async response => [`HTTP ${response.status} ${response.ok ? 'OK' : 'NOK'}`, ...serializeHeaders(response.headers), await response.clone().text()].join('\r\n');
56
+
57
+ export { dumpRequest, dumpResponse };
@@ -0,0 +1,80 @@
1
+ import { getLogger } from '../../internal/logger/index.js';
2
+ import { shouldLog, LevelResolver } from '../../internal/logger/level-resolver.js';
3
+ import { dumpRequest, dumpResponse } from './http-dumper.js';
4
+
5
+ /**
6
+ * Mapper of an header entry.
7
+ *
8
+ * @internal
9
+ */
10
+
11
+ /**
12
+ * HTTP Request with obfuscated secrets.
13
+ *
14
+ * @internal
15
+ */
16
+ class ObfuscatedRequest extends Request {
17
+ constructor(request, obfuscate) {
18
+ super(request);
19
+ this.request = request;
20
+ this.obfuscate = obfuscate;
21
+ }
22
+
23
+ get headers() {
24
+ return new Headers(Array.from(this.request.headers, this.obfuscate));
25
+ }
26
+
27
+ }
28
+ /**
29
+ * Creates an interceptor to obfuscate the requests.
30
+ *
31
+ * @param obfuscate - The Header entries obfuscator mapper
32
+ * @returns The obfuscated Request
33
+ *
34
+ * @internal
35
+ */
36
+
37
+
38
+ const obfuscateInterceptor = obfuscate => request => new ObfuscatedRequest(request, obfuscate);
39
+
40
+ const identity = instance => instance;
41
+ /**
42
+ * Creates an interceptor to log the requests.
43
+ *
44
+ * @param identifier - The request identifier
45
+ * @param obfuscate - The obfuscation interceptor
46
+ * @returns The interceptor
47
+ *
48
+ * @internal
49
+ */
50
+
51
+
52
+ const logRequest = function (identifier, obfuscate) {
53
+ if (obfuscate === void 0) {
54
+ obfuscate = identity;
55
+ }
56
+
57
+ return async request => {
58
+ if (shouldLog(LevelResolver[getLogger().logLevel], 'debug')) getLogger().debug(`--------------- Scaleway SDK REQUEST ${identifier} ---------------
59
+ ${await dumpRequest(await obfuscate(request))}
60
+ ---------------------------------------------------------`);
61
+ return request;
62
+ };
63
+ };
64
+ /**
65
+ * Creates an interceptor to log the responses.
66
+ *
67
+ * @param identifier - The request identifier
68
+ * @returns The interceptor
69
+ *
70
+ * @internal
71
+ */
72
+
73
+ const logResponse = identifier => async response => {
74
+ if (shouldLog(LevelResolver[getLogger().logLevel], 'debug')) getLogger().debug(`--------------- Scaleway SDK RESPONSE ${identifier} ---------------
75
+ ${await dumpResponse(response)}
76
+ ---------------------------------------------------------`);
77
+ return response;
78
+ };
79
+
80
+ export { logRequest, logResponse, obfuscateInterceptor };
@@ -0,0 +1,41 @@
1
+ const extract = key => result => result[key];
2
+
3
+ function* pages(key, fetcher, request, firstPage) {
4
+ if (!Array.isArray(firstPage[key])) throw new Error(`Property ${key} is not a list in paginated result`);
5
+ const getList = extract(key);
6
+ yield Promise.resolve(getList(firstPage));
7
+ const {
8
+ totalCount,
9
+ [key]: {
10
+ length
11
+ }
12
+ } = firstPage;
13
+ let page = (request.page || 1) + 1;
14
+
15
+ while (length && page <= Math.floor((totalCount + length - 1) / length)) page = (yield fetcher({ ...request,
16
+ page
17
+ }).then(getList)) || page + 1;
18
+ }
19
+ /**
20
+ * Fetches a paginated resource.
21
+ * @param key - The resource key of values list
22
+ * @param fetcher - The method to retrieve paginated resources
23
+ * @param request - A request with pagination options
24
+ * @returns An async generator of resources arrays
25
+ */
26
+
27
+
28
+ async function* fetchPaginated(key, fetcher, request) {
29
+ yield* pages(key, fetcher, request, await fetcher(request));
30
+ }
31
+ /**
32
+ * Fetches all paginated resource.
33
+ * @param key - The resource key of values list
34
+ * @param fetcher - The method to retrieve paginated resources
35
+ * @param request - A request with pagination options
36
+ * @returns A resources array Promise
37
+ */
38
+
39
+ const fetchAll = async (key, fetcher, request) => (await Promise.all(Array.from(pages(key, fetcher, request, await fetcher(request))))).flat();
40
+
41
+ export { extract, fetchAll, fetchPaginated };
@@ -0,0 +1,46 @@
1
+ import { isJSONObject } from '../../helpers/json.js';
2
+ import { parseScalewayError } from '../errors/error-parser.js';
3
+ import { ScalewayError } from '../errors/scw-error.js';
4
+
5
+ /**
6
+ * Makes response parser.
7
+ *
8
+ * @param unmarshaller - The response payload unmarshaller
9
+ * @returns An async converter of HTTP Response to desired result
10
+ *
11
+ * @throws {@link ScalewayError}
12
+ * Thrown by the API if the request couldn't be completed.
13
+ *
14
+ * @throws TypeError
15
+ * Thrown if the response parameter isn't of the expected type.
16
+ *
17
+ * @throws Error
18
+ * JSON parsing could trigger an error.
19
+ *
20
+ * @internal
21
+ */
22
+ const responseParser = unmarshaller => async response => {
23
+ if (!(response instanceof Response)) throw new TypeError('Invalid response object');
24
+
25
+ if (response.ok) {
26
+ const contentType = response.headers.get('Content-Type');
27
+
28
+ switch (contentType) {
29
+ case 'application/json':
30
+ try {
31
+ return unmarshaller(await response.json());
32
+ } catch (err) {
33
+ throw new ScalewayError(response.status, `could not parse ${contentType} response`);
34
+ }
35
+
36
+ default:
37
+ throw new ScalewayError(response.status, `invalid content type ${contentType ?? ''}`.trim());
38
+ }
39
+ }
40
+
41
+ const error = await response.clone().json().catch(() => response.text());
42
+ if (isJSONObject(error)) throw parseScalewayError(response.status, error);
43
+ throw new ScalewayError(response.status, typeof error === 'string' ? error : 'cannot read error response body');
44
+ };
45
+
46
+ export { responseParser };
@@ -0,0 +1,103 @@
1
+ import { camelizeKeys } from '../helpers/camelize.js';
2
+ import { isJSONObject } from '../helpers/json.js';
3
+
4
+ /**
5
+ * Unmarshals record to convert iso dates from string to Dates.
6
+ *
7
+ * @param obj - The input
8
+ * @param keys - The keys requiring a conversion
9
+ * @returns The updated input
10
+ *
11
+ * @internal
12
+ */
13
+
14
+ const unmarshalDates = (obj, keys) => {
15
+ if (Array.isArray(obj)) return obj.map(v => unmarshalDates(v, keys));
16
+ if (obj && typeof obj === 'object') return Object.entries(obj).reduce((acc, _ref) => {
17
+ let [key, value] = _ref;
18
+ return { ...acc,
19
+ [key]: typeof value === 'string' && keys.includes(key) ? new Date(value) : unmarshalDates(value, keys)
20
+ };
21
+ }, {});
22
+ return obj;
23
+ };
24
+ /**
25
+ * Unmarshals input to a record with camilized keys and instanciated Date.
26
+ *
27
+ * @param obj - The input
28
+ * @param ignoreKeys - The keys which should be not be transformed
29
+ * @param dateKeys - The keys which should be transformed to Date
30
+ * @returns The record
31
+ *
32
+ * @throws TypeError
33
+ * Thrown if the input isn't {@link JSONObject}.
34
+ *
35
+ * @internal
36
+ */
37
+
38
+ const unmarshalAnyRes = function (obj, ignoreKeys, dateKeys) {
39
+ if (ignoreKeys === void 0) {
40
+ ignoreKeys = [];
41
+ }
42
+
43
+ if (!isJSONObject(obj)) throw new TypeError(`Data isn't a dictionary.`);
44
+ return camelizeKeys(dateKeys && dateKeys.length > 0 ? unmarshalDates(obj, dateKeys) : obj, ignoreKeys);
45
+ };
46
+ /**
47
+ * Throws an exception if the parameter is neither a string or defined.
48
+ *
49
+ * @param name - The parameter name
50
+ * @param param - The parameter value
51
+ *
52
+ * @throws ReferenceError
53
+ * Thrown if the parameter is invalid.
54
+ *
55
+ * @internal
56
+ */
57
+
58
+ function assertNotEmptyParam(name, param) {
59
+ if (typeof param !== 'string' || param.length === 0) throw new ReferenceError(`param ${name} cannot be empty in request`);
60
+ }
61
+ /**
62
+ * Resolves the ideal parameter and value amongst an optional list.
63
+ *
64
+ * @param list - The list to be looking into
65
+ * @returns The parameter and value
66
+ *
67
+ * @throws ReferenceError
68
+ * Thrown if no value or default value is specified.
69
+ *
70
+ * @internal
71
+ */
72
+
73
+ const resolveOneOf = list => {
74
+ const elt = list.find(obj => obj.value) || list.find(obj => obj.default);
75
+ const value = (elt == null ? void 0 : elt.value) || (elt == null ? void 0 : elt.default);
76
+ if (value) return {
77
+ param: elt.param,
78
+ value
79
+ };
80
+ const keyList = list.map(obj => obj.param).join(' or ');
81
+ throw new ReferenceError(`one of ${keyList} must be indicated in the request`);
82
+ };
83
+ /**
84
+ * Filters defined parameters tuples and converts them to URLSearchParams.
85
+ *
86
+ * @param paramTuples - The key/value pairs
87
+ * @returns URLSearchParams
88
+ *
89
+ * @internal
90
+ */
91
+
92
+ const urlParams = function () {
93
+ for (var _len = arguments.length, paramTuples = new Array(_len), _key = 0; _key < _len; _key++) {
94
+ paramTuples[_key] = arguments[_key];
95
+ }
96
+
97
+ return new URLSearchParams(paramTuples.reduce((params, _ref2) => {
98
+ let [key, value] = _ref2;
99
+ return typeof key === 'string' && value != null ? [...params, [key, value.toString()]] : params;
100
+ }, []));
101
+ };
102
+
103
+ export { assertNotEmptyParam, resolveOneOf, unmarshalAnyRes, unmarshalDates, urlParams };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "@scaleway/sdk",
3
+ "version": "0.0.2-alpha.0",
4
+ "license": "Apache-2.0",
5
+ "description": "Scaleway SDK.",
6
+ "main": "dist/index.cjs",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "directory": "packages/clients"
18
+ },
19
+ "engines": {
20
+ "node": ">=14.x"
21
+ },
22
+ "type": "module",
23
+ "dependencies": {
24
+ "@scaleway/random-name": "^3.0.0"
25
+ },
26
+ "gitHead": "348ef780f8af12baa19e8c5b3c02f3087bcf0964"
27
+ }