@slates/provider-handler 1.0.0-rc.4 → 1.0.0-rc.41
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 +1702 -2
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.module.js +1685 -2
- package/package.json +12 -10
- package/src/adminSetup.test.ts +189 -0
- package/src/attachments.test.ts +171 -0
- package/src/exposure.test.ts +216 -0
- package/src/index.ts +1301 -134
- package/src/pQueue.test.ts +18 -0
- package/src/pQueue.ts +18 -0
- package/src/spec.test.ts +232 -0
- package/src/spec.ts +219 -19
- package/src/validation.ts +1 -1
- package/src/webhook.test.ts +65 -0
- package/src/webhook.ts +69 -0
- package/src/webhookProcess.test.ts +99 -0
- package/dist/action/action.d.ts +0 -90
- package/dist/action/action.d.ts.map +0 -1
- package/dist/action/builder.d.ts +0 -27
- package/dist/action/builder.d.ts.map +0 -1
- package/dist/action/index.d.ts +0 -5
- package/dist/action/index.d.ts.map +0 -1
- package/dist/action/tool.d.ts +0 -11
- package/dist/action/tool.d.ts.map +0 -1
- package/dist/action/trigger.d.ts +0 -14
- package/dist/action/trigger.d.ts.map +0 -1
- package/dist/auth/auth.d.ts +0 -26
- package/dist/auth/auth.d.ts.map +0 -1
- package/dist/auth/index.d.ts +0 -3
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/types.d.ts +0 -148
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/axios/index.d.ts +0 -4
- package/dist/axios/index.d.ts.map +0 -1
- package/dist/config/config.d.ts +0 -22
- package/dist/config/config.d.ts.map +0 -1
- package/dist/config/index.d.ts +0 -2
- package/dist/config/index.d.ts.map +0 -1
- package/dist/context/context.d.ts +0 -20
- package/dist/context/context.d.ts.map +0 -1
- package/dist/context/hook.d.ts +0 -4
- package/dist/context/hook.d.ts.map +0 -1
- package/dist/context/index.d.ts +0 -2
- package/dist/context/index.d.ts.map +0 -1
- package/dist/error/base.d.ts +0 -5
- package/dist/error/base.d.ts.map +0 -1
- package/dist/error/declaration.d.ts +0 -6
- package/dist/error/declaration.d.ts.map +0 -1
- package/dist/error/index.d.ts +0 -3
- package/dist/error/index.d.ts.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.modern.js +0 -2
- package/dist/index.modern.js.map +0 -1
- package/dist/index.module.js.map +0 -1
- package/dist/index.umd.js +0 -2
- package/dist/index.umd.js.map +0 -1
- package/dist/spec.d.ts +0 -14
- package/dist/spec.d.ts.map +0 -1
- package/dist/specification/index.d.ts +0 -3
- package/dist/specification/index.d.ts.map +0 -1
- package/dist/specification/slate.d.ts +0 -15
- package/dist/specification/slate.d.ts.map +0 -1
- package/dist/specification/specification.d.ts +0 -30
- package/dist/specification/specification.d.ts.map +0 -1
- package/dist/specification/zero.d.ts +0 -13
- package/dist/specification/zero.d.ts.map +0 -1
- package/dist/state.d.ts +0 -8
- package/dist/state.d.ts.map +0 -1
- package/dist/tokens.d.ts +0 -34
- package/dist/tokens.d.ts.map +0 -1
- package/dist/validation.d.ts +0 -5
- package/dist/validation.d.ts.map +0 -1
package/src/index.ts
CHANGED
|
@@ -1,9 +1,193 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import {
|
|
2
|
+
badRequestError,
|
|
3
|
+
notFoundError,
|
|
4
|
+
preconditionFailedError,
|
|
5
|
+
ServiceError
|
|
6
|
+
} from '@lowerdeck/error';
|
|
7
|
+
import {
|
|
8
|
+
createSlatesProviderProtoHandler,
|
|
9
|
+
SLATES_PROTOCOL_VERSION,
|
|
10
|
+
type SlatesMessageAuthAdminCallbackHandleRequest,
|
|
11
|
+
type SlatesMessageAuthAuthorizationUrlGetRequest,
|
|
12
|
+
type SlatesParticipant
|
|
13
|
+
} from '@slates/proto';
|
|
14
|
+
import {
|
|
15
|
+
AuthConfigSecretRedactor,
|
|
16
|
+
redactUrlAttachmentSecrets,
|
|
17
|
+
runWithContext,
|
|
18
|
+
type Slate,
|
|
19
|
+
type SlateAttachment,
|
|
20
|
+
SlateContext,
|
|
21
|
+
type SlateLiveInvocationInfo,
|
|
22
|
+
SlateLogger,
|
|
23
|
+
type SlateLogListener,
|
|
24
|
+
SlatePublicContext,
|
|
25
|
+
uploadAttachmentDirect
|
|
26
|
+
} from '@slates/provider';
|
|
27
|
+
import { PQueue } from './pQueue';
|
|
28
|
+
import {
|
|
29
|
+
evaluateTriggerMatches,
|
|
30
|
+
getActionWithType,
|
|
31
|
+
getAdapter,
|
|
32
|
+
getAuthMethod,
|
|
33
|
+
getExposedAction,
|
|
34
|
+
getExposedActions,
|
|
35
|
+
getExposedToolCategories,
|
|
36
|
+
getExposedTriggerGroups,
|
|
37
|
+
getTriggerGroup,
|
|
38
|
+
getWebhookAutoRegistration,
|
|
39
|
+
getWebhookManualRegistration,
|
|
40
|
+
isMappableTrigger,
|
|
41
|
+
mapAction,
|
|
42
|
+
mapAdapter,
|
|
43
|
+
mapAuthMethod,
|
|
44
|
+
mapToolCategory,
|
|
45
|
+
mapTriggerGroup
|
|
46
|
+
} from './spec';
|
|
5
47
|
import { State } from './state';
|
|
6
48
|
import { toJsonSchema, validate } from './validation';
|
|
49
|
+
import { serializeWebhookHttpResponse } from './webhook';
|
|
50
|
+
|
|
51
|
+
let DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 100_000_000;
|
|
52
|
+
|
|
53
|
+
let routeAttachmentsThroughDirectUpload = async (
|
|
54
|
+
attachments: SlateAttachment[] | undefined,
|
|
55
|
+
live: SlateLiveInvocationInfo | null
|
|
56
|
+
): Promise<SlateAttachment[] | undefined> => {
|
|
57
|
+
if (!attachments || attachments.length === 0 || !live) return attachments;
|
|
58
|
+
|
|
59
|
+
let contentEntries = attachments
|
|
60
|
+
.map((attachment, index) => ({ attachment, index }))
|
|
61
|
+
.filter(entry => entry.attachment.content.type === 'content');
|
|
62
|
+
if (contentEntries.length === 0) return attachments;
|
|
63
|
+
|
|
64
|
+
let queue = new PQueue({ concurrency: 10 });
|
|
65
|
+
let replacements = new Map<number, SlateAttachment | null>();
|
|
66
|
+
|
|
67
|
+
await Promise.all(
|
|
68
|
+
contentEntries.map(entry =>
|
|
69
|
+
queue.add(async () => {
|
|
70
|
+
try {
|
|
71
|
+
let body =
|
|
72
|
+
entry.attachment.content.type === 'content'
|
|
73
|
+
? entry.attachment.content.encoding === 'base64'
|
|
74
|
+
? Buffer.from(entry.attachment.content.content, 'base64')
|
|
75
|
+
: Buffer.from(entry.attachment.content.content, 'utf-8')
|
|
76
|
+
: Buffer.alloc(0);
|
|
77
|
+
|
|
78
|
+
replacements.set(
|
|
79
|
+
entry.index,
|
|
80
|
+
await uploadAttachmentDirect({
|
|
81
|
+
live,
|
|
82
|
+
mimeType: entry.attachment.mimeType,
|
|
83
|
+
body
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
} catch {
|
|
87
|
+
// Upload failed or was interrupted -- we simply don't have this attachment, not a
|
|
88
|
+
// failed tool call.
|
|
89
|
+
replacements.set(entry.index, null);
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
)
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
return attachments
|
|
96
|
+
.map((attachment, index) =>
|
|
97
|
+
replacements.has(index) ? replacements.get(index) : attachment
|
|
98
|
+
)
|
|
99
|
+
.filter((a): a is SlateAttachment => a != null);
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
103
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
104
|
+
|
|
105
|
+
let getObjectKeyCount = (value: unknown) =>
|
|
106
|
+
isRecord(value) ? Object.keys(value).length : undefined;
|
|
107
|
+
|
|
108
|
+
let DOWNLOAD_ATTACHMENT_URL_KEYS = new Set([
|
|
109
|
+
'downloadUrl',
|
|
110
|
+
'fileUrl',
|
|
111
|
+
'temporaryDownloadUrl',
|
|
112
|
+
'webContentLink'
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
let isAttachmentUrl = (value: string) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
|
|
116
|
+
|
|
117
|
+
let collectOutputUrlAttachments = (
|
|
118
|
+
value: unknown,
|
|
119
|
+
seen = new Set<string>()
|
|
120
|
+
): SlateAttachment[] => {
|
|
121
|
+
if (Array.isArray(value)) {
|
|
122
|
+
return value.flatMap(item => collectOutputUrlAttachments(item, seen));
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (!isRecord(value)) {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
let attachments: SlateAttachment[] = [];
|
|
130
|
+
|
|
131
|
+
for (let [key, nestedValue] of Object.entries(value)) {
|
|
132
|
+
if (
|
|
133
|
+
DOWNLOAD_ATTACHMENT_URL_KEYS.has(key) &&
|
|
134
|
+
typeof nestedValue === 'string' &&
|
|
135
|
+
nestedValue.length > 0 &&
|
|
136
|
+
isAttachmentUrl(nestedValue) &&
|
|
137
|
+
!seen.has(nestedValue)
|
|
138
|
+
) {
|
|
139
|
+
seen.add(nestedValue);
|
|
140
|
+
attachments.push({
|
|
141
|
+
content: {
|
|
142
|
+
type: 'url',
|
|
143
|
+
url: nestedValue
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
attachments.push(...collectOutputUrlAttachments(nestedValue, seen));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return attachments;
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
let mergeAttachments = (
|
|
156
|
+
explicitAttachments: SlateAttachment[] | undefined,
|
|
157
|
+
output: unknown
|
|
158
|
+
): SlateAttachment[] | undefined => {
|
|
159
|
+
let attachments = [...(explicitAttachments ?? [])];
|
|
160
|
+
let seen = new Set(attachments.map(attachment => JSON.stringify(attachment)));
|
|
161
|
+
|
|
162
|
+
for (let attachment of collectOutputUrlAttachments(output)) {
|
|
163
|
+
let key = JSON.stringify(attachment);
|
|
164
|
+
if (seen.has(key)) continue;
|
|
165
|
+
seen.add(key);
|
|
166
|
+
attachments.push(attachment);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return attachments.length > 0 ? attachments : undefined;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
let toErrorMetadata = (error: unknown) => {
|
|
173
|
+
if (error instanceof Error) {
|
|
174
|
+
return {
|
|
175
|
+
errorName: error.name,
|
|
176
|
+
errorMessage: error.message,
|
|
177
|
+
errorStack: error.stack
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return {
|
|
182
|
+
errorValue: String(error)
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
let formatEntityLabel = (name: string, key: string) => `"${name}" (${key})`;
|
|
187
|
+
let resolveTraceMessage = <ResultType>(
|
|
188
|
+
message: string | ((result: ResultType) => string),
|
|
189
|
+
result: ResultType
|
|
190
|
+
) => (typeof message === 'function' ? message(result) : message);
|
|
7
191
|
|
|
8
192
|
export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
9
193
|
slate: Slate<ConfigType, AuthType>,
|
|
@@ -15,10 +199,94 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
15
199
|
|
|
16
200
|
let auth = new State<{ authenticationMethodId: string; output: AuthType } | null>(null);
|
|
17
201
|
let config = new State<{ value: ConfigType } | null>(null);
|
|
18
|
-
|
|
19
202
|
let session = new State<{ id: string; state: any } | null>(null);
|
|
20
203
|
|
|
204
|
+
let hubCapabilities = new State<{
|
|
205
|
+
attachments?: { directUpload?: { enabled: boolean; maxAttachmentSizeBytes?: number } };
|
|
206
|
+
adapters?: boolean;
|
|
207
|
+
triggers?: boolean;
|
|
208
|
+
} | null>(null);
|
|
209
|
+
let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
|
|
210
|
+
null
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
let getLiveInvocation = (): SlateLiveInvocationInfo | null => {
|
|
214
|
+
let directUpload = hubCapabilities.get()?.attachments?.directUpload;
|
|
215
|
+
let live = liveInvocation.get();
|
|
216
|
+
|
|
217
|
+
if (!directUpload?.enabled || !live) return null;
|
|
218
|
+
|
|
219
|
+
return {
|
|
220
|
+
...live,
|
|
221
|
+
maxAttachmentSizeBytes:
|
|
222
|
+
directUpload.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES
|
|
223
|
+
};
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
|
|
227
|
+
let supportsAdapters = () => !!hubCapabilities.get()?.adapters;
|
|
228
|
+
|
|
21
229
|
let logger = new SlateLogger(listeners);
|
|
230
|
+
let providerTrace = {
|
|
231
|
+
providerId: slate.spec.key,
|
|
232
|
+
providerName: slate.spec.name
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
let traceProviderCall = async <ResultType>(
|
|
236
|
+
trace: {
|
|
237
|
+
component: 'config' | 'auth' | 'action';
|
|
238
|
+
functionName: string;
|
|
239
|
+
message: string;
|
|
240
|
+
successMessage: string | ((result: ResultType) => string);
|
|
241
|
+
errorMessage?: string;
|
|
242
|
+
metadata?: Record<string, unknown>;
|
|
243
|
+
onSuccess?: (result: ResultType) => Record<string, unknown> | undefined;
|
|
244
|
+
},
|
|
245
|
+
handler: () => Promise<ResultType>
|
|
246
|
+
): Promise<ResultType> => {
|
|
247
|
+
let startedAt = Date.now();
|
|
248
|
+
|
|
249
|
+
logger.info({
|
|
250
|
+
...providerTrace,
|
|
251
|
+
...trace.metadata,
|
|
252
|
+
component: trace.component,
|
|
253
|
+
functionName: trace.functionName,
|
|
254
|
+
phase: 'start',
|
|
255
|
+
message: trace.message
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
try {
|
|
259
|
+
let result = await handler();
|
|
260
|
+
let successMessage = resolveTraceMessage(trace.successMessage, result);
|
|
261
|
+
|
|
262
|
+
logger.info({
|
|
263
|
+
...providerTrace,
|
|
264
|
+
...trace.metadata,
|
|
265
|
+
...(trace.onSuccess?.(result) ?? {}),
|
|
266
|
+
component: trace.component,
|
|
267
|
+
functionName: trace.functionName,
|
|
268
|
+
phase: 'success',
|
|
269
|
+
durationMs: Date.now() - startedAt,
|
|
270
|
+
message: successMessage
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return result;
|
|
274
|
+
} catch (error) {
|
|
275
|
+
logger.error({
|
|
276
|
+
...providerTrace,
|
|
277
|
+
...trace.metadata,
|
|
278
|
+
...toErrorMetadata(error),
|
|
279
|
+
component: trace.component,
|
|
280
|
+
functionName: trace.functionName,
|
|
281
|
+
phase: 'error',
|
|
282
|
+
durationMs: Date.now() - startedAt,
|
|
283
|
+
message:
|
|
284
|
+
trace.errorMessage ??
|
|
285
|
+
`${typeof trace.successMessage === 'string' ? trace.successMessage : trace.message} failed`
|
|
286
|
+
});
|
|
287
|
+
throw error;
|
|
288
|
+
}
|
|
289
|
+
};
|
|
22
290
|
|
|
23
291
|
let getContextBasic = () => {
|
|
24
292
|
let currentProtocol = protocol.get();
|
|
@@ -65,6 +333,90 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
65
333
|
};
|
|
66
334
|
};
|
|
67
335
|
|
|
336
|
+
let getAuthConfig = (): Record<string, any> => config.get()?.value ?? {};
|
|
337
|
+
|
|
338
|
+
let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec as any, logger);
|
|
339
|
+
let getAuthContext = () =>
|
|
340
|
+
new SlateContext(getAuthConfig(), {}, {}, slate.spec as any, logger);
|
|
341
|
+
let withRequestTraces = <Result extends Record<string, any>>(
|
|
342
|
+
context: SlatePublicContext<any>,
|
|
343
|
+
result: Result
|
|
344
|
+
) => {
|
|
345
|
+
let requestTraces = context.getHttpTraces();
|
|
346
|
+
return requestTraces.length > 0 ? { ...result, requestTraces } : result;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
let buildAuthorizationUrlContext = (
|
|
350
|
+
params: SlatesMessageAuthAuthorizationUrlGetRequest['params']
|
|
351
|
+
) => ({
|
|
352
|
+
redirectUri: params.redirectUri,
|
|
353
|
+
state: params.state,
|
|
354
|
+
input: params.input,
|
|
355
|
+
clientId: params.clientId,
|
|
356
|
+
clientSecret: params.clientSecret,
|
|
357
|
+
scopes: params.scopes,
|
|
358
|
+
config: getAuthConfig()
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
let buildCallbackContext = (
|
|
362
|
+
params: Omit<SlatesMessageAuthAdminCallbackHandleRequest['params'], 'code'>
|
|
363
|
+
) => ({
|
|
364
|
+
state: params.state,
|
|
365
|
+
redirectUri: params.redirectUri,
|
|
366
|
+
input: params.input,
|
|
367
|
+
clientId: params.clientId,
|
|
368
|
+
clientSecret: params.clientSecret,
|
|
369
|
+
scopes: params.scopes,
|
|
370
|
+
callbackParams: params.callbackParams || {},
|
|
371
|
+
callbackState: params.callbackState || {},
|
|
372
|
+
config: getAuthConfig()
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
let getTracedAuthorizationUrl = async (
|
|
376
|
+
params: SlatesMessageAuthAuthorizationUrlGetRequest['params'],
|
|
377
|
+
authenticationMethodName: string,
|
|
378
|
+
trace: {
|
|
379
|
+
functionName: string;
|
|
380
|
+
message: string;
|
|
381
|
+
successMessage: string;
|
|
382
|
+
metadata?: Record<string, unknown>;
|
|
383
|
+
},
|
|
384
|
+
getAuthorizationUrl: (
|
|
385
|
+
ctx: ReturnType<typeof buildAuthorizationUrlContext>
|
|
386
|
+
) => Promise<{ url: string; input?: any; callbackState?: Record<string, any> }>
|
|
387
|
+
) => {
|
|
388
|
+
let context = getEmptyContext();
|
|
389
|
+
let urlRes = await traceProviderCall(
|
|
390
|
+
{
|
|
391
|
+
component: 'auth',
|
|
392
|
+
functionName: trace.functionName,
|
|
393
|
+
message: trace.message,
|
|
394
|
+
successMessage: trace.successMessage,
|
|
395
|
+
metadata: {
|
|
396
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
397
|
+
authenticationMethodName,
|
|
398
|
+
scopeCount: params.scopes.length,
|
|
399
|
+
inputKeyCount: getObjectKeyCount(params.input),
|
|
400
|
+
...trace.metadata
|
|
401
|
+
},
|
|
402
|
+
onSuccess: result => ({
|
|
403
|
+
returnedInput: !!result.input,
|
|
404
|
+
hasCallbackState: !!result.callbackState
|
|
405
|
+
})
|
|
406
|
+
},
|
|
407
|
+
() =>
|
|
408
|
+
runWithContext(context, () =>
|
|
409
|
+
getAuthorizationUrl(buildAuthorizationUrlContext(params))
|
|
410
|
+
)
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
return withRequestTraces(context, {
|
|
414
|
+
authorizationUrl: urlRes.url,
|
|
415
|
+
input: urlRes.input,
|
|
416
|
+
callbackState: urlRes.callbackState
|
|
417
|
+
});
|
|
418
|
+
};
|
|
419
|
+
|
|
68
420
|
manager.onNotification('slates/hello', async ({ params }) => {
|
|
69
421
|
protocol.set(params.protocol);
|
|
70
422
|
});
|
|
@@ -118,6 +470,32 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
118
470
|
});
|
|
119
471
|
});
|
|
120
472
|
|
|
473
|
+
manager.onNotification('slates/hub.capabilities.set', async ({ params }) => {
|
|
474
|
+
hubCapabilities.set(params.capabilities);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
manager.onNotification('slates/hub.live_invocation.set', async ({ params }) => {
|
|
478
|
+
liveInvocation.set({
|
|
479
|
+
token: params.token,
|
|
480
|
+
baseUrl: params.baseUrl
|
|
481
|
+
});
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
manager.onRequest('slates/provider.capabilities.get', async () => ({
|
|
485
|
+
capabilities: {
|
|
486
|
+
hub: {
|
|
487
|
+
capabilitiesNotification: true,
|
|
488
|
+
liveInvocation: true
|
|
489
|
+
},
|
|
490
|
+
provider: {
|
|
491
|
+
triggerGroups:
|
|
492
|
+
(slate.triggerGroups?.length ?? 0) > 0 &&
|
|
493
|
+
(slate.actions ?? []).every(isMappableTrigger),
|
|
494
|
+
toolCategories: (slate.toolCategories?.length ?? 0) > 0
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}));
|
|
498
|
+
|
|
121
499
|
manager.onRequest('slates/config.changed', async ({ params }) => {
|
|
122
500
|
getContextBasic();
|
|
123
501
|
|
|
@@ -133,15 +511,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
133
511
|
return { success: true, config: newConfig };
|
|
134
512
|
}
|
|
135
513
|
|
|
136
|
-
let
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
514
|
+
let context = getEmptyContext();
|
|
515
|
+
let updatedConfig = await traceProviderCall<{ config?: ConfigType } | undefined>(
|
|
516
|
+
{
|
|
517
|
+
component: 'config',
|
|
518
|
+
functionName: 'configChanged',
|
|
519
|
+
message: 'Running config change handler',
|
|
520
|
+
successMessage: 'Config change handler completed',
|
|
521
|
+
metadata: {
|
|
522
|
+
hasPreviousConfig: params.previousConfig !== null,
|
|
523
|
+
newConfigKeyCount: getObjectKeyCount(newConfig)
|
|
524
|
+
},
|
|
525
|
+
onSuccess: result => ({
|
|
526
|
+
returnedConfig: !!result?.config
|
|
527
|
+
})
|
|
528
|
+
},
|
|
529
|
+
() =>
|
|
530
|
+
runWithContext(context, async () =>
|
|
531
|
+
configChanged({
|
|
532
|
+
previousConfig: params.previousConfig as ConfigType | null,
|
|
533
|
+
newConfig
|
|
534
|
+
})
|
|
535
|
+
)
|
|
536
|
+
);
|
|
140
537
|
|
|
141
|
-
return
|
|
538
|
+
return withRequestTraces(context, {
|
|
539
|
+
success: true,
|
|
540
|
+
config: (updatedConfig?.config ?? newConfig) as Record<string, any>
|
|
541
|
+
});
|
|
142
542
|
});
|
|
143
543
|
|
|
144
|
-
manager.onRequest('slates/config.get_default', async (
|
|
544
|
+
manager.onRequest('slates/config.get_default', async () => {
|
|
145
545
|
getContextBasic();
|
|
146
546
|
|
|
147
547
|
let getDefaultConfig = slate.spec.config.handlers.getDefaultConfig;
|
|
@@ -149,32 +549,50 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
149
549
|
return { config: null };
|
|
150
550
|
}
|
|
151
551
|
|
|
152
|
-
let
|
|
153
|
-
|
|
552
|
+
let context = getEmptyContext();
|
|
553
|
+
let defaultConfig = await traceProviderCall<ConfigType>(
|
|
554
|
+
{
|
|
555
|
+
component: 'config',
|
|
556
|
+
functionName: 'getDefaultConfig',
|
|
557
|
+
message: 'Getting default config',
|
|
558
|
+
successMessage: 'Default config retrieved',
|
|
559
|
+
onSuccess: result => ({
|
|
560
|
+
configKeyCount: getObjectKeyCount(result)
|
|
561
|
+
})
|
|
562
|
+
},
|
|
563
|
+
() => runWithContext(context, async () => getDefaultConfig())
|
|
564
|
+
);
|
|
565
|
+
return withRequestTraces(context, {
|
|
566
|
+
config: (defaultConfig ?? null) as Record<string, any> | null
|
|
567
|
+
});
|
|
154
568
|
});
|
|
155
569
|
|
|
156
|
-
manager.onRequest('slates/config.schema.get', async (
|
|
570
|
+
manager.onRequest('slates/config.schema.get', async () => {
|
|
157
571
|
getContextBasic();
|
|
158
572
|
|
|
159
|
-
return {
|
|
573
|
+
return {
|
|
574
|
+
schema: toJsonSchema(slate.spec.configSchema),
|
|
575
|
+
docs: slate.spec.config.docsReferences ?? []
|
|
576
|
+
};
|
|
160
577
|
});
|
|
161
578
|
|
|
162
|
-
manager.onRequest('slates/provider.identify', async (
|
|
579
|
+
manager.onRequest('slates/provider.identify', async () => {
|
|
163
580
|
getContextBasic();
|
|
164
581
|
|
|
165
582
|
return {
|
|
166
|
-
protocol:
|
|
583
|
+
protocol: SLATES_PROTOCOL_VERSION,
|
|
167
584
|
provider: {
|
|
168
585
|
type: 'provider',
|
|
169
586
|
id: slate.spec.key,
|
|
170
587
|
name: slate.spec.name,
|
|
171
588
|
description: slate.spec.description,
|
|
172
589
|
metadata: slate.spec.parameters.metadata
|
|
173
|
-
}
|
|
590
|
+
},
|
|
591
|
+
docs: slate.spec.docs ?? []
|
|
174
592
|
};
|
|
175
593
|
});
|
|
176
594
|
|
|
177
|
-
manager.onRequest('slates/auth.methods.list', async (
|
|
595
|
+
manager.onRequest('slates/auth.methods.list', async () => {
|
|
178
596
|
getContextBasic();
|
|
179
597
|
|
|
180
598
|
return {
|
|
@@ -199,7 +617,25 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
199
617
|
return { input: null };
|
|
200
618
|
}
|
|
201
619
|
|
|
202
|
-
|
|
620
|
+
let context = getEmptyContext();
|
|
621
|
+
let input = await traceProviderCall(
|
|
622
|
+
{
|
|
623
|
+
component: 'auth',
|
|
624
|
+
functionName: 'getDefaultInput',
|
|
625
|
+
message: 'Getting default authentication input',
|
|
626
|
+
successMessage: 'Default authentication input retrieved',
|
|
627
|
+
metadata: {
|
|
628
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
629
|
+
authenticationMethodName: authMethod.name
|
|
630
|
+
},
|
|
631
|
+
onSuccess: result => ({
|
|
632
|
+
inputKeyCount: getObjectKeyCount(result)
|
|
633
|
+
})
|
|
634
|
+
},
|
|
635
|
+
() => runWithContext(context, () => authMethod.getDefaultInput!())
|
|
636
|
+
);
|
|
637
|
+
|
|
638
|
+
return withRequestTraces(context, { input });
|
|
203
639
|
});
|
|
204
640
|
|
|
205
641
|
manager.onRequest('slates/auth.input.changed', async ({ params }) => {
|
|
@@ -210,12 +646,36 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
210
646
|
return { success: true, input: params.newInput };
|
|
211
647
|
}
|
|
212
648
|
|
|
213
|
-
let
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
649
|
+
let context = getEmptyContext();
|
|
650
|
+
let updatedInput = await traceProviderCall(
|
|
651
|
+
{
|
|
652
|
+
component: 'auth',
|
|
653
|
+
functionName: 'onInputChanged',
|
|
654
|
+
message: 'Running authentication input change handler',
|
|
655
|
+
successMessage: 'Authentication input change handler completed',
|
|
656
|
+
metadata: {
|
|
657
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
658
|
+
authenticationMethodName: authMethod.name,
|
|
659
|
+
hasPreviousInput: params.previousInput !== null,
|
|
660
|
+
newInputKeyCount: getObjectKeyCount(params.newInput)
|
|
661
|
+
},
|
|
662
|
+
onSuccess: result => ({
|
|
663
|
+
returnedInput: !!result?.input
|
|
664
|
+
})
|
|
665
|
+
},
|
|
666
|
+
() =>
|
|
667
|
+
runWithContext(context, () =>
|
|
668
|
+
authMethod.onInputChanged!({
|
|
669
|
+
previousInput: params.previousInput as any | null,
|
|
670
|
+
newInput: params.newInput
|
|
671
|
+
})
|
|
672
|
+
)
|
|
673
|
+
);
|
|
217
674
|
|
|
218
|
-
return
|
|
675
|
+
return withRequestTraces(context, {
|
|
676
|
+
success: true,
|
|
677
|
+
input: updatedInput?.input ?? params.newInput
|
|
678
|
+
});
|
|
219
679
|
});
|
|
220
680
|
|
|
221
681
|
manager.onRequest('slates/auth.output.get', async ({ params }) => {
|
|
@@ -234,8 +694,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
234
694
|
}
|
|
235
695
|
|
|
236
696
|
if ('getOutput' in authMethod) {
|
|
237
|
-
let
|
|
238
|
-
|
|
697
|
+
let context = getAuthContext();
|
|
698
|
+
let outputRes = await traceProviderCall(
|
|
699
|
+
{
|
|
700
|
+
component: 'auth',
|
|
701
|
+
functionName: 'getOutput',
|
|
702
|
+
message: 'Getting authentication output',
|
|
703
|
+
successMessage: 'Authentication output retrieved',
|
|
704
|
+
metadata: {
|
|
705
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
706
|
+
authenticationMethodName: authMethod.name,
|
|
707
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
708
|
+
},
|
|
709
|
+
onSuccess: result => ({
|
|
710
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
711
|
+
scopeCount: result.scopes?.length ?? 0
|
|
712
|
+
})
|
|
713
|
+
},
|
|
714
|
+
() =>
|
|
715
|
+
runWithContext(context, () =>
|
|
716
|
+
authMethod.getOutput({
|
|
717
|
+
input,
|
|
718
|
+
config: getAuthConfig()
|
|
719
|
+
})
|
|
720
|
+
)
|
|
721
|
+
);
|
|
722
|
+
return withRequestTraces(context, {
|
|
723
|
+
output: outputRes.output,
|
|
724
|
+
scopes: outputRes.scopes
|
|
725
|
+
});
|
|
239
726
|
}
|
|
240
727
|
|
|
241
728
|
return { output: input as any };
|
|
@@ -246,20 +733,36 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
246
733
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
247
734
|
|
|
248
735
|
if ('handleCallback' in authMethod) {
|
|
249
|
-
let
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
736
|
+
let context = getEmptyContext();
|
|
737
|
+
let callbackRes = await traceProviderCall(
|
|
738
|
+
{
|
|
739
|
+
component: 'auth',
|
|
740
|
+
functionName: 'handleCallback',
|
|
741
|
+
message: 'Handling authentication callback',
|
|
742
|
+
successMessage: 'Authentication callback handled',
|
|
743
|
+
metadata: {
|
|
744
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
745
|
+
authenticationMethodName: authMethod.name,
|
|
746
|
+
scopeCount: params.scopes.length,
|
|
747
|
+
hasCallbackState: !!params.callbackState
|
|
748
|
+
},
|
|
749
|
+
onSuccess: result => ({
|
|
750
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
751
|
+
returnedInput: !!result.input,
|
|
752
|
+
returnedScopeCount: result.scopes?.length
|
|
753
|
+
})
|
|
754
|
+
},
|
|
755
|
+
() =>
|
|
756
|
+
runWithContext(context, () =>
|
|
757
|
+
authMethod.handleCallback({ ...buildCallbackContext(params), code: params.code })
|
|
758
|
+
)
|
|
759
|
+
);
|
|
258
760
|
|
|
259
|
-
return {
|
|
761
|
+
return withRequestTraces(context, {
|
|
260
762
|
output: callbackRes.output,
|
|
261
|
-
input: callbackRes.input
|
|
262
|
-
|
|
763
|
+
input: callbackRes.input,
|
|
764
|
+
scopes: callbackRes.scopes
|
|
765
|
+
});
|
|
263
766
|
}
|
|
264
767
|
|
|
265
768
|
throw new ServiceError(
|
|
@@ -274,24 +777,108 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
274
777
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
275
778
|
|
|
276
779
|
if ('getAuthorizationUrl' in authMethod) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
780
|
+
return getTracedAuthorizationUrl(
|
|
781
|
+
params,
|
|
782
|
+
authMethod.name,
|
|
783
|
+
{
|
|
784
|
+
functionName: 'getAuthorizationUrl',
|
|
785
|
+
message: 'Getting authentication authorization URL',
|
|
786
|
+
successMessage: 'Authentication authorization URL retrieved'
|
|
787
|
+
},
|
|
788
|
+
ctx => authMethod.getAuthorizationUrl(ctx)
|
|
789
|
+
);
|
|
790
|
+
}
|
|
285
791
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
792
|
+
throw new ServiceError(
|
|
793
|
+
preconditionFailedError({
|
|
794
|
+
message: `Authentication method does not support authorization URL retrieval: ${params.authenticationMethodId}`
|
|
795
|
+
})
|
|
796
|
+
);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
manager.onRequest('slates/auth.admin_authorization_url.get', async ({ params }) => {
|
|
800
|
+
getContextBasic();
|
|
801
|
+
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
802
|
+
|
|
803
|
+
if (authMethod.type === 'auth.oauth' && authMethod.adminSetup) {
|
|
804
|
+
let { adminSetup } = authMethod;
|
|
805
|
+
return getTracedAuthorizationUrl(
|
|
806
|
+
params,
|
|
807
|
+
authMethod.name,
|
|
808
|
+
{
|
|
809
|
+
functionName: 'adminSetup.getAuthorizationUrl',
|
|
810
|
+
message: 'Getting admin setup authorization URL',
|
|
811
|
+
successMessage: 'Admin setup authorization URL retrieved',
|
|
812
|
+
metadata: { usesDefault: !adminSetup.getAuthorizationUrl }
|
|
813
|
+
},
|
|
814
|
+
ctx =>
|
|
815
|
+
adminSetup.getAuthorizationUrl
|
|
816
|
+
? adminSetup.getAuthorizationUrl(ctx)
|
|
817
|
+
: authMethod.getAuthorizationUrl(ctx)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
throw new ServiceError(
|
|
822
|
+
preconditionFailedError({
|
|
823
|
+
message: `Authentication method does not support admin setup: ${params.authenticationMethodId}`
|
|
824
|
+
})
|
|
825
|
+
);
|
|
826
|
+
});
|
|
827
|
+
|
|
828
|
+
manager.onRequest('slates/auth.admin_callback.handle', async ({ params }) => {
|
|
829
|
+
getContextBasic();
|
|
830
|
+
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
831
|
+
|
|
832
|
+
if (authMethod.type === 'auth.oauth' && authMethod.adminSetup) {
|
|
833
|
+
let { adminSetup } = authMethod;
|
|
834
|
+
let ctx = { ...buildCallbackContext(params), code: params.code };
|
|
835
|
+
|
|
836
|
+
let handleCallback = async (): Promise<{ instructions?: string }> => {
|
|
837
|
+
if (adminSetup.handleCallback) return adminSetup.handleCallback(ctx);
|
|
838
|
+
|
|
839
|
+
let code = ctx.code;
|
|
840
|
+
if (!code) {
|
|
841
|
+
throw new ServiceError(
|
|
842
|
+
badRequestError({
|
|
843
|
+
message: `Admin setup callback did not include an authorization code: ${params.authenticationMethodId}`
|
|
844
|
+
})
|
|
845
|
+
);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
await authMethod.handleCallback({ ...ctx, code });
|
|
849
|
+
return {};
|
|
289
850
|
};
|
|
851
|
+
|
|
852
|
+
let context = getEmptyContext();
|
|
853
|
+
let callbackRes = await traceProviderCall(
|
|
854
|
+
{
|
|
855
|
+
component: 'auth',
|
|
856
|
+
functionName: 'adminSetup.handleCallback',
|
|
857
|
+
message: 'Handling admin setup callback',
|
|
858
|
+
successMessage: 'Admin setup callback handled',
|
|
859
|
+
metadata: {
|
|
860
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
861
|
+
authenticationMethodName: authMethod.name,
|
|
862
|
+
scopeCount: params.scopes.length,
|
|
863
|
+
hasCode: !!params.code,
|
|
864
|
+
hasCallbackState: !!params.callbackState,
|
|
865
|
+
usesDefault: !adminSetup.handleCallback
|
|
866
|
+
},
|
|
867
|
+
onSuccess: result => ({
|
|
868
|
+
returnedInstructions: !!result.instructions
|
|
869
|
+
})
|
|
870
|
+
},
|
|
871
|
+
() => runWithContext(context, handleCallback)
|
|
872
|
+
);
|
|
873
|
+
|
|
874
|
+
return withRequestTraces(context, {
|
|
875
|
+
instructions: callbackRes.instructions
|
|
876
|
+
});
|
|
290
877
|
}
|
|
291
878
|
|
|
292
879
|
throw new ServiceError(
|
|
293
880
|
preconditionFailedError({
|
|
294
|
-
message: `Authentication method does not support
|
|
881
|
+
message: `Authentication method does not support admin setup: ${params.authenticationMethodId}`
|
|
295
882
|
})
|
|
296
883
|
);
|
|
297
884
|
});
|
|
@@ -301,15 +888,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
301
888
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
302
889
|
|
|
303
890
|
if (authMethod.getProfile) {
|
|
304
|
-
let
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
891
|
+
let context = getAuthContext();
|
|
892
|
+
let profileRes = await traceProviderCall(
|
|
893
|
+
{
|
|
894
|
+
component: 'auth',
|
|
895
|
+
functionName: 'getProfile',
|
|
896
|
+
message: 'Getting authentication profile',
|
|
897
|
+
successMessage: 'Authentication profile retrieved',
|
|
898
|
+
metadata: {
|
|
899
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
900
|
+
authenticationMethodName: authMethod.name,
|
|
901
|
+
scopeCount: params.scopes.length,
|
|
902
|
+
inputKeyCount: getObjectKeyCount(params.input),
|
|
903
|
+
outputKeyCount: getObjectKeyCount(params.output)
|
|
904
|
+
},
|
|
905
|
+
onSuccess: result => ({
|
|
906
|
+
profileKeyCount: getObjectKeyCount(result.profile)
|
|
907
|
+
})
|
|
908
|
+
},
|
|
909
|
+
() =>
|
|
910
|
+
runWithContext(context, () => {
|
|
911
|
+
if (authMethod.type === 'auth.oauth') {
|
|
912
|
+
return authMethod.getProfile!({
|
|
913
|
+
output: params.output as any,
|
|
914
|
+
input: params.input,
|
|
915
|
+
scopes: params.scopes,
|
|
916
|
+
config: getAuthConfig()
|
|
917
|
+
});
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
return authMethod.getProfile!({
|
|
921
|
+
output: params.output as any,
|
|
922
|
+
input: params.input,
|
|
923
|
+
scopes: params.scopes,
|
|
924
|
+
config: getAuthConfig()
|
|
925
|
+
})!;
|
|
926
|
+
})
|
|
927
|
+
);
|
|
309
928
|
|
|
310
|
-
return {
|
|
929
|
+
return withRequestTraces(context, {
|
|
311
930
|
profile: profileRes.profile
|
|
312
|
-
};
|
|
931
|
+
});
|
|
313
932
|
}
|
|
314
933
|
|
|
315
934
|
throw new ServiceError(
|
|
@@ -324,18 +943,53 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
324
943
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
325
944
|
|
|
326
945
|
if ('handleTokenRefresh' in authMethod && authMethod.handleTokenRefresh) {
|
|
327
|
-
let
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
946
|
+
let context = getAuthContext();
|
|
947
|
+
let refreshRes = await traceProviderCall(
|
|
948
|
+
{
|
|
949
|
+
component: 'auth',
|
|
950
|
+
functionName: 'handleTokenRefresh',
|
|
951
|
+
message: 'Refreshing authentication token',
|
|
952
|
+
successMessage: 'Authentication token refreshed',
|
|
953
|
+
metadata: {
|
|
954
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
955
|
+
authenticationMethodName: authMethod.name,
|
|
956
|
+
scopeCount: params.scopes.length,
|
|
957
|
+
inputKeyCount: getObjectKeyCount(params.input),
|
|
958
|
+
outputKeyCount: getObjectKeyCount(params.output)
|
|
959
|
+
},
|
|
960
|
+
onSuccess: result => ({
|
|
961
|
+
refreshedOutputKeyCount: getObjectKeyCount(result.output),
|
|
962
|
+
returnedInput: !!result.input
|
|
963
|
+
})
|
|
964
|
+
},
|
|
965
|
+
() =>
|
|
966
|
+
runWithContext(context, () => {
|
|
967
|
+
if (authMethod.type === 'auth.oauth') {
|
|
968
|
+
return authMethod.handleTokenRefresh!({
|
|
969
|
+
output: params.output as any,
|
|
970
|
+
input: params.input,
|
|
971
|
+
clientId: params.clientId,
|
|
972
|
+
clientSecret: params.clientSecret,
|
|
973
|
+
scopes: params.scopes,
|
|
974
|
+
config: getAuthConfig()
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
return authMethod.handleTokenRefresh!({
|
|
979
|
+
output: params.output as any,
|
|
980
|
+
input: params.input,
|
|
981
|
+
clientId: params.clientId,
|
|
982
|
+
clientSecret: params.clientSecret,
|
|
983
|
+
scopes: params.scopes,
|
|
984
|
+
config: getAuthConfig()
|
|
985
|
+
});
|
|
986
|
+
})
|
|
987
|
+
);
|
|
334
988
|
|
|
335
|
-
return {
|
|
989
|
+
return withRequestTraces(context, {
|
|
336
990
|
output: refreshRes.output,
|
|
337
991
|
input: refreshRes.input
|
|
338
|
-
};
|
|
992
|
+
});
|
|
339
993
|
}
|
|
340
994
|
|
|
341
995
|
throw new ServiceError(
|
|
@@ -348,22 +1002,77 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
348
1002
|
manager.onRequest('slates/actions.list', async ({ params }) => {
|
|
349
1003
|
getContextBasic();
|
|
350
1004
|
|
|
1005
|
+
let actions = getExposedActions(
|
|
1006
|
+
slate,
|
|
1007
|
+
!!params.includeAdapterActions && supportsAdapters()
|
|
1008
|
+
);
|
|
1009
|
+
|
|
1010
|
+
if (!supportsTriggerGroups()) {
|
|
1011
|
+
actions = actions.filter(action => action.type !== 'trigger');
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
return {
|
|
1015
|
+
actions: actions.map(a => mapAction(slate, a))
|
|
1016
|
+
};
|
|
1017
|
+
});
|
|
1018
|
+
|
|
1019
|
+
manager.onRequest('slates/adapters.list', async () => {
|
|
1020
|
+
getContextBasic();
|
|
1021
|
+
|
|
1022
|
+
return {
|
|
1023
|
+
adapters: supportsAdapters() ? (slate.adapters ?? []).map(mapAdapter) : []
|
|
1024
|
+
};
|
|
1025
|
+
});
|
|
1026
|
+
|
|
1027
|
+
manager.onRequest('slates/adapter.get', async ({ params }) => {
|
|
1028
|
+
getContextBasic();
|
|
1029
|
+
if (!supportsAdapters()) {
|
|
1030
|
+
throw new ServiceError(notFoundError(`adapter`, params.adapterId));
|
|
1031
|
+
}
|
|
1032
|
+
let adapter = getAdapter(slate, params.adapterId);
|
|
1033
|
+
|
|
351
1034
|
return {
|
|
352
|
-
|
|
1035
|
+
adapter: mapAdapter(adapter)
|
|
353
1036
|
};
|
|
354
1037
|
});
|
|
355
1038
|
|
|
356
1039
|
manager.onRequest('slates/action.get', async ({ params }) => {
|
|
357
1040
|
getContextBasic();
|
|
358
|
-
let action =
|
|
1041
|
+
let action = getExposedAction(slate, params.actionId, supportsAdapters());
|
|
359
1042
|
|
|
360
1043
|
return {
|
|
361
1044
|
action: mapAction(slate, action)
|
|
362
1045
|
};
|
|
363
1046
|
});
|
|
364
1047
|
|
|
1048
|
+
manager.onRequest('slates/trigger_groups.list', async () => {
|
|
1049
|
+
getContextBasic();
|
|
1050
|
+
|
|
1051
|
+
return {
|
|
1052
|
+
triggerGroups: getExposedTriggerGroups(slate, supportsAdapters()).map(mapTriggerGroup)
|
|
1053
|
+
};
|
|
1054
|
+
});
|
|
1055
|
+
|
|
1056
|
+
manager.onRequest('slates/tool_categories.list', async () => {
|
|
1057
|
+
getContextBasic();
|
|
1058
|
+
|
|
1059
|
+
return {
|
|
1060
|
+
toolCategories: getExposedToolCategories(slate, supportsAdapters()).map(
|
|
1061
|
+
mapToolCategory
|
|
1062
|
+
)
|
|
1063
|
+
};
|
|
1064
|
+
});
|
|
1065
|
+
|
|
1066
|
+
manager.onRequest('slates/trigger_group.get', async ({ params }) => {
|
|
1067
|
+
getContextBasic();
|
|
1068
|
+
let triggerGroup = getTriggerGroup(slate, params.triggerGroupId);
|
|
1069
|
+
|
|
1070
|
+
return {
|
|
1071
|
+
triggerGroup: mapTriggerGroup(triggerGroup)
|
|
1072
|
+
};
|
|
1073
|
+
});
|
|
1074
|
+
|
|
365
1075
|
manager.onRequest('slates/action.tool.invoke', async ({ params }) => {
|
|
366
|
-
let ctx = getContextFull();
|
|
367
1076
|
let action = getActionWithType(slate, 'tool', params.actionId);
|
|
368
1077
|
|
|
369
1078
|
let input = validate(
|
|
@@ -373,11 +1082,69 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
373
1082
|
`Invalid input for tool ID: ${params.actionId}`
|
|
374
1083
|
);
|
|
375
1084
|
|
|
376
|
-
let
|
|
377
|
-
|
|
378
|
-
|
|
1085
|
+
let invoke = async (context: SlatePublicContext<any>) => {
|
|
1086
|
+
let res = await traceProviderCall(
|
|
1087
|
+
{
|
|
1088
|
+
component: 'action',
|
|
1089
|
+
functionName: 'handleInvocation',
|
|
1090
|
+
message: `Starting tool ${formatEntityLabel(action.name, action.key)}`,
|
|
1091
|
+
successMessage: `Completed tool ${formatEntityLabel(action.name, action.key)}`,
|
|
1092
|
+
errorMessage: `Tool ${formatEntityLabel(action.name, action.key)} failed`,
|
|
1093
|
+
metadata: {
|
|
1094
|
+
actionId: action.key,
|
|
1095
|
+
actionName: action.name,
|
|
1096
|
+
actionType: action.type,
|
|
1097
|
+
isPublic: action.isPublic,
|
|
1098
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
1099
|
+
},
|
|
1100
|
+
onSuccess: result => ({
|
|
1101
|
+
hasMessage: !!result.message,
|
|
1102
|
+
actionResultMessage: result.message,
|
|
1103
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
1104
|
+
attachmentCount: result.attachments?.length
|
|
1105
|
+
})
|
|
1106
|
+
},
|
|
1107
|
+
() => runWithContext(context, () => action.handleInvocation(context as any))
|
|
1108
|
+
);
|
|
1109
|
+
|
|
1110
|
+
let contextAttachments = await context._finalizeAttachments();
|
|
1111
|
+
let merged = mergeAttachments(
|
|
1112
|
+
[...(res.attachments ?? []), ...contextAttachments],
|
|
1113
|
+
res.output
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1116
|
+
let redacted = redactUrlAttachmentSecrets(
|
|
1117
|
+
merged,
|
|
1118
|
+
context._getAuthConfigForRedaction()
|
|
1119
|
+
);
|
|
1120
|
+
let finalAttachments = await routeAttachmentsThroughDirectUpload(
|
|
1121
|
+
redacted,
|
|
1122
|
+
getLiveInvocation()
|
|
1123
|
+
);
|
|
379
1124
|
|
|
380
|
-
|
|
1125
|
+
return withRequestTraces(context, {
|
|
1126
|
+
output: res.output,
|
|
1127
|
+
message: res.message,
|
|
1128
|
+
attachments: finalAttachments
|
|
1129
|
+
});
|
|
1130
|
+
};
|
|
1131
|
+
|
|
1132
|
+
if (action.isPublic) {
|
|
1133
|
+
getContextBasic();
|
|
1134
|
+
return invoke(new SlatePublicContext(input, slate.spec, logger, getLiveInvocation()));
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
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
|
+
);
|
|
381
1148
|
});
|
|
382
1149
|
|
|
383
1150
|
manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
|
|
@@ -391,46 +1158,283 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
391
1158
|
`Invalid event for trigger ID: ${params.actionId}`
|
|
392
1159
|
);
|
|
393
1160
|
|
|
394
|
-
let
|
|
395
|
-
|
|
1161
|
+
let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
|
|
1162
|
+
let res = await traceProviderCall(
|
|
1163
|
+
{
|
|
1164
|
+
component: 'action',
|
|
1165
|
+
functionName: 'handleEvent',
|
|
1166
|
+
message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
|
|
1167
|
+
successMessage: result =>
|
|
1168
|
+
`Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
|
|
1169
|
+
errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
|
|
1170
|
+
metadata: {
|
|
1171
|
+
actionId: action.key,
|
|
1172
|
+
actionName: action.name,
|
|
1173
|
+
actionType: action.type,
|
|
1174
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
1175
|
+
},
|
|
1176
|
+
onSuccess: result => ({
|
|
1177
|
+
eventType: result.type,
|
|
1178
|
+
hasEventId: !!result.id,
|
|
1179
|
+
outputKeyCount: getObjectKeyCount(result.output)
|
|
1180
|
+
})
|
|
1181
|
+
},
|
|
1182
|
+
() => runWithContext(context, () => action.map(context))
|
|
396
1183
|
);
|
|
397
1184
|
|
|
398
|
-
return { id: res.id, type: res.type, output: res.output };
|
|
1185
|
+
return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
|
|
399
1186
|
});
|
|
400
1187
|
|
|
401
1188
|
manager.onRequest('slates/action.trigger.poll_events', async ({ params }) => {
|
|
402
|
-
|
|
403
|
-
let action = getActionWithType(slate, 'trigger', params.actionId);
|
|
1189
|
+
getContextBasic();
|
|
404
1190
|
|
|
405
|
-
if (
|
|
1191
|
+
if (supportsTriggerGroups()) {
|
|
406
1192
|
throw new ServiceError(
|
|
407
1193
|
badRequestError({
|
|
408
|
-
message: `
|
|
1194
|
+
message: `Legacy trigger polling is disabled once trigger_group support is announced: ${params.actionId}`
|
|
409
1195
|
})
|
|
410
1196
|
);
|
|
411
1197
|
}
|
|
412
1198
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
1199
|
+
return { inputs: [], updatedState: params.state };
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
manager.onRequest('slates/action.trigger.webhook_handle', async ({ params }) => {
|
|
1203
|
+
getContextBasic();
|
|
1204
|
+
|
|
1205
|
+
if (supportsTriggerGroups()) {
|
|
1206
|
+
throw new ServiceError(
|
|
1207
|
+
badRequestError({
|
|
1208
|
+
message: `Legacy trigger webhook handling is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1209
|
+
})
|
|
1210
|
+
);
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
return { inputs: [], updatedState: params.state, response: null };
|
|
1214
|
+
});
|
|
1215
|
+
|
|
1216
|
+
manager.onRequest('slates/action.trigger.webhook_register', async ({ params }) => {
|
|
1217
|
+
getContextBasic();
|
|
1218
|
+
|
|
1219
|
+
if (supportsTriggerGroups()) {
|
|
1220
|
+
throw new ServiceError(
|
|
1221
|
+
badRequestError({
|
|
1222
|
+
message: `Legacy trigger webhook registration is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1223
|
+
})
|
|
1224
|
+
);
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
return { registrationDetails: null };
|
|
1228
|
+
});
|
|
1229
|
+
|
|
1230
|
+
manager.onRequest('slates/action.trigger.webhook_unregister', async ({ params }) => {
|
|
1231
|
+
getContextBasic();
|
|
1232
|
+
|
|
1233
|
+
if (supportsTriggerGroups()) {
|
|
1234
|
+
throw new ServiceError(
|
|
1235
|
+
badRequestError({
|
|
1236
|
+
message: `Legacy trigger webhook unregistration is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1237
|
+
})
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
return {};
|
|
1242
|
+
});
|
|
1243
|
+
|
|
1244
|
+
manager.onRequest('slates/trigger_group.webhook.targets_list', async ({ params }) => {
|
|
1245
|
+
let ctx = getContextFull();
|
|
1246
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1247
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1248
|
+
|
|
1249
|
+
let context = new SlateContext(
|
|
1250
|
+
ctx.config,
|
|
1251
|
+
{ pageToken: params.pageToken ?? null },
|
|
1252
|
+
ctx.auth?.output!,
|
|
1253
|
+
slate.spec,
|
|
1254
|
+
logger
|
|
1255
|
+
);
|
|
1256
|
+
let res = await traceProviderCall(
|
|
1257
|
+
{
|
|
1258
|
+
component: 'action',
|
|
1259
|
+
functionName: 'webhookTargetList',
|
|
1260
|
+
message: `Listing webhook targets for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1261
|
+
successMessage: result =>
|
|
1262
|
+
`Listed ${result.targets.length} webhook target(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1263
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while listing webhook targets`,
|
|
1264
|
+
metadata: {
|
|
1265
|
+
triggerGroupId: group.key,
|
|
1266
|
+
triggerGroupName: group.name
|
|
1267
|
+
},
|
|
1268
|
+
onSuccess: result => ({
|
|
1269
|
+
targetCount: result.targets.length,
|
|
1270
|
+
hasNextPageToken: result.nextPageToken !== null
|
|
1271
|
+
})
|
|
1272
|
+
},
|
|
1273
|
+
() => runWithContext(context, () => autoRegistration.webhookTargetList(context))
|
|
421
1274
|
);
|
|
422
1275
|
|
|
423
|
-
return
|
|
1276
|
+
return withRequestTraces(context, {
|
|
1277
|
+
targets: res.targets,
|
|
1278
|
+
nextPageToken: res.nextPageToken ?? null,
|
|
1279
|
+
isPartial: res.isPartial ?? false
|
|
1280
|
+
});
|
|
424
1281
|
});
|
|
425
1282
|
|
|
426
|
-
manager.onRequest('slates/
|
|
1283
|
+
manager.onRequest('slates/trigger_group.webhook.register', async ({ params }) => {
|
|
427
1284
|
let ctx = getContextFull();
|
|
428
|
-
let
|
|
1285
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1286
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1287
|
+
|
|
1288
|
+
let context = new SlateContext(
|
|
1289
|
+
ctx.config,
|
|
1290
|
+
{
|
|
1291
|
+
webhookTargetIdentifier: params.webhookTargetIdentifier,
|
|
1292
|
+
webhookTargetPayload: params.webhookTargetPayload,
|
|
1293
|
+
webhookUrl: params.webhookUrl
|
|
1294
|
+
},
|
|
1295
|
+
ctx.auth?.output!,
|
|
1296
|
+
slate.spec,
|
|
1297
|
+
logger
|
|
1298
|
+
);
|
|
1299
|
+
let res = await traceProviderCall(
|
|
1300
|
+
{
|
|
1301
|
+
component: 'action',
|
|
1302
|
+
functionName: 'webhookRegister',
|
|
1303
|
+
message: `Registering webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1304
|
+
successMessage: `Registered webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1305
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while registering a webhook`,
|
|
1306
|
+
metadata: {
|
|
1307
|
+
triggerGroupId: group.key,
|
|
1308
|
+
triggerGroupName: group.name,
|
|
1309
|
+
webhookTargetIdentifier: params.webhookTargetIdentifier
|
|
1310
|
+
}
|
|
1311
|
+
},
|
|
1312
|
+
() => runWithContext(context, () => autoRegistration.webhookRegister(context))
|
|
1313
|
+
);
|
|
429
1314
|
|
|
430
|
-
|
|
1315
|
+
return withRequestTraces(context, {
|
|
1316
|
+
webhookRegistrationIdentifier: res.webhookRegistrationIdentifier,
|
|
1317
|
+
webhookRegistrationPayload: res.webhookRegistrationPayload
|
|
1318
|
+
});
|
|
1319
|
+
});
|
|
1320
|
+
|
|
1321
|
+
manager.onRequest('slates/trigger_group.webhook.unregister', async ({ params }) => {
|
|
1322
|
+
let ctx = getContextFull();
|
|
1323
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1324
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1325
|
+
|
|
1326
|
+
let context = new SlateContext(
|
|
1327
|
+
ctx.config,
|
|
1328
|
+
{
|
|
1329
|
+
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier,
|
|
1330
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1331
|
+
},
|
|
1332
|
+
ctx.auth?.output!,
|
|
1333
|
+
slate.spec,
|
|
1334
|
+
logger
|
|
1335
|
+
);
|
|
1336
|
+
await traceProviderCall(
|
|
1337
|
+
{
|
|
1338
|
+
component: 'action',
|
|
1339
|
+
functionName: 'webhookUnregister',
|
|
1340
|
+
message: `Unregistering webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1341
|
+
successMessage: `Unregistered webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1342
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while unregistering a webhook`,
|
|
1343
|
+
metadata: {
|
|
1344
|
+
triggerGroupId: group.key,
|
|
1345
|
+
triggerGroupName: group.name,
|
|
1346
|
+
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier
|
|
1347
|
+
}
|
|
1348
|
+
},
|
|
1349
|
+
() => runWithContext(context, () => autoRegistration.webhookUnregister(context))
|
|
1350
|
+
);
|
|
1351
|
+
|
|
1352
|
+
return withRequestTraces(context, {});
|
|
1353
|
+
});
|
|
1354
|
+
|
|
1355
|
+
manager.onRequest('slates/trigger_group.webhook.manual_setup', async ({ params }) => {
|
|
1356
|
+
getContextBasic();
|
|
1357
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1358
|
+
let manualRegistration = getWebhookManualRegistration(group);
|
|
1359
|
+
|
|
1360
|
+
let context = new SlateContext(
|
|
1361
|
+
{},
|
|
1362
|
+
{ webhookUrl: params.webhookUrl },
|
|
1363
|
+
{},
|
|
1364
|
+
slate.spec as any,
|
|
1365
|
+
logger
|
|
1366
|
+
);
|
|
1367
|
+
let res = await traceProviderCall(
|
|
1368
|
+
{
|
|
1369
|
+
component: 'action',
|
|
1370
|
+
functionName: 'webhookManualSetup',
|
|
1371
|
+
message: `Building manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1372
|
+
successMessage: `Built manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1373
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while building a manual webhook setup`,
|
|
1374
|
+
metadata: {
|
|
1375
|
+
triggerGroupId: group.key,
|
|
1376
|
+
triggerGroupName: group.name
|
|
1377
|
+
}
|
|
1378
|
+
},
|
|
1379
|
+
() => runWithContext(context, () => manualRegistration.setup(context))
|
|
1380
|
+
);
|
|
1381
|
+
|
|
1382
|
+
return {
|
|
1383
|
+
webhookSetupDocument: res.webhookSetupDocument,
|
|
1384
|
+
partialWebhookRegistrationPayload: res.partialWebhookRegistrationPayload
|
|
1385
|
+
};
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
manager.onRequest('slates/trigger_group.webhook.manual_finish', async ({ params }) => {
|
|
1389
|
+
getContextBasic();
|
|
1390
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1391
|
+
let manualRegistration = getWebhookManualRegistration(group);
|
|
1392
|
+
|
|
1393
|
+
if (!manualRegistration.finish) {
|
|
1394
|
+
throw new ServiceError(
|
|
1395
|
+
badRequestError({
|
|
1396
|
+
message: `Trigger group does not support manual webhook finish: ${params.triggerGroupId}`
|
|
1397
|
+
})
|
|
1398
|
+
);
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
let context = new SlateContext(
|
|
1402
|
+
{},
|
|
1403
|
+
{
|
|
1404
|
+
webhookUrl: params.webhookUrl,
|
|
1405
|
+
partialWebhookRegistrationPayload: params.partialWebhookRegistrationPayload,
|
|
1406
|
+
userWebhookRegistrationPayload: params.userWebhookRegistrationPayload
|
|
1407
|
+
},
|
|
1408
|
+
{},
|
|
1409
|
+
slate.spec as any,
|
|
1410
|
+
logger
|
|
1411
|
+
);
|
|
1412
|
+
let res = await traceProviderCall(
|
|
1413
|
+
{
|
|
1414
|
+
component: 'action',
|
|
1415
|
+
functionName: 'webhookManualFinish',
|
|
1416
|
+
message: `Finishing manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1417
|
+
successMessage: `Finished manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1418
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while finishing a manual webhook setup`,
|
|
1419
|
+
metadata: {
|
|
1420
|
+
triggerGroupId: group.key,
|
|
1421
|
+
triggerGroupName: group.name
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
() => runWithContext(context, () => manualRegistration.finish!(context))
|
|
1425
|
+
);
|
|
1426
|
+
|
|
1427
|
+
return withRequestTraces(context, {
|
|
1428
|
+
webhookRegistrationPayload: res.webhookRegistrationPayload
|
|
1429
|
+
});
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
manager.onRequest('slates/trigger_group.webhook.process', async ({ params }) => {
|
|
1433
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1434
|
+
if (group.source !== 'webhook' || !group.webhook) {
|
|
431
1435
|
throw new ServiceError(
|
|
432
1436
|
badRequestError({
|
|
433
|
-
message: `Trigger
|
|
1437
|
+
message: `Trigger group does not support webhook processing: ${params.triggerGroupId}`
|
|
434
1438
|
})
|
|
435
1439
|
);
|
|
436
1440
|
}
|
|
@@ -443,70 +1447,233 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
443
1447
|
: null
|
|
444
1448
|
});
|
|
445
1449
|
|
|
446
|
-
let
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1450
|
+
let context = new SlateContext(
|
|
1451
|
+
{},
|
|
1452
|
+
{
|
|
1453
|
+
request: req,
|
|
1454
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1455
|
+
},
|
|
1456
|
+
{},
|
|
1457
|
+
slate.spec as any,
|
|
1458
|
+
logger
|
|
1459
|
+
);
|
|
1460
|
+
let res = await traceProviderCall(
|
|
1461
|
+
{
|
|
1462
|
+
component: 'action',
|
|
1463
|
+
functionName: 'webhookProcess',
|
|
1464
|
+
message: `Processing webhook request for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1465
|
+
successMessage: result =>
|
|
1466
|
+
`Extracted ${result.events.length} event(s) from webhook request for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1467
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while processing a webhook request`,
|
|
1468
|
+
metadata: {
|
|
1469
|
+
triggerGroupId: group.key,
|
|
1470
|
+
triggerGroupName: group.name,
|
|
1471
|
+
requestMethod: params.method,
|
|
1472
|
+
hasRequestBody: !!params.body
|
|
1473
|
+
},
|
|
1474
|
+
onSuccess: result => ({
|
|
1475
|
+
eventCount: result.events.length,
|
|
1476
|
+
hasResponse: result.response !== undefined,
|
|
1477
|
+
skipped: !!result.skipped
|
|
1478
|
+
})
|
|
1479
|
+
},
|
|
1480
|
+
() => runWithContext(context, () => group.webhook!.process(context))
|
|
454
1481
|
);
|
|
455
1482
|
|
|
456
|
-
|
|
1483
|
+
for (let event of res.events) {
|
|
1484
|
+
if (!event.matchers) {
|
|
1485
|
+
throw new ServiceError(
|
|
1486
|
+
preconditionFailedError({
|
|
1487
|
+
message: `Trigger group "${group.key}" process handler must return matchers for every event (return [] if there is nothing to assert)`
|
|
1488
|
+
})
|
|
1489
|
+
);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
let response =
|
|
1494
|
+
res.response === undefined
|
|
1495
|
+
? undefined
|
|
1496
|
+
: await serializeWebhookHttpResponse(res.response);
|
|
1497
|
+
|
|
1498
|
+
return withRequestTraces(context, {
|
|
1499
|
+
events: res.events.map(event => ({
|
|
1500
|
+
matchers: event.matchers,
|
|
1501
|
+
payload: event.payload,
|
|
1502
|
+
idempotencyKey: event.idempotencyKey,
|
|
1503
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1504
|
+
})),
|
|
1505
|
+
response,
|
|
1506
|
+
skipped: res.skipped ?? null
|
|
1507
|
+
});
|
|
457
1508
|
});
|
|
458
1509
|
|
|
459
|
-
manager.onRequest('slates/
|
|
1510
|
+
manager.onRequest('slates/trigger_group.routing_matchers.get', async ({ params }) => {
|
|
460
1511
|
let ctx = getContextFull();
|
|
461
|
-
let
|
|
1512
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1513
|
+
|
|
1514
|
+
let context = new SlateContext(ctx.config, {}, ctx.auth?.output!, slate.spec, logger);
|
|
1515
|
+
let matchers = await traceProviderCall(
|
|
1516
|
+
{
|
|
1517
|
+
component: 'action',
|
|
1518
|
+
functionName: 'routingMatchers',
|
|
1519
|
+
message: `Getting routing matchers for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1520
|
+
successMessage: result =>
|
|
1521
|
+
`Retrieved ${result.length} routing matcher(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1522
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while getting routing matchers`,
|
|
1523
|
+
metadata: {
|
|
1524
|
+
triggerGroupId: group.key,
|
|
1525
|
+
triggerGroupName: group.name
|
|
1526
|
+
},
|
|
1527
|
+
onSuccess: result => ({
|
|
1528
|
+
matcherCount: result.length
|
|
1529
|
+
})
|
|
1530
|
+
},
|
|
1531
|
+
() => runWithContext(context, () => group.routingMatchers(context))
|
|
1532
|
+
);
|
|
1533
|
+
|
|
1534
|
+
return withRequestTraces(context, { matchers });
|
|
1535
|
+
});
|
|
462
1536
|
|
|
463
|
-
|
|
1537
|
+
manager.onRequest('slates/trigger_group.polling.poll', async ({ params }) => {
|
|
1538
|
+
let ctx = getContextFull();
|
|
1539
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1540
|
+
if (group.source !== 'polling' || !group.polling) {
|
|
464
1541
|
throw new ServiceError(
|
|
465
1542
|
badRequestError({
|
|
466
|
-
message: `Trigger
|
|
1543
|
+
message: `Trigger group does not support polling: ${params.triggerGroupId}`
|
|
467
1544
|
})
|
|
468
1545
|
);
|
|
469
1546
|
}
|
|
470
1547
|
|
|
471
|
-
let
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
1548
|
+
let context = new SlateContext(
|
|
1549
|
+
ctx.config,
|
|
1550
|
+
{ state: params.state },
|
|
1551
|
+
ctx.auth?.output!,
|
|
1552
|
+
slate.spec,
|
|
1553
|
+
logger
|
|
1554
|
+
);
|
|
1555
|
+
let res = await traceProviderCall(
|
|
1556
|
+
{
|
|
1557
|
+
component: 'action',
|
|
1558
|
+
functionName: 'pollEvents',
|
|
1559
|
+
message: `Polling events for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1560
|
+
successMessage: result =>
|
|
1561
|
+
`Polled ${result.events.length} event(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1562
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while polling events`,
|
|
1563
|
+
metadata: {
|
|
1564
|
+
triggerGroupId: group.key,
|
|
1565
|
+
triggerGroupName: group.name,
|
|
1566
|
+
hasPreviousState: params.state !== null
|
|
1567
|
+
},
|
|
1568
|
+
onSuccess: result => ({
|
|
1569
|
+
eventCount: result.events.length,
|
|
1570
|
+
hasUpdatedState: result.updatedState !== undefined
|
|
1571
|
+
})
|
|
1572
|
+
},
|
|
1573
|
+
() => runWithContext(context, () => group.polling!.pollEvents(context))
|
|
479
1574
|
);
|
|
480
1575
|
|
|
481
|
-
return
|
|
1576
|
+
return withRequestTraces(context, {
|
|
1577
|
+
updatedState: res.updatedState,
|
|
1578
|
+
events: res.events.map(event => ({
|
|
1579
|
+
payload: event.payload,
|
|
1580
|
+
idempotencyKey: event.idempotencyKey,
|
|
1581
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1582
|
+
}))
|
|
1583
|
+
});
|
|
482
1584
|
});
|
|
483
1585
|
|
|
484
|
-
|
|
485
|
-
let
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
if (!action.autoUnregisterWebhook) {
|
|
1586
|
+
let getGatewayTriggerGroup = (triggerGroupId: string) => {
|
|
1587
|
+
let group = getTriggerGroup(slate, triggerGroupId);
|
|
1588
|
+
if (group.source !== 'gateway' || !group.gateway) {
|
|
489
1589
|
throw new ServiceError(
|
|
490
1590
|
badRequestError({
|
|
491
|
-
message: `Trigger
|
|
1591
|
+
message: `Trigger group does not support gateway connections: ${triggerGroupId}`
|
|
492
1592
|
})
|
|
493
1593
|
);
|
|
494
1594
|
}
|
|
1595
|
+
return { group, gateway: group.gateway };
|
|
1596
|
+
};
|
|
495
1597
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
1598
|
+
manager.onRequest('slates/trigger_group.gateway.connect', async ({ params }) => {
|
|
1599
|
+
let ctx = getContextFull();
|
|
1600
|
+
let { group, gateway } = getGatewayTriggerGroup(params.triggerGroupId);
|
|
1601
|
+
|
|
1602
|
+
let context = new SlateContext(
|
|
1603
|
+
ctx.config,
|
|
1604
|
+
{ state: params.state },
|
|
1605
|
+
ctx.auth?.output!,
|
|
1606
|
+
slate.spec,
|
|
1607
|
+
logger
|
|
1608
|
+
);
|
|
1609
|
+
let res = await traceProviderCall(
|
|
1610
|
+
{
|
|
1611
|
+
component: 'action',
|
|
1612
|
+
functionName: 'gatewayConnect',
|
|
1613
|
+
message: `Preparing gateway connection for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1614
|
+
successMessage: () =>
|
|
1615
|
+
`Prepared gateway connection for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1616
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while preparing a gateway connection`,
|
|
1617
|
+
metadata: {
|
|
1618
|
+
triggerGroupId: group.key,
|
|
1619
|
+
triggerGroupName: group.name,
|
|
1620
|
+
hasPreviousState: params.state !== null
|
|
1621
|
+
}
|
|
1622
|
+
},
|
|
1623
|
+
() => runWithContext(context, () => gateway.connect(context))
|
|
1624
|
+
);
|
|
1625
|
+
|
|
1626
|
+
return withRequestTraces(context, { url: res.url, state: res.state });
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
manager.onRequest('slates/trigger_group.gateway.receive', async ({ params }) => {
|
|
1630
|
+
let ctx = getContextFull();
|
|
1631
|
+
let { group, gateway } = getGatewayTriggerGroup(params.triggerGroupId);
|
|
1632
|
+
|
|
1633
|
+
let context = new SlateContext(
|
|
1634
|
+
ctx.config,
|
|
1635
|
+
{ state: params.state, frames: params.frames, closed: params.closed ?? null },
|
|
1636
|
+
ctx.auth?.output!,
|
|
1637
|
+
slate.spec,
|
|
1638
|
+
logger
|
|
1639
|
+
);
|
|
1640
|
+
let res = await traceProviderCall(
|
|
1641
|
+
{
|
|
1642
|
+
component: 'action',
|
|
1643
|
+
functionName: 'gatewayReceive',
|
|
1644
|
+
message: `Handling ${params.frames.length} gateway frame(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1645
|
+
successMessage: result =>
|
|
1646
|
+
`Handled gateway frames for trigger group ${formatEntityLabel(group.name, group.key)} (${result.events?.length ?? 0} event(s))`,
|
|
1647
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while handling gateway frames`,
|
|
1648
|
+
metadata: {
|
|
1649
|
+
triggerGroupId: group.key,
|
|
1650
|
+
triggerGroupName: group.name,
|
|
1651
|
+
frameCount: params.frames.length,
|
|
1652
|
+
closed: params.closed ?? null
|
|
503
1653
|
},
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
1654
|
+
onSuccess: result => ({
|
|
1655
|
+
eventCount: result.events?.length ?? 0,
|
|
1656
|
+
sendCount: result.send?.length ?? 0,
|
|
1657
|
+
close: result.close ?? null
|
|
1658
|
+
})
|
|
1659
|
+
},
|
|
1660
|
+
() => runWithContext(context, () => gateway.receive(context))
|
|
508
1661
|
);
|
|
509
1662
|
|
|
510
|
-
return {
|
|
1663
|
+
return withRequestTraces(context, {
|
|
1664
|
+
state: res.state,
|
|
1665
|
+
// Outbound frames (e.g. IDENTIFY) embed credentials; the hub restores them at the socket.
|
|
1666
|
+
send: new AuthConfigSecretRedactor(
|
|
1667
|
+
context._getAuthConfigForRedaction()
|
|
1668
|
+
).redactEmbedded(res.send ?? []),
|
|
1669
|
+
heartbeat: res.heartbeat ?? null,
|
|
1670
|
+
heartbeatAcked: res.heartbeatAcked,
|
|
1671
|
+
close: res.close ?? null,
|
|
1672
|
+
events: (res.events ?? []).map(event => ({
|
|
1673
|
+
payload: event.payload,
|
|
1674
|
+
idempotencyKey: event.idempotencyKey,
|
|
1675
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1676
|
+
}))
|
|
1677
|
+
});
|
|
511
1678
|
});
|
|
512
1679
|
});
|