@jacebenson/jsn 4.3.8-dev.53 → 4.3.8-dev.54
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/commands/actions.js +51 -3
package/package.json
CHANGED
package/src/commands/actions.js
CHANGED
|
@@ -29,6 +29,53 @@ async function actionID(argv, app) {
|
|
|
29
29
|
return unwrapSysId(await resolveRecord(app.sdk, { table: ACTION_TABLE, identifier: argv.identifier, matchField: 'name', resource: 'action' }));
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
const refID = value => typeof value === 'string' ? value : value?.value ?? value?.sys_id ?? value?.sysId ?? value?.id;
|
|
33
|
+
const asBoolean = value => value === true || value === 1 || (typeof value === 'string' && value.toLowerCase() === 'true');
|
|
34
|
+
const EXAMPLE_TYPES = new Set(['approval_rules', 'conditions', 'schedule_date_time']);
|
|
35
|
+
function collectActionExamples(node, snapshotId, allowedInputs, examples, flowName) {
|
|
36
|
+
if (!node || typeof node !== 'object') return;
|
|
37
|
+
if (Array.isArray(node)) { for (const item of node) collectActionExamples(item, snapshotId, allowedInputs, examples, flowName); return; }
|
|
38
|
+
const typeId = refID(node.actionTypeSysId || node.action_type_sys_id || node.actionType || node.action_type);
|
|
39
|
+
if (typeId === snapshotId && Array.isArray(node.inputs)) {
|
|
40
|
+
const values = Object.fromEntries(node.inputs.filter(input => input?.name && allowedInputs.has(input.name) && input.value !== undefined && input.value !== null)
|
|
41
|
+
.map(input => [input.name, refID(input.value) ?? input.value]));
|
|
42
|
+
if (Object.keys(values).length) examples.push({ flow: flowName, inputs: values });
|
|
43
|
+
}
|
|
44
|
+
for (const [key, value] of Object.entries(node)) if (key !== 'inputs') collectActionExamples(value, snapshotId, allowedInputs, examples, flowName);
|
|
45
|
+
}
|
|
46
|
+
async function findActionExamples(sdk, definition, inputDefinitions) {
|
|
47
|
+
const snapshotId = refID(definition.latest_snapshot);
|
|
48
|
+
if (!/^[a-f0-9]{32}$/i.test(snapshotId || '')) return { examples: [], status: 'unavailable' };
|
|
49
|
+
const allowedInputs = new Set(inputDefinitions.filter(input => EXAMPLE_TYPES.has(input.type)
|
|
50
|
+
&& !/(password|secret|token|credential|authorization|cookie|api[_-]?key)/i.test(input.name)).map(input => input.name));
|
|
51
|
+
if (!allowedInputs.size) return { examples: [], status: 'not_applicable' };
|
|
52
|
+
const query = new URLSearchParams({
|
|
53
|
+
sysparm_query: `action_type=${snapshotId}^ORDERBYsys_id`,
|
|
54
|
+
sysparm_fields: 'sys_id,flow,action_type',
|
|
55
|
+
sysparm_limit: '20',
|
|
56
|
+
sysparm_display_value: 'false',
|
|
57
|
+
});
|
|
58
|
+
const rows = await sdk.listStrict('sys_hub_action_instance', query);
|
|
59
|
+
if (!rows.length) return { examples: [], status: 'none' };
|
|
60
|
+
const flowIds = [...new Set(rows.map(row => refID(row.flow)).filter(value => /^[a-f0-9]{32}$/i.test(value || '')))];
|
|
61
|
+
if (!flowIds.length) return { examples: [], status: 'unavailable' };
|
|
62
|
+
const examples = [];
|
|
63
|
+
let unreadable = false;
|
|
64
|
+
for (const flowId of flowIds.slice(0, 10)) {
|
|
65
|
+
try {
|
|
66
|
+
const result = await sdk.inspectFlow(flowId);
|
|
67
|
+
const payloadActions = result?.payload?.actionInstances;
|
|
68
|
+
if (!Array.isArray(payloadActions) || !payloadActions.some(item => item && typeof item === 'object' && !Array.isArray(item))) {
|
|
69
|
+
unreadable = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
collectActionExamples(result.payload, snapshotId, allowedInputs, examples, result?.flow?.name || flowId);
|
|
73
|
+
} catch { unreadable = true; }
|
|
74
|
+
if (examples.length >= 3) break;
|
|
75
|
+
}
|
|
76
|
+
return { examples: examples.slice(0, 3), status: examples.length ? 'found' : unreadable ? 'unavailable' : 'none' };
|
|
77
|
+
}
|
|
78
|
+
|
|
32
79
|
export const actionsCmd = wrap => {
|
|
33
80
|
// Reuse the existing list/show picker and output contract only.
|
|
34
81
|
const base = buildDevCmd('actions', ACTION_TABLE, ['action'], ['name', 'active', 'sys_scope', 'sys_updated_on'], wrap, {
|
|
@@ -77,12 +124,13 @@ export const actionsCmd = wrap => {
|
|
|
77
124
|
const inputs = (definition.inputs || []).map(input => ({
|
|
78
125
|
name: input.name,
|
|
79
126
|
type: input.type || input.parameter?.type || input.parameter?.inputType || input.valueType || 'unknown',
|
|
80
|
-
mandatory: input.mandatory ?? input.required ?? input.parameter?.mandatory ?? input.parameter?.required
|
|
127
|
+
mandatory: asBoolean(input.mandatory ?? input.required ?? input.parameter?.mandatory ?? input.parameter?.required),
|
|
81
128
|
default: input.defaultValue ?? input.default ?? input.parameter?.defaultValue ?? '',
|
|
82
129
|
description: input.description || input.label || input.parameter?.label || '',
|
|
83
130
|
}));
|
|
84
|
-
|
|
85
|
-
|
|
131
|
+
const exampleResult = await findActionExamples(app.sdk, definition, inputs);
|
|
132
|
+
app.ok({ action: { id, name: definition.name, scope }, inputs, examples: exampleResult.examples,
|
|
133
|
+
examples_status: exampleResult.status },
|
|
86
134
|
{ summary: `Input help for action ${definition.name || id}` });
|
|
87
135
|
}),
|
|
88
136
|
})
|