@teamkeel/functions-runtime 0.411.0-next.2 → 0.411.0
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/package.json +1 -1
- package/src/camelCasePlugin.js +4 -0
- package/src/consts.js +0 -1
- package/src/index.js +3 -8
- package/src/StepRunner.js +0 -97
- package/src/handleFlow.js +0 -126
- package/src/routes/customResponse.ts +0 -16
- package/src/tryExecuteFlow.js +0 -9
package/package.json
CHANGED
package/src/camelCasePlugin.js
CHANGED
|
@@ -25,6 +25,10 @@ class KeelCamelCasePlugin {
|
|
|
25
25
|
}
|
|
26
26
|
mapRow(row) {
|
|
27
27
|
return Object.keys(row).reduce((obj, key) => {
|
|
28
|
+
// Fields using @sequence will have a corresponding __sequence field which we drop as we don't want to return it
|
|
29
|
+
if (key.endsWith("__sequence")) {
|
|
30
|
+
return obj;
|
|
31
|
+
}
|
|
28
32
|
let value = row[key];
|
|
29
33
|
if (Array.isArray(value)) {
|
|
30
34
|
value = value.map((it) =>
|
package/src/consts.js
CHANGED
package/src/index.js
CHANGED
|
@@ -4,7 +4,6 @@ const { handleRequest } = require("./handleRequest");
|
|
|
4
4
|
const { handleJob } = require("./handleJob");
|
|
5
5
|
const { handleSubscriber } = require("./handleSubscriber");
|
|
6
6
|
const { handleRoute } = require("./handleRoute");
|
|
7
|
-
const { handleFlow } = require("./handleFlow");
|
|
8
7
|
const KSUID = require("ksuid");
|
|
9
8
|
const { useDatabase } = require("./database");
|
|
10
9
|
const {
|
|
@@ -16,7 +15,6 @@ const tracing = require("./tracing");
|
|
|
16
15
|
const { InlineFile, File } = require("./File");
|
|
17
16
|
const { Duration } = require("./Duration");
|
|
18
17
|
const { ErrorPresets } = require("./errors");
|
|
19
|
-
const { StepRunner } = require("./StepRunner");
|
|
20
18
|
|
|
21
19
|
module.exports = {
|
|
22
20
|
ModelAPI,
|
|
@@ -25,18 +23,15 @@ module.exports = {
|
|
|
25
23
|
handleJob,
|
|
26
24
|
handleSubscriber,
|
|
27
25
|
handleRoute,
|
|
28
|
-
handleFlow,
|
|
29
|
-
KSUID,
|
|
30
26
|
useDatabase,
|
|
27
|
+
Duration,
|
|
28
|
+
InlineFile,
|
|
29
|
+
File,
|
|
31
30
|
Permissions,
|
|
32
31
|
PERMISSION_STATE,
|
|
33
32
|
checkBuiltInPermissions,
|
|
34
33
|
tracing,
|
|
35
|
-
InlineFile,
|
|
36
|
-
File,
|
|
37
|
-
Duration,
|
|
38
34
|
ErrorPresets,
|
|
39
|
-
StepRunner,
|
|
40
35
|
ksuid() {
|
|
41
36
|
return KSUID.randomSync().string;
|
|
42
37
|
},
|
package/src/StepRunner.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
const { useDatabase } = require("./database");
|
|
2
|
-
|
|
3
|
-
const STEP_STATUS = {
|
|
4
|
-
NEW: "NEW",
|
|
5
|
-
COMPLETED: "COMPLETED",
|
|
6
|
-
FAILED: "FAILED",
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
const STEP_TYPE = {
|
|
10
|
-
FUNCTION: "FUNCTION",
|
|
11
|
-
IO: "IO",
|
|
12
|
-
DELAY: "DELAY",
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const defaultOpts = {
|
|
16
|
-
maxRetries: 5,
|
|
17
|
-
timeoutInMs: 60000,
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
// This is a special type that is thrown to disrupt the execution of a flow
|
|
21
|
-
class FlowDisrupt {
|
|
22
|
-
constructor() {}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class StepRunner {
|
|
26
|
-
constructor(runId) {
|
|
27
|
-
this.runId = runId;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async run(name, fn, opts) {
|
|
31
|
-
const db = useDatabase();
|
|
32
|
-
|
|
33
|
-
// First check if we already have a result for this step
|
|
34
|
-
const completed = await db
|
|
35
|
-
.selectFrom("keel_flow_step")
|
|
36
|
-
.where("run_id", "=", this.runId)
|
|
37
|
-
.where("name", "=", name)
|
|
38
|
-
.where("status", "=", STEP_STATUS.COMPLETED)
|
|
39
|
-
.selectAll()
|
|
40
|
-
.executeTakeFirst();
|
|
41
|
-
|
|
42
|
-
if (completed) {
|
|
43
|
-
return completed.value;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// The step hasn't yet run successfully, so we need to create a NEW run
|
|
47
|
-
const step = await db
|
|
48
|
-
.insertInto("keel_flow_step")
|
|
49
|
-
.values({
|
|
50
|
-
run_id: this.runId,
|
|
51
|
-
name: name,
|
|
52
|
-
status: STEP_STATUS.NEW,
|
|
53
|
-
type: STEP_TYPE.FUNCTION,
|
|
54
|
-
maxRetries: opts?.maxRetries ?? defaultOpts.maxRetries,
|
|
55
|
-
timeoutInMs: opts?.timeoutInMs ?? defaultOpts.timeoutInMs,
|
|
56
|
-
})
|
|
57
|
-
.returningAll()
|
|
58
|
-
.executeTakeFirst();
|
|
59
|
-
|
|
60
|
-
let outcome = STEP_STATUS.COMPLETED;
|
|
61
|
-
|
|
62
|
-
let result = null;
|
|
63
|
-
try {
|
|
64
|
-
result = await withTimeout(fn(), step.timeoutInMs);
|
|
65
|
-
} catch (e) {
|
|
66
|
-
outcome = STEP_STATUS.FAILED;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Very crudely store the result in the database
|
|
70
|
-
await db
|
|
71
|
-
.updateTable("keel_flow_step")
|
|
72
|
-
.set({
|
|
73
|
-
status: outcome,
|
|
74
|
-
value: JSON.stringify(result),
|
|
75
|
-
})
|
|
76
|
-
.where("id", "=", step.id)
|
|
77
|
-
.returningAll()
|
|
78
|
-
.executeTakeFirst();
|
|
79
|
-
|
|
80
|
-
throw new FlowDisrupt();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
function wait(milliseconds) {
|
|
85
|
-
return new Promise((resolve) => setTimeout(resolve, milliseconds));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function withTimeout(promiseFn, timeout) {
|
|
89
|
-
return Promise.race([
|
|
90
|
-
promiseFn,
|
|
91
|
-
wait(timeout).then(() => {
|
|
92
|
-
throw new Error(`flow times out after ${timeout}ms`);
|
|
93
|
-
}),
|
|
94
|
-
]);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
module.exports = { StepRunner, FlowDisrupt, withTimeout };
|
package/src/handleFlow.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
const {
|
|
2
|
-
createJSONRPCErrorResponse,
|
|
3
|
-
createJSONRPCSuccessResponse,
|
|
4
|
-
JSONRPCErrorCode,
|
|
5
|
-
} = require("json-rpc-2.0");
|
|
6
|
-
const { createDatabaseClient } = require("./database");
|
|
7
|
-
const { errorToJSONRPCResponse, RuntimeErrors } = require("./errors");
|
|
8
|
-
const opentelemetry = require("@opentelemetry/api");
|
|
9
|
-
const { withSpan } = require("./tracing");
|
|
10
|
-
const { tryExecuteFlow } = require("./tryExecuteFlow");
|
|
11
|
-
const { parseInputs } = require("./parsing");
|
|
12
|
-
const { FlowDisrupt } = require("./StepRunner");
|
|
13
|
-
|
|
14
|
-
async function handleFlow(request, config) {
|
|
15
|
-
// Try to extract trace context from caller
|
|
16
|
-
const activeContext = opentelemetry.propagation.extract(
|
|
17
|
-
opentelemetry.context.active(),
|
|
18
|
-
request.meta?.tracing
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
// Run the whole request with the extracted context
|
|
22
|
-
return opentelemetry.context.with(activeContext, () => {
|
|
23
|
-
// Wrapping span for the whole request
|
|
24
|
-
return withSpan(request.method, async (span) => {
|
|
25
|
-
let db = null;
|
|
26
|
-
|
|
27
|
-
const runId = request.meta?.runId;
|
|
28
|
-
|
|
29
|
-
try {
|
|
30
|
-
if (!runId) {
|
|
31
|
-
throw new Error("no runId provided");
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const { createFlowContextAPI, flows } = config;
|
|
35
|
-
|
|
36
|
-
if (!(request.method in flows)) {
|
|
37
|
-
const message = `no corresponding flow found for '${request.method}'`;
|
|
38
|
-
span.setStatus({
|
|
39
|
-
code: opentelemetry.SpanStatusCode.ERROR,
|
|
40
|
-
message: message,
|
|
41
|
-
});
|
|
42
|
-
return createJSONRPCErrorResponse(
|
|
43
|
-
request.id,
|
|
44
|
-
JSONRPCErrorCode.MethodNotFound,
|
|
45
|
-
message
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// The ctx argument passed into the flow function.
|
|
50
|
-
const ctx = createFlowContextAPI({
|
|
51
|
-
meta: request.meta,
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
db = createDatabaseClient({
|
|
55
|
-
connString: request.meta?.secrets?.KEEL_DB_CONN,
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
const flowRun = await db
|
|
59
|
-
.selectFrom("keel_flow_run")
|
|
60
|
-
.where("id", "=", runId)
|
|
61
|
-
.selectAll()
|
|
62
|
-
.executeTakeFirst();
|
|
63
|
-
|
|
64
|
-
if (!flowRun) {
|
|
65
|
-
throw new Error("no flow run found");
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const flowFunction = flows[request.method];
|
|
69
|
-
|
|
70
|
-
await tryExecuteFlow(db, async () => {
|
|
71
|
-
// parse request params to convert objects into rich field types (e.g. InlineFile)
|
|
72
|
-
const inputs = parseInputs(flowRun.input);
|
|
73
|
-
|
|
74
|
-
return flowFunction(ctx, inputs);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
// If we reach this point, then we know the entire flow completed successfully
|
|
78
|
-
// TODO: Send FlowRunUpdated event with run_completed = true
|
|
79
|
-
return createJSONRPCSuccessResponse(request.id, {
|
|
80
|
-
runId: runId,
|
|
81
|
-
runCompleted: true,
|
|
82
|
-
});
|
|
83
|
-
} catch (e) {
|
|
84
|
-
// If the flow is disrupted, then we know that a step either completed successfully or failed
|
|
85
|
-
if (e instanceof FlowDisrupt) {
|
|
86
|
-
// TODO: Send FlowRunUpdated event with run_completed = false
|
|
87
|
-
return createJSONRPCSuccessResponse(request.id, {
|
|
88
|
-
runId: runId,
|
|
89
|
-
runCompleted: false,
|
|
90
|
-
});
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (e instanceof Error) {
|
|
94
|
-
span.recordException(e);
|
|
95
|
-
span.setStatus({
|
|
96
|
-
code: opentelemetry.SpanStatusCode.ERROR,
|
|
97
|
-
message: e.message,
|
|
98
|
-
});
|
|
99
|
-
return errorToJSONRPCResponse(request, e);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const message = JSON.stringify(e);
|
|
103
|
-
|
|
104
|
-
span.setStatus({
|
|
105
|
-
code: opentelemetry.SpanStatusCode.ERROR,
|
|
106
|
-
message: message,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
return createJSONRPCErrorResponse(
|
|
110
|
-
request.id,
|
|
111
|
-
RuntimeErrors.UnknownError,
|
|
112
|
-
message
|
|
113
|
-
);
|
|
114
|
-
} finally {
|
|
115
|
-
if (db) {
|
|
116
|
-
await db.destroy();
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
module.exports = {
|
|
124
|
-
handleFlow,
|
|
125
|
-
RuntimeErrors,
|
|
126
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { RouteFunction } from "@teamkeel/sdk";
|
|
2
|
-
|
|
3
|
-
const handler: RouteFunction = async (request, ctx) => {
|
|
4
|
-
return {
|
|
5
|
-
body: JSON.stringify({
|
|
6
|
-
message: "This is a raw HTTP response",
|
|
7
|
-
timestamp: new Date().toISOString(),
|
|
8
|
-
}),
|
|
9
|
-
statusCode: 200,
|
|
10
|
-
headers: {
|
|
11
|
-
"Content-Type": "application/json",
|
|
12
|
-
},
|
|
13
|
-
};
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export default handler;
|