camunda-cli 0.5.0 → 0.6.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/PANDUAN.md +5 -1
- package/README.md +3 -1
- package/bin/camunda.js +4 -3
- package/package.json +1 -1
- package/src/commands/instances.js +46 -22
package/PANDUAN.md
CHANGED
|
@@ -157,12 +157,16 @@ Untuk mengosongkan inbox satu penguji di AlurKerja sekaligus, tanpa peduli prose
|
|
|
157
157
|
```bash
|
|
158
158
|
camunda remove dev-anla me@anla.my.id --dry-run # lihat dulu apa yang akan dihapus
|
|
159
159
|
camunda remove dev-anla me@anla.my.id # hapus, diminta mengetik jumlah instance
|
|
160
|
+
camunda remove dev-anla me@anla.my.id --live # tenant LIVE: peringatan, lalu ketik nama tenant
|
|
160
161
|
```
|
|
161
162
|
|
|
162
163
|
Perintah ini menghapus setiap instance yang punya task terbuka milik email itu di tenant
|
|
163
164
|
`<Nama> (TEST)` workspace tersebut. Workspace boleh slug atau nama (`dev-anla`, `Plan Route`),
|
|
164
165
|
atau id tenant TEST kalau namanya ambigu (`hospital (TEST)` dan `Hospital (TEST)`) atau slug-nya
|
|
165
|
-
tidak mengikuti nama (`new-workspace` berjalan di `Telco (TEST)`). Id tenant LIVE ditolak
|
|
166
|
+
tidak mengikuti nama (`new-workspace` berjalan di `Telco (TEST)`). Id tenant LIVE ditolak kecuali
|
|
167
|
+
memakai `--live`. Dengan `--live`, setelah daftar task muncul peringatan bahwa ini tenant LIVE (request
|
|
168
|
+
sungguhan, bukan data uji) dan nama tenant harus diketik persis untuk melanjutkan; `-y` tidak
|
|
169
|
+
melewati konfirmasi ini.
|
|
166
170
|
Menghapus instance anak call activity ikut menghentikan induknya, jadi seluruh request berakhir,
|
|
167
171
|
termasuk task orang lain di request yang sama.
|
|
168
172
|
|
package/README.md
CHANGED
|
@@ -176,7 +176,9 @@ a process in one go, which matters because testing a model leaves a trail of the
|
|
|
176
176
|
`remove <workspace> <email>` clears one tester's inbox on an AlurKerja engine instead: it
|
|
177
177
|
deletes every instance holding an open task assigned to that email in the workspace's
|
|
178
178
|
`<Name> (TEST)` tenant. The workspace is a slug or name (`dev-anla`, `Plan Route`) or a Test
|
|
179
|
-
tenant id; a Live tenant id is refused. `--dry-run` lists what would go.
|
|
179
|
+
tenant id; a Live tenant id is refused. `--dry-run` lists what would go. `--live` clears the
|
|
180
|
+
workspace's Live tenant instead: after the list it prints a warning and asks for the tenant name
|
|
181
|
+
to be typed, a prompt `--yes` does not skip. Deleting a called
|
|
180
182
|
child instance also terminates its parent, so the whole request ends, not only that task.
|
|
181
183
|
|
|
182
184
|
**Variable types matter.** `--var n=300` sends a string, and `"300" > 200` is a string
|
package/bin/camunda.js
CHANGED
|
@@ -24,7 +24,7 @@ program
|
|
|
24
24
|
'Start with "camunda inspect <key>" to read a deployed model, and\n' +
|
|
25
25
|
'"camunda diagnose <instanceId>" when an instance misbehaves.'
|
|
26
26
|
)
|
|
27
|
-
.version('0.
|
|
27
|
+
.version('0.6.0')
|
|
28
28
|
.option('--json', 'print the raw API payload instead of a formatted view')
|
|
29
29
|
.option('--no-color', 'never emit colour, even on a terminal')
|
|
30
30
|
.showHelpAfterError()
|
|
@@ -132,9 +132,10 @@ withTenant(
|
|
|
132
132
|
|
|
133
133
|
program
|
|
134
134
|
.command('remove <workspace> <assignee>')
|
|
135
|
-
.description("Delete every instance holding an open task for <assignee> in a workspace's TEST tenant (workspace slug, name, or
|
|
135
|
+
.description("Delete every instance holding an open task for <assignee> in a workspace's TEST tenant, or its LIVE tenant with --live (workspace slug, name, or tenant id)")
|
|
136
|
+
.option('--live', 'clear the LIVE tenant instead; always asks you to type the tenant name')
|
|
136
137
|
.option('--dry-run', 'list what would be deleted and stop')
|
|
137
|
-
.option('-y, --yes', 'skip the confirmation prompt')
|
|
138
|
+
.option('-y, --yes', 'skip the confirmation prompt (never the --live one)')
|
|
138
139
|
.action(removeCommand);
|
|
139
140
|
|
|
140
141
|
program
|
package/package.json
CHANGED
|
@@ -188,10 +188,11 @@ export async function cancelCommand(id, options) {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
// AlurKerja runs each workspace on two tenants, "<Name>" for Live and "<Name> (TEST)" for
|
|
191
|
-
// Test.
|
|
192
|
-
//
|
|
191
|
+
// Test. remove clears the Test one unless --live is given, and a tenant id of the other kind
|
|
192
|
+
// is refused rather than used, so the flag alone decides which side gets deleted.
|
|
193
193
|
const TENANT_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
194
194
|
const TEST_SUFFIX = /\s*\(TEST\)\s*$/i;
|
|
195
|
+
const isTestTenant = (t) => TEST_SUFFIX.test(t.name ?? '');
|
|
195
196
|
const workspaceSlug = (name) =>
|
|
196
197
|
String(name ?? '')
|
|
197
198
|
.replace(TEST_SUFFIX, '')
|
|
@@ -199,42 +200,59 @@ const workspaceSlug = (name) =>
|
|
|
199
200
|
.replace(/[^a-z0-9]+/g, '-')
|
|
200
201
|
.replace(/^-+|-+$/g, '');
|
|
201
202
|
|
|
202
|
-
async function
|
|
203
|
+
async function resolveTenant(client, workspace, live) {
|
|
204
|
+
const side = live ? 'Live' : 'Test';
|
|
203
205
|
if (TENANT_ID.test(workspace)) {
|
|
204
206
|
const [tenant] = await client.tenants({ id: workspace });
|
|
205
207
|
if (!tenant) throw new Error(`No tenant with id ${workspace}.`);
|
|
206
|
-
if (
|
|
207
|
-
throw new Error(
|
|
208
|
+
if (isTestTenant(tenant) === live) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
live
|
|
211
|
+
? `${workspace} is "${tenant.name}", a Test tenant. Drop --live to clear it.`
|
|
212
|
+
: `${workspace} is "${tenant.name}", a Live tenant. remove only clears it with --live.`
|
|
213
|
+
);
|
|
208
214
|
}
|
|
209
215
|
return tenant;
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
const wanted = workspaceSlug(workspace);
|
|
213
219
|
const all = await client.tenants({ maxResults: 1000 });
|
|
214
|
-
const matches = all.filter((t) =>
|
|
220
|
+
const matches = all.filter((t) => isTestTenant(t) !== live && workspaceSlug(t.name) === wanted);
|
|
215
221
|
if (matches.length === 1) return matches[0];
|
|
216
222
|
if (matches.length > 1) {
|
|
217
223
|
const list = matches.map((t) => ` ${t.id} ${t.name}`).join('\n');
|
|
218
|
-
throw new Error(`"${workspace}" matches ${matches.length}
|
|
224
|
+
throw new Error(`"${workspace}" matches ${matches.length} ${side} tenants. Pass the tenant id instead:\n${list}`);
|
|
219
225
|
}
|
|
220
226
|
// The engine only knows tenant names, and a workspace slug does not always follow its
|
|
221
227
|
// name (the "new-workspace" slug runs on "Telco (TEST)").
|
|
222
|
-
throw new Error(`No
|
|
228
|
+
throw new Error(`No ${side} tenant named like "${workspace}". Find it with: camunda tenants -s <name>, then pass its id.`);
|
|
223
229
|
}
|
|
224
230
|
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
|
|
231
|
+
// Settles on end of input too. Without that, a script with nothing on stdin leaves the prompt
|
|
232
|
+
// pending and node exits 0 silently, which reads like success.
|
|
233
|
+
async function ask(question) {
|
|
234
|
+
const rl = createInterface({ input: stdin, output: stdout });
|
|
235
|
+
const closed = new Promise((resolve) => rl.once('close', () => resolve(null)));
|
|
236
|
+
const answer = await Promise.race([rl.question(question), closed]);
|
|
237
|
+
rl.close();
|
|
238
|
+
if (answer === null) out.line('');
|
|
239
|
+
return answer?.trim() ?? '';
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Clears every open task assigned to one person in a workspace by deleting the instance each
|
|
243
|
+
// task belongs to. Repeated test runs fill a tester's inbox faster than cancelling them one
|
|
244
|
+
// request at a time in the App can empty it.
|
|
228
245
|
export async function removeCommand(workspace, assignee, options) {
|
|
229
246
|
const client = new Client(requireConfig());
|
|
230
|
-
const
|
|
247
|
+
const live = Boolean(options.live);
|
|
248
|
+
const tenant = await resolveTenant(client, workspace, live);
|
|
231
249
|
|
|
232
250
|
const tasks = await client.tasks({ tenantIdIn: tenant.id, assignee });
|
|
233
251
|
const instanceIds = [...new Set(tasks.map((t) => t.processInstanceId).filter(Boolean))].sort();
|
|
234
252
|
|
|
235
|
-
if (out.isJsonMode() && options.dryRun) return out.json({ tenant, assignee, tasks, instanceIds });
|
|
253
|
+
if (out.isJsonMode() && options.dryRun) return out.json({ tenant, live, assignee, tasks, instanceIds });
|
|
236
254
|
|
|
237
|
-
out.heading(`${tenant.name} ${tenant.id}`);
|
|
255
|
+
out.heading(`${tenant.name} ${tenant.id}${live ? ' LIVE' : ''}`);
|
|
238
256
|
out.kv([['assignee', assignee]]);
|
|
239
257
|
if (tasks.length === 0) return out.note(`\nNo open tasks assigned to ${assignee}.`);
|
|
240
258
|
|
|
@@ -248,14 +266,20 @@ export async function removeCommand(workspace, assignee, options) {
|
|
|
248
266
|
|
|
249
267
|
if (options.dryRun) return out.note('\nDry run, nothing deleted.');
|
|
250
268
|
|
|
251
|
-
if (
|
|
252
|
-
|
|
269
|
+
if (live) {
|
|
270
|
+
// --yes does not cover this prompt: these are real requests, and a flag carried over from
|
|
271
|
+
// a Test command line should not be enough to delete them.
|
|
272
|
+
out.line('');
|
|
273
|
+
for (const l of out.wrap(
|
|
274
|
+
`WARNING: "${tenant.name}" is a LIVE tenant. These are real requests, not test data. ` +
|
|
275
|
+
`Deleting them ends each request for everyone involved in it and cannot be undone.`
|
|
276
|
+
)) out.problem(l);
|
|
277
|
+
const answer = await ask(`Are you sure? Type the tenant name "${tenant.name}" to delete ${instanceIds.length} instance(s): `);
|
|
278
|
+
if (answer !== tenant.name) return out.note('Aborted, that did not match.');
|
|
279
|
+
} else if (!options.yes) {
|
|
253
280
|
const expect = String(instanceIds.length);
|
|
254
|
-
const answer = await
|
|
255
|
-
|
|
256
|
-
);
|
|
257
|
-
rl.close();
|
|
258
|
-
if (answer.trim() !== expect) return out.note('Aborted, that did not match.');
|
|
281
|
+
const answer = await ask(`\nDeleting ${expect} instance(s) cannot be undone. Type "${expect}" to confirm: `);
|
|
282
|
+
if (answer !== expect) return out.note('Aborted, that did not match.');
|
|
259
283
|
}
|
|
260
284
|
|
|
261
285
|
let removed = 0;
|
|
@@ -274,7 +298,7 @@ export async function removeCommand(workspace, assignee, options) {
|
|
|
274
298
|
const left = await client.tasks({ tenantIdIn: tenant.id, assignee });
|
|
275
299
|
|
|
276
300
|
if (out.isJsonMode()) {
|
|
277
|
-
return out.json({ tenant, assignee, removed, alreadyGone: gone, failures, remainingTasks: left.length });
|
|
301
|
+
return out.json({ tenant, live, assignee, removed, alreadyGone: gone, failures, remainingTasks: left.length });
|
|
278
302
|
}
|
|
279
303
|
out.line(`\nDeleted ${removed} instance(s).${gone ? ` ${gone} were already gone.` : ''}`);
|
|
280
304
|
for (const f of failures) out.problem(` ${f}`);
|