@slates/provider-handler 1.0.0-rc.42 → 1.0.0-rc.44
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/index.cjs +247 -83
- package/dist/index.module.js +251 -84
- package/package.json +3 -3
- package/src/index.ts +275 -78
- package/src/runtimeInfo.test.ts +129 -0
- package/src/spec.test.ts +91 -1
- package/src/spec.ts +47 -9
- package/src/validation.ts +2 -1
- package/src/webhookDeliveryAuthenticate.test.ts +179 -0
- package/src/webhookSetup.test.ts +179 -0
package/src/index.ts
CHANGED
|
@@ -9,7 +9,12 @@ import {
|
|
|
9
9
|
SLATES_PROTOCOL_VERSION,
|
|
10
10
|
type SlatesMessageAuthAdminCallbackHandleRequest,
|
|
11
11
|
type SlatesMessageAuthAuthorizationUrlGetRequest,
|
|
12
|
-
type
|
|
12
|
+
type SlatesMessageTriggerGroupWebhookProcessRequest,
|
|
13
|
+
type SlatesParticipant,
|
|
14
|
+
type SlatesRuntimeInfo,
|
|
15
|
+
slatesTriggerGroupWebhookDeliveryAuthentication,
|
|
16
|
+
slatesTriggerGroupWebhookExpansionResult,
|
|
17
|
+
slatesTriggerGroupWebhookRegistrationResult
|
|
13
18
|
} from '@slates/proto';
|
|
14
19
|
import {
|
|
15
20
|
AuthConfigSecretRedactor,
|
|
@@ -22,6 +27,8 @@ import {
|
|
|
22
27
|
SlateLogger,
|
|
23
28
|
type SlateLogListener,
|
|
24
29
|
SlatePublicContext,
|
|
30
|
+
type SlateRuntimeInfo,
|
|
31
|
+
type SlateTriggerGroup,
|
|
25
32
|
uploadAttachmentDirect
|
|
26
33
|
} from '@slates/provider';
|
|
27
34
|
import { PQueue } from './pQueue';
|
|
@@ -36,6 +43,7 @@ import {
|
|
|
36
43
|
getExposedTriggerGroups,
|
|
37
44
|
getTriggerGroup,
|
|
38
45
|
getWebhookAutoRegistration,
|
|
46
|
+
getWebhookDeliveryConfig,
|
|
39
47
|
getWebhookManualRegistration,
|
|
40
48
|
isMappableTrigger,
|
|
41
49
|
mapAction,
|
|
@@ -50,6 +58,39 @@ import { serializeWebhookHttpResponse } from './webhook';
|
|
|
50
58
|
|
|
51
59
|
let DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 100_000_000;
|
|
52
60
|
|
|
61
|
+
let validateTriggerGroupSelection = (
|
|
62
|
+
group: SlateTriggerGroup<any, any>,
|
|
63
|
+
selection: Record<string, any> | undefined
|
|
64
|
+
) =>
|
|
65
|
+
group.selectionSchema
|
|
66
|
+
? validate(
|
|
67
|
+
group.selectionSchema,
|
|
68
|
+
selection === undefined ? {} : selection,
|
|
69
|
+
'trigger_group.selection',
|
|
70
|
+
`Invalid selection for trigger group "${group.key}"`
|
|
71
|
+
)
|
|
72
|
+
: (selection ?? {});
|
|
73
|
+
|
|
74
|
+
let validateWebhookRegistration = (result: unknown) =>
|
|
75
|
+
validate(
|
|
76
|
+
slatesTriggerGroupWebhookRegistrationResult,
|
|
77
|
+
result,
|
|
78
|
+
'trigger_group.webhook.registration',
|
|
79
|
+
'Webhook registration returned an invalid result'
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
let toWebhookRequest = (
|
|
83
|
+
params: Pick<
|
|
84
|
+
SlatesMessageTriggerGroupWebhookProcessRequest['params'],
|
|
85
|
+
'url' | 'method' | 'headers' | 'body'
|
|
86
|
+
>
|
|
87
|
+
) =>
|
|
88
|
+
new Request(params.url, {
|
|
89
|
+
method: params.method,
|
|
90
|
+
headers: params.headers,
|
|
91
|
+
body: params.body ? Uint8Array.from(atob(params.body.content), c => c.charCodeAt(0)) : null
|
|
92
|
+
});
|
|
93
|
+
|
|
53
94
|
let routeAttachmentsThroughDirectUpload = async (
|
|
54
95
|
attachments: SlateAttachment[] | undefined,
|
|
55
96
|
live: SlateLiveInvocationInfo | null
|
|
@@ -189,6 +230,14 @@ let resolveTraceMessage = <ResultType>(
|
|
|
189
230
|
result: ResultType
|
|
190
231
|
) => (typeof message === 'function' ? message(result) : message);
|
|
191
232
|
|
|
233
|
+
// Build fails if SlateRuntimeInfo drifts from the proto schema.
|
|
234
|
+
true satisfies [SlateRuntimeInfo, SlatesRuntimeInfo] extends [
|
|
235
|
+
SlatesRuntimeInfo,
|
|
236
|
+
SlateRuntimeInfo
|
|
237
|
+
]
|
|
238
|
+
? true
|
|
239
|
+
: false;
|
|
240
|
+
|
|
192
241
|
export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
193
242
|
slate: Slate<ConfigType, AuthType>,
|
|
194
243
|
listeners: SlateLogListener[]
|
|
@@ -209,6 +258,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
209
258
|
let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
|
|
210
259
|
null
|
|
211
260
|
);
|
|
261
|
+
let runtimeInfo = new State<SlateRuntimeInfo | null>(null);
|
|
212
262
|
|
|
213
263
|
let getLiveInvocation = (): SlateLiveInvocationInfo | null => {
|
|
214
264
|
let directUpload = hubCapabilities.get()?.attachments?.directUpload;
|
|
@@ -335,9 +385,24 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
335
385
|
|
|
336
386
|
let getAuthConfig = (): Record<string, any> => config.get()?.value ?? {};
|
|
337
387
|
|
|
338
|
-
let
|
|
339
|
-
|
|
340
|
-
|
|
388
|
+
let createContext = <ConfigType extends {}, InputType extends {}, AuthType extends {}>(
|
|
389
|
+
config: ConfigType,
|
|
390
|
+
input: InputType,
|
|
391
|
+
auth: AuthType,
|
|
392
|
+
liveInvocationInfo: SlateLiveInvocationInfo | null = null
|
|
393
|
+
) =>
|
|
394
|
+
new SlateContext(
|
|
395
|
+
config,
|
|
396
|
+
input,
|
|
397
|
+
auth,
|
|
398
|
+
slate.spec as any,
|
|
399
|
+
logger,
|
|
400
|
+
liveInvocationInfo,
|
|
401
|
+
runtimeInfo.get()
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
let getEmptyContext = () => createContext({}, {}, {});
|
|
405
|
+
let getAuthContext = () => createContext(getAuthConfig(), {}, {});
|
|
341
406
|
let withRequestTraces = <Result extends Record<string, any>>(
|
|
342
407
|
context: SlatePublicContext<any>,
|
|
343
408
|
result: Result
|
|
@@ -481,11 +546,16 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
481
546
|
});
|
|
482
547
|
});
|
|
483
548
|
|
|
549
|
+
manager.onNotification('slates/hub.runtime_info.set', async ({ params }) => {
|
|
550
|
+
runtimeInfo.set(params.runtimeInfo);
|
|
551
|
+
});
|
|
552
|
+
|
|
484
553
|
manager.onRequest('slates/provider.capabilities.get', async () => ({
|
|
485
554
|
capabilities: {
|
|
486
555
|
hub: {
|
|
487
556
|
capabilitiesNotification: true,
|
|
488
|
-
liveInvocation: true
|
|
557
|
+
liveInvocation: true,
|
|
558
|
+
runtimeInfo: true
|
|
489
559
|
},
|
|
490
560
|
provider: {
|
|
491
561
|
triggerGroups:
|
|
@@ -1131,20 +1201,19 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1131
1201
|
|
|
1132
1202
|
if (action.isPublic) {
|
|
1133
1203
|
getContextBasic();
|
|
1134
|
-
return invoke(
|
|
1204
|
+
return invoke(
|
|
1205
|
+
new SlatePublicContext(
|
|
1206
|
+
input,
|
|
1207
|
+
slate.spec,
|
|
1208
|
+
logger,
|
|
1209
|
+
getLiveInvocation(),
|
|
1210
|
+
runtimeInfo.get()
|
|
1211
|
+
)
|
|
1212
|
+
);
|
|
1135
1213
|
}
|
|
1136
1214
|
|
|
1137
1215
|
let ctx = getContextFull();
|
|
1138
|
-
return invoke(
|
|
1139
|
-
new SlateContext(
|
|
1140
|
-
ctx.config,
|
|
1141
|
-
input,
|
|
1142
|
-
ctx.auth?.output!,
|
|
1143
|
-
slate.spec,
|
|
1144
|
-
logger,
|
|
1145
|
-
getLiveInvocation()
|
|
1146
|
-
)
|
|
1147
|
-
);
|
|
1216
|
+
return invoke(createContext(ctx.config, input, ctx.auth?.output!, getLiveInvocation()));
|
|
1148
1217
|
});
|
|
1149
1218
|
|
|
1150
1219
|
manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
|
|
@@ -1158,7 +1227,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1158
1227
|
`Invalid event for trigger ID: ${params.actionId}`
|
|
1159
1228
|
);
|
|
1160
1229
|
|
|
1161
|
-
let context =
|
|
1230
|
+
let context = createContext(ctx.config, input, ctx.auth?.output!);
|
|
1162
1231
|
let res = await traceProviderCall(
|
|
1163
1232
|
{
|
|
1164
1233
|
component: 'action',
|
|
@@ -1246,12 +1315,13 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1246
1315
|
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1247
1316
|
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1248
1317
|
|
|
1249
|
-
let context =
|
|
1318
|
+
let context = createContext(
|
|
1250
1319
|
ctx.config,
|
|
1251
|
-
{
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1320
|
+
{
|
|
1321
|
+
pageToken: params.pageToken ?? null,
|
|
1322
|
+
selection: validateTriggerGroupSelection(group, params.selection)
|
|
1323
|
+
},
|
|
1324
|
+
ctx.auth?.output!
|
|
1255
1325
|
);
|
|
1256
1326
|
let res = await traceProviderCall(
|
|
1257
1327
|
{
|
|
@@ -1285,16 +1355,17 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1285
1355
|
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1286
1356
|
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1287
1357
|
|
|
1288
|
-
let context =
|
|
1358
|
+
let context = createContext(
|
|
1289
1359
|
ctx.config,
|
|
1290
1360
|
{
|
|
1291
1361
|
webhookTargetIdentifier: params.webhookTargetIdentifier,
|
|
1292
1362
|
webhookTargetPayload: params.webhookTargetPayload,
|
|
1293
|
-
webhookUrl: params.webhookUrl
|
|
1363
|
+
webhookUrl: params.webhookUrl,
|
|
1364
|
+
operationId: params.operationId,
|
|
1365
|
+
selection: validateTriggerGroupSelection(group, params.selection),
|
|
1366
|
+
deliveryConfig: params.deliveryConfig
|
|
1294
1367
|
},
|
|
1295
|
-
ctx.auth?.output
|
|
1296
|
-
slate.spec,
|
|
1297
|
-
logger
|
|
1368
|
+
ctx.auth?.output!
|
|
1298
1369
|
);
|
|
1299
1370
|
let res = await traceProviderCall(
|
|
1300
1371
|
{
|
|
@@ -1309,13 +1380,58 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1309
1380
|
webhookTargetIdentifier: params.webhookTargetIdentifier
|
|
1310
1381
|
}
|
|
1311
1382
|
},
|
|
1312
|
-
() =>
|
|
1383
|
+
() =>
|
|
1384
|
+
runWithContext(context, async () =>
|
|
1385
|
+
validateWebhookRegistration(await autoRegistration.webhookRegister(context))
|
|
1386
|
+
)
|
|
1313
1387
|
);
|
|
1314
1388
|
|
|
1315
|
-
return withRequestTraces(context,
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1389
|
+
return withRequestTraces(context, res);
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
manager.onRequest('slates/trigger_group.webhook.renew', async ({ params }) => {
|
|
1393
|
+
let ctx = getContextFull();
|
|
1394
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1395
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1396
|
+
let renew = autoRegistration.webhookRenew;
|
|
1397
|
+
if (!renew) {
|
|
1398
|
+
throw new ServiceError(
|
|
1399
|
+
badRequestError({
|
|
1400
|
+
message: `Trigger group does not support webhook renewal: ${params.triggerGroupId}`
|
|
1401
|
+
})
|
|
1402
|
+
);
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
let context = createContext(
|
|
1406
|
+
ctx.config,
|
|
1407
|
+
{
|
|
1408
|
+
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier,
|
|
1409
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload,
|
|
1410
|
+
webhookUrl: params.webhookUrl,
|
|
1411
|
+
webhookTargetIdentifier: params.webhookTargetIdentifier,
|
|
1412
|
+
webhookTargetPayload: params.webhookTargetPayload,
|
|
1413
|
+
operationId: params.operationId,
|
|
1414
|
+
selection: validateTriggerGroupSelection(group, params.selection),
|
|
1415
|
+
deliveryConfig: params.deliveryConfig
|
|
1416
|
+
},
|
|
1417
|
+
ctx.auth?.output!
|
|
1418
|
+
);
|
|
1419
|
+
let res = await traceProviderCall(
|
|
1420
|
+
{
|
|
1421
|
+
component: 'action',
|
|
1422
|
+
functionName: 'webhookRenew',
|
|
1423
|
+
message: `Renewing webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1424
|
+
successMessage: `Renewed webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1425
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while renewing a webhook`,
|
|
1426
|
+
metadata: { triggerGroupId: group.key, triggerGroupName: group.name }
|
|
1427
|
+
},
|
|
1428
|
+
() =>
|
|
1429
|
+
runWithContext(context, async () =>
|
|
1430
|
+
validateWebhookRegistration(await renew(context))
|
|
1431
|
+
)
|
|
1432
|
+
);
|
|
1433
|
+
|
|
1434
|
+
return withRequestTraces(context, res);
|
|
1319
1435
|
});
|
|
1320
1436
|
|
|
1321
1437
|
manager.onRequest('slates/trigger_group.webhook.unregister', async ({ params }) => {
|
|
@@ -1323,15 +1439,13 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1323
1439
|
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1324
1440
|
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1325
1441
|
|
|
1326
|
-
let context =
|
|
1442
|
+
let context = createContext(
|
|
1327
1443
|
ctx.config,
|
|
1328
1444
|
{
|
|
1329
1445
|
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier,
|
|
1330
1446
|
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1331
1447
|
},
|
|
1332
|
-
ctx.auth?.output
|
|
1333
|
-
slate.spec,
|
|
1334
|
-
logger
|
|
1448
|
+
ctx.auth?.output!
|
|
1335
1449
|
);
|
|
1336
1450
|
await traceProviderCall(
|
|
1337
1451
|
{
|
|
@@ -1357,13 +1471,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1357
1471
|
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1358
1472
|
let manualRegistration = getWebhookManualRegistration(group);
|
|
1359
1473
|
|
|
1360
|
-
let context =
|
|
1361
|
-
{},
|
|
1362
|
-
{ webhookUrl: params.webhookUrl },
|
|
1363
|
-
{},
|
|
1364
|
-
slate.spec as any,
|
|
1365
|
-
logger
|
|
1366
|
-
);
|
|
1474
|
+
let context = createContext({}, { webhookUrl: params.webhookUrl }, {});
|
|
1367
1475
|
let res = await traceProviderCall(
|
|
1368
1476
|
{
|
|
1369
1477
|
component: 'action',
|
|
@@ -1398,16 +1506,14 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1398
1506
|
);
|
|
1399
1507
|
}
|
|
1400
1508
|
|
|
1401
|
-
let context =
|
|
1509
|
+
let context = createContext(
|
|
1402
1510
|
{},
|
|
1403
1511
|
{
|
|
1404
1512
|
webhookUrl: params.webhookUrl,
|
|
1405
1513
|
partialWebhookRegistrationPayload: params.partialWebhookRegistrationPayload,
|
|
1406
1514
|
userWebhookRegistrationPayload: params.userWebhookRegistrationPayload
|
|
1407
1515
|
},
|
|
1408
|
-
{}
|
|
1409
|
-
slate.spec as any,
|
|
1410
|
-
logger
|
|
1516
|
+
{}
|
|
1411
1517
|
);
|
|
1412
1518
|
let res = await traceProviderCall(
|
|
1413
1519
|
{
|
|
@@ -1439,23 +1545,13 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1439
1545
|
);
|
|
1440
1546
|
}
|
|
1441
1547
|
|
|
1442
|
-
let
|
|
1443
|
-
method: params.method,
|
|
1444
|
-
headers: params.headers,
|
|
1445
|
-
body: params.body
|
|
1446
|
-
? Uint8Array.from(atob(params.body.content), c => c.charCodeAt(0))
|
|
1447
|
-
: null
|
|
1448
|
-
});
|
|
1449
|
-
|
|
1450
|
-
let context = new SlateContext(
|
|
1548
|
+
let context = createContext(
|
|
1451
1549
|
{},
|
|
1452
1550
|
{
|
|
1453
|
-
request:
|
|
1551
|
+
request: toWebhookRequest(params),
|
|
1454
1552
|
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1455
1553
|
},
|
|
1456
|
-
{}
|
|
1457
|
-
slate.spec as any,
|
|
1458
|
-
logger
|
|
1554
|
+
{}
|
|
1459
1555
|
);
|
|
1460
1556
|
let res = await traceProviderCall(
|
|
1461
1557
|
{
|
|
@@ -1500,18 +1596,126 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1500
1596
|
matchers: event.matchers,
|
|
1501
1597
|
payload: event.payload,
|
|
1502
1598
|
idempotencyKey: event.idempotencyKey,
|
|
1503
|
-
triggerIds:
|
|
1599
|
+
triggerIds: group.webhook?.expandWebhook
|
|
1600
|
+
? []
|
|
1601
|
+
: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1504
1602
|
})),
|
|
1603
|
+
...(group.webhook?.expandWebhook ? { requiresExpansion: true } : {}),
|
|
1505
1604
|
response,
|
|
1506
1605
|
skipped: res.skipped ?? null
|
|
1507
1606
|
});
|
|
1508
1607
|
});
|
|
1509
1608
|
|
|
1609
|
+
manager.onRequest(
|
|
1610
|
+
'slates/trigger_group.webhook.delivery_authenticate',
|
|
1611
|
+
async ({ params }) => {
|
|
1612
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1613
|
+
let deliveryConfig = getWebhookDeliveryConfig(group);
|
|
1614
|
+
|
|
1615
|
+
let context = createContext(
|
|
1616
|
+
{},
|
|
1617
|
+
{
|
|
1618
|
+
request: toWebhookRequest(params),
|
|
1619
|
+
deliveryConfig: params.deliveryConfig
|
|
1620
|
+
},
|
|
1621
|
+
{}
|
|
1622
|
+
);
|
|
1623
|
+
let res = await traceProviderCall(
|
|
1624
|
+
{
|
|
1625
|
+
component: 'action',
|
|
1626
|
+
functionName: 'webhookDeliveryAuthenticate',
|
|
1627
|
+
message: `Authenticating a delivery for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1628
|
+
successMessage: result =>
|
|
1629
|
+
result.rejected
|
|
1630
|
+
? `Rejected a delivery with status ${result.rejected.status} for trigger group ${formatEntityLabel(group.name, group.key)}`
|
|
1631
|
+
: `Authenticated a delivery for ${result.routingKeys.length} routing key(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1632
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while authenticating a delivery`,
|
|
1633
|
+
metadata: {
|
|
1634
|
+
triggerGroupId: group.key,
|
|
1635
|
+
triggerGroupName: group.name,
|
|
1636
|
+
requestMethod: params.method,
|
|
1637
|
+
hasRequestBody: !!params.body
|
|
1638
|
+
},
|
|
1639
|
+
onSuccess: result => ({
|
|
1640
|
+
routingKeyCount: result.routingKeys.length,
|
|
1641
|
+
rejectedStatus: result.rejected?.status ?? null
|
|
1642
|
+
})
|
|
1643
|
+
},
|
|
1644
|
+
() =>
|
|
1645
|
+
runWithContext(context, async () => {
|
|
1646
|
+
let authentication = await deliveryConfig.authenticate(context);
|
|
1647
|
+
return validate(
|
|
1648
|
+
slatesTriggerGroupWebhookDeliveryAuthentication,
|
|
1649
|
+
authentication.rejected
|
|
1650
|
+
? { routingKeys: [], rejected: authentication.rejected }
|
|
1651
|
+
: { routingKeys: authentication.routingKeys, rejected: null },
|
|
1652
|
+
'trigger_group.webhook.delivery_authentication',
|
|
1653
|
+
'Webhook delivery authentication returned an invalid result'
|
|
1654
|
+
);
|
|
1655
|
+
})
|
|
1656
|
+
);
|
|
1657
|
+
|
|
1658
|
+
return withRequestTraces(context, res);
|
|
1659
|
+
}
|
|
1660
|
+
);
|
|
1661
|
+
|
|
1662
|
+
manager.onRequest('slates/trigger_group.webhook.expand', async ({ params }) => {
|
|
1663
|
+
let ctx = getContextFull();
|
|
1664
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1665
|
+
let expand = group.source === 'webhook' ? group.webhook?.expandWebhook : undefined;
|
|
1666
|
+
if (!expand) {
|
|
1667
|
+
throw new ServiceError(
|
|
1668
|
+
badRequestError({
|
|
1669
|
+
message: `Trigger group does not support webhook expansion: ${params.triggerGroupId}`
|
|
1670
|
+
})
|
|
1671
|
+
);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
let context = createContext(
|
|
1675
|
+
ctx.config,
|
|
1676
|
+
{
|
|
1677
|
+
payload: params.payload,
|
|
1678
|
+
state: params.state ?? null,
|
|
1679
|
+
selection: validateTriggerGroupSelection(group, params.selection),
|
|
1680
|
+
deliveryId: params.deliveryId,
|
|
1681
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1682
|
+
},
|
|
1683
|
+
ctx.auth?.output!
|
|
1684
|
+
);
|
|
1685
|
+
let res = await traceProviderCall(
|
|
1686
|
+
{
|
|
1687
|
+
component: 'action',
|
|
1688
|
+
functionName: 'expandWebhook',
|
|
1689
|
+
message: `Expanding webhook delivery for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1690
|
+
successMessage: `Expanded webhook delivery for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1691
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while expanding a webhook delivery`,
|
|
1692
|
+
metadata: { triggerGroupId: group.key, triggerGroupName: group.name }
|
|
1693
|
+
},
|
|
1694
|
+
() =>
|
|
1695
|
+
runWithContext(context, async () =>
|
|
1696
|
+
validate(
|
|
1697
|
+
slatesTriggerGroupWebhookExpansionResult,
|
|
1698
|
+
await expand(context),
|
|
1699
|
+
'trigger_group.webhook.expansion',
|
|
1700
|
+
'Webhook expansion returned an invalid result'
|
|
1701
|
+
)
|
|
1702
|
+
)
|
|
1703
|
+
);
|
|
1704
|
+
|
|
1705
|
+
return withRequestTraces(context, {
|
|
1706
|
+
...(res.updatedState !== undefined ? { updatedState: res.updatedState } : {}),
|
|
1707
|
+
events: res.events.map(event => ({
|
|
1708
|
+
...event,
|
|
1709
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1710
|
+
}))
|
|
1711
|
+
});
|
|
1712
|
+
});
|
|
1713
|
+
|
|
1510
1714
|
manager.onRequest('slates/trigger_group.routing_matchers.get', async ({ params }) => {
|
|
1511
1715
|
let ctx = getContextFull();
|
|
1512
1716
|
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1513
1717
|
|
|
1514
|
-
let context =
|
|
1718
|
+
let context = createContext(ctx.config, {}, ctx.auth?.output!);
|
|
1515
1719
|
let matchers = await traceProviderCall(
|
|
1516
1720
|
{
|
|
1517
1721
|
component: 'action',
|
|
@@ -1545,12 +1749,13 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1545
1749
|
);
|
|
1546
1750
|
}
|
|
1547
1751
|
|
|
1548
|
-
let context =
|
|
1752
|
+
let context = createContext(
|
|
1549
1753
|
ctx.config,
|
|
1550
|
-
{
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1754
|
+
{
|
|
1755
|
+
state: params.state,
|
|
1756
|
+
selection: validateTriggerGroupSelection(group, params.selection)
|
|
1757
|
+
},
|
|
1758
|
+
ctx.auth?.output!
|
|
1554
1759
|
);
|
|
1555
1760
|
let res = await traceProviderCall(
|
|
1556
1761
|
{
|
|
@@ -1599,13 +1804,7 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1599
1804
|
let ctx = getContextFull();
|
|
1600
1805
|
let { group, gateway } = getGatewayTriggerGroup(params.triggerGroupId);
|
|
1601
1806
|
|
|
1602
|
-
let context =
|
|
1603
|
-
ctx.config,
|
|
1604
|
-
{ state: params.state },
|
|
1605
|
-
ctx.auth?.output!,
|
|
1606
|
-
slate.spec,
|
|
1607
|
-
logger
|
|
1608
|
-
);
|
|
1807
|
+
let context = createContext(ctx.config, { state: params.state }, ctx.auth?.output!);
|
|
1609
1808
|
let res = await traceProviderCall(
|
|
1610
1809
|
{
|
|
1611
1810
|
component: 'action',
|
|
@@ -1630,12 +1829,10 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
1630
1829
|
let ctx = getContextFull();
|
|
1631
1830
|
let { group, gateway } = getGatewayTriggerGroup(params.triggerGroupId);
|
|
1632
1831
|
|
|
1633
|
-
let context =
|
|
1832
|
+
let context = createContext(
|
|
1634
1833
|
ctx.config,
|
|
1635
1834
|
{ state: params.state, frames: params.frames, closed: params.closed ?? null },
|
|
1636
|
-
ctx.auth?.output
|
|
1637
|
-
slate.spec,
|
|
1638
|
-
logger
|
|
1835
|
+
ctx.auth?.output!
|
|
1639
1836
|
);
|
|
1640
1837
|
let res = await traceProviderCall(
|
|
1641
1838
|
{
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { SLATES_PROTOCOL_VERSION, SlatesProviderProtoHandlerManager } from '@slates/proto';
|
|
2
|
+
import {
|
|
3
|
+
getRuntimeInfo,
|
|
4
|
+
Slate,
|
|
5
|
+
SlateAuth,
|
|
6
|
+
SlateConfig,
|
|
7
|
+
SlatePublicTool,
|
|
8
|
+
SlateSpecification
|
|
9
|
+
} from '@slates/provider';
|
|
10
|
+
import { describe, expect, it } from 'vitest';
|
|
11
|
+
import { z } from 'zod';
|
|
12
|
+
import { createProviderHandler } from './index';
|
|
13
|
+
|
|
14
|
+
let runtimeInfo = {
|
|
15
|
+
metorial: {
|
|
16
|
+
apiUrl: 'https://api-us1.metorial.test',
|
|
17
|
+
region: 'us1',
|
|
18
|
+
environment: 'staging'
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
let createManager = async () => {
|
|
23
|
+
let spec = SlateSpecification.create({
|
|
24
|
+
key: 'runtime-info-test',
|
|
25
|
+
name: 'Runtime Info Test',
|
|
26
|
+
config: SlateConfig.create(z.object({ apiUrl: z.string().nullable() })).getDefaultConfig(
|
|
27
|
+
() => ({ apiUrl: getRuntimeInfo()?.metorial?.apiUrl ?? null })
|
|
28
|
+
),
|
|
29
|
+
auth: SlateAuth.create().output(z.object({}))
|
|
30
|
+
});
|
|
31
|
+
let tool = SlatePublicTool.create(spec, {
|
|
32
|
+
key: 'read_runtime_info',
|
|
33
|
+
name: 'Read Runtime Info'
|
|
34
|
+
})
|
|
35
|
+
.input(z.object({}))
|
|
36
|
+
.output(z.object({ fromContext: z.any(), fromHelper: z.any() }))
|
|
37
|
+
.handleInvocation(async ctx => ({
|
|
38
|
+
output: { fromContext: ctx.runtimeInfo, fromHelper: getRuntimeInfo() },
|
|
39
|
+
message: 'ok'
|
|
40
|
+
}))
|
|
41
|
+
.build();
|
|
42
|
+
let slate = Slate.create({ spec, triggers: [], tools: [tool] });
|
|
43
|
+
let manager = await createProviderHandler(slate, []).run();
|
|
44
|
+
|
|
45
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
46
|
+
jsonrpc: '2.0',
|
|
47
|
+
method: 'slates/hello',
|
|
48
|
+
params: { protocol: SLATES_PROTOCOL_VERSION }
|
|
49
|
+
});
|
|
50
|
+
await SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
51
|
+
jsonrpc: '2.0',
|
|
52
|
+
method: 'slates/participant.set',
|
|
53
|
+
params: {
|
|
54
|
+
participants: [
|
|
55
|
+
{ type: 'consumer', id: 'consumer', name: 'Consumer' },
|
|
56
|
+
{ type: 'hub', id: 'hub', name: 'Hub' }
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return manager;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
let setRuntimeInfo = (manager: SlatesProviderProtoHandlerManager) =>
|
|
65
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
66
|
+
jsonrpc: '2.0',
|
|
67
|
+
method: 'slates/hub.runtime_info.set',
|
|
68
|
+
params: { runtimeInfo }
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let invokeTool = (manager: SlatesProviderProtoHandlerManager) =>
|
|
72
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
73
|
+
jsonrpc: '2.0',
|
|
74
|
+
id: 'tool',
|
|
75
|
+
method: 'slates/action.tool.invoke',
|
|
76
|
+
params: { actionId: 'read_runtime_info', input: {} }
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
let getDefaultConfig = (manager: SlatesProviderProtoHandlerManager) =>
|
|
80
|
+
SlatesProviderProtoHandlerManager.handleInput(manager, {
|
|
81
|
+
jsonrpc: '2.0',
|
|
82
|
+
id: 'config',
|
|
83
|
+
method: 'slates/config.get_default',
|
|
84
|
+
params: {}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('hub runtime info', () => {
|
|
88
|
+
it('advertises the runtime info capability', async () => {
|
|
89
|
+
let response = await SlatesProviderProtoHandlerManager.handleInput(await createManager(), {
|
|
90
|
+
jsonrpc: '2.0',
|
|
91
|
+
id: 'capabilities',
|
|
92
|
+
method: 'slates/provider.capabilities.get',
|
|
93
|
+
params: {}
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(response).toMatchObject({
|
|
97
|
+
result: { capabilities: { hub: { runtimeInfo: true } } }
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('exposes runtime info to tools through ctx and the context helper', async () => {
|
|
102
|
+
let manager = await createManager();
|
|
103
|
+
await setRuntimeInfo(manager);
|
|
104
|
+
|
|
105
|
+
expect(await invokeTool(manager)).toMatchObject({
|
|
106
|
+
result: { output: { fromContext: runtimeInfo, fromHelper: runtimeInfo } }
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('exposes runtime info to handlers that receive no ctx argument', async () => {
|
|
111
|
+
let manager = await createManager();
|
|
112
|
+
await setRuntimeInfo(manager);
|
|
113
|
+
|
|
114
|
+
expect(await getDefaultConfig(manager)).toMatchObject({
|
|
115
|
+
result: { config: { apiUrl: runtimeInfo.metorial.apiUrl } }
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('returns null when the host sent no runtime info', async () => {
|
|
120
|
+
let manager = await createManager();
|
|
121
|
+
|
|
122
|
+
expect(await invokeTool(manager)).toMatchObject({
|
|
123
|
+
result: { output: { fromContext: null, fromHelper: null } }
|
|
124
|
+
});
|
|
125
|
+
expect(await getDefaultConfig(manager)).toMatchObject({
|
|
126
|
+
result: { config: { apiUrl: null } }
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
});
|