@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
package/dist/src/index.cjs
CHANGED
|
@@ -33,6 +33,9 @@ var LogLevel;
|
|
|
33
33
|
LogLevel["LOG"] = "log";
|
|
34
34
|
LogLevel["DEBUG"] = "debug";
|
|
35
35
|
})(LogLevel || (LogLevel = {}));
|
|
36
|
+
/**
|
|
37
|
+
* Logger class that can be configured with metadata add creation and when logging to add additional context to your logs.
|
|
38
|
+
*/
|
|
36
39
|
class Logger {
|
|
37
40
|
metadata;
|
|
38
41
|
constructor(metadata = {}) {
|
|
@@ -85,12 +88,26 @@ class Logger {
|
|
|
85
88
|
decorate(metadata) {
|
|
86
89
|
this.metadata = { ...this.metadata, ...metadata };
|
|
87
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Return a copy of the Logger's metadata.
|
|
93
|
+
* @returns The {@link Metadata} associated with the logger.
|
|
94
|
+
*/
|
|
88
95
|
getMetadata() {
|
|
89
96
|
return structuredClone(this.metadata);
|
|
90
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Sets a key-value pair in the metadata. If the key already exists, it will be overwritten.
|
|
100
|
+
*
|
|
101
|
+
* @param key Key of the metadata to be set.
|
|
102
|
+
* May be any string other than 'message', which is reserved for the actual message logged.
|
|
103
|
+
* @param value Value of the metadata to be set.
|
|
104
|
+
*/
|
|
91
105
|
setMetadata(key, value) {
|
|
92
106
|
this.metadata[key] = value;
|
|
93
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Clears the Logger's metadata.
|
|
110
|
+
*/
|
|
94
111
|
clearMetadata() {
|
|
95
112
|
this.metadata = {};
|
|
96
113
|
}
|
|
@@ -118,28 +135,78 @@ class Logger {
|
|
|
118
135
|
}
|
|
119
136
|
}
|
|
120
137
|
|
|
138
|
+
/**
|
|
139
|
+
* The Cache class provides caching capabilities that can be used across your integration.
|
|
140
|
+
* It can be backed by a Redis instance (by passing it a URL to the instance) or a local cache.
|
|
141
|
+
*
|
|
142
|
+
* @see {@link Cache.create}
|
|
143
|
+
*/
|
|
121
144
|
class Cache {
|
|
122
145
|
cacheInstance;
|
|
123
146
|
constructor(cacheInstance) {
|
|
124
147
|
this.cacheInstance = cacheInstance;
|
|
125
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Get or fetch a value
|
|
151
|
+
*
|
|
152
|
+
* @param key The key of the value to get
|
|
153
|
+
* @param ttl The time to live of the value in seconds.
|
|
154
|
+
* @param fetchFn The function that can retrieve the original value
|
|
155
|
+
* @param lockTtl Global distributed lock TTL (in seconds) protecting fetching.
|
|
156
|
+
* If undefined, 0 or falsy, locking is not preformed
|
|
157
|
+
* @param shouldCacheError A callback being passed errors, controlling whether
|
|
158
|
+
* to cache or not errors. Defaults to never cache.
|
|
159
|
+
*
|
|
160
|
+
* @returns The cached or fetched value
|
|
161
|
+
*/
|
|
126
162
|
getOrFetchValue(key, ttl, fetcher, lockTtl, shouldCacheError) {
|
|
127
163
|
return this.cacheInstance.getOrFetchValue(key, ttl, fetcher, lockTtl, shouldCacheError);
|
|
128
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Get a value from the cache.
|
|
167
|
+
*
|
|
168
|
+
* @param key The key of the value to get.
|
|
169
|
+
*
|
|
170
|
+
* @return The value associated with the key, or undefined if
|
|
171
|
+
* no such value exists.
|
|
172
|
+
*/
|
|
129
173
|
getValue(key) {
|
|
130
174
|
return this.cacheInstance.getValue(key);
|
|
131
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Set a value in the cache.
|
|
178
|
+
*
|
|
179
|
+
* @param key The key of the value to set.
|
|
180
|
+
* @param value The value to set.
|
|
181
|
+
* @param ttl The time to live of the value in seconds.
|
|
182
|
+
* By default, the value will not expire
|
|
183
|
+
*
|
|
184
|
+
* @return true if the value was stored, false otherwise.
|
|
185
|
+
*/
|
|
132
186
|
setValue(key, value, ttl) {
|
|
133
187
|
return this.cacheInstance.setValue(key, value, ttl);
|
|
134
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* Delete a value from the cache.
|
|
191
|
+
* @param key — The key of the value to set.
|
|
192
|
+
*/
|
|
135
193
|
delValue(key) {
|
|
136
194
|
return this.cacheInstance.delValue(key);
|
|
137
195
|
}
|
|
196
|
+
/**
|
|
197
|
+
* Get the TTL of an entry, in ms
|
|
198
|
+
*
|
|
199
|
+
* @param key The key of the entry whose ttl to retrieve
|
|
200
|
+
*
|
|
201
|
+
* @return The remaining TTL on the entry, in ms.
|
|
202
|
+
* undefined if the entry does not exist.
|
|
203
|
+
* 0 if the entry does not expire.
|
|
204
|
+
*/
|
|
138
205
|
getTtl(key) {
|
|
139
206
|
return this.cacheInstance.getTtl(key);
|
|
140
207
|
}
|
|
141
208
|
/**
|
|
142
|
-
* Initializes a
|
|
209
|
+
* Initializes a Cache backed by the Redis instance at the provided url if present, or a LocalCache otherwise.
|
|
143
210
|
*
|
|
144
211
|
* @param redisUrl - The redis url to connect to (optional).
|
|
145
212
|
* @returns A cache instance.
|
|
@@ -163,43 +230,73 @@ class Cache {
|
|
|
163
230
|
}
|
|
164
231
|
}
|
|
165
232
|
|
|
233
|
+
/**
|
|
234
|
+
* Error class meant to be returned by integrations in case of exceptions. These errors will be caught and handled
|
|
235
|
+
* appropriately. Any other error would result in an unhandled server error accompanied by a 500 status code.
|
|
236
|
+
*
|
|
237
|
+
* @field message - The error message
|
|
238
|
+
* @field status - The HTTP status code to return
|
|
239
|
+
*/
|
|
166
240
|
class HttpError extends Error {
|
|
167
241
|
status;
|
|
168
242
|
constructor(message, status) {
|
|
169
243
|
super(message);
|
|
170
244
|
this.status = status;
|
|
245
|
+
this.name = this.constructor.name;
|
|
171
246
|
}
|
|
172
247
|
}
|
|
248
|
+
/**
|
|
249
|
+
* Used to generate a 400 Bad Request. Usually used when something is missing to properly handle the request.
|
|
250
|
+
*/
|
|
173
251
|
class BadRequestError extends HttpError {
|
|
174
252
|
constructor(message) {
|
|
175
253
|
super(message || 'Bad request', 400);
|
|
176
254
|
}
|
|
177
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Used to generate a 401 Unauthorized. Usually used when the credentials are missing or invalid.
|
|
258
|
+
*/
|
|
178
259
|
class UnauthorizedError extends HttpError {
|
|
179
260
|
constructor(message) {
|
|
180
261
|
super(message || 'Unauthorized', 401);
|
|
181
262
|
}
|
|
182
263
|
}
|
|
264
|
+
/**
|
|
265
|
+
* Used to generate a 404 Not Found. Usually used when the requested `Item` is not found.
|
|
266
|
+
*/
|
|
183
267
|
class NotFoundError extends HttpError {
|
|
184
268
|
constructor(message) {
|
|
185
269
|
super(message || 'Not found', 404);
|
|
186
270
|
}
|
|
187
271
|
}
|
|
272
|
+
/**
|
|
273
|
+
* Used to generate a 408 Timeout Error. Usually used when the call length exceeds the received Operation Deadline.
|
|
274
|
+
*/
|
|
188
275
|
class TimeoutError extends HttpError {
|
|
189
276
|
constructor(message) {
|
|
190
277
|
super(message || 'Not found', 408);
|
|
191
278
|
}
|
|
192
279
|
}
|
|
280
|
+
/**
|
|
281
|
+
* Used to generate a 410 Resource Gone.
|
|
282
|
+
*/
|
|
193
283
|
class ResourceGoneError extends HttpError {
|
|
194
284
|
constructor(message) {
|
|
195
285
|
super(message || 'Resource gone or unavailable', 410);
|
|
196
286
|
}
|
|
197
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Used to generate a 422 Unprocessable Entity. Usually used when an operation is invalid.
|
|
290
|
+
*/
|
|
198
291
|
class UnprocessableEntityError extends HttpError {
|
|
199
292
|
constructor(message) {
|
|
200
293
|
super(message || 'Unprocessable Entity', 422);
|
|
201
294
|
}
|
|
202
295
|
}
|
|
296
|
+
/**
|
|
297
|
+
* Used to generate a 429 Unprocessable Entity. Usually used when an operation triggers or would trigger a rate limit
|
|
298
|
+
* error on the provider's side.
|
|
299
|
+
*/
|
|
203
300
|
class RateLimitExceededError extends HttpError {
|
|
204
301
|
constructor(message) {
|
|
205
302
|
super(message || 'Rate Limit Exceeded', 429);
|
|
@@ -256,13 +353,13 @@ function buildHttpError(responseStatus, message) {
|
|
|
256
353
|
return httpError;
|
|
257
354
|
}
|
|
258
355
|
|
|
259
|
-
const middleware$
|
|
356
|
+
const middleware$8 = (req, res, next) => {
|
|
260
357
|
res.locals.correlationId = req.header('X-Unito-Correlation-Id') ?? uuid__namespace.v4();
|
|
261
358
|
next();
|
|
262
359
|
};
|
|
263
360
|
|
|
264
361
|
const ADDITIONAL_CONTEXT_HEADER = 'X-Unito-Additional-Logging-Context';
|
|
265
|
-
const middleware$
|
|
362
|
+
const middleware$7 = (req, res, next) => {
|
|
266
363
|
const logger = new Logger({ correlation_id: res.locals.correlationId });
|
|
267
364
|
res.locals.logger = logger;
|
|
268
365
|
const rawAdditionalContext = req.header(ADDITIONAL_CONTEXT_HEADER);
|
|
@@ -279,7 +376,7 @@ const middleware$6 = (req, res, next) => {
|
|
|
279
376
|
};
|
|
280
377
|
|
|
281
378
|
const CREDENTIALS_HEADER = 'X-Unito-Credentials';
|
|
282
|
-
const middleware$
|
|
379
|
+
const middleware$6 = (req, res, next) => {
|
|
283
380
|
const credentialsHeader = req.header(CREDENTIALS_HEADER);
|
|
284
381
|
if (credentialsHeader) {
|
|
285
382
|
let credentials;
|
|
@@ -294,6 +391,27 @@ const middleware$5 = (req, res, next) => {
|
|
|
294
391
|
next();
|
|
295
392
|
};
|
|
296
393
|
|
|
394
|
+
const OPERATION_DEADLINE_HEADER = 'X-Unito-Operation-Deadline';
|
|
395
|
+
const middleware$5 = (req, res, next) => {
|
|
396
|
+
const operationDeadlineHeader = Number(req.header(OPERATION_DEADLINE_HEADER));
|
|
397
|
+
if (operationDeadlineHeader) {
|
|
398
|
+
// `operationDeadlineHeader` represents a timestamp in the future, in seconds.
|
|
399
|
+
// We need to convert it to a number of milliseconds.
|
|
400
|
+
const deadline = operationDeadlineHeader * 1000 - Date.now();
|
|
401
|
+
if (deadline > 0) {
|
|
402
|
+
res.locals.signal = AbortSignal.timeout(deadline);
|
|
403
|
+
}
|
|
404
|
+
else {
|
|
405
|
+
throw new TimeoutError('Request already timed out upon reception');
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
// Default to 20s, which is the maximum time frame allowed for an operation by Unito.
|
|
410
|
+
res.locals.signal = AbortSignal.timeout(20000);
|
|
411
|
+
}
|
|
412
|
+
next();
|
|
413
|
+
};
|
|
414
|
+
|
|
297
415
|
const SECRETS_HEADER = 'X-Unito-Secrets';
|
|
298
416
|
const middleware$4 = (req, res, next) => {
|
|
299
417
|
const secretsHeader = req.header(SECRETS_HEADER);
|
|
@@ -491,6 +609,7 @@ class Handler {
|
|
|
491
609
|
selects: res.locals.selects,
|
|
492
610
|
filters: res.locals.filters,
|
|
493
611
|
logger: res.locals.logger,
|
|
612
|
+
signal: res.locals.signal,
|
|
494
613
|
params: req.params,
|
|
495
614
|
query: req.query,
|
|
496
615
|
});
|
|
@@ -510,6 +629,7 @@ class Handler {
|
|
|
510
629
|
secrets: res.locals.secrets,
|
|
511
630
|
body: req.body,
|
|
512
631
|
logger: res.locals.logger,
|
|
632
|
+
signal: res.locals.signal,
|
|
513
633
|
params: req.params,
|
|
514
634
|
query: req.query,
|
|
515
635
|
});
|
|
@@ -527,6 +647,7 @@ class Handler {
|
|
|
527
647
|
credentials: res.locals.credentials,
|
|
528
648
|
secrets: res.locals.secrets,
|
|
529
649
|
logger: res.locals.logger,
|
|
650
|
+
signal: res.locals.signal,
|
|
530
651
|
params: req.params,
|
|
531
652
|
query: req.query,
|
|
532
653
|
});
|
|
@@ -546,6 +667,7 @@ class Handler {
|
|
|
546
667
|
secrets: res.locals.secrets,
|
|
547
668
|
body: req.body,
|
|
548
669
|
logger: res.locals.logger,
|
|
670
|
+
signal: res.locals.signal,
|
|
549
671
|
params: req.params,
|
|
550
672
|
query: req.query,
|
|
551
673
|
});
|
|
@@ -563,6 +685,7 @@ class Handler {
|
|
|
563
685
|
credentials: res.locals.credentials,
|
|
564
686
|
secrets: res.locals.secrets,
|
|
565
687
|
logger: res.locals.logger,
|
|
688
|
+
signal: res.locals.signal,
|
|
566
689
|
params: req.params,
|
|
567
690
|
query: req.query,
|
|
568
691
|
});
|
|
@@ -580,6 +703,7 @@ class Handler {
|
|
|
580
703
|
credentials: res.locals.credentials,
|
|
581
704
|
secrets: res.locals.secrets,
|
|
582
705
|
logger: res.locals.logger,
|
|
706
|
+
signal: res.locals.signal,
|
|
583
707
|
params: req.params,
|
|
584
708
|
query: req.query,
|
|
585
709
|
});
|
|
@@ -594,6 +718,7 @@ class Handler {
|
|
|
594
718
|
const response = await handler({
|
|
595
719
|
secrets: res.locals.secrets,
|
|
596
720
|
logger: res.locals.logger,
|
|
721
|
+
signal: res.locals.signal,
|
|
597
722
|
params: req.params,
|
|
598
723
|
query: req.query,
|
|
599
724
|
body: req.body,
|
|
@@ -609,6 +734,7 @@ class Handler {
|
|
|
609
734
|
const response = await handler({
|
|
610
735
|
secrets: res.locals.secrets,
|
|
611
736
|
logger: res.locals.logger,
|
|
737
|
+
signal: res.locals.signal,
|
|
612
738
|
params: req.params,
|
|
613
739
|
query: req.query,
|
|
614
740
|
body: req.body,
|
|
@@ -629,6 +755,7 @@ class Handler {
|
|
|
629
755
|
credentials: res.locals.credentials,
|
|
630
756
|
body: req.body,
|
|
631
757
|
logger: res.locals.logger,
|
|
758
|
+
signal: res.locals.signal,
|
|
632
759
|
params: req.params,
|
|
633
760
|
query: req.query,
|
|
634
761
|
});
|
|
@@ -645,14 +772,56 @@ function printErrorMessage(message) {
|
|
|
645
772
|
console.error(`\x1b[31m Oops! Something went wrong! \x1b[0m`);
|
|
646
773
|
console.error(message);
|
|
647
774
|
}
|
|
775
|
+
/**
|
|
776
|
+
* Main class for the Integration SDK providing an abstraction layer between the Integration's Graph definition
|
|
777
|
+
* and the underlying HTTP server.
|
|
778
|
+
*
|
|
779
|
+
* An `Integration` instance can have multiple handlers configured to handle different routes. Upon receiving a request,
|
|
780
|
+
* the Integration will parse the request to extract meaninful information, match the request to the appropriate handler
|
|
781
|
+
* method and forward that information in the form a {@link Context} object.
|
|
782
|
+
* The Integration also offer standardized error handling and logging to help you build a robust
|
|
783
|
+
* and reliable Integration.
|
|
784
|
+
*
|
|
785
|
+
* See our {@link https://dev.unito.io/docs/ | documentation} for more examples on how to build an integration.
|
|
786
|
+
*/
|
|
648
787
|
class Integration {
|
|
649
788
|
handlers;
|
|
650
789
|
instance = undefined;
|
|
651
790
|
port;
|
|
791
|
+
/**
|
|
792
|
+
* Creates a new Integration instance with default port set to 9200.
|
|
793
|
+
*
|
|
794
|
+
* @param options The {@link Options} to configure the Integration instance. Can be used to override the default port.
|
|
795
|
+
*/
|
|
652
796
|
constructor(options = {}) {
|
|
653
797
|
this.port = options.port || 9200;
|
|
654
798
|
this.handlers = [];
|
|
655
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* Adds a group of common handlers to the integration.
|
|
802
|
+
*
|
|
803
|
+
* Handlers added to the integration can be one of the following:
|
|
804
|
+
* - `ItemHandlers`: A group of handlers defining the implementation of the Operations available for a given item.
|
|
805
|
+
* - `CredentialAccountHandlers`: A handler returning the CredentialAccount linked to the caller's credentials.
|
|
806
|
+
* - `ParseWebhookHandlers`: A handler parsing the content of an incoming webhook.
|
|
807
|
+
* - `WebhookSubscriptionHandlers`: A handler subscribing or unsubscribing to a particular webhook.
|
|
808
|
+
* - `AcknowledgeWebhookHandlers`: A handler acknowledging the reception of a webhook.
|
|
809
|
+
*
|
|
810
|
+
* To accomodate the fact that ItemHandlers may specify multiple operations, some at the collection level, some at the
|
|
811
|
+
* item level, we need a way to define the route for each of these operations.
|
|
812
|
+
* To achieve this, we assume that if the last part of the path is a variable, then it is the item identifier.
|
|
813
|
+
*
|
|
814
|
+
* @example The following path: `/trainer/:trainerId/pokemons/:pokemonId` will lead to the following
|
|
815
|
+
* routes:
|
|
816
|
+
* - getCollection will be called for `GET /trainer/:trainerId/pokemons/` requests
|
|
817
|
+
* - getItem will be called for `GET /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
818
|
+
* - createItem will be called for `POST /trainer/:trainerId/pokemons/` requests
|
|
819
|
+
* - updateItem will be called for `PATCH /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
820
|
+
* - deleteItem will be called for `DELETE /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
821
|
+
*
|
|
822
|
+
* @param path The path to be used as Route for the handlers.
|
|
823
|
+
* @param handlers The Handlers definition.
|
|
824
|
+
*/
|
|
656
825
|
addHandler(path, handlers) {
|
|
657
826
|
if (this.instance) {
|
|
658
827
|
printErrorMessage(`
|
|
@@ -685,6 +854,13 @@ class Integration {
|
|
|
685
854
|
process.exit(1);
|
|
686
855
|
}
|
|
687
856
|
}
|
|
857
|
+
/**
|
|
858
|
+
* Starts the server and listens on the specified port (default to 9200).
|
|
859
|
+
*
|
|
860
|
+
* @remarks
|
|
861
|
+
* This function should be called after all the handlers have been added to the integration
|
|
862
|
+
* and any other configuration is completed.
|
|
863
|
+
*/
|
|
688
864
|
start() {
|
|
689
865
|
// Express Server initialization
|
|
690
866
|
const app = express();
|
|
@@ -693,11 +869,12 @@ class Integration {
|
|
|
693
869
|
app.use(express.json());
|
|
694
870
|
// Must be one of the first handlers (to catch all the errors).
|
|
695
871
|
app.use(middleware$1);
|
|
872
|
+
app.use(middleware$8);
|
|
696
873
|
app.use(middleware$7);
|
|
697
874
|
app.use(middleware$6);
|
|
698
|
-
app.use(middleware$5);
|
|
699
875
|
app.use(middleware$4);
|
|
700
876
|
app.use(middleware$3);
|
|
877
|
+
app.use(middleware$5);
|
|
701
878
|
// Load handlers as needed.
|
|
702
879
|
if (this.handlers.length) {
|
|
703
880
|
for (const handler of this.handlers) {
|
|
@@ -721,11 +898,21 @@ class Integration {
|
|
|
721
898
|
}
|
|
722
899
|
}
|
|
723
900
|
|
|
901
|
+
/**
|
|
902
|
+
* The Provider class is a wrapper around the fetch function to call a provider's HTTP API.
|
|
903
|
+
*
|
|
904
|
+
* Defines methods for the following HTTP methods: GET, POST, PUT, PATCH, DELETE.
|
|
905
|
+
*
|
|
906
|
+
* Needs to be initialized with a prepareRequest function to define the Provider's base URL and any specific headers to
|
|
907
|
+
* add to the requests, and can also be configured to use a provided rate limiting function.
|
|
908
|
+
* @see {@link RateLimiter}
|
|
909
|
+
* @see {@link prepareRequest}
|
|
910
|
+
*/
|
|
724
911
|
class Provider {
|
|
725
912
|
rateLimiter = undefined;
|
|
726
913
|
prepareRequest;
|
|
727
914
|
/**
|
|
728
|
-
*
|
|
915
|
+
* Initializes a Provider with the given options.
|
|
729
916
|
*
|
|
730
917
|
* @property prepareRequest - function to define the Provider's base URL and specific headers to add to the request.
|
|
731
918
|
* @property rateLimiter - function to limit the rate of calls to the provider based on the caller's credentials.
|
|
@@ -734,52 +921,108 @@ class Provider {
|
|
|
734
921
|
this.prepareRequest = options.prepareRequest;
|
|
735
922
|
this.rateLimiter = options.rateLimiter;
|
|
736
923
|
}
|
|
924
|
+
/**
|
|
925
|
+
* Performs a GET request to the provider.
|
|
926
|
+
*
|
|
927
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
928
|
+
* adds the following headers:
|
|
929
|
+
* - Accept: application/json
|
|
930
|
+
*
|
|
931
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
932
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
933
|
+
* @returns The {@link Response} extracted from the provider.
|
|
934
|
+
*/
|
|
737
935
|
async get(endpoint, options) {
|
|
738
936
|
return this.fetchWrapper(endpoint, null, {
|
|
739
937
|
...options,
|
|
740
938
|
method: 'GET',
|
|
741
939
|
defaultHeaders: {
|
|
742
|
-
'Content-Type': 'application/json',
|
|
743
940
|
Accept: 'application/json',
|
|
744
941
|
},
|
|
745
942
|
});
|
|
746
943
|
}
|
|
944
|
+
/**
|
|
945
|
+
* Performs a POST request to the provider.
|
|
946
|
+
*
|
|
947
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
948
|
+
* adds the following headers:
|
|
949
|
+
* - Content-Type: application/json',
|
|
950
|
+
* - Accept: application/json
|
|
951
|
+
*
|
|
952
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
953
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
954
|
+
* @returns The {@link Response} extracted from the provider.
|
|
955
|
+
*/
|
|
747
956
|
async post(endpoint, body, options) {
|
|
748
957
|
return this.fetchWrapper(endpoint, body, {
|
|
749
958
|
...options,
|
|
750
959
|
method: 'POST',
|
|
751
960
|
defaultHeaders: {
|
|
752
|
-
'Content-Type': 'application/
|
|
961
|
+
'Content-Type': 'application/json',
|
|
753
962
|
Accept: 'application/json',
|
|
754
963
|
},
|
|
755
964
|
});
|
|
756
965
|
}
|
|
966
|
+
/**
|
|
967
|
+
* Performs a PUT request to the provider.
|
|
968
|
+
*
|
|
969
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
970
|
+
* adds the following headers:
|
|
971
|
+
* - Content-Type: application/json',
|
|
972
|
+
* - Accept: application/json
|
|
973
|
+
*
|
|
974
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
975
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
976
|
+
* @returns The {@link Response} extracted from the provider.
|
|
977
|
+
*/
|
|
757
978
|
async put(endpoint, body, options) {
|
|
758
979
|
return this.fetchWrapper(endpoint, body, {
|
|
759
980
|
...options,
|
|
760
981
|
method: 'PUT',
|
|
761
982
|
defaultHeaders: {
|
|
762
|
-
'Content-Type': 'application/
|
|
983
|
+
'Content-Type': 'application/json',
|
|
763
984
|
Accept: 'application/json',
|
|
764
985
|
},
|
|
765
986
|
});
|
|
766
987
|
}
|
|
988
|
+
/**
|
|
989
|
+
* Performs a PATCH request to the provider.
|
|
990
|
+
*
|
|
991
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
992
|
+
* adds the following headers:
|
|
993
|
+
* - Content-Type: application/json',
|
|
994
|
+
* - Accept: application/json
|
|
995
|
+
*
|
|
996
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
997
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
998
|
+
* @returns The {@link Response} extracted from the provider.
|
|
999
|
+
*/
|
|
767
1000
|
async patch(endpoint, body, options) {
|
|
768
1001
|
return this.fetchWrapper(endpoint, body, {
|
|
769
1002
|
...options,
|
|
770
1003
|
method: 'PATCH',
|
|
771
1004
|
defaultHeaders: {
|
|
772
|
-
'Content-Type': 'application/
|
|
1005
|
+
'Content-Type': 'application/json',
|
|
773
1006
|
Accept: 'application/json',
|
|
774
1007
|
},
|
|
775
1008
|
});
|
|
776
1009
|
}
|
|
1010
|
+
/**
|
|
1011
|
+
* Performs a DELETE request to the provider.
|
|
1012
|
+
*
|
|
1013
|
+
* Uses the prepareRequest function to get the base URL and any specific headers to add to the request and by default
|
|
1014
|
+
* adds the following headers:
|
|
1015
|
+
* - Accept: application/json
|
|
1016
|
+
*
|
|
1017
|
+
* @param endpoint Path to the provider's resource. Will be added to the URL returned by the prepareRequest function.
|
|
1018
|
+
* @param options RequestOptions used to adjust the call made to the provider (use to override default headers).
|
|
1019
|
+
* @returns The {@link Response} extracted from the provider.
|
|
1020
|
+
*/
|
|
777
1021
|
async delete(endpoint, options) {
|
|
778
1022
|
return this.fetchWrapper(endpoint, null, {
|
|
779
1023
|
...options,
|
|
780
1024
|
method: 'DELETE',
|
|
781
1025
|
defaultHeaders: {
|
|
782
|
-
'Content-Type': 'application/x-www-form-urlencoded',
|
|
783
1026
|
Accept: 'application/json',
|
|
784
1027
|
},
|
|
785
1028
|
});
|
|
@@ -801,11 +1044,26 @@ class Provider {
|
|
|
801
1044
|
}
|
|
802
1045
|
}
|
|
803
1046
|
const callToProvider = async () => {
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
1047
|
+
let response;
|
|
1048
|
+
try {
|
|
1049
|
+
response = await fetch(absoluteUrl, {
|
|
1050
|
+
method: options.method,
|
|
1051
|
+
signal: options.signal,
|
|
1052
|
+
headers,
|
|
1053
|
+
body: stringifiedBody,
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
1056
|
+
catch (error) {
|
|
1057
|
+
if (error instanceof Error) {
|
|
1058
|
+
switch (error.name) {
|
|
1059
|
+
case 'AbortError':
|
|
1060
|
+
throw buildHttpError(408, 'Request aborted');
|
|
1061
|
+
case 'TimeoutError':
|
|
1062
|
+
throw buildHttpError(408, 'Request timeout');
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
throw buildHttpError(500, `Unexpected error while calling the provider: "${error}"`);
|
|
1066
|
+
}
|
|
809
1067
|
if (response.status >= 400) {
|
|
810
1068
|
const textResult = await response.text();
|
|
811
1069
|
throw buildHttpError(response.status, textResult);
|
package/dist/src/index.d.ts
CHANGED
|
@@ -7,3 +7,4 @@ export type { Secrets } from './middlewares/secrets.js';
|
|
|
7
7
|
export type { Credentials } from './middlewares/credentials.js';
|
|
8
8
|
export * as HttpErrors from './httpErrors.js';
|
|
9
9
|
export * from './resources/context.js';
|
|
10
|
+
export { type default as Logger } from './resources/logger.js';
|
|
@@ -2,12 +2,61 @@ import { HandlersInput } from './handler.js';
|
|
|
2
2
|
type Options = {
|
|
3
3
|
port?: number;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* Main class for the Integration SDK providing an abstraction layer between the Integration's Graph definition
|
|
7
|
+
* and the underlying HTTP server.
|
|
8
|
+
*
|
|
9
|
+
* An `Integration` instance can have multiple handlers configured to handle different routes. Upon receiving a request,
|
|
10
|
+
* the Integration will parse the request to extract meaninful information, match the request to the appropriate handler
|
|
11
|
+
* method and forward that information in the form a {@link Context} object.
|
|
12
|
+
* The Integration also offer standardized error handling and logging to help you build a robust
|
|
13
|
+
* and reliable Integration.
|
|
14
|
+
*
|
|
15
|
+
* See our {@link https://dev.unito.io/docs/ | documentation} for more examples on how to build an integration.
|
|
16
|
+
*/
|
|
5
17
|
export default class Integration {
|
|
6
18
|
private handlers;
|
|
7
19
|
private instance;
|
|
8
20
|
private port;
|
|
21
|
+
/**
|
|
22
|
+
* Creates a new Integration instance with default port set to 9200.
|
|
23
|
+
*
|
|
24
|
+
* @param options The {@link Options} to configure the Integration instance. Can be used to override the default port.
|
|
25
|
+
*/
|
|
9
26
|
constructor(options?: Options);
|
|
27
|
+
/**
|
|
28
|
+
* Adds a group of common handlers to the integration.
|
|
29
|
+
*
|
|
30
|
+
* Handlers added to the integration can be one of the following:
|
|
31
|
+
* - `ItemHandlers`: A group of handlers defining the implementation of the Operations available for a given item.
|
|
32
|
+
* - `CredentialAccountHandlers`: A handler returning the CredentialAccount linked to the caller's credentials.
|
|
33
|
+
* - `ParseWebhookHandlers`: A handler parsing the content of an incoming webhook.
|
|
34
|
+
* - `WebhookSubscriptionHandlers`: A handler subscribing or unsubscribing to a particular webhook.
|
|
35
|
+
* - `AcknowledgeWebhookHandlers`: A handler acknowledging the reception of a webhook.
|
|
36
|
+
*
|
|
37
|
+
* To accomodate the fact that ItemHandlers may specify multiple operations, some at the collection level, some at the
|
|
38
|
+
* item level, we need a way to define the route for each of these operations.
|
|
39
|
+
* To achieve this, we assume that if the last part of the path is a variable, then it is the item identifier.
|
|
40
|
+
*
|
|
41
|
+
* @example The following path: `/trainer/:trainerId/pokemons/:pokemonId` will lead to the following
|
|
42
|
+
* routes:
|
|
43
|
+
* - getCollection will be called for `GET /trainer/:trainerId/pokemons/` requests
|
|
44
|
+
* - getItem will be called for `GET /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
45
|
+
* - createItem will be called for `POST /trainer/:trainerId/pokemons/` requests
|
|
46
|
+
* - updateItem will be called for `PATCH /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
47
|
+
* - deleteItem will be called for `DELETE /trainer/:trainerId/pokemons/:pokemonId` requests
|
|
48
|
+
*
|
|
49
|
+
* @param path The path to be used as Route for the handlers.
|
|
50
|
+
* @param handlers The Handlers definition.
|
|
51
|
+
*/
|
|
10
52
|
addHandler(path: string, handlers: HandlersInput): void;
|
|
53
|
+
/**
|
|
54
|
+
* Starts the server and listens on the specified port (default to 9200).
|
|
55
|
+
*
|
|
56
|
+
* @remarks
|
|
57
|
+
* This function should be called after all the handlers have been added to the integration
|
|
58
|
+
* and any other configuration is completed.
|
|
59
|
+
*/
|
|
11
60
|
start(): void;
|
|
12
61
|
}
|
|
13
62
|
export {};
|