@unito/integration-sdk 0.1.8 → 0.1.10
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 +8 -9
- package/dist/src/handler.js +9 -0
- package/dist/src/index.cjs +10 -1
- package/dist/src/integration.js +1 -1
- package/dist/src/middlewares/secrets.d.ts +13 -0
- package/dist/src/middlewares/secrets.js +17 -0
- package/dist/src/resources/context.d.ts +9 -2
- package/dist/test/middlewares/secrets.test.d.ts +1 -0
- package/dist/test/middlewares/secrets.test.js +28 -0
- package/package.json +1 -1
- package/src/handler.ts +18 -9
- package/src/integration.ts +1 -1
- package/src/middlewares/secrets.ts +35 -0
- package/src/resources/context.ts +9 -2
- package/test/middlewares/secrets.test.ts +39 -0
package/dist/src/handler.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from 'express';
|
|
2
2
|
import * as API from '@unito/integration-api';
|
|
3
|
-
import { GetItemContext, GetCollectionContext, CreateItemContext, UpdateItemContext, DeleteItemContext, GetCredentialAccountContext, ParseWebhooksContext, UpdateWebhookSubscriptionsContext,
|
|
3
|
+
import { GetItemContext, GetCollectionContext, CreateItemContext, UpdateItemContext, DeleteItemContext, GetCredentialAccountContext, ParseWebhooksContext, UpdateWebhookSubscriptionsContext, AcknowledgeWebhooksContext } from './resources/context.js';
|
|
4
4
|
/**
|
|
5
5
|
* Handler called to get an individual item.
|
|
6
6
|
*/
|
|
@@ -36,25 +36,25 @@ export type UpdateWebhookSubscriptionsHandler = (context: UpdateWebhookSubscript
|
|
|
36
36
|
/**
|
|
37
37
|
* Handler called to acknowledge the reception of a webhook.
|
|
38
38
|
*/
|
|
39
|
-
export type
|
|
40
|
-
type ItemHandlers = {
|
|
39
|
+
export type AcknowledgeWebhooksHandler = (context: AcknowledgeWebhooksContext<any, any, any>) => Promise<API.WebhookAcknowledgeResponsePayload>;
|
|
40
|
+
export type ItemHandlers = {
|
|
41
41
|
getItem?: GetItemHandler;
|
|
42
42
|
getCollection?: GetCollectionHandler;
|
|
43
43
|
createItem?: CreateItemHandler;
|
|
44
44
|
updateItem?: UpdateItemHandler;
|
|
45
45
|
deleteItem?: DeleteItemHandler;
|
|
46
46
|
};
|
|
47
|
-
type CredentialAccountHandlers = {
|
|
47
|
+
export type CredentialAccountHandlers = {
|
|
48
48
|
getCredentialAccount: GetCredentialAccountHandler;
|
|
49
49
|
};
|
|
50
|
-
type ParseWebhookHandlers = {
|
|
50
|
+
export type ParseWebhookHandlers = {
|
|
51
51
|
parseWebhooks: ParseWebhooksHandler;
|
|
52
52
|
};
|
|
53
|
-
type WebhookSubscriptionHandlers = {
|
|
53
|
+
export type WebhookSubscriptionHandlers = {
|
|
54
54
|
updateWebhookSubscriptions: UpdateWebhookSubscriptionsHandler;
|
|
55
55
|
};
|
|
56
|
-
type AcknowledgeWebhookHandlers = {
|
|
57
|
-
acknowledgeWebhooks:
|
|
56
|
+
export type AcknowledgeWebhookHandlers = {
|
|
57
|
+
acknowledgeWebhooks: AcknowledgeWebhooksHandler;
|
|
58
58
|
};
|
|
59
59
|
export type HandlersInput = ItemHandlers | CredentialAccountHandlers | ParseWebhookHandlers | WebhookSubscriptionHandlers | AcknowledgeWebhookHandlers;
|
|
60
60
|
export declare class Handler {
|
|
@@ -64,4 +64,3 @@ export declare class Handler {
|
|
|
64
64
|
constructor(inputPath: string, handlers: HandlersInput);
|
|
65
65
|
generate(): Router;
|
|
66
66
|
}
|
|
67
|
-
export {};
|
package/dist/src/handler.js
CHANGED
|
@@ -90,6 +90,7 @@ export class Handler {
|
|
|
90
90
|
}
|
|
91
91
|
const collection = await handler({
|
|
92
92
|
credentials: res.locals.credentials,
|
|
93
|
+
secrets: res.locals.secrets,
|
|
93
94
|
selects: res.locals.selects,
|
|
94
95
|
filters: res.locals.filters,
|
|
95
96
|
logger: res.locals.logger,
|
|
@@ -109,6 +110,7 @@ export class Handler {
|
|
|
109
110
|
assertCreateItemRequestPayload(req.body);
|
|
110
111
|
const createItemSummary = await handler({
|
|
111
112
|
credentials: res.locals.credentials,
|
|
113
|
+
secrets: res.locals.secrets,
|
|
112
114
|
body: req.body,
|
|
113
115
|
logger: res.locals.logger,
|
|
114
116
|
params: req.params,
|
|
@@ -126,6 +128,7 @@ export class Handler {
|
|
|
126
128
|
}
|
|
127
129
|
const item = await handler({
|
|
128
130
|
credentials: res.locals.credentials,
|
|
131
|
+
secrets: res.locals.secrets,
|
|
129
132
|
logger: res.locals.logger,
|
|
130
133
|
params: req.params,
|
|
131
134
|
query: req.query,
|
|
@@ -143,6 +146,7 @@ export class Handler {
|
|
|
143
146
|
assertUpdateItemRequestPayload(req.body);
|
|
144
147
|
const item = await handler({
|
|
145
148
|
credentials: res.locals.credentials,
|
|
149
|
+
secrets: res.locals.secrets,
|
|
146
150
|
body: req.body,
|
|
147
151
|
logger: res.locals.logger,
|
|
148
152
|
params: req.params,
|
|
@@ -160,6 +164,7 @@ export class Handler {
|
|
|
160
164
|
}
|
|
161
165
|
await handler({
|
|
162
166
|
credentials: res.locals.credentials,
|
|
167
|
+
secrets: res.locals.secrets,
|
|
163
168
|
logger: res.locals.logger,
|
|
164
169
|
params: req.params,
|
|
165
170
|
query: req.query,
|
|
@@ -176,6 +181,7 @@ export class Handler {
|
|
|
176
181
|
}
|
|
177
182
|
const credentialAccount = await handler({
|
|
178
183
|
credentials: res.locals.credentials,
|
|
184
|
+
secrets: res.locals.secrets,
|
|
179
185
|
logger: res.locals.logger,
|
|
180
186
|
params: req.params,
|
|
181
187
|
query: req.query,
|
|
@@ -189,6 +195,7 @@ export class Handler {
|
|
|
189
195
|
router.post(this.pathWithIdentifier, async (req, res) => {
|
|
190
196
|
assertWebhookParseRequestPayload(req.body);
|
|
191
197
|
const response = await handler({
|
|
198
|
+
secrets: res.locals.secrets,
|
|
192
199
|
logger: res.locals.logger,
|
|
193
200
|
params: req.params,
|
|
194
201
|
query: req.query,
|
|
@@ -203,6 +210,7 @@ export class Handler {
|
|
|
203
210
|
router.post(this.pathWithIdentifier, async (req, res) => {
|
|
204
211
|
assertWebhookParseRequestPayload(req.body);
|
|
205
212
|
const response = await handler({
|
|
213
|
+
secrets: res.locals.secrets,
|
|
206
214
|
logger: res.locals.logger,
|
|
207
215
|
params: req.params,
|
|
208
216
|
query: req.query,
|
|
@@ -220,6 +228,7 @@ export class Handler {
|
|
|
220
228
|
}
|
|
221
229
|
assertWebhookSubscriptionRequestPayload(req.body);
|
|
222
230
|
const response = await handler({
|
|
231
|
+
secrets: res.locals.secrets,
|
|
223
232
|
credentials: res.locals.credentials,
|
|
224
233
|
body: req.body,
|
|
225
234
|
logger: res.locals.logger,
|
package/dist/src/index.cjs
CHANGED
|
@@ -480,6 +480,7 @@ class Handler {
|
|
|
480
480
|
}
|
|
481
481
|
const collection = await handler({
|
|
482
482
|
credentials: res.locals.credentials,
|
|
483
|
+
secrets: res.locals.secrets,
|
|
483
484
|
selects: res.locals.selects,
|
|
484
485
|
filters: res.locals.filters,
|
|
485
486
|
logger: res.locals.logger,
|
|
@@ -499,6 +500,7 @@ class Handler {
|
|
|
499
500
|
assertCreateItemRequestPayload(req.body);
|
|
500
501
|
const createItemSummary = await handler({
|
|
501
502
|
credentials: res.locals.credentials,
|
|
503
|
+
secrets: res.locals.secrets,
|
|
502
504
|
body: req.body,
|
|
503
505
|
logger: res.locals.logger,
|
|
504
506
|
params: req.params,
|
|
@@ -516,6 +518,7 @@ class Handler {
|
|
|
516
518
|
}
|
|
517
519
|
const item = await handler({
|
|
518
520
|
credentials: res.locals.credentials,
|
|
521
|
+
secrets: res.locals.secrets,
|
|
519
522
|
logger: res.locals.logger,
|
|
520
523
|
params: req.params,
|
|
521
524
|
query: req.query,
|
|
@@ -533,6 +536,7 @@ class Handler {
|
|
|
533
536
|
assertUpdateItemRequestPayload(req.body);
|
|
534
537
|
const item = await handler({
|
|
535
538
|
credentials: res.locals.credentials,
|
|
539
|
+
secrets: res.locals.secrets,
|
|
536
540
|
body: req.body,
|
|
537
541
|
logger: res.locals.logger,
|
|
538
542
|
params: req.params,
|
|
@@ -550,6 +554,7 @@ class Handler {
|
|
|
550
554
|
}
|
|
551
555
|
await handler({
|
|
552
556
|
credentials: res.locals.credentials,
|
|
557
|
+
secrets: res.locals.secrets,
|
|
553
558
|
logger: res.locals.logger,
|
|
554
559
|
params: req.params,
|
|
555
560
|
query: req.query,
|
|
@@ -566,6 +571,7 @@ class Handler {
|
|
|
566
571
|
}
|
|
567
572
|
const credentialAccount = await handler({
|
|
568
573
|
credentials: res.locals.credentials,
|
|
574
|
+
secrets: res.locals.secrets,
|
|
569
575
|
logger: res.locals.logger,
|
|
570
576
|
params: req.params,
|
|
571
577
|
query: req.query,
|
|
@@ -579,6 +585,7 @@ class Handler {
|
|
|
579
585
|
router.post(this.pathWithIdentifier, async (req, res) => {
|
|
580
586
|
assertWebhookParseRequestPayload(req.body);
|
|
581
587
|
const response = await handler({
|
|
588
|
+
secrets: res.locals.secrets,
|
|
582
589
|
logger: res.locals.logger,
|
|
583
590
|
params: req.params,
|
|
584
591
|
query: req.query,
|
|
@@ -593,6 +600,7 @@ class Handler {
|
|
|
593
600
|
router.post(this.pathWithIdentifier, async (req, res) => {
|
|
594
601
|
assertWebhookParseRequestPayload(req.body);
|
|
595
602
|
const response = await handler({
|
|
603
|
+
secrets: res.locals.secrets,
|
|
596
604
|
logger: res.locals.logger,
|
|
597
605
|
params: req.params,
|
|
598
606
|
query: req.query,
|
|
@@ -610,6 +618,7 @@ class Handler {
|
|
|
610
618
|
}
|
|
611
619
|
assertWebhookSubscriptionRequestPayload(req.body);
|
|
612
620
|
const response = await handler({
|
|
621
|
+
secrets: res.locals.secrets,
|
|
613
622
|
credentials: res.locals.credentials,
|
|
614
623
|
body: req.body,
|
|
615
624
|
logger: res.locals.logger,
|
|
@@ -626,7 +635,7 @@ class Handler {
|
|
|
626
635
|
|
|
627
636
|
function printErrorMessage(message) {
|
|
628
637
|
console.error();
|
|
629
|
-
console.error(`\x1b[31m
|
|
638
|
+
console.error(`\x1b[31m Oops! Something went wrong! \x1b[0m`);
|
|
630
639
|
console.error(message);
|
|
631
640
|
}
|
|
632
641
|
class Integration {
|
package/dist/src/integration.js
CHANGED
|
@@ -11,7 +11,7 @@ import { shutdownCaches } from './resources/cache.js';
|
|
|
11
11
|
import { Handler } from './handler.js';
|
|
12
12
|
function printErrorMessage(message) {
|
|
13
13
|
console.error();
|
|
14
|
-
console.error(`\x1b[31m
|
|
14
|
+
console.error(`\x1b[31m Oops! Something went wrong! \x1b[0m`);
|
|
15
15
|
console.error(message);
|
|
16
16
|
}
|
|
17
17
|
export default class Integration {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
declare global {
|
|
3
|
+
namespace Express {
|
|
4
|
+
interface Locals {
|
|
5
|
+
secrets: Secrets;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
export type Secrets = {
|
|
10
|
+
[keys: string]: unknown;
|
|
11
|
+
};
|
|
12
|
+
declare const middleware: (req: Request, res: Response, next: NextFunction) => void;
|
|
13
|
+
export default middleware;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { BadRequestError } from '../httpErrors.js';
|
|
2
|
+
const SECRETS_HEADER = 'X-Unito-Secrets';
|
|
3
|
+
const middleware = (req, res, next) => {
|
|
4
|
+
const secretsHeader = req.header(SECRETS_HEADER);
|
|
5
|
+
if (secretsHeader) {
|
|
6
|
+
let secrets;
|
|
7
|
+
try {
|
|
8
|
+
secrets = JSON.parse(Buffer.from(secretsHeader, 'base64').toString('utf8'));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
throw new BadRequestError(`Malformed HTTP header ${SECRETS_HEADER}`);
|
|
12
|
+
}
|
|
13
|
+
res.locals.secrets = secrets;
|
|
14
|
+
}
|
|
15
|
+
next();
|
|
16
|
+
};
|
|
17
|
+
export default middleware;
|
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import * as API from '@unito/integration-api';
|
|
2
2
|
import Logger from './logger.js';
|
|
3
3
|
import { Credentials } from '../middlewares/credentials.js';
|
|
4
|
+
import { Secrets } from 'src/middlewares/secrets.js';
|
|
4
5
|
import { Filter } from '../middlewares/filters.js';
|
|
5
|
-
type Context<P extends Record<string, string>, Q extends ParsedQueryString> = {
|
|
6
|
+
export type Context<P extends Record<string, string>, Q extends ParsedQueryString> = {
|
|
6
7
|
/**
|
|
7
8
|
* The parsed credentials associated with the request through the X-Unito-Credentials header.
|
|
8
9
|
*
|
|
9
10
|
* Will contain the keys for the variables defined in the corresponding configuration's authorization.
|
|
10
11
|
*/
|
|
11
12
|
credentials: Credentials;
|
|
13
|
+
/**
|
|
14
|
+
* The parsed secrets associated with the request through the X-Unito-Secrets header.
|
|
15
|
+
*
|
|
16
|
+
* Will contain the keys for the secrets defined in the corresponding configuration's secrets.
|
|
17
|
+
*/
|
|
18
|
+
secrets: Secrets;
|
|
12
19
|
/**
|
|
13
20
|
* The logger pre decorated with the correlation ID and the additionnal metadata provided through the request headers.
|
|
14
21
|
*/
|
|
@@ -69,7 +76,7 @@ export type ParseWebhooksContext<P extends Record<string, string> = Record<strin
|
|
|
69
76
|
export type UpdateWebhookSubscriptionsContext<P extends Record<string, string> = Record<string, never>, Q extends Record<string, string> = Record<string, never>, B extends API.WebhookSubscriptionRequestPayload = API.WebhookSubscriptionRequestPayload> = Context<P, Q> & {
|
|
70
77
|
body: B;
|
|
71
78
|
};
|
|
72
|
-
export type
|
|
79
|
+
export type AcknowledgeWebhooksContext<P extends Record<string, string> = Record<string, never>, Q extends Record<string, string> = Record<string, never>, B extends API.WebhookParseRequestPayload = API.WebhookParseRequestPayload> = Omit<Context<P, Q>, 'credentials'> & {
|
|
73
80
|
body: B;
|
|
74
81
|
};
|
|
75
82
|
interface ParsedQueryString {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { describe, it } from 'node:test';
|
|
3
|
+
import middleware from '../../src/middlewares/secrets.js';
|
|
4
|
+
import { BadRequestError } from '../../src/httpErrors.js';
|
|
5
|
+
describe('secrets middleware', () => {
|
|
6
|
+
it('uses header', () => {
|
|
7
|
+
const secrets = Buffer.from(JSON.stringify({
|
|
8
|
+
chut: 'abc',
|
|
9
|
+
})).toString('base64');
|
|
10
|
+
const request = { header: (_key) => secrets };
|
|
11
|
+
const response = { locals: {} };
|
|
12
|
+
middleware(request, response, () => { });
|
|
13
|
+
assert.deepEqual(response.locals, {
|
|
14
|
+
secrets: {
|
|
15
|
+
chut: 'abc',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
it('malformed header', async () => {
|
|
20
|
+
const request = { header: (_key) => 'nope' };
|
|
21
|
+
const response = { locals: {} };
|
|
22
|
+
assert.throws(() => middleware(request, response, () => { }), BadRequestError);
|
|
23
|
+
});
|
|
24
|
+
it('undefined', () => {
|
|
25
|
+
const response = { locals: {} };
|
|
26
|
+
assert.deepEqual(response.locals, {});
|
|
27
|
+
});
|
|
28
|
+
});
|
package/package.json
CHANGED
package/src/handler.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
GetCredentialAccountContext,
|
|
12
12
|
ParseWebhooksContext,
|
|
13
13
|
UpdateWebhookSubscriptionsContext,
|
|
14
|
-
|
|
14
|
+
AcknowledgeWebhooksContext,
|
|
15
15
|
} from './resources/context.js';
|
|
16
16
|
|
|
17
17
|
/**
|
|
@@ -63,11 +63,11 @@ export type UpdateWebhookSubscriptionsHandler = (
|
|
|
63
63
|
/**
|
|
64
64
|
* Handler called to acknowledge the reception of a webhook.
|
|
65
65
|
*/
|
|
66
|
-
export type
|
|
67
|
-
context:
|
|
66
|
+
export type AcknowledgeWebhooksHandler = (
|
|
67
|
+
context: AcknowledgeWebhooksContext<any, any, any>,
|
|
68
68
|
) => Promise<API.WebhookAcknowledgeResponsePayload>;
|
|
69
69
|
|
|
70
|
-
type ItemHandlers = {
|
|
70
|
+
export type ItemHandlers = {
|
|
71
71
|
getItem?: GetItemHandler;
|
|
72
72
|
getCollection?: GetCollectionHandler;
|
|
73
73
|
createItem?: CreateItemHandler;
|
|
@@ -75,20 +75,20 @@ type ItemHandlers = {
|
|
|
75
75
|
deleteItem?: DeleteItemHandler;
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
type CredentialAccountHandlers = {
|
|
78
|
+
export type CredentialAccountHandlers = {
|
|
79
79
|
getCredentialAccount: GetCredentialAccountHandler;
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
type ParseWebhookHandlers = {
|
|
82
|
+
export type ParseWebhookHandlers = {
|
|
83
83
|
parseWebhooks: ParseWebhooksHandler;
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
type WebhookSubscriptionHandlers = {
|
|
86
|
+
export type WebhookSubscriptionHandlers = {
|
|
87
87
|
updateWebhookSubscriptions: UpdateWebhookSubscriptionsHandler;
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
type AcknowledgeWebhookHandlers = {
|
|
91
|
-
acknowledgeWebhooks:
|
|
90
|
+
export type AcknowledgeWebhookHandlers = {
|
|
91
|
+
acknowledgeWebhooks: AcknowledgeWebhooksHandler;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
94
|
export type HandlersInput =
|
|
@@ -230,6 +230,7 @@ export class Handler {
|
|
|
230
230
|
|
|
231
231
|
const collection = await handler({
|
|
232
232
|
credentials: res.locals.credentials,
|
|
233
|
+
secrets: res.locals.secrets,
|
|
233
234
|
selects: res.locals.selects,
|
|
234
235
|
filters: res.locals.filters,
|
|
235
236
|
logger: res.locals.logger,
|
|
@@ -255,6 +256,7 @@ export class Handler {
|
|
|
255
256
|
|
|
256
257
|
const createItemSummary = await handler({
|
|
257
258
|
credentials: res.locals.credentials,
|
|
259
|
+
secrets: res.locals.secrets,
|
|
258
260
|
body: req.body,
|
|
259
261
|
logger: res.locals.logger,
|
|
260
262
|
params: req.params,
|
|
@@ -277,6 +279,7 @@ export class Handler {
|
|
|
277
279
|
|
|
278
280
|
const item = await handler({
|
|
279
281
|
credentials: res.locals.credentials,
|
|
282
|
+
secrets: res.locals.secrets,
|
|
280
283
|
logger: res.locals.logger,
|
|
281
284
|
params: req.params,
|
|
282
285
|
query: req.query,
|
|
@@ -300,6 +303,7 @@ export class Handler {
|
|
|
300
303
|
|
|
301
304
|
const item = await handler({
|
|
302
305
|
credentials: res.locals.credentials,
|
|
306
|
+
secrets: res.locals.secrets,
|
|
303
307
|
body: req.body,
|
|
304
308
|
logger: res.locals.logger,
|
|
305
309
|
params: req.params,
|
|
@@ -322,6 +326,7 @@ export class Handler {
|
|
|
322
326
|
|
|
323
327
|
await handler({
|
|
324
328
|
credentials: res.locals.credentials,
|
|
329
|
+
secrets: res.locals.secrets,
|
|
325
330
|
logger: res.locals.logger,
|
|
326
331
|
params: req.params,
|
|
327
332
|
query: req.query,
|
|
@@ -343,6 +348,7 @@ export class Handler {
|
|
|
343
348
|
|
|
344
349
|
const credentialAccount = await handler({
|
|
345
350
|
credentials: res.locals.credentials,
|
|
351
|
+
secrets: res.locals.secrets,
|
|
346
352
|
logger: res.locals.logger,
|
|
347
353
|
params: req.params,
|
|
348
354
|
query: req.query,
|
|
@@ -361,6 +367,7 @@ export class Handler {
|
|
|
361
367
|
assertWebhookParseRequestPayload(req.body);
|
|
362
368
|
|
|
363
369
|
const response = await handler({
|
|
370
|
+
secrets: res.locals.secrets,
|
|
364
371
|
logger: res.locals.logger,
|
|
365
372
|
params: req.params,
|
|
366
373
|
query: req.query,
|
|
@@ -380,6 +387,7 @@ export class Handler {
|
|
|
380
387
|
assertWebhookParseRequestPayload(req.body);
|
|
381
388
|
|
|
382
389
|
const response = await handler({
|
|
390
|
+
secrets: res.locals.secrets,
|
|
383
391
|
logger: res.locals.logger,
|
|
384
392
|
params: req.params,
|
|
385
393
|
query: req.query,
|
|
@@ -403,6 +411,7 @@ export class Handler {
|
|
|
403
411
|
assertWebhookSubscriptionRequestPayload(req.body);
|
|
404
412
|
|
|
405
413
|
const response = await handler({
|
|
414
|
+
secrets: res.locals.secrets,
|
|
406
415
|
credentials: res.locals.credentials,
|
|
407
416
|
body: req.body,
|
|
408
417
|
logger: res.locals.logger,
|
package/src/integration.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { HandlersInput, Handler } from './handler.js';
|
|
|
14
14
|
|
|
15
15
|
function printErrorMessage(message: string) {
|
|
16
16
|
console.error();
|
|
17
|
-
console.error(`\x1b[31m
|
|
17
|
+
console.error(`\x1b[31m Oops! Something went wrong! \x1b[0m`);
|
|
18
18
|
console.error(message);
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Request, Response, NextFunction } from 'express';
|
|
2
|
+
import { BadRequestError } from '../httpErrors.js';
|
|
3
|
+
|
|
4
|
+
declare global {
|
|
5
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
6
|
+
namespace Express {
|
|
7
|
+
interface Locals {
|
|
8
|
+
secrets: Secrets;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type Secrets = { [keys: string]: unknown };
|
|
14
|
+
|
|
15
|
+
const SECRETS_HEADER = 'X-Unito-Secrets';
|
|
16
|
+
|
|
17
|
+
const middleware = (req: Request, res: Response, next: NextFunction) => {
|
|
18
|
+
const secretsHeader = req.header(SECRETS_HEADER);
|
|
19
|
+
|
|
20
|
+
if (secretsHeader) {
|
|
21
|
+
let secrets: Secrets;
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
secrets = JSON.parse(Buffer.from(secretsHeader, 'base64').toString('utf8'));
|
|
25
|
+
} catch {
|
|
26
|
+
throw new BadRequestError(`Malformed HTTP header ${SECRETS_HEADER}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
res.locals.secrets = secrets;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
next();
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default middleware;
|
package/src/resources/context.ts
CHANGED
|
@@ -3,15 +3,22 @@ import * as API from '@unito/integration-api';
|
|
|
3
3
|
|
|
4
4
|
import Logger from './logger.js';
|
|
5
5
|
import { Credentials } from '../middlewares/credentials.js';
|
|
6
|
+
import { Secrets } from 'src/middlewares/secrets.js';
|
|
6
7
|
import { Filter } from '../middlewares/filters.js';
|
|
7
8
|
|
|
8
|
-
type Context<P extends Record<string, string>, Q extends ParsedQueryString> = {
|
|
9
|
+
export type Context<P extends Record<string, string>, Q extends ParsedQueryString> = {
|
|
9
10
|
/**
|
|
10
11
|
* The parsed credentials associated with the request through the X-Unito-Credentials header.
|
|
11
12
|
*
|
|
12
13
|
* Will contain the keys for the variables defined in the corresponding configuration's authorization.
|
|
13
14
|
*/
|
|
14
15
|
credentials: Credentials;
|
|
16
|
+
/**
|
|
17
|
+
* The parsed secrets associated with the request through the X-Unito-Secrets header.
|
|
18
|
+
*
|
|
19
|
+
* Will contain the keys for the secrets defined in the corresponding configuration's secrets.
|
|
20
|
+
*/
|
|
21
|
+
secrets: Secrets;
|
|
15
22
|
/**
|
|
16
23
|
* The logger pre decorated with the correlation ID and the additionnal metadata provided through the request headers.
|
|
17
24
|
*/
|
|
@@ -101,7 +108,7 @@ export type UpdateWebhookSubscriptionsContext<
|
|
|
101
108
|
B extends API.WebhookSubscriptionRequestPayload = API.WebhookSubscriptionRequestPayload,
|
|
102
109
|
> = Context<P, Q> & { body: B };
|
|
103
110
|
|
|
104
|
-
export type
|
|
111
|
+
export type AcknowledgeWebhooksContext<
|
|
105
112
|
P extends Record<string, string> = Record<string, never>,
|
|
106
113
|
Q extends Record<string, string> = Record<string, never>,
|
|
107
114
|
B extends API.WebhookParseRequestPayload = API.WebhookParseRequestPayload,
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { describe, it } from 'node:test';
|
|
4
|
+
import middleware from '../../src/middlewares/secrets.js';
|
|
5
|
+
import { BadRequestError } from '../../src/httpErrors.js';
|
|
6
|
+
|
|
7
|
+
describe('secrets middleware', () => {
|
|
8
|
+
it('uses header', () => {
|
|
9
|
+
const secrets = Buffer.from(
|
|
10
|
+
JSON.stringify({
|
|
11
|
+
chut: 'abc',
|
|
12
|
+
}),
|
|
13
|
+
).toString('base64');
|
|
14
|
+
|
|
15
|
+
const request = { header: (_key: string) => secrets } as express.Request;
|
|
16
|
+
const response = { locals: {} } as express.Response;
|
|
17
|
+
|
|
18
|
+
middleware(request, response, () => {});
|
|
19
|
+
|
|
20
|
+
assert.deepEqual(response.locals, {
|
|
21
|
+
secrets: {
|
|
22
|
+
chut: 'abc',
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('malformed header', async () => {
|
|
28
|
+
const request = { header: (_key: string) => 'nope' } as express.Request;
|
|
29
|
+
const response = { locals: {} } as express.Response;
|
|
30
|
+
|
|
31
|
+
assert.throws(() => middleware(request, response, () => {}), BadRequestError);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('undefined', () => {
|
|
35
|
+
const response = { locals: {} } as express.Response;
|
|
36
|
+
|
|
37
|
+
assert.deepEqual(response.locals, {});
|
|
38
|
+
});
|
|
39
|
+
});
|