@unito/integration-sdk 0.1.0 → 0.1.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/README.md +1 -1
- package/dist/src/errors.d.ts +3 -2
- package/dist/src/errors.js +11 -9
- package/dist/src/handler.js +12 -1
- package/dist/src/httpErrors.d.ts +3 -0
- package/dist/src/httpErrors.js +6 -1
- package/dist/src/index.d.ts +2 -1
- package/dist/src/index.js +2 -0
- package/dist/src/middlewares/credentials.d.ts +4 -1
- package/dist/src/resources/provider.d.ts +35 -12
- package/dist/src/resources/provider.js +59 -34
- package/dist/test/errors.test.js +7 -8
- package/dist/test/handler.test.js +26 -2
- package/dist/test/resources/provider.test.d.ts +1 -0
- package/dist/test/resources/provider.test.js +229 -0
- package/package.json +1 -3
- package/src/errors.ts +12 -9
- package/src/handler.ts +18 -1
- package/src/httpErrors.ts +7 -1
- package/src/index.ts +4 -0
- package/src/middlewares/credentials.ts +1 -1
- package/src/resources/provider.ts +89 -47
- package/test/errors.test.ts +7 -8
- package/test/handler.test.ts +36 -2
- package/test/resources/provider.test.ts +285 -0
- package/dist/src/api/index.d.ts +0 -2
- package/dist/src/api/index.d.ts.map +0 -1
- package/dist/src/api/index.js +0 -2
- package/dist/src/api/index.js.map +0 -1
- package/dist/src/app/errors/HTTPError.d.ts +0 -5
- package/dist/src/app/errors/HTTPError.d.ts.map +0 -1
- package/dist/src/app/errors/HTTPError.js +0 -8
- package/dist/src/app/errors/HTTPError.js.map +0 -1
- package/dist/src/app/errors/HTTPNotFoundError.d.ts +0 -5
- package/dist/src/app/errors/HTTPNotFoundError.d.ts.map +0 -1
- package/dist/src/app/errors/HTTPNotFoundError.js +0 -7
- package/dist/src/app/errors/HTTPNotFoundError.js.map +0 -1
- package/dist/src/app/errors/HTTPUnprocessableEntityError.d.ts +0 -5
- package/dist/src/app/errors/HTTPUnprocessableEntityError.d.ts.map +0 -1
- package/dist/src/app/errors/HTTPUnprocessableEntityError.js +0 -7
- package/dist/src/app/errors/HTTPUnprocessableEntityError.js.map +0 -1
- package/dist/src/app/errors/index.d.ts +0 -4
- package/dist/src/app/errors/index.d.ts.map +0 -1
- package/dist/src/app/errors/index.js +0 -4
- package/dist/src/app/errors/index.js.map +0 -1
- package/dist/src/app/index.d.ts +0 -6
- package/dist/src/app/index.d.ts.map +0 -1
- package/dist/src/app/index.js +0 -80
- package/dist/src/app/index.js.map +0 -1
- package/dist/src/app/integration.d.ts +0 -5
- package/dist/src/app/integration.d.ts.map +0 -1
- package/dist/src/app/integration.js +0 -86
- package/dist/src/app/integration.js.map +0 -1
- package/dist/src/app/itemNode.d.ts +0 -8
- package/dist/src/app/itemNode.d.ts.map +0 -1
- package/dist/src/app/itemNode.js +0 -13
- package/dist/src/app/itemNode.js.map +0 -1
- package/dist/src/app/middlewares/withCorrelationId.d.ts +0 -11
- package/dist/src/app/middlewares/withCorrelationId.d.ts.map +0 -1
- package/dist/src/app/middlewares/withCorrelationId.js +0 -8
- package/dist/src/app/middlewares/withCorrelationId.js.map +0 -1
- package/dist/src/app/middlewares/withLogger.d.ts +0 -12
- package/dist/src/app/middlewares/withLogger.d.ts.map +0 -1
- package/dist/src/app/middlewares/withLogger.js +0 -18
- package/dist/src/app/middlewares/withLogger.js.map +0 -1
- package/dist/src/index.d.ts.map +0 -1
- package/dist/src/index.js.map +0 -1
- package/dist/src/resources/cache.d.ts.map +0 -1
- package/dist/src/resources/cache.js.map +0 -1
- package/dist/src/resources/logger.d.ts.map +0 -1
- package/dist/src/resources/logger.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
package/README.md
CHANGED
package/dist/src/errors.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as HttpErrors from './httpErrors.js';
|
|
1
2
|
export declare class NoIntegrationFoundError extends Error {
|
|
2
3
|
}
|
|
3
4
|
export declare class NoConfigurationFileError extends Error {
|
|
@@ -7,9 +8,9 @@ export declare class ConfigurationMalformed extends Error {
|
|
|
7
8
|
export declare class InvalidHandler extends Error {
|
|
8
9
|
}
|
|
9
10
|
/**
|
|
10
|
-
* Processes provider response codes and
|
|
11
|
+
* Processes provider response codes and returns the corresponding errors to be translated further in our responses
|
|
11
12
|
*
|
|
12
13
|
* @param responseStatus the reponseStatus of the request. Any HTTP response code passed here will result in an error!
|
|
13
14
|
* @param message The message returned by the provider
|
|
14
15
|
*/
|
|
15
|
-
export declare function
|
|
16
|
+
export declare function buildHttpError(responseStatus: number, message: string): HttpErrors.HttpError;
|
package/dist/src/errors.js
CHANGED
|
@@ -8,32 +8,34 @@ export class ConfigurationMalformed extends Error {
|
|
|
8
8
|
export class InvalidHandler extends Error {
|
|
9
9
|
}
|
|
10
10
|
/**
|
|
11
|
-
* Processes provider response codes and
|
|
11
|
+
* Processes provider response codes and returns the corresponding errors to be translated further in our responses
|
|
12
12
|
*
|
|
13
13
|
* @param responseStatus the reponseStatus of the request. Any HTTP response code passed here will result in an error!
|
|
14
14
|
* @param message The message returned by the provider
|
|
15
15
|
*/
|
|
16
16
|
// Keep in errors.ts instead of httpErrors.ts because we do not need to export it outside of the sdk
|
|
17
|
-
export function
|
|
17
|
+
export function buildHttpError(responseStatus, message) {
|
|
18
|
+
let httpError;
|
|
18
19
|
if (responseStatus === 400) {
|
|
19
|
-
|
|
20
|
+
httpError = new HttpErrors.BadRequestError(message);
|
|
20
21
|
}
|
|
21
22
|
else if (responseStatus === 401 || responseStatus === 403) {
|
|
22
|
-
|
|
23
|
+
httpError = new HttpErrors.UnauthorizedError(message);
|
|
23
24
|
}
|
|
24
25
|
else if (responseStatus === 404) {
|
|
25
|
-
|
|
26
|
+
httpError = new HttpErrors.NotFoundError(message);
|
|
26
27
|
}
|
|
27
28
|
else if (responseStatus === 408) {
|
|
28
|
-
|
|
29
|
+
httpError = new HttpErrors.TimeoutError(message);
|
|
29
30
|
}
|
|
30
31
|
else if (responseStatus === 422) {
|
|
31
|
-
|
|
32
|
+
httpError = new HttpErrors.UnprocessableEntityError(message);
|
|
32
33
|
}
|
|
33
34
|
else if (responseStatus === 429) {
|
|
34
|
-
|
|
35
|
+
httpError = new HttpErrors.RateLimitExceededError(message);
|
|
35
36
|
}
|
|
36
37
|
else {
|
|
37
|
-
|
|
38
|
+
httpError = new HttpErrors.HttpError(message, responseStatus);
|
|
38
39
|
}
|
|
40
|
+
return httpError;
|
|
39
41
|
}
|
package/dist/src/handler.js
CHANGED
|
@@ -9,6 +9,17 @@ function assertValidPath(path) {
|
|
|
9
9
|
throw new InvalidHandler(`The provided path '${path}' is invalid. Paths must not end with a '/'.`);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
function assertValidConfiguration(path, pathWithIdentifier, handlers) {
|
|
13
|
+
if (path === pathWithIdentifier) {
|
|
14
|
+
const individualHandlers = ['getItem', 'updateItem', 'deleteItem'];
|
|
15
|
+
const collectionHandlers = ['getCollection', 'createItem'];
|
|
16
|
+
const hasIndividualHandlers = individualHandlers.some(handler => handler in handlers);
|
|
17
|
+
const hasCollectionHandlers = collectionHandlers.some(handler => handler in handlers);
|
|
18
|
+
if (hasIndividualHandlers && hasCollectionHandlers) {
|
|
19
|
+
throw new InvalidHandler(`The provided path '${path}' doesn't differentiate between individual and collection level operation, so you cannot define both. `);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
12
23
|
function parsePath(path) {
|
|
13
24
|
const pathParts = path.split('/');
|
|
14
25
|
const lastPart = pathParts.at(-1);
|
|
@@ -60,9 +71,9 @@ export class Handler {
|
|
|
60
71
|
pathWithIdentifier;
|
|
61
72
|
handlers;
|
|
62
73
|
constructor(inputPath, handlers) {
|
|
63
|
-
// Build paths.
|
|
64
74
|
assertValidPath(inputPath);
|
|
65
75
|
const { pathWithIdentifier, path } = parsePath(inputPath);
|
|
76
|
+
assertValidConfiguration(path, pathWithIdentifier, handlers);
|
|
66
77
|
this.pathWithIdentifier = pathWithIdentifier;
|
|
67
78
|
this.path = path;
|
|
68
79
|
this.handlers = handlers;
|
package/dist/src/httpErrors.d.ts
CHANGED
|
@@ -20,3 +20,6 @@ export declare class UnprocessableEntityError extends HttpError {
|
|
|
20
20
|
export declare class RateLimitExceededError extends HttpError {
|
|
21
21
|
constructor(message?: string);
|
|
22
22
|
}
|
|
23
|
+
export declare class WouldExceedLimitError extends HttpError {
|
|
24
|
+
constructor(message?: string);
|
|
25
|
+
}
|
package/dist/src/httpErrors.js
CHANGED
|
@@ -32,6 +32,11 @@ export class UnprocessableEntityError extends HttpError {
|
|
|
32
32
|
}
|
|
33
33
|
export class RateLimitExceededError extends HttpError {
|
|
34
34
|
constructor(message) {
|
|
35
|
-
super(message || '
|
|
35
|
+
super(message || 'Rate Limit Exceeded', 429);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class WouldExceedLimitError extends HttpError {
|
|
39
|
+
constructor(message) {
|
|
40
|
+
super(message || 'Would Exceed Limit', 429);
|
|
36
41
|
}
|
|
37
42
|
}
|
package/dist/src/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export * as Api from '@unito/integration-api';
|
|
2
2
|
export { default as Integration } from './integration.js';
|
|
3
3
|
export * from './handler.js';
|
|
4
|
-
export { Provider, type Response as ProviderResponse, type RequestOptions as ProviderRequestOptions, } from './resources/provider.js';
|
|
4
|
+
export { Provider, type Response as ProviderResponse, type RequestOptions as ProviderRequestOptions, type RateLimiter, } from './resources/provider.js';
|
|
5
|
+
export type { Credentials } from './middlewares/credentials.js';
|
|
5
6
|
export * as HttpErrors from './httpErrors.js';
|
|
6
7
|
export * from './resources/context.js';
|
package/dist/src/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
/* c8 ignore start */
|
|
1
2
|
export * as Api from '@unito/integration-api';
|
|
2
3
|
export { default as Integration } from './integration.js';
|
|
3
4
|
export * from './handler.js';
|
|
4
5
|
export { Provider, } from './resources/provider.js';
|
|
5
6
|
export * as HttpErrors from './httpErrors.js';
|
|
6
7
|
export * from './resources/context.js';
|
|
8
|
+
/* c8 ignore stop */
|
|
@@ -6,6 +6,9 @@ declare global {
|
|
|
6
6
|
}
|
|
7
7
|
}
|
|
8
8
|
}
|
|
9
|
-
export type Credentials =
|
|
9
|
+
export type Credentials = {
|
|
10
|
+
accessToken?: string;
|
|
11
|
+
[keys: string]: unknown;
|
|
12
|
+
};
|
|
10
13
|
declare const middleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
11
14
|
export default middleware;
|
|
@@ -1,36 +1,59 @@
|
|
|
1
1
|
import { Credentials } from '../middlewares/credentials.js';
|
|
2
|
+
/**
|
|
3
|
+
* RateLimiter is a wrapper function that you can provide to limit the rate of calls to the provider based on the
|
|
4
|
+
* caller's credentials.
|
|
5
|
+
*
|
|
6
|
+
* When necessary, the Provider's Response headers can be inspected to update the rate limit before being returned.
|
|
7
|
+
*
|
|
8
|
+
* NOTE: make sure to return one of the supported HttpErrors from the SDK, otherwise the error will be translated to a
|
|
9
|
+
* generic server (500) error.
|
|
10
|
+
*
|
|
11
|
+
* @param context - The credentials of the caller.
|
|
12
|
+
* @param targetFunction - The function to call the provider.
|
|
13
|
+
* @returns The response from the provider.
|
|
14
|
+
* @throws RateLimitExceededError when the rate limit is exceeded.
|
|
15
|
+
* @throws WouldExceedRateLimitError when the next call would exceed the rate limit.
|
|
16
|
+
* @throws HttpError when the provider returns an error.
|
|
17
|
+
*/
|
|
18
|
+
export type RateLimiter = <T>(context: {
|
|
19
|
+
credentials: Credentials;
|
|
20
|
+
}, targetFunction: () => Promise<Response<T>>) => Promise<Response<T>>;
|
|
2
21
|
export interface RequestOptions {
|
|
3
22
|
credentials: Credentials;
|
|
4
23
|
queryParams?: {
|
|
5
24
|
[key: string]: string;
|
|
6
25
|
};
|
|
7
|
-
body?: Record<string, unknown>;
|
|
8
26
|
additionnalheaders?: {
|
|
9
27
|
[key: string]: string;
|
|
10
28
|
};
|
|
11
29
|
}
|
|
12
30
|
export interface Response<T> {
|
|
13
|
-
data: T;
|
|
31
|
+
data: T | undefined;
|
|
32
|
+
status: number;
|
|
14
33
|
headers: Headers;
|
|
15
34
|
}
|
|
16
35
|
export declare class Provider {
|
|
17
|
-
protected
|
|
36
|
+
protected rateLimiter: RateLimiter | undefined;
|
|
37
|
+
protected prepareRequest: (context: {
|
|
18
38
|
credentials: Credentials;
|
|
19
39
|
}) => {
|
|
20
|
-
/**
|
|
21
|
-
* The base URL of the provider.
|
|
22
|
-
*/
|
|
23
40
|
url: string;
|
|
24
|
-
/**
|
|
25
|
-
* The additional headers to add to the request.
|
|
26
|
-
*/
|
|
27
41
|
headers: Record<string, string>;
|
|
28
|
-
}
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Initialize a Provider with the given options.
|
|
45
|
+
*
|
|
46
|
+
* @property prepareRequest - function to define the Provider's base URL and specific headers to add to the request.
|
|
47
|
+
* @property rateLimiter - function to limit the rate of calls to the provider based on the caller's credentials.
|
|
48
|
+
*/
|
|
29
49
|
constructor(options: {
|
|
30
50
|
prepareRequest: typeof Provider.prototype.prepareRequest;
|
|
51
|
+
rateLimiter?: RateLimiter;
|
|
31
52
|
});
|
|
32
53
|
get<T>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
|
|
33
|
-
post<T>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
|
|
34
|
-
|
|
54
|
+
post<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
|
|
55
|
+
put<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
|
|
56
|
+
patch<T>(endpoint: string, body: Record<string, unknown>, options: RequestOptions): Promise<Response<T>>;
|
|
57
|
+
delete<T>(endpoint: string, options: RequestOptions): Promise<Response<T>>;
|
|
35
58
|
private fetchWrapper;
|
|
36
59
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { buildHttpError } from '../errors.js';
|
|
2
2
|
export class Provider {
|
|
3
|
-
|
|
3
|
+
rateLimiter = undefined;
|
|
4
|
+
prepareRequest;
|
|
5
|
+
/**
|
|
6
|
+
* Initialize a Provider with the given options.
|
|
7
|
+
*
|
|
8
|
+
* @property prepareRequest - function to define the Provider's base URL and specific headers to add to the request.
|
|
9
|
+
* @property rateLimiter - function to limit the rate of calls to the provider based on the caller's credentials.
|
|
10
|
+
*/
|
|
4
11
|
constructor(options) {
|
|
5
12
|
this.prepareRequest = options.prepareRequest;
|
|
13
|
+
this.rateLimiter = options.rateLimiter;
|
|
6
14
|
}
|
|
7
15
|
async get(endpoint, options) {
|
|
8
|
-
return this.fetchWrapper(endpoint, {
|
|
16
|
+
return this.fetchWrapper(endpoint, null, {
|
|
9
17
|
...options,
|
|
10
18
|
method: 'GET',
|
|
11
19
|
defaultHeaders: {
|
|
@@ -14,8 +22,8 @@ export class Provider {
|
|
|
14
22
|
},
|
|
15
23
|
});
|
|
16
24
|
}
|
|
17
|
-
async post(endpoint, options) {
|
|
18
|
-
return this.fetchWrapper(endpoint, {
|
|
25
|
+
async post(endpoint, body, options) {
|
|
26
|
+
return this.fetchWrapper(endpoint, body, {
|
|
19
27
|
...options,
|
|
20
28
|
method: 'POST',
|
|
21
29
|
defaultHeaders: {
|
|
@@ -24,8 +32,18 @@ export class Provider {
|
|
|
24
32
|
},
|
|
25
33
|
});
|
|
26
34
|
}
|
|
27
|
-
async
|
|
28
|
-
return this.fetchWrapper(endpoint, {
|
|
35
|
+
async put(endpoint, body, options) {
|
|
36
|
+
return this.fetchWrapper(endpoint, body, {
|
|
37
|
+
...options,
|
|
38
|
+
method: 'PUT',
|
|
39
|
+
defaultHeaders: {
|
|
40
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
41
|
+
Accept: 'application/json',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async patch(endpoint, body, options) {
|
|
46
|
+
return this.fetchWrapper(endpoint, body, {
|
|
29
47
|
...options,
|
|
30
48
|
method: 'PATCH',
|
|
31
49
|
defaultHeaders: {
|
|
@@ -34,43 +52,50 @@ export class Provider {
|
|
|
34
52
|
},
|
|
35
53
|
});
|
|
36
54
|
}
|
|
37
|
-
async
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
async delete(endpoint, options) {
|
|
56
|
+
return this.fetchWrapper(endpoint, null, {
|
|
57
|
+
...options,
|
|
58
|
+
method: 'DELETE',
|
|
59
|
+
defaultHeaders: {
|
|
60
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
|
+
Accept: 'application/json',
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async fetchWrapper(endpoint, body, options) {
|
|
41
66
|
const { url: providerUrl, headers: providerHeaders } = this.prepareRequest({ credentials: options.credentials });
|
|
42
67
|
let absoluteUrl = [providerUrl, endpoint.charAt(0) === '/' ? endpoint.substring(1) : endpoint].join('/');
|
|
43
68
|
if (options.queryParams) {
|
|
44
69
|
absoluteUrl = `${absoluteUrl}?${new URLSearchParams(options.queryParams)}`;
|
|
45
70
|
}
|
|
46
71
|
const headers = { ...options.defaultHeaders, ...providerHeaders, ...options.additionnalheaders };
|
|
47
|
-
let
|
|
48
|
-
if (
|
|
72
|
+
let stringifiedBody = null;
|
|
73
|
+
if (body) {
|
|
49
74
|
if (headers['Content-Type'] === 'application/x-www-form-urlencoded') {
|
|
50
|
-
|
|
75
|
+
stringifiedBody = new URLSearchParams(body).toString();
|
|
51
76
|
}
|
|
52
77
|
else if (headers['Content-Type'] === 'application/json') {
|
|
53
|
-
|
|
78
|
+
stringifiedBody = JSON.stringify(body);
|
|
54
79
|
}
|
|
55
80
|
}
|
|
56
|
-
const callToProvider = async () =>
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
74
|
-
return
|
|
81
|
+
const callToProvider = async () => {
|
|
82
|
+
const response = await fetch(absoluteUrl, {
|
|
83
|
+
method: options.method,
|
|
84
|
+
headers,
|
|
85
|
+
body: stringifiedBody,
|
|
86
|
+
});
|
|
87
|
+
if (response.status >= 400) {
|
|
88
|
+
const textResult = await response.text();
|
|
89
|
+
throw buildHttpError(response.status, textResult);
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
const data = response.body ? await response.json() : undefined;
|
|
93
|
+
return { status: response.status, headers: response.headers, data };
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
throw buildHttpError(400, 'Invalid JSON response');
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
return this.rateLimiter ? this.rateLimiter(options, callToProvider) : callToProvider();
|
|
75
100
|
}
|
|
76
101
|
}
|
package/dist/test/errors.test.js
CHANGED
|
@@ -4,13 +4,12 @@ import * as errors from '../src/errors.js';
|
|
|
4
4
|
import * as httpErrors from '../src/httpErrors.js';
|
|
5
5
|
describe('handleErrorResponse', () => {
|
|
6
6
|
it('returns correct httpError given status code', () => {
|
|
7
|
-
assert.
|
|
8
|
-
assert.
|
|
9
|
-
assert.
|
|
10
|
-
assert.
|
|
11
|
-
assert.
|
|
12
|
-
assert.
|
|
13
|
-
assert.
|
|
14
|
-
assert.throws(() => errors.handleErrorResponse(500, 'internal server error'), httpErrors.HttpError);
|
|
7
|
+
assert.ok(errors.buildHttpError(401, 'unauthorized') instanceof httpErrors.UnauthorizedError);
|
|
8
|
+
assert.ok(errors.buildHttpError(403, 'forbidden') instanceof httpErrors.UnauthorizedError);
|
|
9
|
+
assert.ok(errors.buildHttpError(404, 'not found') instanceof httpErrors.NotFoundError);
|
|
10
|
+
assert.ok(errors.buildHttpError(408, 'timeout') instanceof httpErrors.TimeoutError);
|
|
11
|
+
assert.ok(errors.buildHttpError(422, 'unprocessable entity') instanceof httpErrors.UnprocessableEntityError);
|
|
12
|
+
assert.ok(errors.buildHttpError(429, 'rate limit exceeded') instanceof httpErrors.RateLimitExceededError);
|
|
13
|
+
assert.ok(errors.buildHttpError(500, 'internal server error') instanceof httpErrors.HttpError);
|
|
15
14
|
});
|
|
16
15
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { afterEach, beforeEach, describe, it, mock } from 'node:test';
|
|
3
|
-
import { Handler } from '../src/handler.js';
|
|
3
|
+
import { Handler, } from '../src/handler.js';
|
|
4
4
|
import { BadRequestError } from '../src/httpErrors.js';
|
|
5
5
|
describe('Handler', () => {
|
|
6
6
|
beforeEach(() => {
|
|
@@ -13,7 +13,7 @@ describe('Handler', () => {
|
|
|
13
13
|
describe('constructor', () => {
|
|
14
14
|
it('returns a Handler', () => {
|
|
15
15
|
const itemHandler = new Handler('/', {});
|
|
16
|
-
assert.
|
|
16
|
+
assert.ok(itemHandler instanceof Handler);
|
|
17
17
|
});
|
|
18
18
|
it('validates path', () => {
|
|
19
19
|
const paths = [
|
|
@@ -36,6 +36,30 @@ describe('Handler', () => {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
|
+
it('validates configuration', () => {
|
|
40
|
+
const getItem = (() => { });
|
|
41
|
+
const updateItem = (() => { });
|
|
42
|
+
const deleteItem = (() => { });
|
|
43
|
+
const getCollection = (() => { });
|
|
44
|
+
const createItem = (() => { });
|
|
45
|
+
const paths = [
|
|
46
|
+
['/foo', { getItem }, true],
|
|
47
|
+
['/foo', { getCollection }, true],
|
|
48
|
+
['/foo', { getItem, getCollection }, false],
|
|
49
|
+
['/foo', { updateItem, createItem }, false],
|
|
50
|
+
['/foo', { deleteItem, createItem }, false],
|
|
51
|
+
['/foo/:bar', { getItem, getCollection }, true],
|
|
52
|
+
['/foo/:bar', { getItem, updateItem, deleteItem, createItem, getCollection }, true],
|
|
53
|
+
];
|
|
54
|
+
for (const [path, handlers, valid] of paths) {
|
|
55
|
+
if (valid) {
|
|
56
|
+
assert.doesNotThrow(() => new Handler(path, handlers));
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
assert.throws(() => new Handler(path, handlers));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
});
|
|
39
63
|
});
|
|
40
64
|
describe('generate', () => {
|
|
41
65
|
async function executeHandler(handler, request = {}) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|