@unito/integration-sdk 0.1.11 → 1.0.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.
- package/dist/src/handler.d.ts +39 -0
- package/dist/src/handler.js +9 -0
- package/dist/src/httpErrors.d.ts +29 -0
- package/dist/src/httpErrors.js +30 -0
- package/dist/src/index.cjs +274 -16
- package/dist/src/index.d.ts +1 -0
- package/dist/src/integration.d.ts +49 -0
- package/dist/src/integration.js +51 -0
- package/dist/src/middlewares/filters.d.ts +11 -2
- package/dist/src/middlewares/secrets.d.ts +5 -0
- package/dist/src/middlewares/signal.d.ts +15 -0
- package/dist/src/middlewares/signal.js +22 -0
- package/dist/src/resources/cache.d.ts +51 -1
- package/dist/src/resources/cache.js +51 -1
- package/dist/src/resources/context.d.ts +42 -13
- package/dist/src/resources/logger.d.ts +17 -0
- package/dist/src/resources/logger.js +17 -0
- package/dist/src/resources/provider.d.ts +90 -5
- package/dist/src/resources/provider.js +92 -11
- package/dist/test/middlewares/signal.test.d.ts +1 -0
- package/dist/test/middlewares/signal.test.js +20 -0
- package/dist/test/resources/provider.test.js +116 -21
- package/package.json +4 -4
- package/src/handler.ts +48 -0
- package/src/httpErrors.ts +30 -0
- package/src/index.ts +1 -0
- package/src/integration.ts +51 -0
- package/src/middlewares/filters.ts +11 -2
- package/src/middlewares/secrets.ts +5 -0
- package/src/middlewares/signal.ts +41 -0
- package/src/resources/cache.ts +51 -1
- package/src/resources/context.ts +50 -33
- package/src/resources/logger.ts +17 -0
- package/src/resources/provider.ts +115 -16
- package/test/middlewares/signal.test.ts +28 -0
- package/test/resources/provider.test.ts +122 -21
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { buildHttpError } from '../errors.js';
|
|
2
|
+
/**
|
|
3
|
+
* The Provider class is a wrapper around the fetch function to call a provider's HTTP API.
|
|
4
|
+
*
|
|
5
|
+
* Defines methods for the following HTTP methods: GET, POST, PUT, PATCH, DELETE.
|
|
6
|
+
*
|
|
7
|
+
* Needs to be initialized with a prepareRequest function to define the Provider's base URL and any specific headers to
|
|
8
|
+
* add to the requests, and can also be configured to use a provided rate limiting function.
|
|
9
|
+
* @see {@link RateLimiter}
|
|
10
|
+
* @see {@link prepareRequest}
|
|
11
|
+
*/
|
|
2
12
|
export class Provider {
|
|
3
13
|
rateLimiter = undefined;
|
|
4
14
|
prepareRequest;
|
|
5
15
|
/**
|
|
6
|
-
*
|
|
16
|
+
* Initializes a Provider with the given options.
|
|
7
17
|
*
|
|
8
18
|
* @property prepareRequest - function to define the Provider's base URL and specific headers to add to the request.
|
|
9
19
|
* @property rateLimiter - function to limit the rate of calls to the provider based on the caller's credentials.
|
|
@@ -12,52 +22,108 @@ export class Provider {
|
|
|
12
22
|
this.prepareRequest = options.prepareRequest;
|
|
13
23
|
this.rateLimiter = options.rateLimiter;
|
|
14
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* Performs a GET request to the provider.
|
|
27
|
+
*
|
|
28
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
29
|
+
* adds the following headers:
|
|
30
|
+
* - Accept: application/json
|
|
31
|
+
*
|
|
32
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
33
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
34
|
+
* @returns The {@link Response} extracted from the provider.
|
|
35
|
+
*/
|
|
15
36
|
async get(endpoint, options) {
|
|
16
37
|
return this.fetchWrapper(endpoint, null, {
|
|
17
38
|
...options,
|
|
18
39
|
method: 'GET',
|
|
19
40
|
defaultHeaders: {
|
|
20
|
-
'Content-Type': 'application/json',
|
|
21
41
|
Accept: 'application/json',
|
|
22
42
|
},
|
|
23
43
|
});
|
|
24
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Performs a POST request to the provider.
|
|
47
|
+
*
|
|
48
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
49
|
+
* adds the following headers:
|
|
50
|
+
* - Content-Type: application/json',
|
|
51
|
+
* - Accept: application/json
|
|
52
|
+
*
|
|
53
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
54
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
55
|
+
* @returns The {@link Response} extracted from the provider.
|
|
56
|
+
*/
|
|
25
57
|
async post(endpoint, body, options) {
|
|
26
58
|
return this.fetchWrapper(endpoint, body, {
|
|
27
59
|
...options,
|
|
28
60
|
method: 'POST',
|
|
29
61
|
defaultHeaders: {
|
|
30
|
-
'Content-Type': 'application/
|
|
62
|
+
'Content-Type': 'application/json',
|
|
31
63
|
Accept: 'application/json',
|
|
32
64
|
},
|
|
33
65
|
});
|
|
34
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Performs a PUT request to the provider.
|
|
69
|
+
*
|
|
70
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
71
|
+
* adds the following headers:
|
|
72
|
+
* - Content-Type: application/json',
|
|
73
|
+
* - Accept: application/json
|
|
74
|
+
*
|
|
75
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
76
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
77
|
+
* @returns The {@link Response} extracted from the provider.
|
|
78
|
+
*/
|
|
35
79
|
async put(endpoint, body, options) {
|
|
36
80
|
return this.fetchWrapper(endpoint, body, {
|
|
37
81
|
...options,
|
|
38
82
|
method: 'PUT',
|
|
39
83
|
defaultHeaders: {
|
|
40
|
-
'Content-Type': 'application/
|
|
84
|
+
'Content-Type': 'application/json',
|
|
41
85
|
Accept: 'application/json',
|
|
42
86
|
},
|
|
43
87
|
});
|
|
44
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* Performs a PATCH request to the provider.
|
|
91
|
+
*
|
|
92
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
93
|
+
* adds the following headers:
|
|
94
|
+
* - Content-Type: application/json',
|
|
95
|
+
* - Accept: application/json
|
|
96
|
+
*
|
|
97
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
98
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
99
|
+
* @returns The {@link Response} extracted from the provider.
|
|
100
|
+
*/
|
|
45
101
|
async patch(endpoint, body, options) {
|
|
46
102
|
return this.fetchWrapper(endpoint, body, {
|
|
47
103
|
...options,
|
|
48
104
|
method: 'PATCH',
|
|
49
105
|
defaultHeaders: {
|
|
50
|
-
'Content-Type': 'application/
|
|
106
|
+
'Content-Type': 'application/json',
|
|
51
107
|
Accept: 'application/json',
|
|
52
108
|
},
|
|
53
109
|
});
|
|
54
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Performs a DELETE request to the provider.
|
|
113
|
+
*
|
|
114
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
115
|
+
* adds the following headers:
|
|
116
|
+
* - Accept: application/json
|
|
117
|
+
*
|
|
118
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
119
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
120
|
+
* @returns The {@link Response} extracted from the provider.
|
|
121
|
+
*/
|
|
55
122
|
async delete(endpoint, options) {
|
|
56
123
|
return this.fetchWrapper(endpoint, null, {
|
|
57
124
|
...options,
|
|
58
125
|
method: 'DELETE',
|
|
59
126
|
defaultHeaders: {
|
|
60
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
61
127
|
Accept: 'application/json',
|
|
62
128
|
},
|
|
63
129
|
});
|
|
@@ -79,11 +145,26 @@ export class Provider {
|
|
|
79
145
|
}
|
|
80
146
|
}
|
|
81
147
|
const callToProvider = async () => {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
148
|
+
let response;
|
|
149
|
+
try {
|
|
150
|
+
response = await fetch(absoluteUrl, {
|
|
151
|
+
method: options.method,
|
|
152
|
+
signal: options.signal,
|
|
153
|
+
headers,
|
|
154
|
+
body: stringifiedBody,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
catch (error) {
|
|
158
|
+
if (error instanceof Error) {
|
|
159
|
+
switch (error.name) {
|
|
160
|
+
case 'AbortError':
|
|
161
|
+
throw buildHttpError(408, 'Request aborted');
|
|
162
|
+
case 'TimeoutError':
|
|
163
|
+
throw buildHttpError(408, 'Request timeout');
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
throw buildHttpError(500, `Unexpected error while calling the provider: "${error}"`);
|
|
167
|
+
}
|
|
87
168
|
if (response.status >= 400) {
|
|
88
169
|
const textResult = await response.text();
|
|
89
170
|
throw buildHttpError(response.status, textResult);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import middleware from '../../src/middlewares/signal.js';
|
|
4
|
+
describe('signal middleware', () => {
|
|
5
|
+
it('uses header', () => {
|
|
6
|
+
const deadline = Math.floor((Date.now() + 5000) / 1000);
|
|
7
|
+
const request = { header: (_key) => deadline };
|
|
8
|
+
const response = { locals: {} };
|
|
9
|
+
middleware(request, response, () => { });
|
|
10
|
+
assert.ok(response.locals.signal instanceof AbortSignal);
|
|
11
|
+
assert.equal(response.locals.signal.aborted, false);
|
|
12
|
+
});
|
|
13
|
+
it('defaults', () => {
|
|
14
|
+
const request = { header: (_key) => undefined };
|
|
15
|
+
const response = { locals: {} };
|
|
16
|
+
middleware(request, response, () => { });
|
|
17
|
+
assert.ok(response.locals.signal instanceof AbortSignal);
|
|
18
|
+
assert.equal(response.locals.signal.aborted, false);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { describe, it } from 'node:test';
|
|
3
3
|
import { Provider } from '../../src/resources/provider.js';
|
|
4
|
+
import * as HttpErrors from '../../src/httpErrors.js';
|
|
4
5
|
import Logger from '../../src/resources/logger.js';
|
|
5
6
|
describe('Provider', () => {
|
|
6
7
|
const provider = new Provider({
|
|
@@ -24,6 +25,7 @@ describe('Provider', () => {
|
|
|
24
25
|
const actualResponse = await provider.get('/endpoint', {
|
|
25
26
|
credentials: { apiKey: 'apikey#1111' },
|
|
26
27
|
logger: logger,
|
|
28
|
+
signal: new AbortController().signal,
|
|
27
29
|
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
28
30
|
});
|
|
29
31
|
assert.equal(fetchMock.mock.calls.length, 1);
|
|
@@ -32,8 +34,8 @@ describe('Provider', () => {
|
|
|
32
34
|
{
|
|
33
35
|
method: 'GET',
|
|
34
36
|
body: null,
|
|
37
|
+
signal: new AbortController().signal,
|
|
35
38
|
headers: {
|
|
36
|
-
'Content-Type': 'application/json',
|
|
37
39
|
Accept: 'application/json',
|
|
38
40
|
'X-Custom-Provider-Header': 'value',
|
|
39
41
|
'X-Provider-Credential-Header': 'apikey#1111',
|
|
@@ -54,7 +56,8 @@ describe('Provider', () => {
|
|
|
54
56
|
}, {
|
|
55
57
|
credentials: { apiKey: 'apikey#1111' },
|
|
56
58
|
logger: logger,
|
|
57
|
-
|
|
59
|
+
signal: new AbortController().signal,
|
|
60
|
+
additionnalheaders: { 'Content-Type': 'application/x-www-form-urlencoded', 'X-Additional-Header': 'value1' },
|
|
58
61
|
});
|
|
59
62
|
assert.equal(fetchMock.mock.calls.length, 1);
|
|
60
63
|
assert.deepEqual(fetchMock.mock.calls[0]?.arguments, [
|
|
@@ -62,6 +65,7 @@ describe('Provider', () => {
|
|
|
62
65
|
{
|
|
63
66
|
method: 'POST',
|
|
64
67
|
body: 'data=createdItemInfo',
|
|
68
|
+
signal: new AbortController().signal,
|
|
65
69
|
headers: {
|
|
66
70
|
'Content-Type': 'application/x-www-form-urlencoded',
|
|
67
71
|
Accept: 'application/json',
|
|
@@ -85,6 +89,7 @@ describe('Provider', () => {
|
|
|
85
89
|
}, {
|
|
86
90
|
credentials: { apiKey: 'apikey#1111' },
|
|
87
91
|
logger: logger,
|
|
92
|
+
signal: new AbortController().signal,
|
|
88
93
|
additionnalheaders: { 'X-Additional-Header': 'value1', 'Content-Type': 'application/json' },
|
|
89
94
|
});
|
|
90
95
|
assert.equal(fetchMock.mock.calls.length, 1);
|
|
@@ -93,6 +98,7 @@ describe('Provider', () => {
|
|
|
93
98
|
{
|
|
94
99
|
method: 'PUT',
|
|
95
100
|
body: JSON.stringify({ data: 'updatedItemInfo' }),
|
|
101
|
+
signal: new AbortController().signal,
|
|
96
102
|
headers: {
|
|
97
103
|
'Content-Type': 'application/json',
|
|
98
104
|
Accept: 'application/json',
|
|
@@ -115,8 +121,9 @@ describe('Provider', () => {
|
|
|
115
121
|
}, {
|
|
116
122
|
credentials: { apiKey: 'apikey#1111' },
|
|
117
123
|
logger: logger,
|
|
124
|
+
signal: new AbortController().signal,
|
|
118
125
|
queryParams: { param1: 'value1', param2: 'value2' },
|
|
119
|
-
additionnalheaders: { 'X-Additional-Header': 'value1'
|
|
126
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
120
127
|
});
|
|
121
128
|
assert.equal(fetchMock.mock.calls.length, 1);
|
|
122
129
|
assert.deepEqual(fetchMock.mock.calls[0]?.arguments, [
|
|
@@ -124,6 +131,7 @@ describe('Provider', () => {
|
|
|
124
131
|
{
|
|
125
132
|
method: 'PATCH',
|
|
126
133
|
body: JSON.stringify({ data: 'updatedItemInfo' }),
|
|
134
|
+
signal: new AbortController().signal,
|
|
127
135
|
headers: {
|
|
128
136
|
'Content-Type': 'application/json',
|
|
129
137
|
Accept: 'application/json',
|
|
@@ -144,7 +152,8 @@ describe('Provider', () => {
|
|
|
144
152
|
const actualResponse = await provider.delete('/endpoint/123', {
|
|
145
153
|
credentials: { apiKey: 'apikey#1111' },
|
|
146
154
|
logger: logger,
|
|
147
|
-
|
|
155
|
+
signal: new AbortController().signal,
|
|
156
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
148
157
|
});
|
|
149
158
|
assert.equal(fetchMock.mock.calls.length, 1);
|
|
150
159
|
assert.deepEqual(fetchMock.mock.calls[0]?.arguments, [
|
|
@@ -152,8 +161,8 @@ describe('Provider', () => {
|
|
|
152
161
|
{
|
|
153
162
|
method: 'DELETE',
|
|
154
163
|
body: null,
|
|
164
|
+
signal: new AbortController().signal,
|
|
155
165
|
headers: {
|
|
156
|
-
'Content-Type': 'application/json',
|
|
157
166
|
Accept: 'application/json',
|
|
158
167
|
'X-Custom-Provider-Header': 'value',
|
|
159
168
|
'X-Provider-Credential-Header': 'apikey#1111',
|
|
@@ -185,7 +194,8 @@ describe('Provider', () => {
|
|
|
185
194
|
const options = {
|
|
186
195
|
credentials: { apiKey: 'apikey#1111' },
|
|
187
196
|
logger: logger,
|
|
188
|
-
|
|
197
|
+
signal: new AbortController().signal,
|
|
198
|
+
additionnalheaders: { 'X-Additional-Header': 'value1' },
|
|
189
199
|
};
|
|
190
200
|
const actualResponse = await rateLimitedProvider.delete('/endpoint/123', options);
|
|
191
201
|
assert.equal(mockRateLimiter.mock.calls.length, 1);
|
|
@@ -196,8 +206,8 @@ describe('Provider', () => {
|
|
|
196
206
|
{
|
|
197
207
|
method: 'DELETE',
|
|
198
208
|
body: null,
|
|
209
|
+
signal: new AbortController().signal,
|
|
199
210
|
headers: {
|
|
200
|
-
'Content-Type': 'application/json',
|
|
201
211
|
Accept: 'application/json',
|
|
202
212
|
'X-Custom-Provider-Header': 'value',
|
|
203
213
|
'X-Provider-Credential-Header': 'apikey#1111',
|
|
@@ -212,29 +222,114 @@ describe('Provider', () => {
|
|
|
212
222
|
status: 200,
|
|
213
223
|
});
|
|
214
224
|
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
225
|
+
let error;
|
|
226
|
+
try {
|
|
227
|
+
await provider.get('/endpoint/123', {
|
|
228
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
229
|
+
logger: logger,
|
|
230
|
+
signal: new AbortController().signal,
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
error = e;
|
|
235
|
+
}
|
|
236
|
+
assert.ok(error instanceof HttpErrors.BadRequestError);
|
|
237
|
+
assert.equal(error.message, 'Invalid JSON response');
|
|
219
238
|
});
|
|
220
239
|
it('throws on status 400', async (context) => {
|
|
221
|
-
const response = new Response(
|
|
240
|
+
const response = new Response('response body', {
|
|
222
241
|
status: 400,
|
|
223
242
|
});
|
|
224
243
|
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
244
|
+
let error;
|
|
245
|
+
try {
|
|
246
|
+
await provider.get('/endpoint/123', {
|
|
247
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
248
|
+
signal: new AbortController().signal,
|
|
249
|
+
logger: logger,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (e) {
|
|
253
|
+
error = e;
|
|
254
|
+
}
|
|
255
|
+
assert.ok(error instanceof HttpErrors.BadRequestError);
|
|
256
|
+
assert.equal(error.message, 'response body');
|
|
257
|
+
});
|
|
258
|
+
it('throws on timeout', async (context) => {
|
|
259
|
+
context.mock.method(global, 'fetch', () => {
|
|
260
|
+
const error = new Error();
|
|
261
|
+
error.name = 'TimeoutError';
|
|
262
|
+
throw error;
|
|
263
|
+
});
|
|
264
|
+
let error;
|
|
265
|
+
try {
|
|
266
|
+
await provider.get('/endpoint/123', {
|
|
267
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
268
|
+
signal: new AbortController().signal,
|
|
269
|
+
logger: logger,
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
catch (e) {
|
|
273
|
+
error = e;
|
|
274
|
+
}
|
|
275
|
+
assert.ok(error instanceof HttpErrors.TimeoutError);
|
|
276
|
+
assert.equal(error.message, 'Request timeout');
|
|
277
|
+
});
|
|
278
|
+
it('throws on abort', async (context) => {
|
|
279
|
+
context.mock.method(global, 'fetch', () => {
|
|
280
|
+
const error = new Error();
|
|
281
|
+
error.name = 'AbortError';
|
|
282
|
+
throw error;
|
|
283
|
+
});
|
|
284
|
+
let error;
|
|
285
|
+
try {
|
|
286
|
+
await provider.get('/endpoint/123', {
|
|
287
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
288
|
+
signal: new AbortController().signal,
|
|
289
|
+
logger: logger,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch (e) {
|
|
293
|
+
error = e;
|
|
294
|
+
}
|
|
295
|
+
assert.ok(error instanceof HttpErrors.TimeoutError);
|
|
296
|
+
assert.equal(error.message, 'Request aborted');
|
|
297
|
+
});
|
|
298
|
+
it('throws on unknown errors', async (context) => {
|
|
299
|
+
context.mock.method(global, 'fetch', () => {
|
|
300
|
+
throw new Error('foo');
|
|
301
|
+
});
|
|
302
|
+
let error;
|
|
303
|
+
try {
|
|
304
|
+
await provider.get('/endpoint/123', {
|
|
305
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
306
|
+
signal: new AbortController().signal,
|
|
307
|
+
logger: logger,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
catch (e) {
|
|
311
|
+
error = e;
|
|
312
|
+
}
|
|
313
|
+
assert.ok(error instanceof HttpErrors.HttpError);
|
|
314
|
+
assert.equal(error.message, 'Unexpected error while calling the provider: "Error: foo"');
|
|
229
315
|
});
|
|
230
316
|
it('throws on status 429', async (context) => {
|
|
231
|
-
const response = new Response(
|
|
317
|
+
const response = new Response('response body', {
|
|
232
318
|
status: 429,
|
|
233
319
|
});
|
|
234
320
|
context.mock.method(global, 'fetch', () => Promise.resolve(response));
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
321
|
+
let error;
|
|
322
|
+
try {
|
|
323
|
+
await provider.get('/endpoint/123', {
|
|
324
|
+
credentials: { apiKey: 'apikey#1111' },
|
|
325
|
+
signal: new AbortController().signal,
|
|
326
|
+
logger: logger,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
catch (e) {
|
|
330
|
+
error = e;
|
|
331
|
+
}
|
|
332
|
+
assert.ok(error instanceof HttpErrors.RateLimitExceededError);
|
|
333
|
+
assert.equal(error.message, 'response body');
|
|
239
334
|
});
|
|
240
335
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unito/integration-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Integration SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/src/index.d.ts",
|
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
"@types/express": "4.x",
|
|
37
37
|
"@types/node": "20.x",
|
|
38
38
|
"@types/uuid": "9.x",
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "
|
|
40
|
-
"@typescript-eslint/parser": "
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "7.x",
|
|
40
|
+
"@typescript-eslint/parser": "7.x",
|
|
41
41
|
"c8": "9.x",
|
|
42
42
|
"eslint": "8.x",
|
|
43
43
|
"prettier": "3.x",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@unito/integration-api": "0.x",
|
|
51
|
-
"cachette": "
|
|
51
|
+
"cachette": "2.x",
|
|
52
52
|
"express": "^5.0.0-beta.1",
|
|
53
53
|
"uuid": "9.x"
|
|
54
54
|
},
|
package/src/handler.ts
CHANGED
|
@@ -16,31 +16,48 @@ import {
|
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Handler called to get an individual item.
|
|
19
|
+
*
|
|
20
|
+
* @param context {@link GetItemContext}
|
|
21
|
+
* @returns The requested {@link API.Item}.
|
|
19
22
|
*/
|
|
20
23
|
export type GetItemHandler = (context: GetItemContext<any, any>) => Promise<API.Item>;
|
|
21
24
|
|
|
22
25
|
/**
|
|
23
26
|
* Handler called to retrieve a collection of items.
|
|
27
|
+
*
|
|
28
|
+
* @param context {@link GetCollectionContext}
|
|
29
|
+
* @return An {@link API.Collection} containing requested items and a link to the next page, if applicable.
|
|
24
30
|
*/
|
|
25
31
|
export type GetCollectionHandler = (context: GetCollectionContext<any, any>) => Promise<API.Collection>;
|
|
26
32
|
|
|
27
33
|
/**
|
|
28
34
|
* Handler called to create an item.
|
|
35
|
+
*
|
|
36
|
+
* @param context {@link CreateItemContext}
|
|
37
|
+
* @returns An {@link API.ItemSummary} containing a path to the created item.
|
|
29
38
|
*/
|
|
30
39
|
export type CreateItemHandler = (context: CreateItemContext<any, any, any>) => Promise<API.ItemSummary>;
|
|
31
40
|
|
|
32
41
|
/**
|
|
33
42
|
* Handler called to update an item.
|
|
43
|
+
*
|
|
44
|
+
* @param context {@link UpdateItemContext}
|
|
45
|
+
* @returns The updated {@link API.Item}.
|
|
34
46
|
*/
|
|
35
47
|
export type UpdateItemHandler = (context: UpdateItemContext<any, any, any>) => Promise<API.Item>;
|
|
36
48
|
|
|
37
49
|
/**
|
|
38
50
|
* Handler called to delete an item.
|
|
51
|
+
*
|
|
52
|
+
* @param context {@link DeleteItemContext}
|
|
39
53
|
*/
|
|
40
54
|
export type DeleteItemHandler = (context: DeleteItemContext<any, any>) => Promise<void>;
|
|
41
55
|
|
|
42
56
|
/**
|
|
43
57
|
* Handler called to retrieve the account details associated with the credentials.
|
|
58
|
+
*
|
|
59
|
+
* @param context {@link GetCredentialAccountContext}
|
|
60
|
+
* @returns The {@link API.CredentialAccount} associated with the credentials.
|
|
44
61
|
*/
|
|
45
62
|
export type GetCredentialAccountHandler = (
|
|
46
63
|
context: GetCredentialAccountContext<any, any>,
|
|
@@ -48,6 +65,9 @@ export type GetCredentialAccountHandler = (
|
|
|
48
65
|
|
|
49
66
|
/**
|
|
50
67
|
* Handler called to parse the content of an incoming webhook.
|
|
68
|
+
*
|
|
69
|
+
* @param context {@link ParseWebhooksContext}
|
|
70
|
+
* @returns The parsed content of the webhook as a {@link API.WebhookParseResponsePayload}.
|
|
51
71
|
*/
|
|
52
72
|
export type ParseWebhooksHandler = (
|
|
53
73
|
context: ParseWebhooksContext<any, any, any>,
|
|
@@ -55,6 +75,8 @@ export type ParseWebhooksHandler = (
|
|
|
55
75
|
|
|
56
76
|
/**
|
|
57
77
|
* Handler called to subscribe or unsubscribe to a particular webhook.
|
|
78
|
+
*
|
|
79
|
+
* @param context {@link UpdateWebhookSubscriptionsContext}
|
|
58
80
|
*/
|
|
59
81
|
export type UpdateWebhookSubscriptionsHandler = (
|
|
60
82
|
context: UpdateWebhookSubscriptionsContext<any, any, any>,
|
|
@@ -62,11 +84,28 @@ export type UpdateWebhookSubscriptionsHandler = (
|
|
|
62
84
|
|
|
63
85
|
/**
|
|
64
86
|
* Handler called to acknowledge the reception of a webhook.
|
|
87
|
+
*
|
|
88
|
+
* @param context {@link AcknowledgeWebhooksContext}
|
|
89
|
+
* @returns The {@link API.WebhookAcknowledgeResponsePayload} to be sent back to the webhook provider.
|
|
65
90
|
*/
|
|
66
91
|
export type AcknowledgeWebhooksHandler = (
|
|
67
92
|
context: AcknowledgeWebhooksContext<any, any, any>,
|
|
68
93
|
) => Promise<API.WebhookAcknowledgeResponsePayload>;
|
|
69
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Defines the implementation of the operations available for a given `Item`.
|
|
97
|
+
* - In some cases (e.g. defining the `root` or {@link https://dev.unito.io/docs/connectors/apiSpecification/credentialAccount | me}
|
|
98
|
+
* handlers), only a {@link GetItemHandler | getItem} handler is necessary.
|
|
99
|
+
* - In most cases, you will want to define {@link GetCollectionHandler | getCollection},
|
|
100
|
+
* most likely {@link GetItemHandler | getItem}, and any other handlers relevant to the item.
|
|
101
|
+
*
|
|
102
|
+
* The `ItemHandlers` object can contain any of the following:
|
|
103
|
+
* - {@link GetItemHandler | getItem}: A handler called to get an individual item.
|
|
104
|
+
* - {@link GetCollectionHandler | getCollection}: A handler called to retrieve a collection of items.
|
|
105
|
+
* - {@link CreateItemHandler | createItem}: A handler called to create an item.
|
|
106
|
+
* - {@link UpdateItemHandler | updateItem}: A handler called to update an item.
|
|
107
|
+
* - {@link DeleteItemHandler | deleteItem}: A handler called to delete an item.
|
|
108
|
+
*/
|
|
70
109
|
export type ItemHandlers = {
|
|
71
110
|
getItem?: GetItemHandler;
|
|
72
111
|
getCollection?: GetCollectionHandler;
|
|
@@ -234,6 +273,7 @@ export class Handler {
|
|
|
234
273
|
selects: res.locals.selects,
|
|
235
274
|
filters: res.locals.filters,
|
|
236
275
|
logger: res.locals.logger,
|
|
276
|
+
signal: res.locals.signal,
|
|
237
277
|
params: req.params,
|
|
238
278
|
query: req.query,
|
|
239
279
|
});
|
|
@@ -259,6 +299,7 @@ export class Handler {
|
|
|
259
299
|
secrets: res.locals.secrets,
|
|
260
300
|
body: req.body,
|
|
261
301
|
logger: res.locals.logger,
|
|
302
|
+
signal: res.locals.signal,
|
|
262
303
|
params: req.params,
|
|
263
304
|
query: req.query,
|
|
264
305
|
});
|
|
@@ -281,6 +322,7 @@ export class Handler {
|
|
|
281
322
|
credentials: res.locals.credentials,
|
|
282
323
|
secrets: res.locals.secrets,
|
|
283
324
|
logger: res.locals.logger,
|
|
325
|
+
signal: res.locals.signal,
|
|
284
326
|
params: req.params,
|
|
285
327
|
query: req.query,
|
|
286
328
|
});
|
|
@@ -306,6 +348,7 @@ export class Handler {
|
|
|
306
348
|
secrets: res.locals.secrets,
|
|
307
349
|
body: req.body,
|
|
308
350
|
logger: res.locals.logger,
|
|
351
|
+
signal: res.locals.signal,
|
|
309
352
|
params: req.params,
|
|
310
353
|
query: req.query,
|
|
311
354
|
});
|
|
@@ -328,6 +371,7 @@ export class Handler {
|
|
|
328
371
|
credentials: res.locals.credentials,
|
|
329
372
|
secrets: res.locals.secrets,
|
|
330
373
|
logger: res.locals.logger,
|
|
374
|
+
signal: res.locals.signal,
|
|
331
375
|
params: req.params,
|
|
332
376
|
query: req.query,
|
|
333
377
|
});
|
|
@@ -350,6 +394,7 @@ export class Handler {
|
|
|
350
394
|
credentials: res.locals.credentials,
|
|
351
395
|
secrets: res.locals.secrets,
|
|
352
396
|
logger: res.locals.logger,
|
|
397
|
+
signal: res.locals.signal,
|
|
353
398
|
params: req.params,
|
|
354
399
|
query: req.query,
|
|
355
400
|
});
|
|
@@ -369,6 +414,7 @@ export class Handler {
|
|
|
369
414
|
const response = await handler({
|
|
370
415
|
secrets: res.locals.secrets,
|
|
371
416
|
logger: res.locals.logger,
|
|
417
|
+
signal: res.locals.signal,
|
|
372
418
|
params: req.params,
|
|
373
419
|
query: req.query,
|
|
374
420
|
body: req.body,
|
|
@@ -389,6 +435,7 @@ export class Handler {
|
|
|
389
435
|
const response = await handler({
|
|
390
436
|
secrets: res.locals.secrets,
|
|
391
437
|
logger: res.locals.logger,
|
|
438
|
+
signal: res.locals.signal,
|
|
392
439
|
params: req.params,
|
|
393
440
|
query: req.query,
|
|
394
441
|
body: req.body,
|
|
@@ -415,6 +462,7 @@ export class Handler {
|
|
|
415
462
|
credentials: res.locals.credentials,
|
|
416
463
|
body: req.body,
|
|
417
464
|
logger: res.locals.logger,
|
|
465
|
+
signal: res.locals.signal,
|
|
418
466
|
params: req.params,
|
|
419
467
|
query: req.query,
|
|
420
468
|
});
|