docs(skill): update formSubmitted trigger docs for multi-form support

Reflect the formId(formId) -> formIds[] change across skill + CLI + docs:
- skill api-reference/trigger-types.md: formIds array config + legacy note
- skill guides (automation-guide.md, cli-reference.md): --form-ids <csv>
- inkreach-cli: add --form-ids option (csv parse), keep --form-id legacy
- docs/automation/triggers.md: P1 formSubmitted marked done
- docs/automation/README.md: formSubmitted config fields updated
This commit is contained in:
yeuimu
2026-08-12 03:59:12 +00:00
parent ef013690f3
commit 73998173a8
5 changed files with 67 additions and 17 deletions
@@ -122,14 +122,19 @@ inkreach-teable trigger set <wfId> --type recordCreatedOrUpdated --table-id tblX
## 5. formSubmitted — When a form is submitted ## 5. formSubmitted — When a form is submitted
```bash ```bash
inkreach-teable trigger set <wfId> --type formSubmitted --table-id tblXXX --form-id viwXXX inkreach-teable trigger set <wfId> --type formSubmitted --table-id tblXXX --form-ids 'viwXXX1,viwXXX2'
``` ```
**Config:** **Config:**
```json ```json
{ "tableId": "tblXXX", "formId": "viwXXX" } { "tableId": "tblXXX", "formIds": ["viwXXX1", "viwXXX2"] }
``` ```
- `formId` is the **form view's id** (a view of type `form`). - `formIds` is an array of **form view ids** (views of type `form`). The trigger
fires when **any** of the listed forms is submitted.
- **Legacy compatibility:** the older single-string `formId` key is still
accepted (migrated to `[formId]` automatically), so existing workflows keep
working.
- An empty `formIds` array means the trigger never fires.
**Output:** `record.id`, `record.fields.*`, `user`, `tableId`, `formId`. **Output:** `record.id`, `record.fields.*`, `user`, `tableId`, `formId`.
@@ -44,7 +44,7 @@ Automations (workflows) are event-driven: a **trigger** fires, then **actions**
| React to record changes | `recordUpdated` | `--table-id`, `--field-ids` (empty = all) | | React to record changes | `recordUpdated` | `--table-id`, `--field-ids` (empty = all) |
| React when a condition matches | `recordCondition` | `--table-id`, `--conditions` (required) | | React when a condition matches | `recordCondition` | `--table-id`, `--conditions` (required) |
| React to create OR update | `recordCreatedOrUpdated` | `--table-id`, `--conditions` (optional) | | React to create OR update | `recordCreatedOrUpdated` | `--table-id`, `--conditions` (optional) |
| Form submission | `formSubmitted` | `--table-id`, `--form-id` | | Form submission | `formSubmitted` | `--table-id`, `--form-ids <csv>` |
| Schedule (only type+interval work) | `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` | | Schedule (only type+interval work) | `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` |
| Button field click | `buttonClick` | `--table-id`, `--field-ids` | | Button field click | `buttonClick` | `--table-id`, `--field-ids` |
| External HTTP POST | `webhook` | (none; auth backend-ignored) | | External HTTP POST | `webhook` | (none; auth backend-ignored) |
@@ -93,7 +93,7 @@ inkreach-teable trigger set <workflowId> --type <T> [options] [--base-id <id>]
| `recordUpdated` | `--table-id`, `--field-ids '<json>'` | | `recordUpdated` | `--table-id`, `--field-ids '<json>'` |
| `recordCondition` | `--table-id`, `--conditions '<json>'` | | `recordCondition` | `--table-id`, `--conditions '<json>'` |
| `recordCreatedOrUpdated` | `--table-id` | | `recordCreatedOrUpdated` | `--table-id` |
| `formSubmitted` | `--table-id`, `--form-id <id>` | | `formSubmitted` | `--table-id`, `--form-ids <csv>` |
| `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` | | `scheduledTime` | `--schedule '{"timing":{"type":"minutes","interval":5}}'` |
| `buttonClick` | `--table-id`, `--field-ids '<json>'` | | `buttonClick` | `--table-id`, `--field-ids '<json>'` |
| `webhook` | (none) | | `webhook` | (none) |
+42 -10
View File
@@ -158,7 +158,10 @@ function workflowCommand() {
if (ctx.json) if (ctx.json)
(0, output_1.printJson)({ ok: true, ...res }); (0, output_1.printJson)({ ok: true, ...res });
else else
(0, output_1.printKv)([['snapshotId', res.snapshotId], ['status', 'activated']]); (0, output_1.printKv)([
['snapshotId', res.snapshotId],
['status', 'activated'],
]);
})); }));
cmd cmd
.command('deactivate <workflowId>') .command('deactivate <workflowId>')
@@ -206,8 +209,14 @@ function workflowCommand() {
exports.workflowCommand = workflowCommand; exports.workflowCommand = workflowCommand;
// ===================== trigger ===================== // ===================== trigger =====================
const TRIGGER_TYPES = [ const TRIGGER_TYPES = [
'recordCreated', 'recordUpdated', 'recordCondition', 'recordCreatedOrUpdated', 'recordCreated',
'formSubmitted', 'scheduledTime', 'buttonClick', 'webhook', 'recordUpdated',
'recordCondition',
'recordCreatedOrUpdated',
'formSubmitted',
'scheduledTime',
'buttonClick',
'webhook',
]; ];
function triggerCommand() { function triggerCommand() {
const cmd = new commander_1.Command('trigger'); const cmd = new commander_1.Command('trigger');
@@ -218,7 +227,8 @@ function triggerCommand() {
.requiredOption('--type <type>', `Trigger type: ${TRIGGER_TYPES.join('|')}`) .requiredOption('--type <type>', `Trigger type: ${TRIGGER_TYPES.join('|')}`)
.option('--table-id <id>', 'Table id (most record/button triggers)') .option('--table-id <id>', 'Table id (most record/button triggers)')
.option('--field-ids <json>', 'Field ids JSON array, e.g. \'["fldXXX"]\' (recordUpdated, buttonClick)') .option('--field-ids <json>', 'Field ids JSON array, e.g. \'["fldXXX"]\' (recordUpdated, buttonClick)')
.option('--form-id <id>', 'Form view id (formSubmitted)') .option('--form-id <id>', 'Form view id (formSubmitted, legacy single). Prefer --form-ids.')
.option('--form-ids <csv>', 'Comma-separated form view ids (formSubmitted, multi-select)')
.option('--conditions <json>', 'Filter conditions JSON (recordCondition/recordCreated)') .option('--conditions <json>', 'Filter conditions JSON (recordCondition/recordCreated)')
.option('--schedule <json>', 'Schedule JSON {timing:{type,interval}} (scheduledTime)') .option('--schedule <json>', 'Schedule JSON {timing:{type,interval}} (scheduledTime)')
.option('--batch <json>', 'Batch config JSON {enabled,batchWindow,batchSize}') .option('--batch <json>', 'Batch config JSON {enabled,batchWindow,batchSize}')
@@ -228,12 +238,15 @@ function triggerCommand() {
const opts = o; const opts = o;
const baseId = (0, ctx_1.requireBaseId)(ctx, opts.baseId); const baseId = (0, ctx_1.requireBaseId)(ctx, opts.baseId);
if (!TRIGGER_TYPES.includes(opts.type)) { if (!TRIGGER_TYPES.includes(opts.type)) {
throw Object.assign(new Error(`Invalid trigger type: ${opts.type}`), { code: 'INVALID_TYPE' }); throw Object.assign(new Error(`Invalid trigger type: ${opts.type}`), {
code: 'INVALID_TYPE',
});
} }
const config = buildTriggerConfig(opts.type, { const config = buildTriggerConfig(opts.type, {
tableId: opts.tableId, tableId: opts.tableId,
fieldIds: opts.fieldIds, fieldIds: opts.fieldIds,
formId: opts.formId, formId: opts.formId,
formIds: opts.formIds,
conditions: opts.conditions, conditions: opts.conditions,
schedule: opts.schedule, schedule: opts.schedule,
batch: opts.batch, batch: opts.batch,
@@ -295,14 +308,25 @@ function buildTriggerConfig(type, opts) {
if (opts.tableId) if (opts.tableId)
config.tableId = opts.tableId; config.tableId = opts.tableId;
// conditions is required for recordCondition // conditions is required for recordCondition
config.conditions = opts.conditions ? parse(opts.conditions) : { conjunction: 'and', filterSet: [] }; config.conditions = opts.conditions
? parse(opts.conditions)
: { conjunction: 'and', filterSet: [] };
break; break;
} }
case 'formSubmitted': { case 'formSubmitted': {
if (opts.tableId) if (opts.tableId)
config.tableId = opts.tableId; config.tableId = opts.tableId;
if (opts.formId) // Prefer --form-ids (csv) which populates formIds:string[]. Fall back to
// legacy --form-id (single string) for backward compatibility.
if (opts.formIds) {
config.formIds = opts.formIds
.split(',')
.map((s) => s.trim())
.filter((s) => s.length > 0);
}
else if (opts.formId) {
config.formId = opts.formId; config.formId = opts.formId;
}
break; break;
} }
case 'scheduledTime': { case 'scheduledTime': {
@@ -328,7 +352,8 @@ function buildTriggerConfig(type, opts) {
default: default:
break; break;
} }
if (opts.batch && ['recordCreated', 'recordUpdated', 'recordCondition', 'recordCreatedOrUpdated'].includes(type)) { if (opts.batch &&
['recordCreated', 'recordUpdated', 'recordCondition', 'recordCreatedOrUpdated'].includes(type)) {
config.batch = parse(opts.batch); config.batch = parse(opts.batch);
} }
return config; return config;
@@ -336,7 +361,12 @@ function buildTriggerConfig(type, opts) {
exports.buildTriggerConfig = buildTriggerConfig; exports.buildTriggerConfig = buildTriggerConfig;
// ===================== action ===================== // ===================== action =====================
const ACTION_TYPES = [ const ACTION_TYPES = [
'script', 'sendEmail', 'httpRequest', 'createRecord', 'updateRecord', 'getRecords', 'script',
'sendEmail',
'httpRequest',
'createRecord',
'updateRecord',
'getRecords',
]; ];
function actionCommand() { function actionCommand() {
const cmd = new commander_1.Command('action'); const cmd = new commander_1.Command('action');
@@ -367,7 +397,9 @@ function actionCommand() {
const opts = o; const opts = o;
const baseId = (0, ctx_1.requireBaseId)(ctx, opts.baseId); const baseId = (0, ctx_1.requireBaseId)(ctx, opts.baseId);
if (!ACTION_TYPES.includes(opts.type)) { if (!ACTION_TYPES.includes(opts.type)) {
throw Object.assign(new Error(`Invalid action type: ${opts.type}`), { code: 'INVALID_TYPE' }); throw Object.assign(new Error(`Invalid action type: ${opts.type}`), {
code: 'INVALID_TYPE',
});
} }
const config = opts.config ? JSON.parse(opts.config) : buildActionConfig(opts); const config = opts.config ? JSON.parse(opts.config) : buildActionConfig(opts);
const wf = await ctx.client.getWorkflowInBase(baseId, workflowId); const wf = await ctx.client.getWorkflowInBase(baseId, workflowId);
+15 -2
View File
@@ -261,7 +261,8 @@ export function triggerCommand(): Command {
'--field-ids <json>', '--field-ids <json>',
'Field ids JSON array, e.g. \'["fldXXX"]\' (recordUpdated, buttonClick)' 'Field ids JSON array, e.g. \'["fldXXX"]\' (recordUpdated, buttonClick)'
) )
.option('--form-id <id>', 'Form view id (formSubmitted)') .option('--form-id <id>', 'Form view id (formSubmitted, legacy single). Prefer --form-ids.')
.option('--form-ids <csv>', 'Comma-separated form view ids (formSubmitted, multi-select)')
.option('--conditions <json>', 'Filter conditions JSON (recordCondition/recordCreated)') .option('--conditions <json>', 'Filter conditions JSON (recordCondition/recordCreated)')
.option('--schedule <json>', 'Schedule JSON {timing:{type,interval}} (scheduledTime)') .option('--schedule <json>', 'Schedule JSON {timing:{type,interval}} (scheduledTime)')
.option('--batch <json>', 'Batch config JSON {enabled,batchWindow,batchSize}') .option('--batch <json>', 'Batch config JSON {enabled,batchWindow,batchSize}')
@@ -274,6 +275,7 @@ export function triggerCommand(): Command {
tableId?: string; tableId?: string;
fieldIds?: string; fieldIds?: string;
formId?: string; formId?: string;
formIds?: string;
conditions?: string; conditions?: string;
schedule?: string; schedule?: string;
batch?: string; batch?: string;
@@ -289,6 +291,7 @@ export function triggerCommand(): Command {
tableId: opts.tableId, tableId: opts.tableId,
fieldIds: opts.fieldIds, fieldIds: opts.fieldIds,
formId: opts.formId, formId: opts.formId,
formIds: opts.formIds,
conditions: opts.conditions, conditions: opts.conditions,
schedule: opts.schedule, schedule: opts.schedule,
batch: opts.batch, batch: opts.batch,
@@ -332,6 +335,7 @@ export function buildTriggerConfig(
tableId?: string; tableId?: string;
fieldIds?: string; fieldIds?: string;
formId?: string; formId?: string;
formIds?: string;
conditions?: string; conditions?: string;
schedule?: string; schedule?: string;
batch?: string; batch?: string;
@@ -362,7 +366,16 @@ export function buildTriggerConfig(
} }
case 'formSubmitted': { case 'formSubmitted': {
if (opts.tableId) config.tableId = opts.tableId; if (opts.tableId) config.tableId = opts.tableId;
if (opts.formId) config.formId = opts.formId; // Prefer --form-ids (csv) which populates formIds:string[]. Fall back to
// legacy --form-id (single string) for backward compatibility.
if (opts.formIds) {
config.formIds = opts.formIds
.split(',')
.map((s) => s.trim())
.filter((s) => s.length > 0);
} else if (opts.formId) {
config.formId = opts.formId;
}
break; break;
} }
case 'scheduledTime': { case 'scheduledTime': {