@rex0220/ksql-flownet 1.0.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/LICENSE +21 -0
- package/README.ja.md +81 -0
- package/README.md +117 -0
- package/dist/bundle/bundle-builder.d.ts +49 -0
- package/dist/bundle/bundle-builder.js +208 -0
- package/dist/bundle/index.d.ts +4 -0
- package/dist/bundle/index.js +4 -0
- package/dist/bundle/upload.d.ts +28 -0
- package/dist/bundle/upload.js +72 -0
- package/dist/bundle/zip-store.d.ts +17 -0
- package/dist/bundle/zip-store.js +175 -0
- package/dist/cli/archive-run-command.d.ts +12 -0
- package/dist/cli/archive-run-command.js +131 -0
- package/dist/cli/cancel-run-command.d.ts +9 -0
- package/dist/cli/cancel-run-command.js +77 -0
- package/dist/cli/force-unlock-network-command.d.ts +8 -0
- package/dist/cli/force-unlock-network-command.js +129 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.js +132 -0
- package/dist/cli/plan-command.d.ts +2 -0
- package/dist/cli/plan-command.js +100 -0
- package/dist/cli/poll-requests-command.d.ts +3 -0
- package/dist/cli/poll-requests-command.js +74 -0
- package/dist/cli/record-job-unlock-command.d.ts +11 -0
- package/dist/cli/record-job-unlock-command.js +78 -0
- package/dist/cli/resolve-node-command.d.ts +13 -0
- package/dist/cli/resolve-node-command.js +174 -0
- package/dist/cli/run-network-command.d.ts +14 -0
- package/dist/cli/run-network-command.js +418 -0
- package/dist/cli/status-command.d.ts +5 -0
- package/dist/cli/status-command.js +137 -0
- package/dist/cli/validate-command.d.ts +6 -0
- package/dist/cli/validate-command.js +17 -0
- package/dist/dag/descendants.d.ts +9 -0
- package/dist/dag/descendants.js +67 -0
- package/dist/dag/topological-sort.d.ts +7 -0
- package/dist/dag/topological-sort.js +32 -0
- package/dist/domain/business-key.d.ts +15 -0
- package/dist/domain/business-key.js +249 -0
- package/dist/domain/canonical-lock-key.d.ts +17 -0
- package/dist/domain/canonical-lock-key.js +60 -0
- package/dist/domain/canonical-record-key.d.ts +18 -0
- package/dist/domain/canonical-record-key.js +70 -0
- package/dist/domain/load-network.d.ts +4 -0
- package/dist/domain/load-network.js +47 -0
- package/dist/domain/network-definition.d.ts +47 -0
- package/dist/domain/network-definition.js +7 -0
- package/dist/domain/persistence-model.d.ts +198 -0
- package/dist/domain/persistence-model.js +2 -0
- package/dist/domain/run-aggregate.d.ts +3 -0
- package/dist/domain/run-aggregate.js +15 -0
- package/dist/domain/validate-network-path.d.ts +11 -0
- package/dist/domain/validate-network-path.js +33 -0
- package/dist/domain/validate-network.d.ts +3 -0
- package/dist/domain/validate-network.js +180 -0
- package/dist/executor/attempt-executor.d.ts +43 -0
- package/dist/executor/attempt-executor.js +236 -0
- package/dist/executor/job-log-reader.d.ts +37 -0
- package/dist/executor/job-log-reader.js +91 -0
- package/dist/executor/ksql-flow-cli.d.ts +86 -0
- package/dist/executor/ksql-flow-cli.js +161 -0
- package/dist/executor/preflight.d.ts +41 -0
- package/dist/executor/preflight.js +150 -0
- package/dist/executor/result-classifier.d.ts +69 -0
- package/dist/executor/result-classifier.js +237 -0
- package/dist/executor/run-subprocess.d.ts +64 -0
- package/dist/executor/run-subprocess.js +175 -0
- package/dist/io/input-baseline.d.ts +19 -0
- package/dist/io/input-baseline.js +214 -0
- package/dist/io/io-config.d.ts +6 -0
- package/dist/io/io-config.js +33 -0
- package/dist/io/io-path.d.ts +37 -0
- package/dist/io/io-path.js +279 -0
- package/dist/orchestration/archive-run.d.ts +53 -0
- package/dist/orchestration/archive-run.js +323 -0
- package/dist/orchestration/cancel-request.d.ts +18 -0
- package/dist/orchestration/cancel-request.js +67 -0
- package/dist/orchestration/ensure-run.d.ts +83 -0
- package/dist/orchestration/ensure-run.js +632 -0
- package/dist/orchestration/job-lock-audit.d.ts +24 -0
- package/dist/orchestration/job-lock-audit.js +123 -0
- package/dist/orchestration/orphan-attempt-adjudication.d.ts +39 -0
- package/dist/orchestration/orphan-attempt-adjudication.js +167 -0
- package/dist/orchestration/reconciliation.d.ts +41 -0
- package/dist/orchestration/reconciliation.js +526 -0
- package/dist/orchestration/resolve-node.d.ts +29 -0
- package/dist/orchestration/resolve-node.js +105 -0
- package/dist/orchestration/run-activity.d.ts +13 -0
- package/dist/orchestration/run-activity.js +14 -0
- package/dist/orchestration/sequential-scheduler.d.ts +79 -0
- package/dist/orchestration/sequential-scheduler.js +706 -0
- package/dist/orchestration/status.d.ts +91 -0
- package/dist/orchestration/status.js +171 -0
- package/dist/persistence/in-memory-repository.d.ts +41 -0
- package/dist/persistence/in-memory-repository.js +333 -0
- package/dist/persistence/kintone/client.d.ts +36 -0
- package/dist/persistence/kintone/client.js +93 -0
- package/dist/persistence/kintone/design-notes.d.ts +38 -0
- package/dist/persistence/kintone/design-notes.js +38 -0
- package/dist/persistence/kintone/repository.d.ts +44 -0
- package/dist/persistence/kintone/repository.js +836 -0
- package/dist/persistence/kintone/schema.d.ts +116 -0
- package/dist/persistence/kintone/schema.js +115 -0
- package/dist/persistence/network-lock-reader.d.ts +24 -0
- package/dist/persistence/network-lock-reader.js +43 -0
- package/dist/persistence/network-lock-recovery.d.ts +66 -0
- package/dist/persistence/network-lock-recovery.js +392 -0
- package/dist/persistence/network-lock.d.ts +97 -0
- package/dist/persistence/network-lock.js +408 -0
- package/dist/persistence/repository.d.ts +101 -0
- package/dist/persistence/repository.js +11 -0
- package/dist/persistence/state-transition.d.ts +4 -0
- package/dist/persistence/state-transition.js +25 -0
- package/dist/requests/flownet-child-client.d.ts +78 -0
- package/dist/requests/flownet-child-client.js +243 -0
- package/dist/requests/kintone-request-store.d.ts +44 -0
- package/dist/requests/kintone-request-store.js +202 -0
- package/dist/requests/poll-requests-config.d.ts +26 -0
- package/dist/requests/poll-requests-config.js +105 -0
- package/dist/requests/request-model.d.ts +57 -0
- package/dist/requests/request-model.js +305 -0
- package/dist/requests/request-poller.d.ts +33 -0
- package/dist/requests/request-poller.js +353 -0
- package/dist/requests/request-result.d.ts +18 -0
- package/dist/requests/request-result.js +215 -0
- package/dist/requests/start-request.d.ts +26 -0
- package/dist/requests/start-request.js +83 -0
- package/package.json +63 -0
- package/schemas/.gitkeep +1 -0
- package/schemas/network-definition.schema.json +121 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChildProcessResult, RunNetworkJsonOutput } from "./flownet-child-client.js";
|
|
2
|
+
import type { ArchiveRunOutcome } from "../orchestration/archive-run.js";
|
|
3
|
+
import type { RequestResult } from "./kintone-request-store.js";
|
|
4
|
+
export declare function classifyRunNetworkResult(input: {
|
|
5
|
+
readonly output: RunNetworkJsonOutput | null;
|
|
6
|
+
readonly process: ChildProcessResult;
|
|
7
|
+
}): RequestResult;
|
|
8
|
+
export declare function classifyStartNetworkResult(input: {
|
|
9
|
+
readonly output: RunNetworkJsonOutput | null;
|
|
10
|
+
readonly process: ChildProcessResult;
|
|
11
|
+
}): RequestResult;
|
|
12
|
+
export declare function classifyCancelResult(result: ChildProcessResult, release: boolean): RequestResult;
|
|
13
|
+
export declare function classifyArchiveRun(input: {
|
|
14
|
+
readonly output: ArchiveRunOutcome | null;
|
|
15
|
+
readonly process: ChildProcessResult;
|
|
16
|
+
}): RequestResult;
|
|
17
|
+
export declare function rejected(code: string, message: string): RequestResult;
|
|
18
|
+
//# sourceMappingURL=request-result.d.ts.map
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
export function classifyRunNetworkResult(input) {
|
|
2
|
+
const output = validRunNetworkOutput(input.output) ? input.output : null;
|
|
3
|
+
if (output !== null && output.invocation_id !== null) {
|
|
4
|
+
const retryBrakeNodeIds = output.retry_brake_node_ids ?? [];
|
|
5
|
+
if (retryBrakeNodeIds.length > 0) {
|
|
6
|
+
return {
|
|
7
|
+
state: "DONE",
|
|
8
|
+
code: "RETRY_BRAKE",
|
|
9
|
+
message: `aggregate=${output.aggregate_status ?? "UNKNOWN"}; invocation_id=${output.invocation_id}; retry_brake_node_ids=${retryBrakeNodeIds.join(",")}`,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
return {
|
|
13
|
+
state: "DONE",
|
|
14
|
+
code: output.invocation_result_code,
|
|
15
|
+
message: `aggregate=${output.aggregate_status ?? "UNKNOWN"}; invocation_id=${output.invocation_id}`,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (output?.outcome === "NOOP") {
|
|
19
|
+
return {
|
|
20
|
+
state: "DONE",
|
|
21
|
+
code: output.invocation_result_code,
|
|
22
|
+
message: `aggregate=${output.aggregate_status ?? "SUCCESS"}; invocation_id=none`,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (input.process.spawnError !== undefined) {
|
|
26
|
+
return rejected("CHILD_SPAWN_FAILED", "run-network child could not be started");
|
|
27
|
+
}
|
|
28
|
+
if (output?.outcome === "REJECTED") {
|
|
29
|
+
return rejected(output.invocation_result_code, "run-network rejected the request before creating an Invocation");
|
|
30
|
+
}
|
|
31
|
+
return rejected("CHILD_RESULT_INVALID", "run-network did not return a valid machine-readable result");
|
|
32
|
+
}
|
|
33
|
+
export function classifyStartNetworkResult(input) {
|
|
34
|
+
const output = validRunNetworkOutput(input.output) ? input.output : null;
|
|
35
|
+
if (output !== null && output.invocation_id !== null) {
|
|
36
|
+
return classifyRunNetworkResult(input);
|
|
37
|
+
}
|
|
38
|
+
if (output?.outcome === "NOOP" && output.run_id !== null) {
|
|
39
|
+
return {
|
|
40
|
+
state: "DONE",
|
|
41
|
+
code: "NOOP_ALREADY_SUCCESS",
|
|
42
|
+
message: `既存Run #${output.run_id} はSUCCESSのため起動をスキップしました。補正実行は別のbusiness_keyを指定してください。`,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (input.process.spawnError !== undefined) {
|
|
46
|
+
return rejected("CHILD_SPAWN_FAILED", "run-network child could not be started");
|
|
47
|
+
}
|
|
48
|
+
if (output?.outcome === "REJECTED") {
|
|
49
|
+
const blockedRunIds = output.blocked_run_ids ?? [];
|
|
50
|
+
if (output.invocation_result_code === "RUN_ALREADY_EXISTS") {
|
|
51
|
+
const runId = blockedRunIds[0];
|
|
52
|
+
return rejected("RUN_ALREADY_EXISTS", runId === undefined
|
|
53
|
+
? "同一業務キーの未完了Runがあります。RERUNを使用してください。"
|
|
54
|
+
: `同一業務キーの未完了Run #${runId} があります。RERUNを使用してください。`);
|
|
55
|
+
}
|
|
56
|
+
if (output.invocation_result_code === "MAX_ACTIVE_RUNS") {
|
|
57
|
+
const runId = blockedRunIds[0] ?? "不明";
|
|
58
|
+
return rejected("MAX_ACTIVE_RUNS", `未完了Run #${runId}があるため起動できません。失敗Runのやり直しはRERUNを、整理できない場合は二次対応者へ`);
|
|
59
|
+
}
|
|
60
|
+
if (output.invocation_result_code === "LOCK_CONFLICT") {
|
|
61
|
+
return rejected("LOCK_CONFLICT", "network実行ロックが競合しました。時間をおいて再起票してください。");
|
|
62
|
+
}
|
|
63
|
+
return rejected(output.invocation_result_code, "run-network rejected the START request before creating an Invocation");
|
|
64
|
+
}
|
|
65
|
+
return rejected("CHILD_RESULT_INVALID", "run-network did not return a valid machine-readable result");
|
|
66
|
+
}
|
|
67
|
+
export function classifyCancelResult(result, release) {
|
|
68
|
+
if (result.spawnError !== undefined) {
|
|
69
|
+
return rejected("CHILD_SPAWN_FAILED", "cancel-run child could not be started");
|
|
70
|
+
}
|
|
71
|
+
if (result.exitCode === 0) {
|
|
72
|
+
return {
|
|
73
|
+
state: "DONE",
|
|
74
|
+
code: release ? "RELEASED" : "STOP_REQUESTED",
|
|
75
|
+
message: release
|
|
76
|
+
? "Run hold was released; no automatic resume was started"
|
|
77
|
+
: "Run hold was requested at the next node boundary",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
return rejected("CANCEL_RUN_REJECTED", "cancel-run rejected the request or failed before completing the operation");
|
|
81
|
+
}
|
|
82
|
+
const ARCHIVE_PENDING_CODES = new Set([
|
|
83
|
+
"ARCHIVE_AUDIT_FAILED",
|
|
84
|
+
"AUDIT_CONFLICT",
|
|
85
|
+
"LEASE_INTERRUPTED_AFTER_ARCHIVE",
|
|
86
|
+
]);
|
|
87
|
+
const ARCHIVE_REJECTED_CODES = new Set([
|
|
88
|
+
"LOCK_CONFLICT",
|
|
89
|
+
"LOCK_UNAVAILABLE",
|
|
90
|
+
"LEASE_INTERRUPTED",
|
|
91
|
+
"RUN_READ_FAILED",
|
|
92
|
+
"RUN_STATUS_NOT_CLOSABLE",
|
|
93
|
+
"RUN_UNKNOWN_NOT_CLOSABLE",
|
|
94
|
+
"RUN_NOT_TERMINAL",
|
|
95
|
+
"RUN_ON_HOLD",
|
|
96
|
+
"RUN_LIVE",
|
|
97
|
+
"ARCHIVE_WRITE_FAILED",
|
|
98
|
+
]);
|
|
99
|
+
export function classifyArchiveRun(input) {
|
|
100
|
+
const output = validArchiveRunOutput(input.output) ? input.output : null;
|
|
101
|
+
if (output === null ||
|
|
102
|
+
input.process.spawnError !== undefined ||
|
|
103
|
+
input.process.stdoutTruncated ||
|
|
104
|
+
input.process.stderrTruncated ||
|
|
105
|
+
input.process.exitCode !== expectedArchiveExitCode(output)) {
|
|
106
|
+
return rejected("CHILD_RESULT_INVALID", "archive-run result was invalid; the Run may already be ARCHIVED, so verify it with status --json");
|
|
107
|
+
}
|
|
108
|
+
const event = `event_id=${output.event_id}`;
|
|
109
|
+
const lockFailure = output.lock_released ? "" : " lock_release_failed=true";
|
|
110
|
+
switch (output.outcome) {
|
|
111
|
+
case "ARCHIVED":
|
|
112
|
+
if (output.audit === "PENDING") {
|
|
113
|
+
return {
|
|
114
|
+
state: "DONE",
|
|
115
|
+
code: "RUN_ARCHIVED_AUDIT_PENDING",
|
|
116
|
+
message: `code=${output.code} ${event}${lockFailure}`,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (!output.lock_released) {
|
|
120
|
+
return {
|
|
121
|
+
state: "DONE",
|
|
122
|
+
code: "RUN_ARCHIVED_LOCK_UNRELEASED",
|
|
123
|
+
message: `${event} lock_release_failed=true`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
state: "DONE",
|
|
128
|
+
code: "RUN_ARCHIVED",
|
|
129
|
+
message: event,
|
|
130
|
+
};
|
|
131
|
+
case "ALREADY_ARCHIVED":
|
|
132
|
+
return output.lock_released
|
|
133
|
+
? {
|
|
134
|
+
state: "DONE",
|
|
135
|
+
code: "RUN_ALREADY_ARCHIVED",
|
|
136
|
+
message: event,
|
|
137
|
+
}
|
|
138
|
+
: {
|
|
139
|
+
state: "DONE",
|
|
140
|
+
code: "RUN_ARCHIVED_LOCK_UNRELEASED",
|
|
141
|
+
message: `${event} already_archived=true lock_release_failed=true`,
|
|
142
|
+
};
|
|
143
|
+
case "UNCONFIRMED":
|
|
144
|
+
return rejected("ARCHIVE_UNCONFIRMED", `Run archive state is unconfirmed; verify it with status --json ${event}${lockFailure}`);
|
|
145
|
+
case "REJECTED":
|
|
146
|
+
return rejected(output.code, `${event}${lockFailure}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export function rejected(code, message) {
|
|
150
|
+
return { state: "REJECTED", code, message };
|
|
151
|
+
}
|
|
152
|
+
function validRunNetworkOutput(value) {
|
|
153
|
+
return (value !== null &&
|
|
154
|
+
["NEW", "RESUME", "NOOP", "REJECTED"].includes(value.outcome) &&
|
|
155
|
+
(typeof value.run_id === "string" || value.run_id === null) &&
|
|
156
|
+
(typeof value.invocation_id === "string" || value.invocation_id === null) &&
|
|
157
|
+
(typeof value.aggregate_status === "string" ||
|
|
158
|
+
value.aggregate_status === null) &&
|
|
159
|
+
typeof value.invocation_result_code === "string" &&
|
|
160
|
+
value.invocation_result_code !== "" &&
|
|
161
|
+
(value.retry_brake_node_ids === undefined ||
|
|
162
|
+
(Array.isArray(value.retry_brake_node_ids) &&
|
|
163
|
+
value.retry_brake_node_ids.every((nodeId) => typeof nodeId === "string"))) &&
|
|
164
|
+
(value.blocked_run_ids === undefined ||
|
|
165
|
+
(Array.isArray(value.blocked_run_ids) &&
|
|
166
|
+
value.blocked_run_ids.every((runId) => typeof runId === "string"))));
|
|
167
|
+
}
|
|
168
|
+
function expectedArchiveExitCode(output) {
|
|
169
|
+
return (output.outcome === "ARCHIVED" &&
|
|
170
|
+
output.audit === "RECORDED" &&
|
|
171
|
+
output.lock_released) ||
|
|
172
|
+
(output.outcome === "ALREADY_ARCHIVED" && output.lock_released)
|
|
173
|
+
? 0
|
|
174
|
+
: 1;
|
|
175
|
+
}
|
|
176
|
+
function validArchiveRunOutput(value) {
|
|
177
|
+
if (value === null ||
|
|
178
|
+
typeof value !== "object" ||
|
|
179
|
+
typeof value.run_id !== "string" ||
|
|
180
|
+
value.run_id === "" ||
|
|
181
|
+
typeof value.event_id !== "string" ||
|
|
182
|
+
value.event_id === "" ||
|
|
183
|
+
typeof value.lock_released !== "boolean") {
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
const revision = value.run_revision;
|
|
187
|
+
switch (value.outcome) {
|
|
188
|
+
case "ARCHIVED":
|
|
189
|
+
if (typeof revision !== "number" ||
|
|
190
|
+
!Number.isSafeInteger(revision) ||
|
|
191
|
+
revision < 1)
|
|
192
|
+
return false;
|
|
193
|
+
if (value.audit === "RECORDED")
|
|
194
|
+
return !("code" in value);
|
|
195
|
+
return (value.audit === "PENDING" &&
|
|
196
|
+
"code" in value &&
|
|
197
|
+
ARCHIVE_PENDING_CODES.has(value.code));
|
|
198
|
+
case "ALREADY_ARCHIVED":
|
|
199
|
+
return (typeof revision === "number" &&
|
|
200
|
+
Number.isSafeInteger(revision) &&
|
|
201
|
+
revision >= 1 &&
|
|
202
|
+
!("code" in value));
|
|
203
|
+
case "UNCONFIRMED":
|
|
204
|
+
return revision === null && value.code === "ARCHIVE_UNCONFIRMED";
|
|
205
|
+
case "REJECTED":
|
|
206
|
+
return ((revision === null ||
|
|
207
|
+
(typeof revision === "number" &&
|
|
208
|
+
Number.isSafeInteger(revision) &&
|
|
209
|
+
revision >= 1)) &&
|
|
210
|
+
ARCHIVE_REJECTED_CODES.has(value.code));
|
|
211
|
+
default:
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
//# sourceMappingURL=request-result.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NetworkDefinition } from "../domain/network-definition.js";
|
|
2
|
+
import type { RequestResult } from "./kintone-request-store.js";
|
|
3
|
+
import type { PollRequestsNetwork } from "./poll-requests-config.js";
|
|
4
|
+
import type { RequestRecord } from "./request-model.js";
|
|
5
|
+
export type StartNetworkInput = {
|
|
6
|
+
readonly businessKey: string;
|
|
7
|
+
readonly scheduledFor?: string;
|
|
8
|
+
} | {
|
|
9
|
+
readonly scheduledFor: string;
|
|
10
|
+
readonly businessKey?: string;
|
|
11
|
+
};
|
|
12
|
+
export interface PreparedStartRequest {
|
|
13
|
+
readonly definition: NetworkDefinition;
|
|
14
|
+
readonly businessKey: string;
|
|
15
|
+
readonly input: StartNetworkInput;
|
|
16
|
+
}
|
|
17
|
+
export type StartPreparation = {
|
|
18
|
+
readonly ok: true;
|
|
19
|
+
readonly value: PreparedStartRequest;
|
|
20
|
+
} | {
|
|
21
|
+
readonly ok: false;
|
|
22
|
+
readonly result: RequestResult;
|
|
23
|
+
};
|
|
24
|
+
export declare function prepareStartRequest(network: PollRequestsNetwork, request: Pick<RequestRecord, "businessKey" | "scheduledFor">): StartPreparation;
|
|
25
|
+
export declare function startNetworkNotAllowed(detail: "NOT_IN_ALLOWLIST" | "APP_START_DISABLED"): RequestResult;
|
|
26
|
+
//# sourceMappingURL=start-request.d.ts.map
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { normalizeScheduledFor, resolveBusinessKey, } from "../domain/business-key.js";
|
|
2
|
+
import { loadNetworkDefinition } from "../domain/load-network.js";
|
|
3
|
+
export function prepareStartRequest(network, request) {
|
|
4
|
+
const loaded = loadNetworkDefinition(network.definitionPath);
|
|
5
|
+
if (loaded.definition === undefined &&
|
|
6
|
+
loaded.errors.some(({ path, message }) => path.startsWith("$/nodes/") &&
|
|
7
|
+
message.includes("required property 'idempotent'"))) {
|
|
8
|
+
return failure("NETWORK_NOT_IDEMPOTENT", "全ノードの冪等性が確認できないためSTARTでは起動できません。");
|
|
9
|
+
}
|
|
10
|
+
if (loaded.definition === undefined ||
|
|
11
|
+
loaded.errors.length > 0 ||
|
|
12
|
+
loaded.definition.network_id !== network.networkId) {
|
|
13
|
+
return failure("NETWORK_DEFINITION_INVALID", "network定義を再読込できないため起動できません。二次対応者へ連絡してください。");
|
|
14
|
+
}
|
|
15
|
+
const definition = loaded.definition;
|
|
16
|
+
if (definition.nodes.some((node) => node.idempotent !== true)) {
|
|
17
|
+
return failure("NETWORK_NOT_IDEMPOTENT", "全ノードの冪等性が確認できないためSTARTでは起動できません。");
|
|
18
|
+
}
|
|
19
|
+
const businessKey = request.businessKey ?? undefined;
|
|
20
|
+
const scheduledForInput = request.scheduledFor ?? undefined;
|
|
21
|
+
if (definition.business_key_policy.type === "explicit") {
|
|
22
|
+
if (businessKey === undefined || scheduledForInput !== undefined) {
|
|
23
|
+
return failure("KEY_POLICY_MISMATCH", "networkの業務キー規則と入力内容が一致しません。");
|
|
24
|
+
}
|
|
25
|
+
const resolved = resolveBusinessKey({
|
|
26
|
+
networkId: definition.network_id,
|
|
27
|
+
policy: definition.business_key_policy,
|
|
28
|
+
businessKey,
|
|
29
|
+
});
|
|
30
|
+
if (resolved.businessKey === undefined || resolved.errors.length > 0) {
|
|
31
|
+
return failure("KEY_POLICY_MISMATCH", "networkの業務キー規則と入力内容が一致しません。");
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
ok: true,
|
|
35
|
+
value: {
|
|
36
|
+
definition,
|
|
37
|
+
businessKey: resolved.businessKey,
|
|
38
|
+
input: { businessKey: resolved.businessKey },
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (businessKey === undefined && scheduledForInput === undefined) {
|
|
43
|
+
return failure("KEY_POLICY_MISMATCH", "networkの業務キー規則と入力内容が一致しません。");
|
|
44
|
+
}
|
|
45
|
+
if (businessKey !== undefined && scheduledForInput === undefined) {
|
|
46
|
+
return failure("AS_OF_UNDEFINED", "補正用business_keyには対象期間のscheduled_forも指定してください。");
|
|
47
|
+
}
|
|
48
|
+
const scheduledFor = normalizeScheduledFor(scheduledForInput);
|
|
49
|
+
if (scheduledFor === undefined) {
|
|
50
|
+
return failure("INVALID_TIMESTAMP_FORMAT", "scheduled_forは実在する日時を明示offset付きで指定してください。");
|
|
51
|
+
}
|
|
52
|
+
const resolved = resolveBusinessKey({
|
|
53
|
+
networkId: definition.network_id,
|
|
54
|
+
policy: definition.business_key_policy,
|
|
55
|
+
scheduledFor,
|
|
56
|
+
...(businessKey === undefined ? {} : { businessKey }),
|
|
57
|
+
});
|
|
58
|
+
if (resolved.businessKey === undefined || resolved.errors.length > 0) {
|
|
59
|
+
return failure("KEY_POLICY_MISMATCH", "networkの業務キー規則と入力内容が一致しません。");
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
ok: true,
|
|
63
|
+
value: {
|
|
64
|
+
definition,
|
|
65
|
+
businessKey: resolved.businessKey,
|
|
66
|
+
input: {
|
|
67
|
+
scheduledFor,
|
|
68
|
+
...(businessKey === undefined ? {} : { businessKey }),
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
export function startNetworkNotAllowed(detail) {
|
|
74
|
+
return {
|
|
75
|
+
state: "REJECTED",
|
|
76
|
+
code: "NETWORK_NOT_ALLOWED",
|
|
77
|
+
message: `このnetworkはSTARTで起動できません。detail=${detail}`,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function failure(code, message) {
|
|
81
|
+
return { ok: false, result: { state: "REJECTED", code, message } };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=start-request.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rex0220/ksql-flownet",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Control Plane CLI for kSQL-Flow job networks",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/rex0220/ksql-flownet.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/rex0220/ksql-flownet#readme",
|
|
11
|
+
"bugs": "https://github.com/rex0220/ksql-flownet/issues",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"kintone",
|
|
14
|
+
"ksql",
|
|
15
|
+
"ksql-flow",
|
|
16
|
+
"batch",
|
|
17
|
+
"dag",
|
|
18
|
+
"orchestration",
|
|
19
|
+
"cli"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"type": "module",
|
|
25
|
+
"bin": {
|
|
26
|
+
"ksql-flownet": "dist/cli/index.js"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"dist",
|
|
30
|
+
"!dist/plugin",
|
|
31
|
+
"!dist/**/*.map",
|
|
32
|
+
"schemas"
|
|
33
|
+
],
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=22.0.0"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "tsc -p tsconfig.json",
|
|
39
|
+
"build:plugin": "node plugin/scripts/build.mjs",
|
|
40
|
+
"format": "prettier --write .",
|
|
41
|
+
"format:check": "prettier --check .",
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"pack:plugin": "npm run build:plugin && kintone-plugin-packer --ppk %USERPROFILE%/.ksql-flownet/flownet-activity-plugin.ppk --out plugin/zip/flownet-activity-plugin.zip plugin",
|
|
44
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
45
|
+
"pretest": "npm run build && npm run build:plugin",
|
|
46
|
+
"test": "node --test tests/unit/*.test.mjs",
|
|
47
|
+
"test:plugin": "npm run build:plugin && node --test tests/unit/activity-plugin-*.test.mjs"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@eslint/js": "^10.0.0",
|
|
51
|
+
"@kintone/plugin-packer": "^9.1.2",
|
|
52
|
+
"@types/node": "^24.0.0",
|
|
53
|
+
"esbuild": "^0.28.2",
|
|
54
|
+
"eslint": "^10.0.0",
|
|
55
|
+
"prettier": "^3.0.0",
|
|
56
|
+
"typescript": "^5.0.0",
|
|
57
|
+
"typescript-eslint": "^8.0.0"
|
|
58
|
+
},
|
|
59
|
+
"dependencies": {
|
|
60
|
+
"ajv": "^8.20.0",
|
|
61
|
+
"yaml": "^2.9.0"
|
|
62
|
+
}
|
|
63
|
+
}
|
package/schemas/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/rex0220/ksql-flownet/schemas/network-definition.schema.json",
|
|
4
|
+
"title": "kSQL-FlowNet network definition",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"additionalProperties": false,
|
|
7
|
+
"required": [
|
|
8
|
+
"schema_version",
|
|
9
|
+
"network_id",
|
|
10
|
+
"business_key_policy",
|
|
11
|
+
"network_lock",
|
|
12
|
+
"nodes"
|
|
13
|
+
],
|
|
14
|
+
"properties": {
|
|
15
|
+
"schema_version": { "const": 1 },
|
|
16
|
+
"network_id": { "$ref": "#/$defs/identifier" },
|
|
17
|
+
"description": { "type": "string" },
|
|
18
|
+
"business_key_policy": {
|
|
19
|
+
"oneOf": [
|
|
20
|
+
{
|
|
21
|
+
"type": "object",
|
|
22
|
+
"additionalProperties": false,
|
|
23
|
+
"required": ["type", "period", "timezone", "format"],
|
|
24
|
+
"properties": {
|
|
25
|
+
"type": { "const": "scheduled_period" },
|
|
26
|
+
"period": { "enum": ["day", "month"] },
|
|
27
|
+
"timezone": { "type": "string", "minLength": 1 },
|
|
28
|
+
"format": { "type": "string", "minLength": 1 }
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"type": "object",
|
|
33
|
+
"additionalProperties": false,
|
|
34
|
+
"required": ["type"],
|
|
35
|
+
"properties": {
|
|
36
|
+
"type": { "const": "explicit" }
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"max_active_runs": {
|
|
42
|
+
"type": "integer",
|
|
43
|
+
"minimum": 1,
|
|
44
|
+
"default": 1
|
|
45
|
+
},
|
|
46
|
+
"network_lock": {
|
|
47
|
+
"type": "object",
|
|
48
|
+
"additionalProperties": false,
|
|
49
|
+
"required": ["lease_duration_sec", "heartbeat_interval_sec"],
|
|
50
|
+
"properties": {
|
|
51
|
+
"lease_duration_sec": { "type": "integer", "minimum": 1 },
|
|
52
|
+
"heartbeat_interval_sec": { "type": "integer", "minimum": 1 }
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"nodes": {
|
|
56
|
+
"type": "array",
|
|
57
|
+
"minItems": 1,
|
|
58
|
+
"items": {
|
|
59
|
+
"type": "object",
|
|
60
|
+
"additionalProperties": false,
|
|
61
|
+
"required": [
|
|
62
|
+
"id",
|
|
63
|
+
"job_id",
|
|
64
|
+
"sql",
|
|
65
|
+
"depends_on",
|
|
66
|
+
"trigger_rule",
|
|
67
|
+
"idempotent"
|
|
68
|
+
],
|
|
69
|
+
"properties": {
|
|
70
|
+
"id": { "$ref": "#/$defs/identifier" },
|
|
71
|
+
"job_id": { "$ref": "#/$defs/identifier" },
|
|
72
|
+
"sql": { "type": "string", "minLength": 1 },
|
|
73
|
+
"depends_on": {
|
|
74
|
+
"type": "array",
|
|
75
|
+
"items": { "$ref": "#/$defs/identifier" }
|
|
76
|
+
},
|
|
77
|
+
"trigger_rule": {
|
|
78
|
+
"enum": ["all_success", "none_failed", "all_done"]
|
|
79
|
+
},
|
|
80
|
+
"idempotent": { "type": "boolean" },
|
|
81
|
+
"inputs": {
|
|
82
|
+
"type": "object",
|
|
83
|
+
"propertyNames": { "$ref": "#/$defs/sourceIdentifier" },
|
|
84
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
85
|
+
},
|
|
86
|
+
"outputs": {
|
|
87
|
+
"type": "object",
|
|
88
|
+
"propertyNames": { "$ref": "#/$defs/sourceIdentifier" },
|
|
89
|
+
"additionalProperties": { "type": "string", "minLength": 1 }
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
"$defs": {
|
|
96
|
+
"identifier": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"minLength": 1,
|
|
99
|
+
"maxLength": 128,
|
|
100
|
+
"not": {
|
|
101
|
+
"anyOf": [
|
|
102
|
+
{ "pattern": ":" },
|
|
103
|
+
{ "pattern": "\\u0000" },
|
|
104
|
+
{ "const": "__net__" }
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"sourceIdentifier": {
|
|
109
|
+
"type": "string",
|
|
110
|
+
"minLength": 1,
|
|
111
|
+
"maxLength": 128,
|
|
112
|
+
"not": {
|
|
113
|
+
"anyOf": [
|
|
114
|
+
{ "pattern": "[:=]" },
|
|
115
|
+
{ "pattern": "[\\u0000-\\u001f\\u007f]" },
|
|
116
|
+
{ "const": "__net__" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|