@powerhousedao/switchboard 6.2.3-dev.30 → 6.2.3-dev.32
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/.tsbuild/test/workflow/harness.d.ts +12 -1
- package/.tsbuild/test/workflow/harness.d.ts.map +1 -1
- package/.tsbuild/test/workflow/oauth.test.d.ts +2 -0
- package/.tsbuild/test/workflow/oauth.test.d.ts.map +1 -0
- package/.tsbuild/test/workflow/webhook-trigger-test.test.d.ts +2 -0
- package/.tsbuild/test/workflow/webhook-trigger-test.test.d.ts.map +1 -0
- package/.tsbuild/tsconfig.tsbuildinfo +1 -1
- package/CHANGELOG.md +31 -0
- package/dist/index.mjs +1 -1
- package/dist/{server-Cf0DB8sW.mjs → server-CKKn0aXV.mjs} +429 -45
- package/dist/server-CKKn0aXV.mjs.map +1 -0
- package/dist/server.d.mts +2 -0
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +3 -3
- package/package.json +15 -15
- package/test/workflow/harness.ts +61 -9
- package/test/workflow/oauth.test.ts +214 -0
- package/test/workflow/resolvers.test.ts +194 -3
- package/test/workflow/webhook-dedupe.test.ts +3 -3
- package/test/workflow/webhook-signed.test.ts +2 -2
- package/test/workflow/webhook-trigger-test.test.ts +82 -0
- package/test/workflow/webhook-unsigned.test.ts +19 -6
- package/test/workflow-runtime.test.ts +14 -0
- package/dist/server-Cf0DB8sW.mjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="ec5120f6-92b3-53b4-bab1-94d88478784d")}catch(e){}}();
|
|
3
3
|
import { n as addDefaultReactorDrive, r as isPostgresUrl, t as addDefaultDrive } from "./utils-uaBlzyjS.mjs";
|
|
4
4
|
import { createRequire, register } from "node:module";
|
|
5
5
|
import { ReactorInstrumentation } from "@powerhousedao/opentelemetry-instrumentation-reactor";
|
|
@@ -31,7 +31,7 @@ import os from "node:os";
|
|
|
31
31
|
import { fileURLToPath } from "node:url";
|
|
32
32
|
import { EnvVarProvider } from "@openfeature/env-var-provider";
|
|
33
33
|
import { OpenFeature } from "@openfeature/server-sdk";
|
|
34
|
-
import { GraphQLError } from "graphql";
|
|
34
|
+
import { GraphQLError, Kind } from "graphql";
|
|
35
35
|
import { gql } from "graphql-tag";
|
|
36
36
|
import { PGliteDialect } from "kysely-pglite-dialect";
|
|
37
37
|
//#region src/pglite-version.ts
|
|
@@ -1112,10 +1112,115 @@ async function initFeatureFlags() {
|
|
|
1112
1112
|
return OpenFeature.getClient();
|
|
1113
1113
|
}
|
|
1114
1114
|
//#endregion
|
|
1115
|
+
//#region src/workflow/oauth-callback.ts
|
|
1116
|
+
const OAUTH_CALLBACK_PATH = "oauth/callback";
|
|
1117
|
+
function escapeHtml(text) {
|
|
1118
|
+
return text.replace(/[&<>"']/g, (char) => ({
|
|
1119
|
+
"&": "&",
|
|
1120
|
+
"<": "<",
|
|
1121
|
+
">": ">",
|
|
1122
|
+
"\"": """,
|
|
1123
|
+
"'": "'"
|
|
1124
|
+
})[char]);
|
|
1125
|
+
}
|
|
1126
|
+
function resultPage(ok, detail) {
|
|
1127
|
+
const title = ok ? "Connected" : "Sign-in failed";
|
|
1128
|
+
return `<!doctype html>
|
|
1129
|
+
<html><head><meta charset="utf-8"><title>${title}</title>
|
|
1130
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1131
|
+
<style>body{font:15px system-ui,sans-serif;margin:0;display:grid;place-items:center;min-height:100vh;background:#fafafa;color:#222}main{max-width:28rem;padding:1.5rem;text-align:center}h1{font-size:1.1rem}</style>
|
|
1132
|
+
</head><body><main><h1>${title}</h1><p>${ok ? "You can close this window." : escapeHtml(detail ?? "The sign-in did not finish.")}</p></main>
|
|
1133
|
+
${ok ? "<script>setTimeout(function(){window.close()},600)<\/script>" : ""}
|
|
1134
|
+
</body></html>`;
|
|
1135
|
+
}
|
|
1136
|
+
function registerOAuthCallback(http, runtime) {
|
|
1137
|
+
return http.get(OAUTH_CALLBACK_PATH, { auth: "public" }, async (request) => {
|
|
1138
|
+
const params = new URL(request.url).searchParams;
|
|
1139
|
+
const state = params.get("state");
|
|
1140
|
+
const result = state ? await runtime.completeOAuth({
|
|
1141
|
+
state,
|
|
1142
|
+
code: params.get("code") ?? void 0,
|
|
1143
|
+
error: params.get("error") ?? void 0,
|
|
1144
|
+
errorDescription: params.get("error_description") ?? void 0
|
|
1145
|
+
}) : {
|
|
1146
|
+
ok: false,
|
|
1147
|
+
detail: "The provider sent no state",
|
|
1148
|
+
returnUrl: null
|
|
1149
|
+
};
|
|
1150
|
+
if (result.returnUrl && state) {
|
|
1151
|
+
const back = new URL(result.returnUrl);
|
|
1152
|
+
back.searchParams.set("ph_oauth", state);
|
|
1153
|
+
return new Response(null, {
|
|
1154
|
+
status: 303,
|
|
1155
|
+
headers: {
|
|
1156
|
+
location: back.href,
|
|
1157
|
+
"cache-control": "no-store"
|
|
1158
|
+
}
|
|
1159
|
+
});
|
|
1160
|
+
}
|
|
1161
|
+
return new Response(resultPage(result.ok, result.detail), {
|
|
1162
|
+
status: result.ok ? 200 : 400,
|
|
1163
|
+
headers: {
|
|
1164
|
+
"content-type": "text/html; charset=utf-8",
|
|
1165
|
+
"cache-control": "no-store",
|
|
1166
|
+
"referrer-policy": "no-referrer"
|
|
1167
|
+
}
|
|
1168
|
+
});
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1171
|
+
function callbackUrlOf(http) {
|
|
1172
|
+
return `${http.baseUrl.replace(/\/+$/, "")}/${OAUTH_CALLBACK_PATH}`;
|
|
1173
|
+
}
|
|
1174
|
+
//#endregion
|
|
1115
1175
|
//#region src/workflow/resolvers.ts
|
|
1176
|
+
function blockRef(input) {
|
|
1177
|
+
if (input.kind !== "action" && input.kind !== "trigger") throw new GraphQLError(`A block's kind is "action" or "trigger", got "${input.kind}"`);
|
|
1178
|
+
return {
|
|
1179
|
+
pieceName: input.pieceName,
|
|
1180
|
+
pieceVersion: input.pieceVersion,
|
|
1181
|
+
name: input.name,
|
|
1182
|
+
kind: input.kind
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
async function cursorAware(call) {
|
|
1186
|
+
try {
|
|
1187
|
+
return await call();
|
|
1188
|
+
} catch (error) {
|
|
1189
|
+
if (error instanceof Error && error.name === "InvalidRunCursorError") throw new GraphQLError(error.message, { extensions: { code: "BAD_USER_INPUT" } });
|
|
1190
|
+
throw error;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function selectsStepData(selections, path) {
|
|
1194
|
+
let level = selections;
|
|
1195
|
+
for (const name of [...path, "steps"]) {
|
|
1196
|
+
const next = [];
|
|
1197
|
+
for (const set of level) for (const selection of set.selections) {
|
|
1198
|
+
if (selection.kind !== Kind.FIELD) return true;
|
|
1199
|
+
if (selection.name.value === name && selection.selectionSet) next.push(selection.selectionSet);
|
|
1200
|
+
}
|
|
1201
|
+
if (next.length === 0) return false;
|
|
1202
|
+
level = next;
|
|
1203
|
+
}
|
|
1204
|
+
return level.some((set) => set.selections.some((selection) => selection.kind !== Kind.FIELD || selection.name.value === "input" || selection.name.value === "output"));
|
|
1205
|
+
}
|
|
1206
|
+
function readsStepData(info, path) {
|
|
1207
|
+
if (!info) return true;
|
|
1208
|
+
return selectsStepData(info.fieldNodes.flatMap((node) => node.selectionSet ? [node.selectionSet] : []), path);
|
|
1209
|
+
}
|
|
1116
1210
|
function requireAdmin(authorizationService, ctx) {
|
|
1117
1211
|
if (!authorizationService.isSupremeAdmin(ctx.user?.address)) throw new GraphQLError("Admin access required");
|
|
1118
1212
|
}
|
|
1213
|
+
async function syncAware(call) {
|
|
1214
|
+
try {
|
|
1215
|
+
return await call();
|
|
1216
|
+
} catch (error) {
|
|
1217
|
+
if (error instanceof Error && error.name === "WorkflowSyncingError") throw new GraphQLError(error.message, { extensions: {
|
|
1218
|
+
code: "WORKFLOW_SYNCING",
|
|
1219
|
+
retryable: true
|
|
1220
|
+
} });
|
|
1221
|
+
throw error;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1119
1224
|
function parseJson(value) {
|
|
1120
1225
|
if (value === null) return null;
|
|
1121
1226
|
try {
|
|
@@ -1128,14 +1233,19 @@ function toStepRecord(row) {
|
|
|
1128
1233
|
return {
|
|
1129
1234
|
stepId: row.step_id,
|
|
1130
1235
|
stepKey: row.step_key,
|
|
1131
|
-
|
|
1236
|
+
pieceName: row.piece_name,
|
|
1237
|
+
blockName: row.block_name,
|
|
1132
1238
|
status: row.status,
|
|
1133
1239
|
input: parseJson(row.input),
|
|
1134
1240
|
output: parseJson(row.output),
|
|
1135
1241
|
port: row.port,
|
|
1136
1242
|
error: row.error,
|
|
1137
1243
|
startedAt: row.started_at,
|
|
1138
|
-
endedAt: row.ended_at
|
|
1244
|
+
endedAt: row.ended_at,
|
|
1245
|
+
pieceVersion: row.piece_version,
|
|
1246
|
+
pieceSource: row.piece_source,
|
|
1247
|
+
versionMatch: row.version_match,
|
|
1248
|
+
versionNote: row.version_note
|
|
1139
1249
|
};
|
|
1140
1250
|
}
|
|
1141
1251
|
function toRunRecord(row, steps) {
|
|
@@ -1151,24 +1261,56 @@ function toRunRecord(row, steps) {
|
|
|
1151
1261
|
startedAt: row.started_at,
|
|
1152
1262
|
endedAt: row.ended_at,
|
|
1153
1263
|
rerunOf: row.rerun_of,
|
|
1264
|
+
warnings: row.warnings,
|
|
1265
|
+
warningNotes: parseJson(row.warning_notes) ?? [],
|
|
1154
1266
|
steps: steps.map(toStepRecord)
|
|
1155
1267
|
};
|
|
1156
1268
|
}
|
|
1157
|
-
|
|
1269
|
+
function isAbsolute(url) {
|
|
1270
|
+
return /^https?:\/\//i.test(url);
|
|
1271
|
+
}
|
|
1272
|
+
function redirectUriFor(routing, requested) {
|
|
1273
|
+
if (!routing) throw new GraphQLError("This host serves no OAuth2 callback");
|
|
1274
|
+
if (isAbsolute(routing.callbackUrl)) return routing.callbackUrl;
|
|
1275
|
+
if (!requested) throw new GraphQLError("This host does not know its public URL; pass redirectUri");
|
|
1276
|
+
let url;
|
|
1277
|
+
try {
|
|
1278
|
+
url = new URL(requested);
|
|
1279
|
+
} catch {
|
|
1280
|
+
throw new GraphQLError(`"${requested}" is not a URL`);
|
|
1281
|
+
}
|
|
1282
|
+
if (url.protocol !== "https:" && url.protocol !== "http:" || url.pathname !== routing.callbackUrl || url.search !== "" || url.hash !== "") throw new GraphQLError(`redirectUri must be this host's ${routing.callbackUrl}`);
|
|
1283
|
+
return url.href;
|
|
1284
|
+
}
|
|
1285
|
+
function returnUrlFor(requested, ctx) {
|
|
1286
|
+
if (!requested) return void 0;
|
|
1287
|
+
const origin = ctx.headers.origin;
|
|
1288
|
+
let url;
|
|
1289
|
+
try {
|
|
1290
|
+
url = new URL(requested);
|
|
1291
|
+
} catch {
|
|
1292
|
+
url = void 0;
|
|
1293
|
+
}
|
|
1294
|
+
if (!url || !origin || url.origin !== origin) throw new GraphQLError("returnUrl must be on the requesting page's origin");
|
|
1295
|
+
return url.href;
|
|
1296
|
+
}
|
|
1297
|
+
const getResolvers = (runtime, authorizationService, oauth) => {
|
|
1158
1298
|
return {
|
|
1159
1299
|
Query: { workflowRuntime: () => ({}) },
|
|
1160
1300
|
WorkflowRuntimeQueries: {
|
|
1161
1301
|
health: () => "ok",
|
|
1162
|
-
blockDescriptor: (_parent, args) => runtime.blockDescriptor(args.
|
|
1163
|
-
blockOptions: (_parent, args, ctx) => runtime.blockOptions(args.
|
|
1302
|
+
blockDescriptor: (_parent, args) => runtime.blockDescriptor(blockRef(args.block)),
|
|
1303
|
+
blockOptions: (_parent, args, ctx) => runtime.blockOptions(blockRef(args.block), args.propName, args.input, args.connectionId ?? void 0, ctx, args.searchValue ?? void 0),
|
|
1164
1304
|
pieceCatalog: () => runtime.pieceCatalog(),
|
|
1165
|
-
pieceActions: (_parent, args) => runtime.pieceActions(args.packageName),
|
|
1166
|
-
pieceTriggers: (_parent, args) => runtime.pieceTriggers(args.packageName),
|
|
1167
|
-
blockOutputTree: (_parent, args) => runtime.blockOutputTree(args.
|
|
1168
|
-
|
|
1305
|
+
pieceActions: (_parent, args) => runtime.pieceActions(args.packageName, args.version ?? void 0),
|
|
1306
|
+
pieceTriggers: (_parent, args) => runtime.pieceTriggers(args.packageName, args.version ?? void 0),
|
|
1307
|
+
blockOutputTree: (_parent, args) => runtime.blockOutputTree(blockRef(args.block), args.config),
|
|
1308
|
+
stepOutputTree: (_parent, args, ctx) => runtime.stepOutputTree(args.workflowId, args.stepId, ctx),
|
|
1309
|
+
blockResolutions: (_parent, args, ctx) => runtime.blockResolutions(args.workflowId, ctx),
|
|
1310
|
+
pieceDetail: (_parent, args) => runtime.pieceDetail(args.packageName, args.version ?? void 0),
|
|
1169
1311
|
searchBlocks: (_parent, args) => runtime.searchBlocks(args.query, args.limit ?? void 0),
|
|
1170
1312
|
connections: (_parent, _args, ctx) => runtime.connections(ctx),
|
|
1171
|
-
webhookEndpoint: (_parent, args, ctx) => runtime.webhookEndpoint(args.workflowId, ctx),
|
|
1313
|
+
webhookEndpoint: (_parent, args, ctx) => syncAware(() => runtime.webhookEndpoint(args.workflowId, ctx, { driveId: args.driveId ?? void 0 })),
|
|
1172
1314
|
secret: async (_parent, args) => {
|
|
1173
1315
|
try {
|
|
1174
1316
|
return await (await runtime.secrets()).stat(args.ref);
|
|
@@ -1177,17 +1319,45 @@ const getResolvers = (runtime, authorizationService) => {
|
|
|
1177
1319
|
}
|
|
1178
1320
|
},
|
|
1179
1321
|
secrets: async () => (await runtime.secrets()).list(),
|
|
1322
|
+
oauthRedirectUri: () => oauth?.callbackUrl ?? null,
|
|
1323
|
+
oauthAttempt: (_parent, args, ctx) => runtime.oauthAttempt(args.state, ctx),
|
|
1180
1324
|
triggerStates: async (_parent, _args, ctx) => (await runtime.triggerStates(ctx)).map((row) => ({
|
|
1181
1325
|
workflowId: row.workflow_id,
|
|
1182
|
-
|
|
1326
|
+
pieceName: row.piece_name,
|
|
1327
|
+
triggerName: row.trigger_name,
|
|
1183
1328
|
status: row.status,
|
|
1184
1329
|
intervalMs: row.interval_ms,
|
|
1185
1330
|
nextPollAt: row.next_poll_at,
|
|
1186
1331
|
lastPollAt: row.last_poll_at,
|
|
1187
1332
|
lastError: row.last_error,
|
|
1188
|
-
consecutiveFailures: row.consecutive_failures
|
|
1333
|
+
consecutiveFailures: row.consecutive_failures,
|
|
1334
|
+
nextRenewAt: row.next_renew_at,
|
|
1335
|
+
renewError: row.renew_error,
|
|
1336
|
+
renewFailures: row.renew_failures,
|
|
1337
|
+
pieceVersion: row.piece_version,
|
|
1338
|
+
pieceSource: row.piece_source,
|
|
1339
|
+
versionMatch: row.version_match,
|
|
1340
|
+
versionNote: row.version_note
|
|
1189
1341
|
})),
|
|
1190
|
-
runs: async (_parent, args, ctx) => (await runtime.runs(
|
|
1342
|
+
runs: async (_parent, args, ctx, info) => (await runtime.runs({
|
|
1343
|
+
...args,
|
|
1344
|
+
withStepData: readsStepData(info, [])
|
|
1345
|
+
}, ctx)).map((record) => toRunRecord(record.row, record.steps)),
|
|
1346
|
+
runsPage: async (_parent, args, ctx, info) => {
|
|
1347
|
+
const { paging, ...scope } = args;
|
|
1348
|
+
const page = await cursorAware(() => runtime.runsPage({
|
|
1349
|
+
...scope,
|
|
1350
|
+
limit: paging?.limit ?? void 0,
|
|
1351
|
+
cursor: paging?.cursor ?? null,
|
|
1352
|
+
withStepData: readsStepData(info, ["items"])
|
|
1353
|
+
}, ctx));
|
|
1354
|
+
return {
|
|
1355
|
+
items: page.records.map((record) => toRunRecord(record.row, record.steps)),
|
|
1356
|
+
hasNextPage: page.hasNextPage,
|
|
1357
|
+
hasPreviousPage: Boolean(paging?.cursor),
|
|
1358
|
+
cursor: page.cursor
|
|
1359
|
+
};
|
|
1360
|
+
},
|
|
1191
1361
|
run: async (_parent, args, ctx) => {
|
|
1192
1362
|
const record = await runtime.run(args.id, ctx);
|
|
1193
1363
|
return record ? toRunRecord(record.row, record.steps) : null;
|
|
@@ -1196,7 +1366,13 @@ const getResolvers = (runtime, authorizationService) => {
|
|
|
1196
1366
|
Mutation: { workflowRuntime: () => ({}) },
|
|
1197
1367
|
WorkflowRuntimeMutations: {
|
|
1198
1368
|
fire: (_parent, args, ctx) => runtime.fire(args.workflowId, args.payload, "manual", void 0, ctx),
|
|
1199
|
-
testTrigger: (_parent, args, ctx) => runtime.testTrigger(args.workflowId, ctx
|
|
1369
|
+
testTrigger: (_parent, args, ctx) => syncAware(() => runtime.testTrigger(args.workflowId, ctx, {
|
|
1370
|
+
...args.payload !== void 0 ? { payload: args.payload } : {},
|
|
1371
|
+
...typeof args.timeoutSeconds === "number" ? { timeoutMs: args.timeoutSeconds * 1e3 } : {},
|
|
1372
|
+
driveId: args.driveId ?? void 0
|
|
1373
|
+
})),
|
|
1374
|
+
cancelTriggerTest: (_parent, args, ctx) => runtime.cancelTriggerTestFor(args.workflowId, ctx),
|
|
1375
|
+
testStep: (_parent, args, ctx) => syncAware(() => runtime.testStep(args.workflowId, args.stepId, ctx, { driveId: args.driveId ?? void 0 })),
|
|
1200
1376
|
rerun: (_parent, args, ctx) => runtime.rerun(args.runId, ctx),
|
|
1201
1377
|
createSecret: async (_parent, args, ctx) => {
|
|
1202
1378
|
requireAdmin(authorizationService, ctx);
|
|
@@ -1214,7 +1390,15 @@ const getResolvers = (runtime, authorizationService) => {
|
|
|
1214
1390
|
await (await runtime.secrets()).delete(args.ref);
|
|
1215
1391
|
return true;
|
|
1216
1392
|
},
|
|
1217
|
-
checkConnection: (_parent, args, ctx) => runtime.checkConnection(args.connectionId, ctx)
|
|
1393
|
+
checkConnection: (_parent, args, ctx) => runtime.checkConnection(args.connectionId, ctx),
|
|
1394
|
+
startOAuth: (_parent, args, ctx) => {
|
|
1395
|
+
requireAdmin(authorizationService, ctx);
|
|
1396
|
+
const returnUrl = returnUrlFor(args.returnUrl, ctx);
|
|
1397
|
+
return runtime.startOAuth(args.connectionId, ctx, {
|
|
1398
|
+
redirectUri: redirectUriFor(oauth, args.redirectUri),
|
|
1399
|
+
...returnUrl ? { returnUrl } : {}
|
|
1400
|
+
});
|
|
1401
|
+
}
|
|
1218
1402
|
}
|
|
1219
1403
|
};
|
|
1220
1404
|
};
|
|
@@ -1230,19 +1414,38 @@ const schema = gql`
|
|
|
1230
1414
|
Persisted runs, newest first. Scope them to one workflow, or to every
|
|
1231
1415
|
workflow a drive holds; workflowId wins when both are given.
|
|
1232
1416
|
"""
|
|
1233
|
-
runs(
|
|
1417
|
+
runs(
|
|
1418
|
+
workflowId: String
|
|
1419
|
+
driveId: String
|
|
1420
|
+
limit: Int
|
|
1421
|
+
"Trigger kinds to leave out, e.g. test."
|
|
1422
|
+
excludeTriggerKinds: [String!]
|
|
1423
|
+
): [WorkflowRunRecord!]!
|
|
1424
|
+
"""
|
|
1425
|
+
The same listing a page at a time, newest journaled first. Pass a page's
|
|
1426
|
+
cursor back in paging to read the next; a page is short only when
|
|
1427
|
+
hasNextPage is false, or when the caller could read none of a long
|
|
1428
|
+
stretch of runs. A run that starts between pages keeps its place.
|
|
1429
|
+
"""
|
|
1430
|
+
runsPage(
|
|
1431
|
+
workflowId: String
|
|
1432
|
+
driveId: String
|
|
1433
|
+
excludeTriggerKinds: [String!]
|
|
1434
|
+
paging: WorkflowRunsPagingInput
|
|
1435
|
+
): WorkflowRunResultPage!
|
|
1234
1436
|
run(id: String!): WorkflowRunRecord
|
|
1235
1437
|
"""
|
|
1236
|
-
|
|
1438
|
+
The descriptor (props, auth, ports) of one action or trigger, as the
|
|
1439
|
+
version that runs it describes it; triggers come back under "trigger".
|
|
1237
1440
|
"""
|
|
1238
|
-
blockDescriptor(
|
|
1441
|
+
blockDescriptor(block: BlockInput!): Unknown
|
|
1239
1442
|
"""
|
|
1240
1443
|
Resolves a dynamic prop against the current config values: a DROPDOWN
|
|
1241
1444
|
yields { options, placeholder, disabled }, a DYNAMIC prop yields the
|
|
1242
1445
|
resolved sub-property descriptor list.
|
|
1243
1446
|
"""
|
|
1244
1447
|
blockOptions(
|
|
1245
|
-
|
|
1448
|
+
block: BlockInput!
|
|
1246
1449
|
propName: String!
|
|
1247
1450
|
input: Unknown
|
|
1248
1451
|
connectionId: String
|
|
@@ -1250,21 +1453,34 @@ const schema = gql`
|
|
|
1250
1453
|
searchValue: String
|
|
1251
1454
|
): Unknown
|
|
1252
1455
|
"""
|
|
1253
|
-
|
|
1456
|
+
Every piece this reactor offers: the core piece, its package pieces, and
|
|
1457
|
+
the pieces the registry and Activepieces publish.
|
|
1254
1458
|
"""
|
|
1255
1459
|
pieceCatalog: Unknown
|
|
1256
1460
|
"""
|
|
1257
|
-
A piece's actions
|
|
1461
|
+
A piece's actions at one version; pin a step to that version.
|
|
1258
1462
|
"""
|
|
1259
|
-
pieceActions(
|
|
1463
|
+
pieceActions(
|
|
1464
|
+
packageName: String!
|
|
1465
|
+
"That version; the installed or latest one when omitted."
|
|
1466
|
+
version: String
|
|
1467
|
+
): Unknown
|
|
1260
1468
|
"""
|
|
1261
|
-
A piece's triggers
|
|
1469
|
+
A piece's triggers at one version; pin a trigger to that version.
|
|
1262
1470
|
"""
|
|
1263
|
-
pieceTriggers(
|
|
1471
|
+
pieceTriggers(
|
|
1472
|
+
packageName: String!
|
|
1473
|
+
"That version; the installed or latest one when omitted."
|
|
1474
|
+
version: String
|
|
1475
|
+
): Unknown
|
|
1264
1476
|
"""
|
|
1265
1477
|
Full piece detail (PieceMetadataModel-shaped), verbatim from the cloud API.
|
|
1266
1478
|
"""
|
|
1267
|
-
pieceDetail(
|
|
1479
|
+
pieceDetail(
|
|
1480
|
+
packageName: String!
|
|
1481
|
+
"That version; the installed or latest one when omitted."
|
|
1482
|
+
version: String
|
|
1483
|
+
): Unknown
|
|
1268
1484
|
"""
|
|
1269
1485
|
Action/trigger name search across the catalog. The index builds lazily on
|
|
1270
1486
|
first use; poll while status is "indexing".
|
|
@@ -1277,7 +1493,17 @@ const schema = gql`
|
|
|
1277
1493
|
"""
|
|
1278
1494
|
Authored output shape of a block (SDL / outputSchema / sampleData).
|
|
1279
1495
|
"""
|
|
1280
|
-
blockOutputTree(
|
|
1496
|
+
blockOutputTree(block: BlockInput!, config: Unknown): Unknown
|
|
1497
|
+
"""
|
|
1498
|
+
Output shape of a draft step or trigger: its latest test output
|
|
1499
|
+
(source "test", with sample and testedAt) when tested, else as blockOutputTree.
|
|
1500
|
+
"""
|
|
1501
|
+
stepOutputTree(workflowId: String!, stepId: String!): Unknown
|
|
1502
|
+
"""
|
|
1503
|
+
Every block of the draft (trigger first) as this reactor resolves it:
|
|
1504
|
+
which piece version runs, from where, and how it matches the pin.
|
|
1505
|
+
"""
|
|
1506
|
+
blockResolutions(workflowId: String!): [BlockResolutionRecord!]!
|
|
1281
1507
|
"""
|
|
1282
1508
|
Every powerhouse/connection document, for connection pickers.
|
|
1283
1509
|
"""
|
|
@@ -1286,19 +1512,62 @@ const schema = gql`
|
|
|
1286
1512
|
The webhook endpoint for a workflow: the URL to hand the provider. Minted
|
|
1287
1513
|
on first ask, whether or not the workflow is armed — an author needs the
|
|
1288
1514
|
URL before enabling, and the armed field carries that difference. Null
|
|
1289
|
-
only when the host has no webhook service.
|
|
1515
|
+
only when the host has no webhook service. With driveId, a workflow not
|
|
1516
|
+
synced here yet is waited for briefly, then fails as WORKFLOW_SYNCING.
|
|
1290
1517
|
"""
|
|
1291
|
-
webhookEndpoint(workflowId: String
|
|
1518
|
+
webhookEndpoint(workflowId: String!, driveId: String): WebhookEndpointRecord
|
|
1292
1519
|
"""
|
|
1293
1520
|
Secret metadata (label, version, status). Never the value.
|
|
1294
1521
|
"""
|
|
1295
1522
|
secret(ref: String!): SecretRecord
|
|
1296
1523
|
secrets: [SecretRecord!]!
|
|
1524
|
+
"""
|
|
1525
|
+
The URL an OAuth2 app must register as its redirect. A bare path when
|
|
1526
|
+
this host does not know its public origin; resolve it against the URL
|
|
1527
|
+
this API is reached at. Null when the host serves no HTTP routes.
|
|
1528
|
+
"""
|
|
1529
|
+
oauthRedirectUri: String
|
|
1530
|
+
"""
|
|
1531
|
+
How an OAuth2 sign-in opened by startOAuth stands; null when unknown.
|
|
1532
|
+
"""
|
|
1533
|
+
oauthAttempt(state: String!): OAuthAttempt
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
type OAuthAttempt {
|
|
1537
|
+
connectionId: String!
|
|
1538
|
+
"PENDING | EXCHANGING | OK | ERROR"
|
|
1539
|
+
status: String!
|
|
1540
|
+
error: String
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
type OAuthStart {
|
|
1544
|
+
state: String!
|
|
1545
|
+
"Where to send the user to sign in."
|
|
1546
|
+
authorizationUrl: String!
|
|
1547
|
+
redirectUri: String!
|
|
1548
|
+
expiresAt: String!
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
"""
|
|
1552
|
+
One action or trigger of a piece, as a workflow pins it. The engine's own
|
|
1553
|
+
blocks are the piece @powerhousedao/piece-core.
|
|
1554
|
+
"""
|
|
1555
|
+
input BlockInput {
|
|
1556
|
+
pieceName: String!
|
|
1557
|
+
"Exact semver."
|
|
1558
|
+
pieceVersion: String!
|
|
1559
|
+
"The action or trigger name."
|
|
1560
|
+
name: String!
|
|
1561
|
+
"action | trigger"
|
|
1562
|
+
kind: String!
|
|
1297
1563
|
}
|
|
1298
1564
|
|
|
1299
1565
|
type BlockSearchHit {
|
|
1300
|
-
blockType: String!
|
|
1301
1566
|
pieceName: String!
|
|
1567
|
+
"The version the hit is listed at; pin to it when picking the block."
|
|
1568
|
+
pieceVersion: String!
|
|
1569
|
+
"The action or trigger name."
|
|
1570
|
+
name: String!
|
|
1302
1571
|
pieceDisplayName: String!
|
|
1303
1572
|
logoUrl: String!
|
|
1304
1573
|
displayName: String!
|
|
@@ -1311,6 +1580,28 @@ const schema = gql`
|
|
|
1311
1580
|
unsupported: String
|
|
1312
1581
|
}
|
|
1313
1582
|
|
|
1583
|
+
type BlockResolutionRecord {
|
|
1584
|
+
"The trigger's or step's id."
|
|
1585
|
+
stepId: String!
|
|
1586
|
+
pieceName: String!
|
|
1587
|
+
"The version the block pins."
|
|
1588
|
+
pieceVersion: String!
|
|
1589
|
+
"The action or trigger name."
|
|
1590
|
+
name: String!
|
|
1591
|
+
"action | trigger"
|
|
1592
|
+
kind: String!
|
|
1593
|
+
"The version that runs; null when nothing resolves."
|
|
1594
|
+
resolvedVersion: String
|
|
1595
|
+
"local | registry | activepieces | npm; null when nothing resolves."
|
|
1596
|
+
source: String
|
|
1597
|
+
"exact | compatible | fallback | installed | missing"
|
|
1598
|
+
match: String!
|
|
1599
|
+
"Why the version that runs differs from the pin, or why nothing resolves."
|
|
1600
|
+
note: String
|
|
1601
|
+
"The newest version any source offers."
|
|
1602
|
+
latestVersion: String
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1314
1605
|
type BlockSearchResult {
|
|
1315
1606
|
"ready | indexing | error"
|
|
1316
1607
|
status: String!
|
|
@@ -1351,8 +1642,9 @@ const schema = gql`
|
|
|
1351
1642
|
workflowId: String!
|
|
1352
1643
|
url: String!
|
|
1353
1644
|
"""
|
|
1354
|
-
False when the
|
|
1355
|
-
above is a bare path
|
|
1645
|
+
False when the host gave the webhook service no public origin, so the url
|
|
1646
|
+
above is a bare path. Switchboard always gives one, falling back to
|
|
1647
|
+
http://localhost:<port> when PUBLIC_URL is unset.
|
|
1356
1648
|
"""
|
|
1357
1649
|
absoluteUrl: Boolean!
|
|
1358
1650
|
"True while the workflow is ENABLED with a valid webhook trigger"
|
|
@@ -1362,13 +1654,24 @@ const schema = gql`
|
|
|
1362
1654
|
|
|
1363
1655
|
type TriggerStateRecord {
|
|
1364
1656
|
workflowId: String!
|
|
1365
|
-
|
|
1657
|
+
pieceName: String!
|
|
1658
|
+
triggerName: String!
|
|
1366
1659
|
status: String!
|
|
1367
1660
|
intervalMs: Int!
|
|
1368
1661
|
nextPollAt: String
|
|
1369
1662
|
lastPollAt: String
|
|
1370
1663
|
lastError: String
|
|
1371
1664
|
consecutiveFailures: Int!
|
|
1665
|
+
"When a webhook trigger next renews its subscription; null if it never does."
|
|
1666
|
+
nextRenewAt: String
|
|
1667
|
+
"The last failed renewal, kept apart from lastError, the poll's."
|
|
1668
|
+
renewError: String
|
|
1669
|
+
renewFailures: Int!
|
|
1670
|
+
"The piece version the trigger armed with; null for a host-fed trigger."
|
|
1671
|
+
pieceVersion: String
|
|
1672
|
+
pieceSource: String
|
|
1673
|
+
versionMatch: String
|
|
1674
|
+
versionNote: String
|
|
1372
1675
|
}
|
|
1373
1676
|
|
|
1374
1677
|
type Query {
|
|
@@ -1378,7 +1681,9 @@ const schema = gql`
|
|
|
1378
1681
|
type WorkflowStepRunRecord {
|
|
1379
1682
|
stepId: String!
|
|
1380
1683
|
stepKey: String!
|
|
1381
|
-
|
|
1684
|
+
pieceName: String!
|
|
1685
|
+
"The action the step ran, or the trigger a trigger test sampled."
|
|
1686
|
+
blockName: String!
|
|
1382
1687
|
status: String!
|
|
1383
1688
|
input: Unknown
|
|
1384
1689
|
output: Unknown
|
|
@@ -1386,6 +1691,14 @@ const schema = gql`
|
|
|
1386
1691
|
error: String
|
|
1387
1692
|
startedAt: String
|
|
1388
1693
|
endedAt: String
|
|
1694
|
+
"The piece version that ran; null for an unresolved block."
|
|
1695
|
+
pieceVersion: String
|
|
1696
|
+
"local | registry | activepieces | npm"
|
|
1697
|
+
pieceSource: String
|
|
1698
|
+
"exact | compatible | fallback | installed | missing"
|
|
1699
|
+
versionMatch: String
|
|
1700
|
+
"Why the version that ran differs from the pin."
|
|
1701
|
+
versionNote: String
|
|
1389
1702
|
}
|
|
1390
1703
|
|
|
1391
1704
|
type WorkflowRunRecord {
|
|
@@ -1397,16 +1710,38 @@ const schema = gql`
|
|
|
1397
1710
|
triggerPayload: Unknown
|
|
1398
1711
|
status: String!
|
|
1399
1712
|
error: String
|
|
1713
|
+
"When the run began executing; a PENDING run's is when it was journaled."
|
|
1400
1714
|
startedAt: String!
|
|
1401
1715
|
endedAt: String
|
|
1402
1716
|
rerunOf: String
|
|
1717
|
+
"How many warningNotes the run has; a run with any is not a plain success."
|
|
1718
|
+
warnings: Int!
|
|
1719
|
+
"Steps that ran a fallback piece version, edges on ports nothing emits."
|
|
1720
|
+
warningNotes: [String!]!
|
|
1403
1721
|
steps: [WorkflowStepRunRecord!]!
|
|
1404
1722
|
}
|
|
1405
1723
|
|
|
1724
|
+
input WorkflowRunsPagingInput {
|
|
1725
|
+
"Page size; default 25, at most 100."
|
|
1726
|
+
limit: Int
|
|
1727
|
+
"The cursor of the previous page."
|
|
1728
|
+
cursor: String
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
type WorkflowRunResultPage {
|
|
1732
|
+
items: [WorkflowRunRecord!]!
|
|
1733
|
+
hasNextPage: Boolean!
|
|
1734
|
+
"True when the page was read after a cursor."
|
|
1735
|
+
hasPreviousPage: Boolean!
|
|
1736
|
+
"Resumes after this page's last run; null for an empty page."
|
|
1737
|
+
cursor: String
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1406
1740
|
type WorkflowStepRun {
|
|
1407
1741
|
stepId: String!
|
|
1408
1742
|
key: String!
|
|
1409
|
-
|
|
1743
|
+
pieceName: String!
|
|
1744
|
+
blockName: String!
|
|
1410
1745
|
status: String!
|
|
1411
1746
|
input: Unknown
|
|
1412
1747
|
output: Unknown
|
|
@@ -1421,18 +1756,51 @@ const schema = gql`
|
|
|
1421
1756
|
steps: [WorkflowStepRun!]!
|
|
1422
1757
|
}
|
|
1423
1758
|
|
|
1759
|
+
type WorkflowStepTestResult {
|
|
1760
|
+
"Null when the test never started, e.g. an upstream block is untested."
|
|
1761
|
+
runId: String
|
|
1762
|
+
"SUCCEEDED | FAILED"
|
|
1763
|
+
status: String!
|
|
1764
|
+
output: Unknown
|
|
1765
|
+
error: String
|
|
1766
|
+
durationMs: Int!
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1424
1769
|
"""
|
|
1425
1770
|
WorkflowRuntime Mutations
|
|
1426
1771
|
"""
|
|
1427
1772
|
type WorkflowRuntimeMutations {
|
|
1428
1773
|
"""
|
|
1429
|
-
Fires a workflow's
|
|
1774
|
+
Fires a workflow's manual trigger (the core piece's) and runs it to completion.
|
|
1430
1775
|
"""
|
|
1431
1776
|
fire(workflowId: String!, payload: Unknown): WorkflowRunPayload!
|
|
1432
1777
|
"""
|
|
1433
|
-
|
|
1778
|
+
Samples the draft trigger and saves it as the trigger's last test. A piece
|
|
1779
|
+
trigger runs its test hook (no cursor changes). Of the core piece's
|
|
1780
|
+
triggers, manual takes payload, schedule samples a fire now, and webhook
|
|
1781
|
+
waits for the next delivery to the workflow's endpoint, up to
|
|
1782
|
+
timeoutSeconds (default and cap 300), and that delivery runs nothing.
|
|
1783
|
+
"""
|
|
1784
|
+
testTrigger(
|
|
1785
|
+
workflowId: String!
|
|
1786
|
+
payload: Unknown
|
|
1787
|
+
timeoutSeconds: Int
|
|
1788
|
+
driveId: String
|
|
1789
|
+
): Unknown
|
|
1790
|
+
"""
|
|
1791
|
+
Stops a webhook trigger test that is waiting; false when none was.
|
|
1792
|
+
"""
|
|
1793
|
+
cancelTriggerTest(workflowId: String!): Boolean!
|
|
1434
1794
|
"""
|
|
1435
|
-
|
|
1795
|
+
Runs one draft step against the latest test outputs of the blocks it
|
|
1796
|
+
reads, journaled as a "test" run and noted as the step's lastTest.
|
|
1797
|
+
driveId waits for an unsynced workflow, as for webhookEndpoint.
|
|
1798
|
+
"""
|
|
1799
|
+
testStep(
|
|
1800
|
+
workflowId: String!
|
|
1801
|
+
stepId: String!
|
|
1802
|
+
driveId: String
|
|
1803
|
+
): WorkflowStepTestResult!
|
|
1436
1804
|
"""
|
|
1437
1805
|
Resumes a FAILED run: succeeded steps replay from the journal,
|
|
1438
1806
|
execution restarts at the failure. Produces a new run.
|
|
@@ -1457,6 +1825,18 @@ const schema = gql`
|
|
|
1457
1825
|
outcome on the connection document.
|
|
1458
1826
|
"""
|
|
1459
1827
|
checkConnection(connectionId: String!): ConnectionCheckResult!
|
|
1828
|
+
"""
|
|
1829
|
+
Opens an OAuth2 sign-in with the connection's own app (client_id in its
|
|
1830
|
+
config, client_secret in its secretRefs). redirectUri is needed only when
|
|
1831
|
+
oauthRedirectUri is a bare path. With returnUrl, which must be on the
|
|
1832
|
+
caller's origin, the callback sends the browser back there with
|
|
1833
|
+
?ph_oauth=<state> instead of closing the window.
|
|
1834
|
+
"""
|
|
1835
|
+
startOAuth(
|
|
1836
|
+
connectionId: String!
|
|
1837
|
+
redirectUri: String
|
|
1838
|
+
returnUrl: String
|
|
1839
|
+
): OAuthStart!
|
|
1460
1840
|
}
|
|
1461
1841
|
|
|
1462
1842
|
type Mutation {
|
|
@@ -1467,11 +1847,11 @@ const schema = gql`
|
|
|
1467
1847
|
//#region src/workflow/subgraph.ts
|
|
1468
1848
|
/** The runtime's read/write surface. The runtime itself is composed by the
|
|
1469
1849
|
* host, so the subgraph only serves what it is handed. */
|
|
1470
|
-
function createWorkflowRuntimeSubgraph(runtime) {
|
|
1850
|
+
function createWorkflowRuntimeSubgraph(runtime, oauth) {
|
|
1471
1851
|
return class WorkflowRuntimeSubgraph extends BaseSubgraph {
|
|
1472
1852
|
name = "workflow-runtime";
|
|
1473
1853
|
typeDefs = schema;
|
|
1474
|
-
resolvers = getResolvers(runtime, this.authorizationService);
|
|
1854
|
+
resolvers = getResolvers(runtime, this.authorizationService, oauth);
|
|
1475
1855
|
additionalContextFields = {};
|
|
1476
1856
|
};
|
|
1477
1857
|
}
|
|
@@ -1579,7 +1959,7 @@ async function registerWorkflowTriggersReadModel(engine, runtime, clientModule)
|
|
|
1579
1959
|
}
|
|
1580
1960
|
function bindPackagePieces(registry, source) {
|
|
1581
1961
|
const apply = (byPackage) => {
|
|
1582
|
-
registry.setPieces([...byPackage.
|
|
1962
|
+
registry.setPieces([...byPackage.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).flatMap(([, pieces]) => pieces));
|
|
1583
1963
|
};
|
|
1584
1964
|
apply(source.getPieces());
|
|
1585
1965
|
source.onPiecesChange(apply);
|
|
@@ -1610,8 +1990,9 @@ async function composeWorkflowRuntime(deps) {
|
|
|
1610
1990
|
if (triggers.status === "available") deps.logger.info(`Workflow trigger read model registered (${engine.WORKFLOW_TRIGGERS_READ_MODEL}, ${engine.WORKFLOW_TRIGGERS_READ_MODEL_STAGE})`);
|
|
1611
1991
|
else deps.logger.error("Workflow document triggers are NOT armed (@reason): this reactor's read-model coordinator takes no live registration, so no document operation reaches the runtime. Webhook and schedule triggers are unaffected.", triggers.reason);
|
|
1612
1992
|
let stopped = false;
|
|
1993
|
+
const oauthCallback = deps.http ? registerOAuthCallback(deps.http, runtime) : void 0;
|
|
1613
1994
|
return {
|
|
1614
|
-
subgraph: createWorkflowRuntimeSubgraph(runtime),
|
|
1995
|
+
subgraph: createWorkflowRuntimeSubgraph(runtime, deps.http ? { callbackUrl: callbackUrlOf(deps.http) } : void 0),
|
|
1615
1996
|
triggers,
|
|
1616
1997
|
async start() {
|
|
1617
1998
|
await runtime.registerWebhookEndpoint();
|
|
@@ -1620,6 +2001,7 @@ async function composeWorkflowRuntime(deps) {
|
|
|
1620
2001
|
stop() {
|
|
1621
2002
|
if (stopped) return Promise.resolve();
|
|
1622
2003
|
stopped = true;
|
|
2004
|
+
oauthCallback?.dispose();
|
|
1623
2005
|
runtime.shutdown();
|
|
1624
2006
|
return Promise.resolve();
|
|
1625
2007
|
}
|
|
@@ -2314,6 +2696,7 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2314
2696
|
attachmentReferences: api.attachmentReferenceIndex.store,
|
|
2315
2697
|
attachmentReferenceProjection: api.attachmentReferenceProjection,
|
|
2316
2698
|
webhooks: api.httpRoutes.scopeFor(WORKFLOW_PACKAGE_NAME).webhooks,
|
|
2699
|
+
http: api.httpRoutes.scopeFor(WORKFLOW_PACKAGE_NAME),
|
|
2317
2700
|
authorizationService: api.authorizationService,
|
|
2318
2701
|
pieces: api.packageManager,
|
|
2319
2702
|
pieceRegistryUrl: registryUrl,
|
|
@@ -2430,6 +2813,7 @@ async function initServer(serverPort, options, renown, renownConfig) {
|
|
|
2430
2813
|
attachmentService,
|
|
2431
2814
|
attachmentReferenceProjection: api.attachmentReferenceProjection,
|
|
2432
2815
|
workflowTriggers: workflows?.triggers,
|
|
2816
|
+
workflowsEnabled,
|
|
2433
2817
|
renown,
|
|
2434
2818
|
port: serverPort,
|
|
2435
2819
|
shutdown
|
|
@@ -2494,5 +2878,5 @@ if (import.meta.main) await startSwitchboard({ fatalErrorShutdown: true });
|
|
|
2494
2878
|
//#endregion
|
|
2495
2879
|
export { parseForcePgVersion as a, applySwitchboardReactorDefaults as i, isPortAvailable as n, startSwitchboard as r, deriveAttachmentServiceConfig as t };
|
|
2496
2880
|
|
|
2497
|
-
//# sourceMappingURL=server-
|
|
2498
|
-
//# debugId=
|
|
2881
|
+
//# sourceMappingURL=server-CKKn0aXV.mjs.map
|
|
2882
|
+
//# debugId=ec5120f6-92b3-53b4-bab1-94d88478784d
|