@contractspec/example.workflow-system 3.7.7 → 3.8.2
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/README.md +3 -0
- package/dist/browser/handlers/index.js +43 -43
- package/dist/browser/handlers/workflow.handlers.js +43 -43
- package/dist/browser/index.js +572 -183
- package/dist/browser/shared/demo-scenario.js +213 -0
- package/dist/browser/ui/WorkflowDashboard.visualizations.js +239 -0
- package/dist/browser/ui/hooks/index.js +0 -47
- package/dist/browser/ui/hooks/useWorkflowList.js +5 -3
- package/dist/browser/ui/index.js +5 -3
- package/dist/browser/ui/renderers/index.js +409 -73
- package/dist/browser/ui/renderers/workflow.markdown.js +409 -73
- package/dist/browser/visualizations/catalog.js +132 -0
- package/dist/browser/visualizations/index.js +133 -0
- package/dist/browser/visualizations/selectors.js +195 -0
- package/dist/example.test.d.ts +1 -0
- package/dist/handlers/index.js +43 -43
- package/dist/handlers/workflow.handlers.js +43 -43
- package/dist/index.d.ts +1 -0
- package/dist/index.js +572 -183
- package/dist/shared/demo-scenario.d.ts +43 -0
- package/dist/shared/demo-scenario.js +214 -0
- package/dist/ui/WorkflowDashboard.visualizations.d.ts +4 -0
- package/dist/ui/WorkflowDashboard.visualizations.js +240 -0
- package/dist/ui/hooks/index.js +0 -47
- package/dist/ui/hooks/useWorkflowList.d.ts +2 -1
- package/dist/ui/hooks/useWorkflowList.js +5 -3
- package/dist/ui/index.js +5 -3
- package/dist/ui/renderers/index.js +409 -73
- package/dist/ui/renderers/workflow.markdown.js +409 -73
- package/dist/visualizations/catalog.d.ts +11 -0
- package/dist/visualizations/catalog.js +133 -0
- package/dist/visualizations/index.d.ts +2 -0
- package/dist/visualizations/index.js +134 -0
- package/dist/visualizations/selectors.d.ts +11 -0
- package/dist/visualizations/selectors.js +196 -0
- package/dist/visualizations/selectors.test.d.ts +1 -0
- package/package.json +69 -8
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ Website: https://contractspec.io
|
|
|
11
11
|
- Multi-entity domain (workflow, instance, approval).
|
|
12
12
|
- Per-entity schema/enum/event/handler/operations pattern.
|
|
13
13
|
- React UI with WorkflowDashboard, hooks, and renderers.
|
|
14
|
+
- Contract-backed visualizations for workflow status, throughput, and workload comparison.
|
|
14
15
|
- Capability and feature definition patterns.
|
|
15
16
|
- Seeder and test-spec patterns.
|
|
16
17
|
|
|
@@ -19,6 +20,7 @@ Website: https://contractspec.io
|
|
|
19
20
|
From `packages/examples/workflow-system`:
|
|
20
21
|
- `bun run dev`
|
|
21
22
|
- `bun run build`
|
|
23
|
+
- `bun run test`
|
|
22
24
|
- `bun run typecheck`
|
|
23
25
|
|
|
24
26
|
## Usage
|
|
@@ -63,6 +65,7 @@ Use `@contractspec/example.workflow-system` as a reference implementation, or im
|
|
|
63
65
|
- `bun run build:bundle` — contractspec-bun-build transpile
|
|
64
66
|
- `bun run build:types` — contractspec-bun-build types
|
|
65
67
|
- `bun run prebuild` — contractspec-bun-build prebuild
|
|
68
|
+
- `bun run test` — bun test
|
|
66
69
|
|
|
67
70
|
## Recent Updates
|
|
68
71
|
|
|
@@ -54,9 +54,19 @@ function rowToApproval(row) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
function createWorkflowHandlers(db) {
|
|
57
|
+
function normalizeSql(sql) {
|
|
58
|
+
let placeholderIndex = 0;
|
|
59
|
+
return sql.replace(/\?/g, () => `$${++placeholderIndex}`);
|
|
60
|
+
}
|
|
61
|
+
async function queryRows(sql, params = []) {
|
|
62
|
+
return (await db.query(normalizeSql(sql), params)).rows;
|
|
63
|
+
}
|
|
64
|
+
async function execute(sql, params = []) {
|
|
65
|
+
await db.execute(normalizeSql(sql), params);
|
|
66
|
+
}
|
|
57
67
|
async function listDefinitions(input) {
|
|
58
68
|
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
59
|
-
let whereClause =
|
|
69
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
60
70
|
const params = [projectId];
|
|
61
71
|
if (status && status !== "all") {
|
|
62
72
|
whereClause += " AND status = ?";
|
|
@@ -66,9 +76,9 @@ function createWorkflowHandlers(db) {
|
|
|
66
76
|
whereClause += " AND name LIKE ?";
|
|
67
77
|
params.push(`%${search}%`);
|
|
68
78
|
}
|
|
69
|
-
const countResult =
|
|
79
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params);
|
|
70
80
|
const total = countResult[0]?.count ?? 0;
|
|
71
|
-
const rows =
|
|
81
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition ${whereClause} ORDER BY "updatedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
72
82
|
return {
|
|
73
83
|
definitions: rows.map(rowToDefinition),
|
|
74
84
|
total
|
|
@@ -77,7 +87,7 @@ function createWorkflowHandlers(db) {
|
|
|
77
87
|
async function createDefinition(input, context) {
|
|
78
88
|
const id = generateId("wfdef");
|
|
79
89
|
const now = new Date().toISOString();
|
|
80
|
-
await
|
|
90
|
+
await execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
81
91
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
82
92
|
id,
|
|
83
93
|
context.projectId,
|
|
@@ -89,15 +99,15 @@ function createWorkflowHandlers(db) {
|
|
|
89
99
|
now,
|
|
90
100
|
now
|
|
91
101
|
]);
|
|
92
|
-
const rows =
|
|
102
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition WHERE id = ?`, [id]);
|
|
93
103
|
return rowToDefinition(rows[0]);
|
|
94
104
|
}
|
|
95
105
|
async function addStep(input) {
|
|
96
106
|
const id = generateId("wfstep");
|
|
97
107
|
const now = new Date().toISOString();
|
|
98
|
-
const maxOrderResult =
|
|
108
|
+
const maxOrderResult = await queryRows(`SELECT MAX("stepOrder") as maxOrder FROM workflow_step WHERE "definitionId" = ?`, [input.definitionId]);
|
|
99
109
|
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
100
|
-
await
|
|
110
|
+
await execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "autoApproveCondition", "timeoutHours", "createdAt")
|
|
101
111
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
102
112
|
id,
|
|
103
113
|
input.definitionId,
|
|
@@ -110,11 +120,11 @@ function createWorkflowHandlers(db) {
|
|
|
110
120
|
input.timeoutHours ?? null,
|
|
111
121
|
now
|
|
112
122
|
]);
|
|
113
|
-
const rows =
|
|
123
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [id]);
|
|
114
124
|
return rowToStep(rows[0]);
|
|
115
125
|
}
|
|
116
126
|
async function getSteps(definitionId) {
|
|
117
|
-
const rows =
|
|
127
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder"`, [definitionId]);
|
|
118
128
|
return rows.map(rowToStep);
|
|
119
129
|
}
|
|
120
130
|
async function listInstances(input) {
|
|
@@ -126,10 +136,10 @@ function createWorkflowHandlers(db) {
|
|
|
126
136
|
limit = 20,
|
|
127
137
|
offset = 0
|
|
128
138
|
} = input;
|
|
129
|
-
let whereClause =
|
|
139
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
130
140
|
const params = [projectId];
|
|
131
141
|
if (definitionId) {
|
|
132
|
-
whereClause +=
|
|
142
|
+
whereClause += ' AND "definitionId" = ?';
|
|
133
143
|
params.push(definitionId);
|
|
134
144
|
}
|
|
135
145
|
if (status && status !== "all") {
|
|
@@ -137,12 +147,12 @@ function createWorkflowHandlers(db) {
|
|
|
137
147
|
params.push(status);
|
|
138
148
|
}
|
|
139
149
|
if (requestedBy) {
|
|
140
|
-
whereClause +=
|
|
150
|
+
whereClause += ' AND "requestedBy" = ?';
|
|
141
151
|
params.push(requestedBy);
|
|
142
152
|
}
|
|
143
|
-
const countResult =
|
|
153
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params);
|
|
144
154
|
const total = countResult[0]?.count ?? 0;
|
|
145
|
-
const rows =
|
|
155
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance ${whereClause} ORDER BY "startedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
146
156
|
return {
|
|
147
157
|
instances: rows.map(rowToInstance),
|
|
148
158
|
total
|
|
@@ -151,9 +161,9 @@ function createWorkflowHandlers(db) {
|
|
|
151
161
|
async function startInstance(input, context) {
|
|
152
162
|
const id = generateId("wfinst");
|
|
153
163
|
const now = new Date().toISOString();
|
|
154
|
-
const steps =
|
|
164
|
+
const steps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder" LIMIT 1`, [input.definitionId]);
|
|
155
165
|
const firstStepId = steps[0]?.id ?? null;
|
|
156
|
-
await
|
|
166
|
+
await execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt")
|
|
157
167
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
158
168
|
id,
|
|
159
169
|
context.projectId,
|
|
@@ -165,36 +175,32 @@ function createWorkflowHandlers(db) {
|
|
|
165
175
|
now
|
|
166
176
|
]);
|
|
167
177
|
if (firstStepId) {
|
|
168
|
-
await
|
|
178
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
169
179
|
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
170
180
|
}
|
|
171
|
-
const rows =
|
|
181
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [id]);
|
|
172
182
|
return rowToInstance(rows[0]);
|
|
173
183
|
}
|
|
174
184
|
async function approveStep(input, context) {
|
|
175
185
|
const now = new Date().toISOString();
|
|
176
|
-
const instances =
|
|
177
|
-
input.instanceId
|
|
178
|
-
])).rows;
|
|
186
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
179
187
|
if (!instances[0]) {
|
|
180
188
|
throw new Error("NOT_FOUND");
|
|
181
189
|
}
|
|
182
190
|
const instance = instances[0];
|
|
183
|
-
await
|
|
184
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
191
|
+
await execute(`UPDATE workflow_approval SET status = 'APPROVED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
192
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
185
193
|
context.actorId,
|
|
186
194
|
input.comment ?? null,
|
|
187
195
|
now,
|
|
188
196
|
input.instanceId,
|
|
189
197
|
instance.currentStepId
|
|
190
198
|
]);
|
|
191
|
-
const currentStep =
|
|
192
|
-
|
|
193
|
-
])).rows;
|
|
194
|
-
const nextSteps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? AND stepOrder > ? ORDER BY stepOrder LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0])).rows;
|
|
199
|
+
const currentStep = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [instance.currentStepId]);
|
|
200
|
+
const nextSteps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? AND "stepOrder" > ? ORDER BY "stepOrder" LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0]);
|
|
195
201
|
if (nextSteps[0]) {
|
|
196
|
-
await
|
|
197
|
-
await
|
|
202
|
+
await execute(`UPDATE workflow_instance SET "currentStepId" = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
203
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
198
204
|
VALUES (?, ?, ?, ?, ?)`, [
|
|
199
205
|
generateId("wfappr"),
|
|
200
206
|
input.instanceId,
|
|
@@ -203,37 +209,31 @@ function createWorkflowHandlers(db) {
|
|
|
203
209
|
now
|
|
204
210
|
]);
|
|
205
211
|
} else {
|
|
206
|
-
await
|
|
212
|
+
await execute(`UPDATE workflow_instance SET status = 'COMPLETED', "currentStepId" = NULL, "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
207
213
|
}
|
|
208
|
-
const updated =
|
|
209
|
-
input.instanceId
|
|
210
|
-
])).rows;
|
|
214
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
211
215
|
return rowToInstance(updated[0]);
|
|
212
216
|
}
|
|
213
217
|
async function rejectStep(input, context) {
|
|
214
218
|
const now = new Date().toISOString();
|
|
215
|
-
const instances =
|
|
216
|
-
input.instanceId
|
|
217
|
-
])).rows;
|
|
219
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
218
220
|
if (!instances[0]) {
|
|
219
221
|
throw new Error("NOT_FOUND");
|
|
220
222
|
}
|
|
221
|
-
await
|
|
222
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
223
|
+
await execute(`UPDATE workflow_approval SET status = 'REJECTED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
224
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
223
225
|
context.actorId,
|
|
224
226
|
input.reason,
|
|
225
227
|
now,
|
|
226
228
|
input.instanceId,
|
|
227
229
|
instances[0].currentStepId
|
|
228
230
|
]);
|
|
229
|
-
await
|
|
230
|
-
const updated =
|
|
231
|
-
input.instanceId
|
|
232
|
-
])).rows;
|
|
231
|
+
await execute(`UPDATE workflow_instance SET status = 'REJECTED', "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
232
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
233
233
|
return rowToInstance(updated[0]);
|
|
234
234
|
}
|
|
235
235
|
async function getApprovals(instanceId) {
|
|
236
|
-
const rows =
|
|
236
|
+
const rows = await queryRows(`SELECT * FROM workflow_approval WHERE "instanceId" = ? ORDER BY "createdAt"`, [instanceId]);
|
|
237
237
|
return rows.map(rowToApproval);
|
|
238
238
|
}
|
|
239
239
|
return {
|
|
@@ -54,9 +54,19 @@ function rowToApproval(row) {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
function createWorkflowHandlers(db) {
|
|
57
|
+
function normalizeSql(sql) {
|
|
58
|
+
let placeholderIndex = 0;
|
|
59
|
+
return sql.replace(/\?/g, () => `$${++placeholderIndex}`);
|
|
60
|
+
}
|
|
61
|
+
async function queryRows(sql, params = []) {
|
|
62
|
+
return (await db.query(normalizeSql(sql), params)).rows;
|
|
63
|
+
}
|
|
64
|
+
async function execute(sql, params = []) {
|
|
65
|
+
await db.execute(normalizeSql(sql), params);
|
|
66
|
+
}
|
|
57
67
|
async function listDefinitions(input) {
|
|
58
68
|
const { projectId, status, search, limit = 20, offset = 0 } = input;
|
|
59
|
-
let whereClause =
|
|
69
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
60
70
|
const params = [projectId];
|
|
61
71
|
if (status && status !== "all") {
|
|
62
72
|
whereClause += " AND status = ?";
|
|
@@ -66,9 +76,9 @@ function createWorkflowHandlers(db) {
|
|
|
66
76
|
whereClause += " AND name LIKE ?";
|
|
67
77
|
params.push(`%${search}%`);
|
|
68
78
|
}
|
|
69
|
-
const countResult =
|
|
79
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_definition ${whereClause}`, params);
|
|
70
80
|
const total = countResult[0]?.count ?? 0;
|
|
71
|
-
const rows =
|
|
81
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition ${whereClause} ORDER BY "updatedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
72
82
|
return {
|
|
73
83
|
definitions: rows.map(rowToDefinition),
|
|
74
84
|
total
|
|
@@ -77,7 +87,7 @@ function createWorkflowHandlers(db) {
|
|
|
77
87
|
async function createDefinition(input, context) {
|
|
78
88
|
const id = generateId("wfdef");
|
|
79
89
|
const now = new Date().toISOString();
|
|
80
|
-
await
|
|
90
|
+
await execute(`INSERT INTO workflow_definition (id, "projectId", "organizationId", name, description, type, status, "createdAt", "updatedAt")
|
|
81
91
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
82
92
|
id,
|
|
83
93
|
context.projectId,
|
|
@@ -89,15 +99,15 @@ function createWorkflowHandlers(db) {
|
|
|
89
99
|
now,
|
|
90
100
|
now
|
|
91
101
|
]);
|
|
92
|
-
const rows =
|
|
102
|
+
const rows = await queryRows(`SELECT * FROM workflow_definition WHERE id = ?`, [id]);
|
|
93
103
|
return rowToDefinition(rows[0]);
|
|
94
104
|
}
|
|
95
105
|
async function addStep(input) {
|
|
96
106
|
const id = generateId("wfstep");
|
|
97
107
|
const now = new Date().toISOString();
|
|
98
|
-
const maxOrderResult =
|
|
108
|
+
const maxOrderResult = await queryRows(`SELECT MAX("stepOrder") as maxOrder FROM workflow_step WHERE "definitionId" = ?`, [input.definitionId]);
|
|
99
109
|
const nextOrder = (maxOrderResult[0]?.maxOrder ?? 0) + 1;
|
|
100
|
-
await
|
|
110
|
+
await execute(`INSERT INTO workflow_step (id, "definitionId", name, description, "stepOrder", type, "requiredRoles", "autoApproveCondition", "timeoutHours", "createdAt")
|
|
101
111
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
102
112
|
id,
|
|
103
113
|
input.definitionId,
|
|
@@ -110,11 +120,11 @@ function createWorkflowHandlers(db) {
|
|
|
110
120
|
input.timeoutHours ?? null,
|
|
111
121
|
now
|
|
112
122
|
]);
|
|
113
|
-
const rows =
|
|
123
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [id]);
|
|
114
124
|
return rowToStep(rows[0]);
|
|
115
125
|
}
|
|
116
126
|
async function getSteps(definitionId) {
|
|
117
|
-
const rows =
|
|
127
|
+
const rows = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder"`, [definitionId]);
|
|
118
128
|
return rows.map(rowToStep);
|
|
119
129
|
}
|
|
120
130
|
async function listInstances(input) {
|
|
@@ -126,10 +136,10 @@ function createWorkflowHandlers(db) {
|
|
|
126
136
|
limit = 20,
|
|
127
137
|
offset = 0
|
|
128
138
|
} = input;
|
|
129
|
-
let whereClause =
|
|
139
|
+
let whereClause = 'WHERE "projectId" = ?';
|
|
130
140
|
const params = [projectId];
|
|
131
141
|
if (definitionId) {
|
|
132
|
-
whereClause +=
|
|
142
|
+
whereClause += ' AND "definitionId" = ?';
|
|
133
143
|
params.push(definitionId);
|
|
134
144
|
}
|
|
135
145
|
if (status && status !== "all") {
|
|
@@ -137,12 +147,12 @@ function createWorkflowHandlers(db) {
|
|
|
137
147
|
params.push(status);
|
|
138
148
|
}
|
|
139
149
|
if (requestedBy) {
|
|
140
|
-
whereClause +=
|
|
150
|
+
whereClause += ' AND "requestedBy" = ?';
|
|
141
151
|
params.push(requestedBy);
|
|
142
152
|
}
|
|
143
|
-
const countResult =
|
|
153
|
+
const countResult = await queryRows(`SELECT COUNT(*) as count FROM workflow_instance ${whereClause}`, params);
|
|
144
154
|
const total = countResult[0]?.count ?? 0;
|
|
145
|
-
const rows =
|
|
155
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance ${whereClause} ORDER BY "startedAt" DESC LIMIT ? OFFSET ?`, [...params, limit, offset]);
|
|
146
156
|
return {
|
|
147
157
|
instances: rows.map(rowToInstance),
|
|
148
158
|
total
|
|
@@ -151,9 +161,9 @@ function createWorkflowHandlers(db) {
|
|
|
151
161
|
async function startInstance(input, context) {
|
|
152
162
|
const id = generateId("wfinst");
|
|
153
163
|
const now = new Date().toISOString();
|
|
154
|
-
const steps =
|
|
164
|
+
const steps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? ORDER BY "stepOrder" LIMIT 1`, [input.definitionId]);
|
|
155
165
|
const firstStepId = steps[0]?.id ?? null;
|
|
156
|
-
await
|
|
166
|
+
await execute(`INSERT INTO workflow_instance (id, "projectId", "definitionId", status, "currentStepId", data, "requestedBy", "startedAt")
|
|
157
167
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
158
168
|
id,
|
|
159
169
|
context.projectId,
|
|
@@ -165,36 +175,32 @@ function createWorkflowHandlers(db) {
|
|
|
165
175
|
now
|
|
166
176
|
]);
|
|
167
177
|
if (firstStepId) {
|
|
168
|
-
await
|
|
178
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
169
179
|
VALUES (?, ?, ?, ?, ?)`, [generateId("wfappr"), id, firstStepId, "PENDING", now]);
|
|
170
180
|
}
|
|
171
|
-
const rows =
|
|
181
|
+
const rows = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [id]);
|
|
172
182
|
return rowToInstance(rows[0]);
|
|
173
183
|
}
|
|
174
184
|
async function approveStep(input, context) {
|
|
175
185
|
const now = new Date().toISOString();
|
|
176
|
-
const instances =
|
|
177
|
-
input.instanceId
|
|
178
|
-
])).rows;
|
|
186
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
179
187
|
if (!instances[0]) {
|
|
180
188
|
throw new Error("NOT_FOUND");
|
|
181
189
|
}
|
|
182
190
|
const instance = instances[0];
|
|
183
|
-
await
|
|
184
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
191
|
+
await execute(`UPDATE workflow_approval SET status = 'APPROVED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
192
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
185
193
|
context.actorId,
|
|
186
194
|
input.comment ?? null,
|
|
187
195
|
now,
|
|
188
196
|
input.instanceId,
|
|
189
197
|
instance.currentStepId
|
|
190
198
|
]);
|
|
191
|
-
const currentStep =
|
|
192
|
-
|
|
193
|
-
])).rows;
|
|
194
|
-
const nextSteps = (await db.query(`SELECT * FROM workflow_step WHERE definitionId = ? AND stepOrder > ? ORDER BY stepOrder LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0])).rows;
|
|
199
|
+
const currentStep = await queryRows(`SELECT * FROM workflow_step WHERE id = ?`, [instance.currentStepId]);
|
|
200
|
+
const nextSteps = await queryRows(`SELECT * FROM workflow_step WHERE "definitionId" = ? AND "stepOrder" > ? ORDER BY "stepOrder" LIMIT 1`, [instance.definitionId, currentStep[0]?.stepOrder ?? 0]);
|
|
195
201
|
if (nextSteps[0]) {
|
|
196
|
-
await
|
|
197
|
-
await
|
|
202
|
+
await execute(`UPDATE workflow_instance SET "currentStepId" = ? WHERE id = ?`, [nextSteps[0].id, input.instanceId]);
|
|
203
|
+
await execute(`INSERT INTO workflow_approval (id, "instanceId", "stepId", status, "createdAt")
|
|
198
204
|
VALUES (?, ?, ?, ?, ?)`, [
|
|
199
205
|
generateId("wfappr"),
|
|
200
206
|
input.instanceId,
|
|
@@ -203,37 +209,31 @@ function createWorkflowHandlers(db) {
|
|
|
203
209
|
now
|
|
204
210
|
]);
|
|
205
211
|
} else {
|
|
206
|
-
await
|
|
212
|
+
await execute(`UPDATE workflow_instance SET status = 'COMPLETED', "currentStepId" = NULL, "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
207
213
|
}
|
|
208
|
-
const updated =
|
|
209
|
-
input.instanceId
|
|
210
|
-
])).rows;
|
|
214
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
211
215
|
return rowToInstance(updated[0]);
|
|
212
216
|
}
|
|
213
217
|
async function rejectStep(input, context) {
|
|
214
218
|
const now = new Date().toISOString();
|
|
215
|
-
const instances =
|
|
216
|
-
input.instanceId
|
|
217
|
-
])).rows;
|
|
219
|
+
const instances = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
218
220
|
if (!instances[0]) {
|
|
219
221
|
throw new Error("NOT_FOUND");
|
|
220
222
|
}
|
|
221
|
-
await
|
|
222
|
-
WHERE instanceId = ? AND stepId = ? AND status = 'PENDING'`, [
|
|
223
|
+
await execute(`UPDATE workflow_approval SET status = 'REJECTED', "actorId" = ?, comment = ?, "decidedAt" = ?
|
|
224
|
+
WHERE "instanceId" = ? AND "stepId" = ? AND status = 'PENDING'`, [
|
|
223
225
|
context.actorId,
|
|
224
226
|
input.reason,
|
|
225
227
|
now,
|
|
226
228
|
input.instanceId,
|
|
227
229
|
instances[0].currentStepId
|
|
228
230
|
]);
|
|
229
|
-
await
|
|
230
|
-
const updated =
|
|
231
|
-
input.instanceId
|
|
232
|
-
])).rows;
|
|
231
|
+
await execute(`UPDATE workflow_instance SET status = 'REJECTED', "completedAt" = ? WHERE id = ?`, [now, input.instanceId]);
|
|
232
|
+
const updated = await queryRows(`SELECT * FROM workflow_instance WHERE id = ?`, [input.instanceId]);
|
|
233
233
|
return rowToInstance(updated[0]);
|
|
234
234
|
}
|
|
235
235
|
async function getApprovals(instanceId) {
|
|
236
|
-
const rows =
|
|
236
|
+
const rows = await queryRows(`SELECT * FROM workflow_approval WHERE "instanceId" = ? ORDER BY "createdAt"`, [instanceId]);
|
|
237
237
|
return rows.map(rowToApproval);
|
|
238
238
|
}
|
|
239
239
|
return {
|