@slates/provider-handler 1.0.0-rc.3 → 1.0.0-rc.31
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 +1462 -2
- package/dist/index.d.cts +8 -0
- package/dist/index.d.ts +6 -3
- package/dist/index.module.js +1439 -2
- package/package.json +12 -11
- package/src/attachments.test.ts +171 -0
- package/src/index.ts +1054 -136
- package/src/pQueue.test.ts +18 -0
- package/src/pQueue.ts +18 -0
- package/src/spec.ts +130 -21
- package/src/validation.ts +1 -1
- package/src/webhook.test.ts +65 -0
- package/src/webhook.ts +69 -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,180 @@
|
|
|
1
1
|
import { badRequestError, preconditionFailedError, ServiceError } from '@lowerdeck/error';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {
|
|
3
|
+
createSlatesProviderProtoHandler,
|
|
4
|
+
SLATES_PROTOCOL_VERSION,
|
|
5
|
+
type SlatesParticipant
|
|
6
|
+
} from '@slates/proto';
|
|
7
|
+
import {
|
|
8
|
+
redactUrlAttachmentSecrets,
|
|
9
|
+
runWithContext,
|
|
10
|
+
type Slate,
|
|
11
|
+
type SlateAttachment,
|
|
12
|
+
SlateContext,
|
|
13
|
+
type SlateLiveInvocationInfo,
|
|
14
|
+
SlateLogger,
|
|
15
|
+
type SlateLogListener,
|
|
16
|
+
SlatePublicContext,
|
|
17
|
+
uploadAttachmentDirect
|
|
18
|
+
} from '@slates/provider';
|
|
19
|
+
import { PQueue } from './pQueue';
|
|
20
|
+
import {
|
|
21
|
+
evaluateTriggerMatches,
|
|
22
|
+
getAction,
|
|
23
|
+
getActionWithType,
|
|
24
|
+
getAdapter,
|
|
25
|
+
getAuthMethod,
|
|
26
|
+
getTriggerGroup,
|
|
27
|
+
getWebhookAutoRegistration,
|
|
28
|
+
getWebhookManualRegistration,
|
|
29
|
+
mapAction,
|
|
30
|
+
mapAdapter,
|
|
31
|
+
mapAuthMethod,
|
|
32
|
+
mapTriggerGroup
|
|
33
|
+
} from './spec';
|
|
5
34
|
import { State } from './state';
|
|
6
35
|
import { toJsonSchema, validate } from './validation';
|
|
36
|
+
import { serializeWebhookHttpResponse } from './webhook';
|
|
37
|
+
|
|
38
|
+
let DEFAULT_MAX_ATTACHMENT_SIZE_BYTES = 100_000_000;
|
|
39
|
+
|
|
40
|
+
let routeAttachmentsThroughDirectUpload = async (
|
|
41
|
+
attachments: SlateAttachment[] | undefined,
|
|
42
|
+
live: SlateLiveInvocationInfo | null
|
|
43
|
+
): Promise<SlateAttachment[] | undefined> => {
|
|
44
|
+
if (!attachments || attachments.length === 0 || !live) return attachments;
|
|
45
|
+
|
|
46
|
+
let contentEntries = attachments
|
|
47
|
+
.map((attachment, index) => ({ attachment, index }))
|
|
48
|
+
.filter(entry => entry.attachment.content.type === 'content');
|
|
49
|
+
if (contentEntries.length === 0) return attachments;
|
|
50
|
+
|
|
51
|
+
let queue = new PQueue({ concurrency: 10 });
|
|
52
|
+
let replacements = new Map<number, SlateAttachment | null>();
|
|
53
|
+
|
|
54
|
+
await Promise.all(
|
|
55
|
+
contentEntries.map(entry =>
|
|
56
|
+
queue.add(async () => {
|
|
57
|
+
try {
|
|
58
|
+
let body =
|
|
59
|
+
entry.attachment.content.type === 'content'
|
|
60
|
+
? entry.attachment.content.encoding === 'base64'
|
|
61
|
+
? Buffer.from(entry.attachment.content.content, 'base64')
|
|
62
|
+
: Buffer.from(entry.attachment.content.content, 'utf-8')
|
|
63
|
+
: Buffer.alloc(0);
|
|
64
|
+
|
|
65
|
+
replacements.set(
|
|
66
|
+
entry.index,
|
|
67
|
+
await uploadAttachmentDirect({
|
|
68
|
+
live,
|
|
69
|
+
mimeType: entry.attachment.mimeType,
|
|
70
|
+
body
|
|
71
|
+
})
|
|
72
|
+
);
|
|
73
|
+
} catch {
|
|
74
|
+
// Upload failed or was interrupted -- we simply don't have this attachment, not a
|
|
75
|
+
// failed tool call.
|
|
76
|
+
replacements.set(entry.index, null);
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
)
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
return attachments
|
|
83
|
+
.map((attachment, index) =>
|
|
84
|
+
replacements.has(index) ? replacements.get(index) : attachment
|
|
85
|
+
)
|
|
86
|
+
.filter((a): a is SlateAttachment => a != null);
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
let isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
90
|
+
typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
91
|
+
|
|
92
|
+
let getObjectKeyCount = (value: unknown) =>
|
|
93
|
+
isRecord(value) ? Object.keys(value).length : undefined;
|
|
94
|
+
|
|
95
|
+
let DOWNLOAD_ATTACHMENT_URL_KEYS = new Set([
|
|
96
|
+
'downloadUrl',
|
|
97
|
+
'fileUrl',
|
|
98
|
+
'temporaryDownloadUrl',
|
|
99
|
+
'webContentLink'
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
let isAttachmentUrl = (value: string) => /^[a-z][a-z0-9+.-]*:\/\//i.test(value);
|
|
103
|
+
|
|
104
|
+
let collectOutputUrlAttachments = (
|
|
105
|
+
value: unknown,
|
|
106
|
+
seen = new Set<string>()
|
|
107
|
+
): SlateAttachment[] => {
|
|
108
|
+
if (Array.isArray(value)) {
|
|
109
|
+
return value.flatMap(item => collectOutputUrlAttachments(item, seen));
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!isRecord(value)) {
|
|
113
|
+
return [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let attachments: SlateAttachment[] = [];
|
|
117
|
+
|
|
118
|
+
for (let [key, nestedValue] of Object.entries(value)) {
|
|
119
|
+
if (
|
|
120
|
+
DOWNLOAD_ATTACHMENT_URL_KEYS.has(key) &&
|
|
121
|
+
typeof nestedValue === 'string' &&
|
|
122
|
+
nestedValue.length > 0 &&
|
|
123
|
+
isAttachmentUrl(nestedValue) &&
|
|
124
|
+
!seen.has(nestedValue)
|
|
125
|
+
) {
|
|
126
|
+
seen.add(nestedValue);
|
|
127
|
+
attachments.push({
|
|
128
|
+
content: {
|
|
129
|
+
type: 'url',
|
|
130
|
+
url: nestedValue
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
attachments.push(...collectOutputUrlAttachments(nestedValue, seen));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return attachments;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
let mergeAttachments = (
|
|
143
|
+
explicitAttachments: SlateAttachment[] | undefined,
|
|
144
|
+
output: unknown
|
|
145
|
+
): SlateAttachment[] | undefined => {
|
|
146
|
+
let attachments = [...(explicitAttachments ?? [])];
|
|
147
|
+
let seen = new Set(attachments.map(attachment => JSON.stringify(attachment)));
|
|
148
|
+
|
|
149
|
+
for (let attachment of collectOutputUrlAttachments(output)) {
|
|
150
|
+
let key = JSON.stringify(attachment);
|
|
151
|
+
if (seen.has(key)) continue;
|
|
152
|
+
seen.add(key);
|
|
153
|
+
attachments.push(attachment);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return attachments.length > 0 ? attachments : undefined;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
let toErrorMetadata = (error: unknown) => {
|
|
160
|
+
if (error instanceof Error) {
|
|
161
|
+
return {
|
|
162
|
+
errorName: error.name,
|
|
163
|
+
errorMessage: error.message,
|
|
164
|
+
errorStack: error.stack
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
errorValue: String(error)
|
|
170
|
+
};
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
let formatEntityLabel = (name: string, key: string) => `"${name}" (${key})`;
|
|
174
|
+
let resolveTraceMessage = <ResultType>(
|
|
175
|
+
message: string | ((result: ResultType) => string),
|
|
176
|
+
result: ResultType
|
|
177
|
+
) => (typeof message === 'function' ? message(result) : message);
|
|
7
178
|
|
|
8
179
|
export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
9
180
|
slate: Slate<ConfigType, AuthType>,
|
|
@@ -15,10 +186,92 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
15
186
|
|
|
16
187
|
let auth = new State<{ authenticationMethodId: string; output: AuthType } | null>(null);
|
|
17
188
|
let config = new State<{ value: ConfigType } | null>(null);
|
|
18
|
-
|
|
19
189
|
let session = new State<{ id: string; state: any } | null>(null);
|
|
20
190
|
|
|
191
|
+
let hubCapabilities = new State<{
|
|
192
|
+
attachments?: { directUpload?: { enabled: boolean; maxAttachmentSizeBytes?: number } };
|
|
193
|
+
triggers?: boolean;
|
|
194
|
+
} | null>(null);
|
|
195
|
+
let liveInvocation = new State<Pick<SlateLiveInvocationInfo, 'token' | 'baseUrl'> | null>(
|
|
196
|
+
null
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
let getLiveInvocation = (): SlateLiveInvocationInfo | null => {
|
|
200
|
+
let directUpload = hubCapabilities.get()?.attachments?.directUpload;
|
|
201
|
+
let live = liveInvocation.get();
|
|
202
|
+
|
|
203
|
+
if (!directUpload?.enabled || !live) return null;
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
...live,
|
|
207
|
+
maxAttachmentSizeBytes:
|
|
208
|
+
directUpload.maxAttachmentSizeBytes ?? DEFAULT_MAX_ATTACHMENT_SIZE_BYTES
|
|
209
|
+
};
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
let supportsTriggerGroups = () => !!hubCapabilities.get()?.triggers;
|
|
213
|
+
|
|
21
214
|
let logger = new SlateLogger(listeners);
|
|
215
|
+
let providerTrace = {
|
|
216
|
+
providerId: slate.spec.key,
|
|
217
|
+
providerName: slate.spec.name
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
let traceProviderCall = async <ResultType>(
|
|
221
|
+
trace: {
|
|
222
|
+
component: 'config' | 'auth' | 'action';
|
|
223
|
+
functionName: string;
|
|
224
|
+
message: string;
|
|
225
|
+
successMessage: string | ((result: ResultType) => string);
|
|
226
|
+
errorMessage?: string;
|
|
227
|
+
metadata?: Record<string, unknown>;
|
|
228
|
+
onSuccess?: (result: ResultType) => Record<string, unknown> | undefined;
|
|
229
|
+
},
|
|
230
|
+
handler: () => Promise<ResultType>
|
|
231
|
+
): Promise<ResultType> => {
|
|
232
|
+
let startedAt = Date.now();
|
|
233
|
+
|
|
234
|
+
logger.info({
|
|
235
|
+
...providerTrace,
|
|
236
|
+
...trace.metadata,
|
|
237
|
+
component: trace.component,
|
|
238
|
+
functionName: trace.functionName,
|
|
239
|
+
phase: 'start',
|
|
240
|
+
message: trace.message
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
let result = await handler();
|
|
245
|
+
let successMessage = resolveTraceMessage(trace.successMessage, result);
|
|
246
|
+
|
|
247
|
+
logger.info({
|
|
248
|
+
...providerTrace,
|
|
249
|
+
...trace.metadata,
|
|
250
|
+
...(trace.onSuccess?.(result) ?? {}),
|
|
251
|
+
component: trace.component,
|
|
252
|
+
functionName: trace.functionName,
|
|
253
|
+
phase: 'success',
|
|
254
|
+
durationMs: Date.now() - startedAt,
|
|
255
|
+
message: successMessage
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
return result;
|
|
259
|
+
} catch (error) {
|
|
260
|
+
logger.error({
|
|
261
|
+
...providerTrace,
|
|
262
|
+
...trace.metadata,
|
|
263
|
+
...toErrorMetadata(error),
|
|
264
|
+
component: trace.component,
|
|
265
|
+
functionName: trace.functionName,
|
|
266
|
+
phase: 'error',
|
|
267
|
+
durationMs: Date.now() - startedAt,
|
|
268
|
+
message:
|
|
269
|
+
trace.errorMessage ??
|
|
270
|
+
`${typeof trace.successMessage === 'string' ? trace.successMessage : trace.message} failed`
|
|
271
|
+
});
|
|
272
|
+
throw error;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
22
275
|
|
|
23
276
|
let getContextBasic = () => {
|
|
24
277
|
let currentProtocol = protocol.get();
|
|
@@ -65,6 +318,19 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
65
318
|
};
|
|
66
319
|
};
|
|
67
320
|
|
|
321
|
+
let getAuthConfig = (): Record<string, any> => config.get()?.value ?? {};
|
|
322
|
+
|
|
323
|
+
let getEmptyContext = () => new SlateContext({}, {}, {}, slate.spec as any, logger);
|
|
324
|
+
let getAuthContext = () =>
|
|
325
|
+
new SlateContext(getAuthConfig(), {}, {}, slate.spec as any, logger);
|
|
326
|
+
let withRequestTraces = <Result extends Record<string, any>>(
|
|
327
|
+
context: SlatePublicContext<any>,
|
|
328
|
+
result: Result
|
|
329
|
+
) => {
|
|
330
|
+
let requestTraces = context.getHttpTraces();
|
|
331
|
+
return requestTraces.length > 0 ? { ...result, requestTraces } : result;
|
|
332
|
+
};
|
|
333
|
+
|
|
68
334
|
manager.onNotification('slates/hello', async ({ params }) => {
|
|
69
335
|
protocol.set(params.protocol);
|
|
70
336
|
});
|
|
@@ -118,6 +384,29 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
118
384
|
});
|
|
119
385
|
});
|
|
120
386
|
|
|
387
|
+
manager.onNotification('slates/hub.capabilities.set', async ({ params }) => {
|
|
388
|
+
hubCapabilities.set(params.capabilities);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
manager.onNotification('slates/hub.live_invocation.set', async ({ params }) => {
|
|
392
|
+
liveInvocation.set({
|
|
393
|
+
token: params.token,
|
|
394
|
+
baseUrl: params.baseUrl
|
|
395
|
+
});
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
manager.onRequest('slates/provider.capabilities.get', async () => ({
|
|
399
|
+
capabilities: {
|
|
400
|
+
hub: {
|
|
401
|
+
capabilitiesNotification: true,
|
|
402
|
+
liveInvocation: true
|
|
403
|
+
},
|
|
404
|
+
provider: {
|
|
405
|
+
triggerGroups: true
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}));
|
|
409
|
+
|
|
121
410
|
manager.onRequest('slates/config.changed', async ({ params }) => {
|
|
122
411
|
getContextBasic();
|
|
123
412
|
|
|
@@ -133,15 +422,37 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
133
422
|
return { success: true, config: newConfig };
|
|
134
423
|
}
|
|
135
424
|
|
|
136
|
-
let
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
425
|
+
let context = getEmptyContext();
|
|
426
|
+
let updatedConfig = await traceProviderCall<{ config?: ConfigType } | undefined>(
|
|
427
|
+
{
|
|
428
|
+
component: 'config',
|
|
429
|
+
functionName: 'configChanged',
|
|
430
|
+
message: 'Running config change handler',
|
|
431
|
+
successMessage: 'Config change handler completed',
|
|
432
|
+
metadata: {
|
|
433
|
+
hasPreviousConfig: params.previousConfig !== null,
|
|
434
|
+
newConfigKeyCount: getObjectKeyCount(newConfig)
|
|
435
|
+
},
|
|
436
|
+
onSuccess: result => ({
|
|
437
|
+
returnedConfig: !!result?.config
|
|
438
|
+
})
|
|
439
|
+
},
|
|
440
|
+
() =>
|
|
441
|
+
runWithContext(context, async () =>
|
|
442
|
+
configChanged({
|
|
443
|
+
previousConfig: params.previousConfig as ConfigType | null,
|
|
444
|
+
newConfig
|
|
445
|
+
})
|
|
446
|
+
)
|
|
447
|
+
);
|
|
140
448
|
|
|
141
|
-
return
|
|
449
|
+
return withRequestTraces(context, {
|
|
450
|
+
success: true,
|
|
451
|
+
config: (updatedConfig?.config ?? newConfig) as Record<string, any>
|
|
452
|
+
});
|
|
142
453
|
});
|
|
143
454
|
|
|
144
|
-
manager.onRequest('slates/config.get_default', async (
|
|
455
|
+
manager.onRequest('slates/config.get_default', async () => {
|
|
145
456
|
getContextBasic();
|
|
146
457
|
|
|
147
458
|
let getDefaultConfig = slate.spec.config.handlers.getDefaultConfig;
|
|
@@ -149,32 +460,50 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
149
460
|
return { config: null };
|
|
150
461
|
}
|
|
151
462
|
|
|
152
|
-
let
|
|
153
|
-
|
|
463
|
+
let context = getEmptyContext();
|
|
464
|
+
let defaultConfig = await traceProviderCall<ConfigType>(
|
|
465
|
+
{
|
|
466
|
+
component: 'config',
|
|
467
|
+
functionName: 'getDefaultConfig',
|
|
468
|
+
message: 'Getting default config',
|
|
469
|
+
successMessage: 'Default config retrieved',
|
|
470
|
+
onSuccess: result => ({
|
|
471
|
+
configKeyCount: getObjectKeyCount(result)
|
|
472
|
+
})
|
|
473
|
+
},
|
|
474
|
+
() => runWithContext(context, async () => getDefaultConfig())
|
|
475
|
+
);
|
|
476
|
+
return withRequestTraces(context, {
|
|
477
|
+
config: (defaultConfig ?? null) as Record<string, any> | null
|
|
478
|
+
});
|
|
154
479
|
});
|
|
155
480
|
|
|
156
|
-
manager.onRequest('slates/config.schema.get', async (
|
|
481
|
+
manager.onRequest('slates/config.schema.get', async () => {
|
|
157
482
|
getContextBasic();
|
|
158
483
|
|
|
159
|
-
return {
|
|
484
|
+
return {
|
|
485
|
+
schema: toJsonSchema(slate.spec.configSchema),
|
|
486
|
+
docs: slate.spec.config.docsReferences ?? []
|
|
487
|
+
};
|
|
160
488
|
});
|
|
161
489
|
|
|
162
|
-
manager.onRequest('slates/provider.identify', async (
|
|
490
|
+
manager.onRequest('slates/provider.identify', async () => {
|
|
163
491
|
getContextBasic();
|
|
164
492
|
|
|
165
493
|
return {
|
|
166
|
-
protocol:
|
|
494
|
+
protocol: SLATES_PROTOCOL_VERSION,
|
|
167
495
|
provider: {
|
|
168
496
|
type: 'provider',
|
|
169
497
|
id: slate.spec.key,
|
|
170
498
|
name: slate.spec.name,
|
|
171
499
|
description: slate.spec.description,
|
|
172
500
|
metadata: slate.spec.parameters.metadata
|
|
173
|
-
}
|
|
501
|
+
},
|
|
502
|
+
docs: slate.spec.docs ?? []
|
|
174
503
|
};
|
|
175
504
|
});
|
|
176
505
|
|
|
177
|
-
manager.onRequest('slates/auth.methods.list', async (
|
|
506
|
+
manager.onRequest('slates/auth.methods.list', async () => {
|
|
178
507
|
getContextBasic();
|
|
179
508
|
|
|
180
509
|
return {
|
|
@@ -199,7 +528,25 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
199
528
|
return { input: null };
|
|
200
529
|
}
|
|
201
530
|
|
|
202
|
-
|
|
531
|
+
let context = getEmptyContext();
|
|
532
|
+
let input = await traceProviderCall(
|
|
533
|
+
{
|
|
534
|
+
component: 'auth',
|
|
535
|
+
functionName: 'getDefaultInput',
|
|
536
|
+
message: 'Getting default authentication input',
|
|
537
|
+
successMessage: 'Default authentication input retrieved',
|
|
538
|
+
metadata: {
|
|
539
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
540
|
+
authenticationMethodName: authMethod.name
|
|
541
|
+
},
|
|
542
|
+
onSuccess: result => ({
|
|
543
|
+
inputKeyCount: getObjectKeyCount(result)
|
|
544
|
+
})
|
|
545
|
+
},
|
|
546
|
+
() => runWithContext(context, () => authMethod.getDefaultInput!())
|
|
547
|
+
);
|
|
548
|
+
|
|
549
|
+
return withRequestTraces(context, { input });
|
|
203
550
|
});
|
|
204
551
|
|
|
205
552
|
manager.onRequest('slates/auth.input.changed', async ({ params }) => {
|
|
@@ -210,12 +557,36 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
210
557
|
return { success: true, input: params.newInput };
|
|
211
558
|
}
|
|
212
559
|
|
|
213
|
-
let
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
560
|
+
let context = getEmptyContext();
|
|
561
|
+
let updatedInput = await traceProviderCall(
|
|
562
|
+
{
|
|
563
|
+
component: 'auth',
|
|
564
|
+
functionName: 'onInputChanged',
|
|
565
|
+
message: 'Running authentication input change handler',
|
|
566
|
+
successMessage: 'Authentication input change handler completed',
|
|
567
|
+
metadata: {
|
|
568
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
569
|
+
authenticationMethodName: authMethod.name,
|
|
570
|
+
hasPreviousInput: params.previousInput !== null,
|
|
571
|
+
newInputKeyCount: getObjectKeyCount(params.newInput)
|
|
572
|
+
},
|
|
573
|
+
onSuccess: result => ({
|
|
574
|
+
returnedInput: !!result?.input
|
|
575
|
+
})
|
|
576
|
+
},
|
|
577
|
+
() =>
|
|
578
|
+
runWithContext(context, () =>
|
|
579
|
+
authMethod.onInputChanged!({
|
|
580
|
+
previousInput: params.previousInput as any | null,
|
|
581
|
+
newInput: params.newInput
|
|
582
|
+
})
|
|
583
|
+
)
|
|
584
|
+
);
|
|
217
585
|
|
|
218
|
-
return
|
|
586
|
+
return withRequestTraces(context, {
|
|
587
|
+
success: true,
|
|
588
|
+
input: updatedInput?.input ?? params.newInput
|
|
589
|
+
});
|
|
219
590
|
});
|
|
220
591
|
|
|
221
592
|
manager.onRequest('slates/auth.output.get', async ({ params }) => {
|
|
@@ -234,8 +605,35 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
234
605
|
}
|
|
235
606
|
|
|
236
607
|
if ('getOutput' in authMethod) {
|
|
237
|
-
let
|
|
238
|
-
|
|
608
|
+
let context = getAuthContext();
|
|
609
|
+
let outputRes = await traceProviderCall(
|
|
610
|
+
{
|
|
611
|
+
component: 'auth',
|
|
612
|
+
functionName: 'getOutput',
|
|
613
|
+
message: 'Getting authentication output',
|
|
614
|
+
successMessage: 'Authentication output retrieved',
|
|
615
|
+
metadata: {
|
|
616
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
617
|
+
authenticationMethodName: authMethod.name,
|
|
618
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
619
|
+
},
|
|
620
|
+
onSuccess: result => ({
|
|
621
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
622
|
+
scopeCount: result.scopes?.length ?? 0
|
|
623
|
+
})
|
|
624
|
+
},
|
|
625
|
+
() =>
|
|
626
|
+
runWithContext(context, () =>
|
|
627
|
+
authMethod.getOutput({
|
|
628
|
+
input,
|
|
629
|
+
config: getAuthConfig()
|
|
630
|
+
})
|
|
631
|
+
)
|
|
632
|
+
);
|
|
633
|
+
return withRequestTraces(context, {
|
|
634
|
+
output: outputRes.output,
|
|
635
|
+
scopes: outputRes.scopes
|
|
636
|
+
});
|
|
239
637
|
}
|
|
240
638
|
|
|
241
639
|
return { output: input as any };
|
|
@@ -246,20 +644,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
246
644
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
247
645
|
|
|
248
646
|
if ('handleCallback' in authMethod) {
|
|
249
|
-
let
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
647
|
+
let context = getEmptyContext();
|
|
648
|
+
let callbackRes = await traceProviderCall(
|
|
649
|
+
{
|
|
650
|
+
component: 'auth',
|
|
651
|
+
functionName: 'handleCallback',
|
|
652
|
+
message: 'Handling authentication callback',
|
|
653
|
+
successMessage: 'Authentication callback handled',
|
|
654
|
+
metadata: {
|
|
655
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
656
|
+
authenticationMethodName: authMethod.name,
|
|
657
|
+
scopeCount: params.scopes.length,
|
|
658
|
+
hasCallbackState: !!params.callbackState
|
|
659
|
+
},
|
|
660
|
+
onSuccess: result => ({
|
|
661
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
662
|
+
returnedInput: !!result.input,
|
|
663
|
+
returnedScopeCount: result.scopes?.length
|
|
664
|
+
})
|
|
665
|
+
},
|
|
666
|
+
() =>
|
|
667
|
+
runWithContext(context, () =>
|
|
668
|
+
authMethod.handleCallback({
|
|
669
|
+
code: params.code,
|
|
670
|
+
state: params.state,
|
|
671
|
+
redirectUri: params.redirectUri,
|
|
672
|
+
input: params.input,
|
|
673
|
+
clientId: params.clientId,
|
|
674
|
+
clientSecret: params.clientSecret,
|
|
675
|
+
scopes: params.scopes,
|
|
676
|
+
callbackParams: params.callbackParams || {},
|
|
677
|
+
callbackState: params.callbackState || {},
|
|
678
|
+
config: getAuthConfig()
|
|
679
|
+
})
|
|
680
|
+
)
|
|
681
|
+
);
|
|
258
682
|
|
|
259
|
-
return {
|
|
683
|
+
return withRequestTraces(context, {
|
|
260
684
|
output: callbackRes.output,
|
|
261
|
-
input: callbackRes.input
|
|
262
|
-
|
|
685
|
+
input: callbackRes.input,
|
|
686
|
+
scopes: callbackRes.scopes
|
|
687
|
+
});
|
|
263
688
|
}
|
|
264
689
|
|
|
265
690
|
throw new ServiceError(
|
|
@@ -274,19 +699,43 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
274
699
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
275
700
|
|
|
276
701
|
if ('getAuthorizationUrl' in authMethod) {
|
|
277
|
-
let
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
702
|
+
let context = getEmptyContext();
|
|
703
|
+
let urlRes = await traceProviderCall(
|
|
704
|
+
{
|
|
705
|
+
component: 'auth',
|
|
706
|
+
functionName: 'getAuthorizationUrl',
|
|
707
|
+
message: 'Getting authentication authorization URL',
|
|
708
|
+
successMessage: 'Authentication authorization URL retrieved',
|
|
709
|
+
metadata: {
|
|
710
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
711
|
+
authenticationMethodName: authMethod.name,
|
|
712
|
+
scopeCount: params.scopes.length,
|
|
713
|
+
inputKeyCount: getObjectKeyCount(params.input)
|
|
714
|
+
},
|
|
715
|
+
onSuccess: result => ({
|
|
716
|
+
returnedInput: !!result.input,
|
|
717
|
+
hasCallbackState: !!result.callbackState
|
|
718
|
+
})
|
|
719
|
+
},
|
|
720
|
+
() =>
|
|
721
|
+
runWithContext(context, () =>
|
|
722
|
+
authMethod.getAuthorizationUrl({
|
|
723
|
+
redirectUri: params.redirectUri,
|
|
724
|
+
state: params.state,
|
|
725
|
+
input: params.input,
|
|
726
|
+
clientId: params.clientId,
|
|
727
|
+
clientSecret: params.clientSecret,
|
|
728
|
+
scopes: params.scopes,
|
|
729
|
+
config: getAuthConfig()
|
|
730
|
+
})
|
|
731
|
+
)
|
|
732
|
+
);
|
|
285
733
|
|
|
286
|
-
return {
|
|
734
|
+
return withRequestTraces(context, {
|
|
287
735
|
authorizationUrl: urlRes.url,
|
|
288
|
-
input: urlRes.input
|
|
289
|
-
|
|
736
|
+
input: urlRes.input,
|
|
737
|
+
callbackState: urlRes.callbackState
|
|
738
|
+
});
|
|
290
739
|
}
|
|
291
740
|
|
|
292
741
|
throw new ServiceError(
|
|
@@ -301,15 +750,47 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
301
750
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
302
751
|
|
|
303
752
|
if (authMethod.getProfile) {
|
|
304
|
-
let
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
753
|
+
let context = getAuthContext();
|
|
754
|
+
let profileRes = await traceProviderCall(
|
|
755
|
+
{
|
|
756
|
+
component: 'auth',
|
|
757
|
+
functionName: 'getProfile',
|
|
758
|
+
message: 'Getting authentication profile',
|
|
759
|
+
successMessage: 'Authentication profile retrieved',
|
|
760
|
+
metadata: {
|
|
761
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
762
|
+
authenticationMethodName: authMethod.name,
|
|
763
|
+
scopeCount: params.scopes.length,
|
|
764
|
+
inputKeyCount: getObjectKeyCount(params.input),
|
|
765
|
+
outputKeyCount: getObjectKeyCount(params.output)
|
|
766
|
+
},
|
|
767
|
+
onSuccess: result => ({
|
|
768
|
+
profileKeyCount: getObjectKeyCount(result.profile)
|
|
769
|
+
})
|
|
770
|
+
},
|
|
771
|
+
() =>
|
|
772
|
+
runWithContext(context, () => {
|
|
773
|
+
if (authMethod.type === 'auth.oauth') {
|
|
774
|
+
return authMethod.getProfile!({
|
|
775
|
+
output: params.output as any,
|
|
776
|
+
input: params.input,
|
|
777
|
+
scopes: params.scopes,
|
|
778
|
+
config: getAuthConfig()
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
return authMethod.getProfile!({
|
|
783
|
+
output: params.output as any,
|
|
784
|
+
input: params.input,
|
|
785
|
+
scopes: params.scopes,
|
|
786
|
+
config: getAuthConfig()
|
|
787
|
+
})!;
|
|
788
|
+
})
|
|
789
|
+
);
|
|
309
790
|
|
|
310
|
-
return {
|
|
791
|
+
return withRequestTraces(context, {
|
|
311
792
|
profile: profileRes.profile
|
|
312
|
-
};
|
|
793
|
+
});
|
|
313
794
|
}
|
|
314
795
|
|
|
315
796
|
throw new ServiceError(
|
|
@@ -324,18 +805,53 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
324
805
|
let authMethod = getAuthMethod(slate, params.authenticationMethodId);
|
|
325
806
|
|
|
326
807
|
if ('handleTokenRefresh' in authMethod && authMethod.handleTokenRefresh) {
|
|
327
|
-
let
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
808
|
+
let context = getAuthContext();
|
|
809
|
+
let refreshRes = await traceProviderCall(
|
|
810
|
+
{
|
|
811
|
+
component: 'auth',
|
|
812
|
+
functionName: 'handleTokenRefresh',
|
|
813
|
+
message: 'Refreshing authentication token',
|
|
814
|
+
successMessage: 'Authentication token refreshed',
|
|
815
|
+
metadata: {
|
|
816
|
+
authenticationMethodId: params.authenticationMethodId,
|
|
817
|
+
authenticationMethodName: authMethod.name,
|
|
818
|
+
scopeCount: params.scopes.length,
|
|
819
|
+
inputKeyCount: getObjectKeyCount(params.input),
|
|
820
|
+
outputKeyCount: getObjectKeyCount(params.output)
|
|
821
|
+
},
|
|
822
|
+
onSuccess: result => ({
|
|
823
|
+
refreshedOutputKeyCount: getObjectKeyCount(result.output),
|
|
824
|
+
returnedInput: !!result.input
|
|
825
|
+
})
|
|
826
|
+
},
|
|
827
|
+
() =>
|
|
828
|
+
runWithContext(context, () => {
|
|
829
|
+
if (authMethod.type === 'auth.oauth') {
|
|
830
|
+
return authMethod.handleTokenRefresh!({
|
|
831
|
+
output: params.output as any,
|
|
832
|
+
input: params.input,
|
|
833
|
+
clientId: params.clientId,
|
|
834
|
+
clientSecret: params.clientSecret,
|
|
835
|
+
scopes: params.scopes,
|
|
836
|
+
config: getAuthConfig()
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
return authMethod.handleTokenRefresh!({
|
|
841
|
+
output: params.output as any,
|
|
842
|
+
input: params.input,
|
|
843
|
+
clientId: params.clientId,
|
|
844
|
+
clientSecret: params.clientSecret,
|
|
845
|
+
scopes: params.scopes,
|
|
846
|
+
config: getAuthConfig()
|
|
847
|
+
});
|
|
848
|
+
})
|
|
849
|
+
);
|
|
334
850
|
|
|
335
|
-
return {
|
|
851
|
+
return withRequestTraces(context, {
|
|
336
852
|
output: refreshRes.output,
|
|
337
853
|
input: refreshRes.input
|
|
338
|
-
};
|
|
854
|
+
});
|
|
339
855
|
}
|
|
340
856
|
|
|
341
857
|
throw new ServiceError(
|
|
@@ -348,8 +864,33 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
348
864
|
manager.onRequest('slates/actions.list', async ({ params }) => {
|
|
349
865
|
getContextBasic();
|
|
350
866
|
|
|
867
|
+
let actions = params.includeAdapterActions
|
|
868
|
+
? slate.actions
|
|
869
|
+
: slate.actions.filter(action => !action.adapter);
|
|
870
|
+
|
|
871
|
+
if (!supportsTriggerGroups()) {
|
|
872
|
+
actions = actions.filter(action => action.type !== 'trigger');
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
return {
|
|
876
|
+
actions: actions.map(a => mapAction(slate, a))
|
|
877
|
+
};
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
manager.onRequest('slates/adapters.list', async () => {
|
|
881
|
+
getContextBasic();
|
|
882
|
+
|
|
351
883
|
return {
|
|
352
|
-
|
|
884
|
+
adapters: slate.adapters.map(mapAdapter)
|
|
885
|
+
};
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
manager.onRequest('slates/adapter.get', async ({ params }) => {
|
|
889
|
+
getContextBasic();
|
|
890
|
+
let adapter = getAdapter(slate, params.adapterId);
|
|
891
|
+
|
|
892
|
+
return {
|
|
893
|
+
adapter: mapAdapter(adapter)
|
|
353
894
|
};
|
|
354
895
|
});
|
|
355
896
|
|
|
@@ -362,8 +903,24 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
362
903
|
};
|
|
363
904
|
});
|
|
364
905
|
|
|
906
|
+
manager.onRequest('slates/trigger_groups.list', async () => {
|
|
907
|
+
getContextBasic();
|
|
908
|
+
|
|
909
|
+
return {
|
|
910
|
+
triggerGroups: slate.triggerGroups.map(mapTriggerGroup)
|
|
911
|
+
};
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
manager.onRequest('slates/trigger_group.get', async ({ params }) => {
|
|
915
|
+
getContextBasic();
|
|
916
|
+
let triggerGroup = getTriggerGroup(slate, params.triggerGroupId);
|
|
917
|
+
|
|
918
|
+
return {
|
|
919
|
+
triggerGroup: mapTriggerGroup(triggerGroup)
|
|
920
|
+
};
|
|
921
|
+
});
|
|
922
|
+
|
|
365
923
|
manager.onRequest('slates/action.tool.invoke', async ({ params }) => {
|
|
366
|
-
let ctx = getContextFull();
|
|
367
924
|
let action = getActionWithType(slate, 'tool', params.actionId);
|
|
368
925
|
|
|
369
926
|
let input = validate(
|
|
@@ -373,11 +930,69 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
373
930
|
`Invalid input for tool ID: ${params.actionId}`
|
|
374
931
|
);
|
|
375
932
|
|
|
376
|
-
let
|
|
377
|
-
|
|
378
|
-
|
|
933
|
+
let invoke = async (context: SlatePublicContext<any>) => {
|
|
934
|
+
let res = await traceProviderCall(
|
|
935
|
+
{
|
|
936
|
+
component: 'action',
|
|
937
|
+
functionName: 'handleInvocation',
|
|
938
|
+
message: `Starting tool ${formatEntityLabel(action.name, action.key)}`,
|
|
939
|
+
successMessage: `Completed tool ${formatEntityLabel(action.name, action.key)}`,
|
|
940
|
+
errorMessage: `Tool ${formatEntityLabel(action.name, action.key)} failed`,
|
|
941
|
+
metadata: {
|
|
942
|
+
actionId: action.key,
|
|
943
|
+
actionName: action.name,
|
|
944
|
+
actionType: action.type,
|
|
945
|
+
isPublic: action.isPublic,
|
|
946
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
947
|
+
},
|
|
948
|
+
onSuccess: result => ({
|
|
949
|
+
hasMessage: !!result.message,
|
|
950
|
+
actionResultMessage: result.message,
|
|
951
|
+
outputKeyCount: getObjectKeyCount(result.output),
|
|
952
|
+
attachmentCount: result.attachments?.length
|
|
953
|
+
})
|
|
954
|
+
},
|
|
955
|
+
() => runWithContext(context, () => action.handleInvocation(context as any))
|
|
956
|
+
);
|
|
379
957
|
|
|
380
|
-
|
|
958
|
+
let contextAttachments = await context._finalizeAttachments();
|
|
959
|
+
let merged = mergeAttachments(
|
|
960
|
+
[...(res.attachments ?? []), ...contextAttachments],
|
|
961
|
+
res.output
|
|
962
|
+
);
|
|
963
|
+
|
|
964
|
+
let redacted = redactUrlAttachmentSecrets(
|
|
965
|
+
merged,
|
|
966
|
+
context._getAuthConfigForRedaction()
|
|
967
|
+
);
|
|
968
|
+
let finalAttachments = await routeAttachmentsThroughDirectUpload(
|
|
969
|
+
redacted,
|
|
970
|
+
getLiveInvocation()
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
return withRequestTraces(context, {
|
|
974
|
+
output: res.output,
|
|
975
|
+
message: res.message,
|
|
976
|
+
attachments: finalAttachments
|
|
977
|
+
});
|
|
978
|
+
};
|
|
979
|
+
|
|
980
|
+
if (action.isPublic) {
|
|
981
|
+
getContextBasic();
|
|
982
|
+
return invoke(new SlatePublicContext(input, slate.spec, logger, getLiveInvocation()));
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
let ctx = getContextFull();
|
|
986
|
+
return invoke(
|
|
987
|
+
new SlateContext(
|
|
988
|
+
ctx.config,
|
|
989
|
+
input,
|
|
990
|
+
ctx.auth?.output!,
|
|
991
|
+
slate.spec,
|
|
992
|
+
logger,
|
|
993
|
+
getLiveInvocation()
|
|
994
|
+
)
|
|
995
|
+
);
|
|
381
996
|
});
|
|
382
997
|
|
|
383
998
|
manager.onRequest('slates/action.trigger.map_event', async ({ params }) => {
|
|
@@ -391,46 +1006,282 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
391
1006
|
`Invalid event for trigger ID: ${params.actionId}`
|
|
392
1007
|
);
|
|
393
1008
|
|
|
394
|
-
let
|
|
395
|
-
|
|
1009
|
+
let context = new SlateContext(ctx.config, input, ctx.auth?.output!, slate.spec, logger);
|
|
1010
|
+
let res = await traceProviderCall(
|
|
1011
|
+
{
|
|
1012
|
+
component: 'action',
|
|
1013
|
+
functionName: 'handleEvent',
|
|
1014
|
+
message: `Mapping event for trigger ${formatEntityLabel(action.name, action.key)}`,
|
|
1015
|
+
successMessage: result =>
|
|
1016
|
+
`Mapped trigger event "${result.type}" for ${formatEntityLabel(action.name, action.key)}`,
|
|
1017
|
+
errorMessage: `Trigger ${formatEntityLabel(action.name, action.key)} failed while mapping an event`,
|
|
1018
|
+
metadata: {
|
|
1019
|
+
actionId: action.key,
|
|
1020
|
+
actionName: action.name,
|
|
1021
|
+
actionType: action.type,
|
|
1022
|
+
inputKeyCount: getObjectKeyCount(input)
|
|
1023
|
+
},
|
|
1024
|
+
onSuccess: result => ({
|
|
1025
|
+
eventType: result.type,
|
|
1026
|
+
hasEventId: !!result.id,
|
|
1027
|
+
outputKeyCount: getObjectKeyCount(result.output)
|
|
1028
|
+
})
|
|
1029
|
+
},
|
|
1030
|
+
() => runWithContext(context, () => action.map(context))
|
|
396
1031
|
);
|
|
397
1032
|
|
|
398
|
-
return { id: res.id, type: res.type, output: res.output };
|
|
1033
|
+
return withRequestTraces(context, { id: res.id, type: res.type, output: res.output });
|
|
399
1034
|
});
|
|
400
1035
|
|
|
401
1036
|
manager.onRequest('slates/action.trigger.poll_events', async ({ params }) => {
|
|
402
|
-
|
|
403
|
-
let action = getActionWithType(slate, 'trigger', params.actionId);
|
|
1037
|
+
getContextBasic();
|
|
404
1038
|
|
|
405
|
-
if (
|
|
1039
|
+
if (supportsTriggerGroups()) {
|
|
406
1040
|
throw new ServiceError(
|
|
407
1041
|
badRequestError({
|
|
408
|
-
message: `
|
|
1042
|
+
message: `Legacy trigger polling is disabled once trigger_group support is announced: ${params.actionId}`
|
|
409
1043
|
})
|
|
410
1044
|
);
|
|
411
1045
|
}
|
|
412
1046
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
1047
|
+
return { inputs: [], updatedState: params.state };
|
|
1048
|
+
});
|
|
1049
|
+
|
|
1050
|
+
manager.onRequest('slates/action.trigger.webhook_handle', async ({ params }) => {
|
|
1051
|
+
getContextBasic();
|
|
1052
|
+
|
|
1053
|
+
if (supportsTriggerGroups()) {
|
|
1054
|
+
throw new ServiceError(
|
|
1055
|
+
badRequestError({
|
|
1056
|
+
message: `Legacy trigger webhook handling is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1057
|
+
})
|
|
1058
|
+
);
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
return { inputs: [], updatedState: params.state, response: null };
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
manager.onRequest('slates/action.trigger.webhook_register', async ({ params }) => {
|
|
1065
|
+
getContextBasic();
|
|
1066
|
+
|
|
1067
|
+
if (supportsTriggerGroups()) {
|
|
1068
|
+
throw new ServiceError(
|
|
1069
|
+
badRequestError({
|
|
1070
|
+
message: `Legacy trigger webhook registration is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1071
|
+
})
|
|
1072
|
+
);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
return { registrationDetails: null };
|
|
1076
|
+
});
|
|
1077
|
+
|
|
1078
|
+
manager.onRequest('slates/action.trigger.webhook_unregister', async ({ params }) => {
|
|
1079
|
+
getContextBasic();
|
|
1080
|
+
|
|
1081
|
+
if (supportsTriggerGroups()) {
|
|
1082
|
+
throw new ServiceError(
|
|
1083
|
+
badRequestError({
|
|
1084
|
+
message: `Legacy trigger webhook unregistration is disabled once trigger_group support is announced: ${params.actionId}`
|
|
1085
|
+
})
|
|
1086
|
+
);
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
return {};
|
|
1090
|
+
});
|
|
1091
|
+
|
|
1092
|
+
manager.onRequest('slates/trigger_group.webhook.targets_list', async ({ params }) => {
|
|
1093
|
+
let ctx = getContextFull();
|
|
1094
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1095
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1096
|
+
|
|
1097
|
+
let context = new SlateContext(
|
|
1098
|
+
ctx.config,
|
|
1099
|
+
{ pageToken: params.pageToken ?? null },
|
|
1100
|
+
ctx.auth?.output!,
|
|
1101
|
+
slate.spec,
|
|
1102
|
+
logger
|
|
1103
|
+
);
|
|
1104
|
+
let res = await traceProviderCall(
|
|
1105
|
+
{
|
|
1106
|
+
component: 'action',
|
|
1107
|
+
functionName: 'webhookTargetList',
|
|
1108
|
+
message: `Listing webhook targets for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1109
|
+
successMessage: result =>
|
|
1110
|
+
`Listed ${result.targets.length} webhook target(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1111
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while listing webhook targets`,
|
|
1112
|
+
metadata: {
|
|
1113
|
+
triggerGroupId: group.key,
|
|
1114
|
+
triggerGroupName: group.name
|
|
1115
|
+
},
|
|
1116
|
+
onSuccess: result => ({
|
|
1117
|
+
targetCount: result.targets.length,
|
|
1118
|
+
hasNextPageToken: result.nextPageToken !== null
|
|
1119
|
+
})
|
|
1120
|
+
},
|
|
1121
|
+
() => runWithContext(context, () => autoRegistration.webhookTargetList(context))
|
|
421
1122
|
);
|
|
422
1123
|
|
|
423
|
-
return
|
|
1124
|
+
return withRequestTraces(context, {
|
|
1125
|
+
targets: res.targets,
|
|
1126
|
+
nextPageToken: res.nextPageToken ?? null
|
|
1127
|
+
});
|
|
424
1128
|
});
|
|
425
1129
|
|
|
426
|
-
manager.onRequest('slates/
|
|
1130
|
+
manager.onRequest('slates/trigger_group.webhook.register', async ({ params }) => {
|
|
427
1131
|
let ctx = getContextFull();
|
|
428
|
-
let
|
|
1132
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1133
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1134
|
+
|
|
1135
|
+
let context = new SlateContext(
|
|
1136
|
+
ctx.config,
|
|
1137
|
+
{
|
|
1138
|
+
webhookTargetIdentifier: params.webhookTargetIdentifier,
|
|
1139
|
+
webhookTargetPayload: params.webhookTargetPayload,
|
|
1140
|
+
webhookUrl: params.webhookUrl
|
|
1141
|
+
},
|
|
1142
|
+
ctx.auth?.output!,
|
|
1143
|
+
slate.spec,
|
|
1144
|
+
logger
|
|
1145
|
+
);
|
|
1146
|
+
let res = await traceProviderCall(
|
|
1147
|
+
{
|
|
1148
|
+
component: 'action',
|
|
1149
|
+
functionName: 'webhookRegister',
|
|
1150
|
+
message: `Registering webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1151
|
+
successMessage: `Registered webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1152
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while registering a webhook`,
|
|
1153
|
+
metadata: {
|
|
1154
|
+
triggerGroupId: group.key,
|
|
1155
|
+
triggerGroupName: group.name,
|
|
1156
|
+
webhookTargetIdentifier: params.webhookTargetIdentifier
|
|
1157
|
+
}
|
|
1158
|
+
},
|
|
1159
|
+
() => runWithContext(context, () => autoRegistration.webhookRegister(context))
|
|
1160
|
+
);
|
|
1161
|
+
|
|
1162
|
+
return withRequestTraces(context, {
|
|
1163
|
+
webhookRegistrationIdentifier: res.webhookRegistrationIdentifier,
|
|
1164
|
+
webhookRegistrationPayload: res.webhookRegistrationPayload
|
|
1165
|
+
});
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
manager.onRequest('slates/trigger_group.webhook.unregister', async ({ params }) => {
|
|
1169
|
+
let ctx = getContextFull();
|
|
1170
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1171
|
+
let autoRegistration = getWebhookAutoRegistration(group);
|
|
1172
|
+
|
|
1173
|
+
let context = new SlateContext(
|
|
1174
|
+
ctx.config,
|
|
1175
|
+
{
|
|
1176
|
+
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier,
|
|
1177
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1178
|
+
},
|
|
1179
|
+
ctx.auth?.output!,
|
|
1180
|
+
slate.spec,
|
|
1181
|
+
logger
|
|
1182
|
+
);
|
|
1183
|
+
await traceProviderCall(
|
|
1184
|
+
{
|
|
1185
|
+
component: 'action',
|
|
1186
|
+
functionName: 'webhookUnregister',
|
|
1187
|
+
message: `Unregistering webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1188
|
+
successMessage: `Unregistered webhook for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1189
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while unregistering a webhook`,
|
|
1190
|
+
metadata: {
|
|
1191
|
+
triggerGroupId: group.key,
|
|
1192
|
+
triggerGroupName: group.name,
|
|
1193
|
+
webhookRegistrationIdentifier: params.webhookRegistrationIdentifier
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
() => runWithContext(context, () => autoRegistration.webhookUnregister(context))
|
|
1197
|
+
);
|
|
429
1198
|
|
|
430
|
-
|
|
1199
|
+
return withRequestTraces(context, {});
|
|
1200
|
+
});
|
|
1201
|
+
|
|
1202
|
+
manager.onRequest('slates/trigger_group.webhook.manual_setup', async ({ params }) => {
|
|
1203
|
+
getContextBasic();
|
|
1204
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1205
|
+
let manualRegistration = getWebhookManualRegistration(group);
|
|
1206
|
+
|
|
1207
|
+
let context = new SlateContext(
|
|
1208
|
+
{},
|
|
1209
|
+
{ webhookUrl: params.webhookUrl },
|
|
1210
|
+
{},
|
|
1211
|
+
slate.spec as any,
|
|
1212
|
+
logger
|
|
1213
|
+
);
|
|
1214
|
+
let res = await traceProviderCall(
|
|
1215
|
+
{
|
|
1216
|
+
component: 'action',
|
|
1217
|
+
functionName: 'webhookManualSetup',
|
|
1218
|
+
message: `Building manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1219
|
+
successMessage: `Built manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1220
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while building a manual webhook setup`,
|
|
1221
|
+
metadata: {
|
|
1222
|
+
triggerGroupId: group.key,
|
|
1223
|
+
triggerGroupName: group.name
|
|
1224
|
+
}
|
|
1225
|
+
},
|
|
1226
|
+
() => runWithContext(context, () => manualRegistration.setup(context))
|
|
1227
|
+
);
|
|
1228
|
+
|
|
1229
|
+
return {
|
|
1230
|
+
webhookSetupDocument: res.webhookSetupDocument,
|
|
1231
|
+
partialWebhookRegistrationPayload: res.partialWebhookRegistrationPayload
|
|
1232
|
+
};
|
|
1233
|
+
});
|
|
1234
|
+
|
|
1235
|
+
manager.onRequest('slates/trigger_group.webhook.manual_finish', async ({ params }) => {
|
|
1236
|
+
getContextBasic();
|
|
1237
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1238
|
+
let manualRegistration = getWebhookManualRegistration(group);
|
|
1239
|
+
|
|
1240
|
+
if (!manualRegistration.finish) {
|
|
1241
|
+
throw new ServiceError(
|
|
1242
|
+
badRequestError({
|
|
1243
|
+
message: `Trigger group does not support manual webhook finish: ${params.triggerGroupId}`
|
|
1244
|
+
})
|
|
1245
|
+
);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
let context = new SlateContext(
|
|
1249
|
+
{},
|
|
1250
|
+
{
|
|
1251
|
+
webhookUrl: params.webhookUrl,
|
|
1252
|
+
partialWebhookRegistrationPayload: params.partialWebhookRegistrationPayload,
|
|
1253
|
+
userWebhookRegistrationPayload: params.userWebhookRegistrationPayload
|
|
1254
|
+
},
|
|
1255
|
+
{},
|
|
1256
|
+
slate.spec as any,
|
|
1257
|
+
logger
|
|
1258
|
+
);
|
|
1259
|
+
let res = await traceProviderCall(
|
|
1260
|
+
{
|
|
1261
|
+
component: 'action',
|
|
1262
|
+
functionName: 'webhookManualFinish',
|
|
1263
|
+
message: `Finishing manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1264
|
+
successMessage: `Finished manual webhook setup for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1265
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while finishing a manual webhook setup`,
|
|
1266
|
+
metadata: {
|
|
1267
|
+
triggerGroupId: group.key,
|
|
1268
|
+
triggerGroupName: group.name
|
|
1269
|
+
}
|
|
1270
|
+
},
|
|
1271
|
+
() => runWithContext(context, () => manualRegistration.finish!(context))
|
|
1272
|
+
);
|
|
1273
|
+
|
|
1274
|
+
return withRequestTraces(context, {
|
|
1275
|
+
webhookRegistrationPayload: res.webhookRegistrationPayload
|
|
1276
|
+
});
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
manager.onRequest('slates/trigger_group.webhook.process', async ({ params }) => {
|
|
1280
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1281
|
+
if (group.source !== 'webhook' || !group.webhook) {
|
|
431
1282
|
throw new ServiceError(
|
|
432
1283
|
badRequestError({
|
|
433
|
-
message: `Trigger
|
|
1284
|
+
message: `Trigger group does not support webhook processing: ${params.triggerGroupId}`
|
|
434
1285
|
})
|
|
435
1286
|
);
|
|
436
1287
|
}
|
|
@@ -443,70 +1294,137 @@ export let createProviderHandler = <ConfigType extends {}, AuthType extends {}>(
|
|
|
443
1294
|
: null
|
|
444
1295
|
});
|
|
445
1296
|
|
|
446
|
-
let
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
1297
|
+
let context = new SlateContext(
|
|
1298
|
+
{},
|
|
1299
|
+
{
|
|
1300
|
+
request: req,
|
|
1301
|
+
webhookRegistrationPayload: params.webhookRegistrationPayload
|
|
1302
|
+
},
|
|
1303
|
+
{},
|
|
1304
|
+
slate.spec as any,
|
|
1305
|
+
logger
|
|
1306
|
+
);
|
|
1307
|
+
let res = await traceProviderCall(
|
|
1308
|
+
{
|
|
1309
|
+
component: 'action',
|
|
1310
|
+
functionName: 'webhookProcess',
|
|
1311
|
+
message: `Processing webhook request for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1312
|
+
successMessage: result =>
|
|
1313
|
+
`Extracted ${result.events.length} event(s) from webhook request for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1314
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while processing a webhook request`,
|
|
1315
|
+
metadata: {
|
|
1316
|
+
triggerGroupId: group.key,
|
|
1317
|
+
triggerGroupName: group.name,
|
|
1318
|
+
requestMethod: params.method,
|
|
1319
|
+
hasRequestBody: !!params.body
|
|
1320
|
+
},
|
|
1321
|
+
onSuccess: result => ({
|
|
1322
|
+
eventCount: result.events.length,
|
|
1323
|
+
hasResponse: result.response !== undefined
|
|
1324
|
+
})
|
|
1325
|
+
},
|
|
1326
|
+
() => runWithContext(context, () => group.webhook!.process(context))
|
|
454
1327
|
);
|
|
455
1328
|
|
|
456
|
-
|
|
1329
|
+
for (let event of res.events) {
|
|
1330
|
+
if (!event.matchers) {
|
|
1331
|
+
throw new ServiceError(
|
|
1332
|
+
preconditionFailedError({
|
|
1333
|
+
message: `Trigger group "${group.key}" process handler must return matchers for every event (return [] if there is nothing to assert)`
|
|
1334
|
+
})
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
let response =
|
|
1340
|
+
res.response === undefined
|
|
1341
|
+
? undefined
|
|
1342
|
+
: await serializeWebhookHttpResponse(res.response);
|
|
1343
|
+
|
|
1344
|
+
return withRequestTraces(context, {
|
|
1345
|
+
events: res.events.map(event => ({
|
|
1346
|
+
matchers: event.matchers,
|
|
1347
|
+
payload: event.payload,
|
|
1348
|
+
idempotencyKey: event.idempotencyKey,
|
|
1349
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1350
|
+
})),
|
|
1351
|
+
response
|
|
1352
|
+
});
|
|
457
1353
|
});
|
|
458
1354
|
|
|
459
|
-
manager.onRequest('slates/
|
|
1355
|
+
manager.onRequest('slates/trigger_group.routing_matchers.get', async ({ params }) => {
|
|
460
1356
|
let ctx = getContextFull();
|
|
461
|
-
let
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
1357
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1358
|
+
|
|
1359
|
+
let context = new SlateContext(ctx.config, {}, ctx.auth?.output!, slate.spec, logger);
|
|
1360
|
+
let matchers = await traceProviderCall(
|
|
1361
|
+
{
|
|
1362
|
+
component: 'action',
|
|
1363
|
+
functionName: 'routingMatchers',
|
|
1364
|
+
message: `Getting routing matchers for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1365
|
+
successMessage: result =>
|
|
1366
|
+
`Retrieved ${result.length} routing matcher(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1367
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while getting routing matchers`,
|
|
1368
|
+
metadata: {
|
|
1369
|
+
triggerGroupId: group.key,
|
|
1370
|
+
triggerGroupName: group.name
|
|
1371
|
+
},
|
|
1372
|
+
onSuccess: result => ({
|
|
1373
|
+
matcherCount: result.length
|
|
467
1374
|
})
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
let res = await action.autoRegisterWebhook(
|
|
472
|
-
new SlateContext(
|
|
473
|
-
ctx.config,
|
|
474
|
-
{ webhookBaseUrl: params.webhookBaseUrl },
|
|
475
|
-
ctx.auth?.output!,
|
|
476
|
-
slate.spec,
|
|
477
|
-
logger
|
|
478
|
-
)
|
|
1375
|
+
},
|
|
1376
|
+
() => runWithContext(context, () => group.routingMatchers(context))
|
|
479
1377
|
);
|
|
480
1378
|
|
|
481
|
-
return
|
|
1379
|
+
return withRequestTraces(context, { matchers });
|
|
482
1380
|
});
|
|
483
1381
|
|
|
484
|
-
manager.onRequest('slates/
|
|
1382
|
+
manager.onRequest('slates/trigger_group.polling.poll', async ({ params }) => {
|
|
485
1383
|
let ctx = getContextFull();
|
|
486
|
-
let
|
|
487
|
-
|
|
488
|
-
if (!action.autoUnregisterWebhook) {
|
|
1384
|
+
let group = getTriggerGroup(slate, params.triggerGroupId);
|
|
1385
|
+
if (group.source !== 'polling' || !group.polling) {
|
|
489
1386
|
throw new ServiceError(
|
|
490
1387
|
badRequestError({
|
|
491
|
-
message: `Trigger
|
|
1388
|
+
message: `Trigger group does not support polling: ${params.triggerGroupId}`
|
|
492
1389
|
})
|
|
493
1390
|
);
|
|
494
1391
|
}
|
|
495
1392
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
1393
|
+
let context = new SlateContext(
|
|
1394
|
+
ctx.config,
|
|
1395
|
+
{ state: params.state },
|
|
1396
|
+
ctx.auth?.output!,
|
|
1397
|
+
slate.spec,
|
|
1398
|
+
logger
|
|
1399
|
+
);
|
|
1400
|
+
let res = await traceProviderCall(
|
|
1401
|
+
{
|
|
1402
|
+
component: 'action',
|
|
1403
|
+
functionName: 'pollEvents',
|
|
1404
|
+
message: `Polling events for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1405
|
+
successMessage: result =>
|
|
1406
|
+
`Polled ${result.events.length} event(s) for trigger group ${formatEntityLabel(group.name, group.key)}`,
|
|
1407
|
+
errorMessage: `Trigger group ${formatEntityLabel(group.name, group.key)} failed while polling events`,
|
|
1408
|
+
metadata: {
|
|
1409
|
+
triggerGroupId: group.key,
|
|
1410
|
+
triggerGroupName: group.name,
|
|
1411
|
+
hasPreviousState: params.state !== null
|
|
503
1412
|
},
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
1413
|
+
onSuccess: result => ({
|
|
1414
|
+
eventCount: result.events.length,
|
|
1415
|
+
hasUpdatedState: result.updatedState !== undefined
|
|
1416
|
+
})
|
|
1417
|
+
},
|
|
1418
|
+
() => runWithContext(context, () => group.polling!.pollEvents(context))
|
|
508
1419
|
);
|
|
509
1420
|
|
|
510
|
-
return {
|
|
1421
|
+
return withRequestTraces(context, {
|
|
1422
|
+
updatedState: res.updatedState,
|
|
1423
|
+
events: res.events.map(event => ({
|
|
1424
|
+
payload: event.payload,
|
|
1425
|
+
idempotencyKey: event.idempotencyKey,
|
|
1426
|
+
triggerIds: evaluateTriggerMatches(slate, group.key, event.payload)
|
|
1427
|
+
}))
|
|
1428
|
+
});
|
|
511
1429
|
});
|
|
512
1430
|
});
|