writing-settings-forms

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
<!-- TIER:1 -->
<!-- TIER:1 -->

Writing Settings Forms

编写 Settings Forms

A settings form (
settingsForm
) is the form the Celigo UI renders to collect a resource's
settings
: which fields appear, their labels and choices, how they're grouped, and which show when. The values it collects are stored in the same resource's
settings
, and that's what steps read at run time --
{{settings.flow.region}}
in a template,
options.settings.flow.region
in a script.
Seven places carry a form, each with its own
settings
: an integration, each of an integration's flow groups (
flowGroupings[]
), a flow, an export, an import, a connection, and an iClient. An Integration App's settings are a different mechanism, driven by the app's own code -- see managing-integrations > Integration Apps.
What a form is not:
  • Not validation. Only the UI renders the form and applies its rules. An API, CLI, or MCP write of
    settings
    stores the object it's given: a field the form marks
    required
    can be missing, keys the form doesn't define are kept, and nothing checks types or choices.
  • Not checked on save. A layout entry or a condition naming a field the form doesn't have is stored as sent. Only
    form
    and
    init
    are kept: any other top-level key in
    settingsForm
    is dropped without an error.
  • Not what steps read. Steps read
    settings
    , never the form. A new field gives steps nothing until its value is in
    settings
    , so write the value along with the field.
设置表单(
settingsForm
)是 Celigo UI 渲染、用于收集资源
settings
的表单:决定显示哪些字段、它们的标签和选项、如何分组,以及哪些字段在何时显示。表单收集的值存储在同一资源的
settings
中,这正是 steps 在运行时读取的内容——在 template 中是
{{settings.flow.region}}
,在 script 中是
options.settings.flow.region
。
共有七类资源携带表单,每类资源都有自己的
settings
:一个 integration、一个 integration 的各个 flow group(
flowGroupings[]
)、一个 flow、一个 export、一个 import、一个 connection 和一个 iClient。Integration App 的 settings 采用另一套机制,由该 app 自身的代码驱动——参见 managing-integrations > Integration Apps。
表单不是什么:
  • 不是 validation。 只有 UI 会渲染表单并应用其规则。通过 API、CLI 或 MCP 写入
    settings
    时,会原样存储传入的对象:表单标记为
    required
    的字段仍可能缺失,表单未定义的 key 也会保留,而且不会检查类型或选项。
  • 保存时不会校验。 引用了表单中不存在字段的 layout entry 或 condition 会按发送内容存储。
    settingsForm
    中仅保留
    form
    和
    init
    :任何其他顶层 key 都会在不报错的情况下被丢弃。
  • 不是 steps 读取的内容。 Steps 读取的是
    settings
    ,绝不是表单。新增字段不会自动向 steps 提供任何值,除非该值已写入
    settings
    ,因此添加字段时必须同时写入值。

How to Run the Steps

如何运行 Steps

This skill names each step by what it reads or changes; the exact calls depend on how you reach the account. Read the one reference that matches your tools -- both follow the step numbers of How to Build a Settings Form, then How to Check a Form:
  • Celigo MCP tools (
    list_integrations
    ,
    upsert_integration
    ,
    upsert_flow
    ,
    upsert_export
    , ...) -- references/mcp.md. The deployed server writes forms only through the full-replace
    upsert_*
    tools, and can't run an init script or preview a dropdown's export.
  • The
    celigo
    CLI
    -- references/cli.md
Reading a resource only reads. Writing a form or its
settings
updates the resource, and a full update replaces the whole document: a body that leaves out
settingsForm
or
settings
erases them.
本 skill 根据每个 step 读取或更改的内容来命名;具体调用方式取决于访问 account 的方式。阅读与所用工具匹配的那份 reference——两者均遵循“如何构建 Settings Form”的步骤编号,然后阅读“如何检查表单”:
  • Celigo MCP tools(
    list_integrations
    、
    upsert_integration
    、
    upsert_flow
    、
    upsert_export
    等)——references/mcp.md。已部署的 server 只能通过完整替换的
    upsert_*
    tools 写入表单,无法运行 init script,也无法预览 dropdown 的 export。
  • celigo
    CLI
    ——references/cli.md
读取资源只会执行读取。写入表单或其
settings
会更新资源,而完整更新会替换整个 document:如果请求 body 未包含
settingsForm
或
settings
,它们就会被清除。

The Seven Scopes

七种 Scope

ScopeFormValuesSteps read them as
Integration
settingsForm
settings
settings.integration.<name>
, in every flow of the integration
Flow group
flowGroupings[].settingsForm
on the integration
flowGroupings[].settings
settings.flowGrouping.<name>
, in the flows of that group
Flow
settingsForm
settings
settings.flow.<name>
, in the flow's steps
Export (lookups too)
settingsForm
settings
settings.export.<name>
, in that step only
Import
settingsForm
settings
settings.import.<name>
, in that step only
Connection
settingsForm
settings
settings.connection.<name>
, in steps using the connection
iClient
settingsForm
settings
settings.iClient.<name>
, in steps whose connection uses it
The scope key is literal:
{{settings.region}}
(no scope) and paths built from field labels or section names render empty. After the scope, the path follows the stored JSON -- a top-level value is
settings.flow.region
, and each nested key adds a segment. Templates, filters, and Mapper 2.0 read these paths (writing-handlebars > Context Decision Matrix); scripts read
options.settings.<scope>
(writing-scripts > Common Options).
Which scopes a step sees depends on how it runs:
  • A flow run gives the whole chain: integration, flow group, flow, connection, iClient, and the step's own scope.
  • A step inside an API or a Tool gets only its own scope plus connection and iClient; integration, flow group, and flow resolve empty there.
  • A step run on its own -- an export or import invoke with no parent -- gets only its own scope. An import invoke run as a step of a flow sees the flow's chain.
Pick the narrowest scope that every step reading the value shares: a whole integration, a flow group for flows that share a region or subsidiary, a flow, or a step's own form for a knob only that step uses.
ScopeFormValuesSteps read them as
Integration
settingsForm
settings
settings.integration.<name>
,在该 integration 的每个 flow 中
Flow groupintegration 上的
flowGroupings[].settingsForm
flowGroupings[].settings
settings.flowGrouping.<name>
,在该 group 的 flows 中
Flow
settingsForm
settings
settings.flow.<name>
,在该 flow 的 steps 中
Export(包括 lookups)
settingsForm
settings
settings.export.<name>
,仅在该 step 中
Import
settingsForm
settings
settings.import.<name>
,仅在该 step 中
Connection
settingsForm
settings
settings.connection.<name>
,在使用该 connection 的 steps 中
iClient
settingsForm
settings
settings.iClient.<name>
,在使用该 iClient 的 connection 所对应的 steps 中
scope key 是字面量:
{{settings.region}}
(没有 scope)以及根据字段标签或 section 名称构建的路径都会渲染为空。在 scope 之后,路径遵循存储的 JSON——顶层 value 是
settings.flow.region
,每增加一层嵌套 key 就增加一段路径。Templates、filters 和 Mapper 2.0 读取这些路径(writing-handlebars > Context Decision Matrix);scripts 读取
options.settings.<scope>
(writing-scripts > Common Options)。
Step 能看到哪些 scopes 取决于其运行方式:
  • Flow run 会获得完整链路:integration、flow group、flow、connection、iClient,以及该 step 自身的 scope。
  • API 或 Tool 内的 step 只能获得自身 scope,以及 connection 和 iClient;在该处 integration、flow group 和 flow 都会解析为空。
  • 单独运行的 step——没有父级的 export 或 import invoke——只能获得自身 scope。作为 flow 中一个 step 运行的 import invoke 则能看到该 flow 的完整链路。
选择所有读取该 value 的 steps 共享的最窄 scope:整个 integration、用于共享 region 或 subsidiary 的 flow group、flow,或仅供某个 step 使用的自身表单。

Quick Reference

快速参考

The Shape

结构

json
{
  "settingsForm": {
    "form": {
      "fieldMap": {
        "region": {
          "id": "region", "name": "region", "type": "select", "label": "Region", "required": true,
          "options": [{ "items": [{ "label": "United States", "value": "us" }, { "label": "Europe", "value": "eu" }] }]
        },
        "vatNumber": {
          "id": "vatNumber", "name": "vatNumber", "type": "text", "label": "VAT number",
          "visibleWhen": [{ "field": "region", "is": ["eu"] }]
        },
        "statusMap": {
          "id": "statusMap", "name": "statusMap", "type": "staticMap", "label": "Order status map",
          "keyName": "source", "keyLabel": "Source status", "valueName": "target", "valueLabel": "Destination status",
          "valueOptions": [{ "label": "Open", "value": "open" }, { "label": "Closed", "value": "closed" }]
        }
      },
      "layout": {
        "type": "collapse",
        "containers": [
          { "label": "General", "fields": ["region", "vatNumber"] },
          { "label": "Advanced", "type": "box", "containers": [{ "label": "Status mapping", "fields": ["statusMap"] }] }
        ]
      }
    }
  },
  "settings": { "region": "eu", "vatNumber": "EU123", "statusMap": [{ "source": "pending", "target": "open" }] }
}
json
{
  "settingsForm": {
    "form": {
      "fieldMap": {
        "region": {
          "id": "region", "name": "region", "type": "select", "label": "Region", "required": true,
          "options": [{ "items": [{ "label": "United States", "value": "us" }, { "label": "Europe", "value": "eu" }] }]
        },
        "vatNumber": {
          "id": "vatNumber", "name": "vatNumber", "type": "text", "label": "VAT number",
          "visibleWhen": [{ "field": "region", "is": ["eu"] }]
        },
        "statusMap": {
          "id": "statusMap", "name": "statusMap", "type": "staticMap", "label": "Order status map",
          "keyName": "source", "keyLabel": "Source status", "valueName": "target", "valueLabel": "Destination status",
          "valueOptions": [{ "label": "Open", "value": "open" }, { "label": "Closed", "value": "closed" }]
        }
      },
      "layout": {
        "type": "collapse",
        "containers": [
          { "label": "General", "fields": ["region", "vatNumber"] },
          { "label": "Advanced", "type": "box", "containers": [{ "label": "Status mapping", "fields": ["statusMap"] }] }
        ]
      }
    }
  },
  "settings": { "region": "eu", "vatNumber": "EU123", "statusMap": [{ "source": "pending", "target": "open" }] }
}

Field Properties

字段属性

PropertyRules
fieldMap key,
id
,
name
Use one string for all three.
name
is the key the value is saved under, directly in
settings
;
id
is how the layout and conditions refer to the field. Keep it a plain key with no dots: the UI saves
a.b
as nested objects but reads it back from a flat
a.b
key, so the field reloads empty
type
The control -- see Field Types
label
,
description
,
helpText
The label beside the field, a line of text under it, and the help icon's text
required
Blocks the UI's Save while the field is empty (default
false
); API writes skip it
defaultValue
What the UI shows while
settings
holds no value under the field's
name
options
The choices of a
select
,
multiselect
, or
radiogroup
: groups (
[{"items": [...]}]
, each item a string or a
{label, value}
pair) or bare
{label, value}
entries. A
toggle
takes bare
{label, value}
entries. The saved value is the item's
value
Conditions
visibleWhen
and its relatives -- see Conditions
omitWhenHidden
true
leaves the field's value out of a save while it's hidden; without it, a hidden field's value is saved like any other
属性规则
fieldMap key、
id
、
name
三者使用同一个字符串。
name
是 value 保存时直接写入
settings
的 key;
id
是 layout 和 conditions 引用字段的方式。保持为不含点号的普通 key:UI 会将
a.b
保存为嵌套对象,却从扁平的
a.b
key 读取,因此重新加载后字段为空
type
控件类型——参见 Field Types
label
、
description
、
helpText
字段旁的标签、字段下方的一行文本,以及帮助图标的文本
required
字段为空时阻止 UI 保存(默认
false
);API 写入会跳过此规则
defaultValue
当
settings
中该字段的
name
下没有 value 时,UI 所显示的内容
options
select
、
multiselect
或
radiogroup
的选项:可以是 groups(
[{"items": [...]}]
,其中每个 item 是一个字符串或
{label, value}
对象),也可以是裸
{label, value}
entries。
toggle
接受裸
{label, value}
entries。保存的 value 是所选 item 的
value
Conditions
visibleWhen
及其相关属性——参见 Conditions
omitWhenHidden
为
true
时,字段隐藏期间不会将其 value 纳入保存;未设置时,隐藏字段的 value 会像其他字段一样保存

Field Types

字段类型

type
ControlSaved value
text
Text input;
multiline: true
for a text area,
inputType
(
number
,
email
,
password
,
tel
,
url
) for a typed input,
delimiter
to split the input into an array
String (array with
delimiter
)
checkbox
CheckboxBoolean
toggle
A switch between the entries of
options
The chosen entry's
value
select
,
radiogroup
Dropdown, radio buttonsThe chosen item's
value
multiselect
Dropdown allowing several choicesArray of
value
s
date
,
datetime
Date or date-and-time picker;
doNotAllowFutureDates
,
skipTimezoneConversion
The picked date
relativeuri
Text input for a relative URIString
editor
Code editor;
mode
is
json
,
xml
,
csv
, or
text
The editor's content
keyvalue
Rows of two free-text inputs;
keyName
/
valueName
name them (default
key
/
value
),
showDelete
adds a delete button per row
Array of
{<keyName>, <valueName>}
rows
staticMap
Two-column mapping table whose columns can offer choicesArray of
{<keyName>, <valueName>}
rows -- see Dynamic Fields
exportSelect
Dropdown whose options an export returnsThe chosen option's
value
(an array with
multiselect: true
) -- see Dynamic Fields
type
控件保存的 value
text
文本输入;
multiline: true
表示 textarea,
inputType
(
number
、
email
、
password
、
tel
、
url
)表示特定类型的输入,
delimiter
用于将输入拆分为数组
String(使用
delimiter
时为数组)
checkbox
CheckboxBoolean
toggle
在
options
的 entries 之间切换的开关
所选 entry 的
value
select
、
radiogroup
Dropdown、radio buttons所选 item 的
value
multiselect
允许多选的 dropdown
value
的数组
date
、
datetime
日期或日期时间选择器;
doNotAllowFutureDates
、
skipTimezoneConversion
所选日期
relativeuri
相对 URI 的文本输入String
editor
Code editor;
mode
可为
json
、
xml
、
csv
或
text
编辑器中的内容
keyvalue
每行包含两个自由文本输入框;
keyName
/
valueName
用于命名(默认
key
/
value
),
showDelete
会为每行添加删除按钮
{<keyName>, <valueName>}
行的数组
staticMap
可为列提供选项的双列映射表
{<keyName>, <valueName>}
行的数组——参见 Dynamic Fields
exportSelect
选项由 export 返回的 dropdown所选 option 的
value
(启用
multiselect: true
时为数组)——参见 Dynamic Fields

Conditions

Conditions

A condition rule is
{"field": "<field id>", "is": [<values>]}
(or
"isNot"
). Two set values compare as strings, so
"is": ["true"]
and
"is": [true]
both match a checked checkbox; match an unchecked one with
[false]
.
  • visibleWhen
    shows the field while any rule matches;
    visibleWhenAll
    while all of them do. With neither, the field always shows.
  • requiredWhen
    /
    requiredWhenAll
    and
    disabledWhen
    /
    disabledWhenAll
    make the field required or read-only the same way.
  • A rule can hold nested
    "AND": [rules]
    or
    "OR": [rules]
    in place of
    field
    .
  • A rule naming a field the form doesn't have counts as matching for
    visibleWhen
    (the field always shows) and as not matching for
    requiredWhen
    and
    disabledWhen
    -- the save doesn't catch it.
条件规则为
{"field": "<field id>", "is": [<values>]}
(或使用
"isNot"
)。所有值都按字符串比较,因此
"is": ["true"]
和
"is": [true]
都会匹配已勾选的 checkbox;要匹配未勾选的 checkbox,则使用
[false]
。
  • visibleWhen
    在任一规则匹配时显示字段;
    visibleWhenAll
    在所有规则都匹配时显示。两者均未设置时,字段始终显示。
  • requiredWhen
    /
    requiredWhenAll
    和
    disabledWhen
    /
    disabledWhenAll
    以相同方式将字段设为必填或只读。
  • 规则可以在
    field
    的位置包含嵌套的
    "AND": [rules]
    或
    "OR": [rules]
    。
  • 如果规则引用了表单中不存在的字段,则对于
    visibleWhen
    视为匹配(字段始终显示),对于
    requiredWhen
    和
    disabledWhen
    视为不匹配——保存操作不会发现此问题。

Layout

Layout

form.layout
arranges fields; a field the layout doesn't list isn't shown, and a layout id with no
fieldMap
entry renders nothing.
  • A layout has a
    type
    ,
    containers
    , and optionally
    fields
    (ids shown before the containers).
  • The layout's
    type
    sets how its containers render:
    column
    side by side,
    collapse
    as collapsible sections (closed until opened;
    "collapsed": false
    opens one),
    box
    as bordered groups,
    indent
    as indented groups,
    tabWithoutSave
    and
    verticalTabWithoutSave
    as tabs.
  • Each container has a
    label
    (its heading) and
    fields
    , and can carry its own
    type
    and
    containers
    to nest another level, as the Advanced container does in The Shape.
form.layout
负责排列字段;layout 未列出的字段不会显示,而没有
fieldMap
entry 的 layout id 不会渲染任何内容。
  • 一个 layout 包含
    type
    、
    containers
    ,并可选包含
    fields
    (显示在 containers 之前的 ids)。
  • Layout 的
    type
    决定其 containers 的渲染方式:
    column
    为并排显示,
    collapse
    为可折叠 sections(默认关闭;
    "collapsed": false
    会打开某一项),
    box
    为带边框的 groups,
    indent
    为缩进的 groups,
    tabWithoutSave
    和
    verticalTabWithoutSave
    为 tabs。
  • 每个 container 包含一个
    label
    (其标题)和
    fields
    ,还可以拥有自己的
    type
    和
    containers
    来嵌套另一层,正如“结构”示例中的 Advanced container。

Init Scripts

Init Scripts

settingsForm.init
--
{"_scriptId": "<scriptId>", "function": "<name>"}
-- runs a script function each time the UI opens the form. The UI renders the form the function returns instead of
settingsForm.form
, then fills in the values from
settings
; a throw shows its message in place of the form.
javascript
function initForm(options) {
  var form = options.resource.settingsForm.form;
  // add, remove, or change fields from the resource's own state
  return form;
}
  • What the function gets:
    options.resource
    (the resource whose form it is -- for a flow group, the group:
    _id
    ,
    name
    ,
    settings
    ,
    settingsForm
    ),
    options.sandbox
    , and on an integration and a flow group the parents: a flow group gets its integration as
    parentResource
    (and a parent integration as
    grandparentResource
    ), a child integration gets its parent as
    parentResource
    .
    options.license
    and
    options.parentLicense
    appear only in Integration Apps. Flows, exports, imports, and connections get only
    resource
    and
    sandbox
    .
  • The script must be the account's own. Any other
    _scriptId
    is refused with 422 "Only a script owned by the user or a script shared by the Integration App can be used at settingsForm.init._scriptId" (on a flow group the path is
    flowGroupings.<index>.settingsForm.init._scriptId
    ).
  • Not on iClients. The platform's form-init route serves integrations, flow groups, flows, exports, imports, and connections only, so an iClient form with an
    init
    shows an error instead of the form -- and an iClient save stores any
    _scriptId
    without checking it.
  • Testing: no CLI command or MCP tool calls the init route. Run the function through the JavaScript processor with an
    options
    object you build -- see How to Check a Form.
settingsForm.init
——
{"_scriptId": "<scriptId>", "function": "<name>"}
——会在每次 UI 打开表单时运行一个 script function。UI 会渲染该函数返回的表单,而不是
settingsForm.form
,随后从
settings
填入 values;如果发生异常,则在表单所在位置显示其 message。
javascript
function initForm(options) {
  var form = options.resource.settingsForm.form;
  // add, remove, or change fields from the resource's own state
  return form;
}
  • 函数获得的内容:
    options.resource
    (表单所属的资源;对于 flow group,是该 group:
    _id
    、
    name
    、
    settings
    、
    settingsForm
    )、
    options.sandbox
    ;integration 和 flow group 还可获得父资源:flow group 的
    parentResource
    是其 integration(如果该 integration 本身还有父 integration,则后者是
    grandparentResource
    ),child integration 的
    parentResource
    是其父级。
    options.license
    和
    options.parentLicense
    仅出现在 Integration Apps 中。Flows、exports、imports 和 connections 仅获得
    resource
    和
    sandbox
    。
  • Script 必须属于当前 account。 任何其他
    _scriptId
    都会被拒绝,并返回 422 “Only a script owned by the user or a script shared by the Integration App can be used at settingsForm.init._scriptId”(对于 flow group,路径为
    flowGroupings.<index>.settingsForm.init._scriptId
    )。
  • iClients 不支持。 平台的 form-init route 仅适用于 integrations、flow groups、flows、exports、imports 和 connections,因此带有
    init
    的 iClient form 会显示错误,而不是表单——而 iClient 保存时会存储任何
    _scriptId
    ,不会对其进行检查。
  • **测试:**没有 CLI command 或 MCP tool 会调用 init route。请通过 JavaScript processor 运行该函数,并自行构建它所需的
    options
    对象——参见“如何检查表单”。

Schema Index

Schema Index

The form's full shape is the
Form
schema on the API reference's Integrations page (agent-readable markdown; exports and imports embed the same schema), and
settings
is a free-form object on every scope. Over MCP,
get_schema({ target: "resource", name: "common/form" })
returns it.
表单的完整结构是 API reference 的 Integrations 页面上的
Form
schema(可供 agent 阅读的 markdown;exports 和 imports 嵌入相同 schema),而
settings
在每个 scope 上都是 free-form object。通过 MCP,
get_schema({ target: "resource", name: "common/form" })
会返回它。

Related Skills

Related Skills

  • writing-handlebars > Context Decision Matrix -- reading settings values in templates
  • writing-scripts > Common Options --
    options.settings
    in hooks, and the init function point
  • configuring-filters > Gotchas -- settings scopes in filter operands
  • configuring-exports -- the export body an
    exportSelect
    runs
  • managing-integrations > Organize the resources inside -- creating flow groups and putting flows in them
  • managing-integrations > Integration Apps -- app settings, which the app's own code saves
  • configuring-connections -- full updates of connections and iClients, which carry masked secrets
<!-- TIER:2 -->
  • writing-handlebars > Context Decision Matrix——在 templates 中读取 settings values
  • writing-scripts > Common Options——hooks 中的
    options.settings
    以及 init function point
  • configuring-filters > Gotchas——filter operands 中的 settings scopes
  • configuring-exports——
    exportSelect
    运行的 export body
  • managing-integrations > Organize the resources inside——创建 flow groups 并在其中放入 flows
  • managing-integrations > Integration Apps——由 app 自身代码保存的 app settings
  • configuring-connections——connections 和 iClients 的完整更新,其中包含被遮蔽的 secrets
<!-- TIER:2 -->

Dynamic Fields: exportSelect and staticMap

Dynamic Fields:exportSelect 和 staticMap

exportSelect

exportSelect

A dropdown whose options come from an export the form carries inline, in
resource.virtual
:
json
"warehouse": {
  "id": "warehouse", "name": "warehouse", "type": "exportSelect", "label": "Warehouse",
  "resource": { "virtual": {
    "_connectionId": "<connectionId>",
    "adaptorType": "HTTPExport",
    "http": { "relativeURI": "/v1/warehouses", "method": "GET", "formType": "http", "response": { "resourcePath": "warehouses" } },
    "transform": { "type": "expression", "expression": { "version": "2", "rulesTwoDotZero": { "mode": "create", "mappings": [
      { "generate": "label", "extract": "$.name", "dataType": "string", "status": "Active" },
      { "generate": "value", "extract": "$.code", "dataType": "string", "status": "Active" } ] } } }
  } }
}
  • The export is an export body, not a reference. There's no way to point at a saved export by id: put
    adaptorType
    , its adaptor block, and an optional
    transform
    in
    virtual
    . Without
    resource.virtual
    the field shows "requires export resource."
    _connectionId
    defaults to the connection of the export or import the form is on; on any other scope, set it.
  • The UI previews the export when the form opens (and on Refresh) and lists the preview's
    data
    -- the records after the transform, output filter, and preSavePage hook. Each record needs a
    value
    and a
    label
    ; a bare string or number serves as both. The preview reads at most 100 records: the UI sends
    test.limit
    100 unless the export sets its own, which can't exceed 100. An HTTP export that finds no records answers without a
    data
    list, so the field shows "expecting array. try transform?" rather than an empty list -- as it does for any answer that isn't a list.
  • multiselect: true
    allows several choices, saved as an array.
  • supportHandlebars: true
    renders the export body as a template before each preview (everything but
    transform
    ,
    hooks
    , and
    filter
    ). The template's context isn't the runtime's: the form's current values sit under the form's own scope name (
    integration
    ,
    flowGrouping
    ,
    flow
    ,
    export
    , or
    import
    ), beside the saved settings of the scopes above it and, on an export or import form, the whole document of the connection the export uses -- with no
    settings.
    prefix:
    {{integration.accountId}}
    ,
    {{export.country}}
    . Connection and iClient forms get an empty context. A templated list doesn't load on its own when the form opens: it loads through
    refreshOptionsOnChangesTo
    or
    typeAheadSearch
    , or when the user clicks Refresh.
  • refreshOptionsOnChangesTo: ["<field id>", ...]
    loads the templated list once one of those fields has a value and reloads it whenever one changes -- a cascading dropdown. It works only with
    supportHandlebars
    .
  • typeAheadSearch: true
    reloads it as the user types (300 ms after the last keystroke), with the typed text as this field's own value in the context; an empty box clears the list, and free text is accepted.
一种 dropdown,其选项来自表单通过
resource.virtual
内联携带的 export:
json
"warehouse": {
  "id": "warehouse", "name": "warehouse", "type": "exportSelect", "label": "Warehouse",
  "resource": { "virtual": {
    "_connectionId": "<connectionId>",
    "adaptorType": "HTTPExport",
    "http": { "relativeURI": "/v1/warehouses", "method": "GET", "formType": "http", "response": { "resourcePath": "warehouses" } },
    "transform": { "type": "expression", "expression": { "version": "2", "rulesTwoDotZero": { "mode": "create", "mappings": [
      { "generate": "label", "extract": "$.name", "dataType": "string", "status": "Active" },
      { "generate": "value", "extract": "$.code", "dataType": "string", "status": "Active" } ] } } }
  } }
}
  • 这个 export 是 export body,而不是引用。 无法通过 id 指向已保存的 export:需要在
    virtual
    中放置
    adaptorType
    、对应的 adaptor block,以及可选的
    transform
    。如果没有
    resource.virtual
    ,字段会显示“requires export resource.”。
    _connectionId
    默认使用该表单所属 export 或 import 的 connection;在其他 scope 上则必须显式设置。
  • UI 会在表单打开时以及点击 Refresh 时预览该 export,并列出预览结果的
    data
    ——即经过 transform、output filter 和 preSavePage hook 处理后的 records。每条 record 都需要包含
    value
    和
    label
    ;裸字符串或数字可以同时充当两者。预览最多读取 100 条 records:除非 export 自行设置,否则 UI 会发送
    test.limit
    100,而 export 自行设置的值不能超过 100。如果 HTTP export 未找到 records,响应中将没有
    data
    列表,因此字段会显示“expecting array. try transform?”,而不是显示空列表;对于任何非列表形式的响应也是如此。
  • multiselect: true
    允许选择多个选项,并将其保存为数组。
  • supportHandlebars: true
    会在每次预览前将 export body 渲染为 template(
    transform
    、
    hooks
    和
    filter
    除外)。该 template 的 context 与 runtime 不同:表单当前的 values 位于表单自身 scope name(
    integration
    、
    flowGrouping
    、
    flow
    、
    export
    或
    import
    )下,与上级 scopes 已保存的 settings 并列;对于 export 或 import form,还包括该 export 所使用 connection 的完整 document,且没有
    settings.
    前缀:
    {{integration.accountId}}
    、
    {{export.country}}
    。Connection 和 iClient forms 得到空 context。带有 template 的列表不会在表单打开时自行加载:它通过
    refreshOptionsOnChangesTo
    或
    typeAheadSearch
    加载,或者在用户点击 Refresh 时加载。
  • refreshOptionsOnChangesTo: ["<field id>", ...]
    会在指定字段之一已有 value 后加载 templated list,并在其中任一字段发生变化时重新加载——这就是 cascading dropdown。它仅在启用
    supportHandlebars
    时有效。
  • typeAheadSearch: true
    会在用户输入时重新加载列表(最后一次按键后 300 ms),并将输入文本作为该字段自身的 value 放入 context;清空输入框会清除列表,同时允许使用自由文本。

staticMap

staticMap

A two-column table, saved under the field's
name
as an array of rows:
[{"source": "pending", "target": "open"}]
.
  • keyName
    /
    valueName
    name the columns -- the keys of each saved row.
    keyLabel
    /
    valueLabel
    head them.
  • keyOptions
    /
    valueOptions
    give a column a suggestion list:
    {label, value}
    items (
    {id, text}
    and
    [value, label]
    work too). The cell still accepts typed values.
  • keyResource
    /
    valueResource
    --
    {"virtual": {<export body>}}
    -- fill a column's suggestions from an inline export, previewed as for
    exportSelect
    (no templating). With either one set, both columns are required unless the field sets
    keyValueRequired: false
    , and the UI drops incomplete rows when it saves.
  • Steps read the whole array:
    options.settings.flow.statusMap.find(r => r.source === status)
    in a script, or
    #each
    over it in a template.
一种双列表格,以字段的
name
为 key 保存为行数组:
[{"source": "pending", "target": "open"}]
。
  • keyName
    /
    valueName
    用于命名各列,也就是每条保存记录中的 keys。
    keyLabel
    /
    valueLabel
    用作列标题。
  • keyOptions
    /
    valueOptions
    为某一列提供建议列表:采用
    {label, value}
    items(
    {id, text}
    和
    [value, label]
    格式也有效)。单元格仍接受手动输入的值。
  • keyResource
    /
    valueResource
    ——
    {"virtual": {<export body>}}
    ——通过内联 export 为某一列填充建议值,其预览方式与
    exportSelect
    相同(不支持 templating)。设置其中任意一个后,两列都会成为必填项,除非字段设置了
    keyValueRequired: false
    ;UI 保存时会丢弃不完整的行。
  • Steps 会读取整个数组:在 script 中使用
    options.settings.flow.statusMap.find(r => r.source === status)
    ,或在 template 中对其执行
    #each
    。

How to Build a Settings Form

如何构建 Settings Form

1. Decide the scope

1. 决定 scope

Who reads the value? Put the form where every reader shares it (The Seven Scopes), and remember that steps in an API or a Tool see only their own scope plus connection and iClient. For a flow group, create the group first and put the flows in it (managing-integrations > Organize the resources inside).
谁会读取该 value?将表单放在所有读取者共享的位置(七种 Scope),并记住 API 或 Tool 中的 steps 只能看到自身 scope,以及 connection 和 iClient。对于 flow group,请先创建 group,再将 flows 放入其中(managing-integrations > Organize the resources inside)。

2. Read the resource

2. 读取资源

Read the whole resource: an existing
settingsForm
to extend, the current
settings
, and everything else a full update must send back. On an integration, a flow group's form and values live in its
flowGroupings[]
entry.
读取完整资源:需要扩展的现有
settingsForm
、当前
settings
,以及完整更新必须回传的所有其他内容。对于 integration,flow group 的表单和 values 位于其
flowGroupings[]
entry 中。

3. Write the fields

3. 编写字段

One
fieldMap
entry per setting, with the same string as key,
id
, and
name
-- a plain key, no dots. Choose the
type
(Field Types), give
options
to choice fields, and a
defaultValue
where the UI should suggest one. For a list of values the user picks from another system, use an
exportSelect
; for a value-to-value table, a
staticMap
(step 6).
每个 setting 对应一个
fieldMap
entry,其 key、
id
和
name
使用同一个字符串——普通 key,不含点号。选择
type
(Field Types),为选择类字段提供
options
,并在 UI 应建议默认值时设置
defaultValue
。如果用户需要从另一个系统选择一组 values,请使用
exportSelect
;如果需要 value-to-value table,请使用
staticMap
(步骤 6)。

4. Lay the fields out

4. 排列字段布局

List every field in the layout exactly once. Choose the top-level
type
, group related fields into labeled containers, and nest a container's own
type
and
containers
for a second level.
在 layout 中将每个字段恰好列出一次。选择顶层
type
,将相关字段分组到带标签的 containers 中,并可嵌套 container 自身的
type
和
containers
以形成第二层结构。

5. Add conditions

5. 添加 conditions

Show, require, or lock fields with
visibleWhen
/
requiredWhen
/
disabledWhen
(or their
*All
forms), naming fields by id. Add
omitWhenHidden: true
to a field whose value shouldn't be saved while it's hidden.
使用
visibleWhen
/
requiredWhen
/
disabledWhen
(或其
*All
形式)显示、要求或锁定字段,并通过 id 引用字段。对于隐藏期间不应保存 value 的字段,添加
omitWhenHidden: true
。

6. Build the dynamic fields

6. 构建动态字段

Write the inline export of each
exportSelect
(or a
staticMap
column's
keyResource
/
valueResource
) and preview it before you save the form: it must return
label
/
value
records -- shape them with a
transform
. Add
supportHandlebars
and
refreshOptionsOnChangesTo
for a dropdown that depends on another field.
编写每个
exportSelect
的内联 export(或某个
staticMap
列的
keyResource
/
valueResource
),并在保存表单前进行预览:它必须返回包含
label
/
value
的 records——可使用
transform
对其结构进行转换。对于依赖另一个字段的 dropdown,添加
supportHandlebars
和
refreshOptionsOnChangesTo
。

7. Add an init script (optional)

7. 添加 init script(可选)

When the form must change with the resource's state, write an init function that returns the form, test it (How to Check a Form), save the script, and set
settingsForm.init
. The returned form replaces
settingsForm.form
in the UI, so keep
form
as the fallback the function starts from.
当表单必须随资源状态变化时,编写一个返回表单的 init function,对它进行测试(如何检查表单),保存 script,然后设置
settingsForm.init
。返回的表单会替代
settingsForm.form
,因此应保留
form
作为函数开始执行时使用的 fallback。

8. Save the form and the values together

8. 同时保存表单和值

Send the form and the matching
settings
values in one update -- the full document, read in step 2 -- so steps have values before anyone opens the form. Keep every value steps read in the form: a save in the UI writes
settings
from the form's fields.
在一次更新中发送表单及对应的
settings
values——使用步骤 2 中读取的完整 document——从而确保在任何人打开表单之前,steps 就已经拥有所需的 values。保留 steps 会读取的每个 value:UI 保存时会根据表单字段写入
settings
。

9. Read the values in steps

9. 在 steps 中读取值

Reference each value through its scope key:
{{settings.flowGrouping.region}}
in a template or filter operand,
options.settings.export.batchLimit
in a script, or
$.settings.flow.region
in a Mapper 2.0 extract with the envelope input context (writing-mappings > Choose the input context).
通过各自的 scope key 引用 value:在 template 或 filter operand 中使用
{{settings.flowGrouping.region}}
,在 script 中使用
options.settings.export.batchLimit
,或在带 envelope input context 的 Mapper 2.0 extract 中使用
$.settings.flow.region
(writing-mappings > Choose the input context)。

How to Check a Form

如何检查表单

  1. Read the resource back and compare
    settingsForm
    and
    settings
    with what you sent. A save never validates the form, so check it yourself: every layout id has a
    fieldMap
    entry, every field is in the layout once, every condition names an existing field, key =
    id
    =
    name
    on each field, and choice fields have
    options
    .
  2. Preview each inline export an
    exportSelect
    or
    staticMap
    runs, with
    "test": {"limit": 100}
    added the way the UI sends it -- a preview without it returns one record. Its
    data
    must be a list of
    label
    /
    value
    records.
  3. Run the init function through the JavaScript processor, with the
    options
    object it will receive (
    resource
    ,
    sandbox
    , and the parents its scope gets). The reply's
    data
    is the form it returns; a throw comes back as 422 with the message.
  4. Render a template that reads the values with the Handlebars processor, with
    settings
    in its data (
    {"record": {...}, "settings": {"flow": {"region": "eu"}}}
    ), or preview the step with
    settings
    in the document. An import invoke writes to the destination; run one only against a test destination.
  5. Open the form in the UI. Only the UI renders it -- the layout, the conditions, the dropdowns, and the init script's result.
<!-- TIER:3 -->
  1. 重新读取资源,并将
    settingsForm
    和
    settings
    与发送的内容进行比较。保存操作绝不会校验表单,因此需要自行检查:每个 layout id 都有
    fieldMap
    entry,每个字段在 layout 中恰好出现一次,每个 condition 都引用现有字段,每个字段都满足 key =
    id
    =
    name
    ,且选择类字段具有
    options
    。
  2. 预览每个内联 export,即
    exportSelect
    或
    staticMap
    运行的 export,并按照 UI 的方式添加
    "test": {"limit": 100}
    ——不带它进行预览只会返回一条 record。其
    data
    必须是包含
    label
    /
    value
    的 record 列表。
  3. 通过 JavaScript processor 运行 init function,并提供它将收到的
    options
    对象(
    resource
    、
    sandbox
    ,以及其 scope 对应的父资源)。响应中的
    data
    就是函数返回的表单;如果发生异常,则会以 422 和相应 message 返回。
  4. 渲染读取这些值的 template,通过 Handlebars processor 并在数据中提供
    settings
    (
    {"record": {...}, "settings": {"flow": {"region": "eu"}}}
    ),或者在 document 中包含
    settings
    并预览该 step。Import invoke 会写入目标系统;仅可针对测试目标运行。
  5. 在 UI 中打开表单。 只有 UI 会实际渲染它——包括 layout、conditions、dropdowns 和 init script 的结果。
<!-- TIER:3 -->

Pre-Submit Checklist

提交前检查清单

  • The form sits on the narrowest scope every reading step shares
  • Each field's
    fieldMap
    key,
    id
    , and
    name
    are the same plain string
  • Every field appears in the layout once, and every layout id and condition names an existing field
  • Choice fields have
    options
    ; each
    exportSelect
    has
    resource.virtual
    whose preview returns
    label
    /
    value
    records
  • An
    init._scriptId
    names a script in this account, and the function was tested; not on an iClient
  • settings
    holds a value for every field steps read, written in the same update as the form
  • The update sent the complete document:
    settingsForm
    ,
    settings
    , and everything else the resource had
  • Steps reference each value through its scope key
  • 表单位于所有读取 steps 共享的最窄 scope
  • 每个字段的
    fieldMap
    key、
    id
    和
    name
    都是同一个普通字符串
  • 每个字段在 layout 中恰好出现一次,且每个 layout id 和 condition 都引用现有字段
  • 选择类字段具有
    options
    ;每个
    exportSelect
    都有
    resource.virtual
    ,且其预览返回包含
    label
    /
    value
    的 records
  • init._scriptId
    引用当前 account 中的 script,且该函数已经过测试;iClient 不得使用它
  • settings
    为每个供 steps 读取的字段保存了 value,并与表单在同一次更新中写入
  • 更新发送了完整 document:
    settingsForm
    、
    settings
    以及资源原有的所有其他内容
  • Steps 通过各自的 scope key 引用 value

Gotchas

Gotchas

  1. A full update erases what it omits. A body without
    settingsForm
    or
    settings
    removes them, and a body whose
    settingsForm
    has no
    init
    removes the init script. Read the whole resource first, and change only
    settingsForm.form
    when that's all you mean to change.
  2. Saves aren't checked against the form. A missing
    required
    value, extra keys, wrong types, and dangling layout ids or conditions all save; only the UI enforces the form.
  3. Only
    form
    and
    init
    are kept.
    Anything else at the top of
    settingsForm
    is dropped silently.
  4. Values sit directly under
    settings
    , keyed by
    name
    .
    A dotted
    name
    saves as nested objects but reloads empty, and a value under any other key is invisible to the field.
  5. Steps need the scope key.
    {{settings.region}}
    renders empty; so does a path built from a field's label or a section heading.
  6. A step run on its own sees only its own scope. An export or import invoke without a parent renders
    settings.integration
    ,
    settings.flowGrouping
    , and
    settings.flow
    empty -- run it as a step of the flow, or the flow itself, to see them.
  7. A hidden field is still saved unless it sets
    omitWhenHidden
    .
  8. A condition on a missing field fails open for visibility.
    visibleWhen
    naming a field that isn't there leaves the field always visible;
    requiredWhen
    and
    disabledWhen
    never apply.
  9. The init script replaces the form. The UI shows what the function returns, not
    settingsForm.form
    ; a throw replaces the form with the error message.
  10. iClient forms can't use init, and an iClient save accepts any
    _scriptId
    unchecked.
  11. An exportSelect lists the preview's
    data
    , at most 100 records.
    Records without
    label
    and
    value
    don't make usable options, and an HTTP export that finds nothing shows an error instead of an empty list.
  12. Keep every value steps read in the form. A UI save writes
    settings
    from the form's fields, so a key you wrote over the API that the form doesn't define won't survive someone saving the form.
  13. Integration App settings are separate. An app's settings form is in its
    settings
    (
    settings.general
    ,
    settings.sections
    ), and changes go through the app's own save route -- see managing-integrations > Integration Apps.
  1. 完整更新会清除未包含的内容。 未包含
    settingsForm
    或
    settings
    的 body 会将其移除,而
    settingsForm
    中没有
    init
    的 body 会移除 init script。请先读取完整资源;如果只想修改
    settingsForm.form
    ,就只修改它。
  2. 保存时不会根据表单进行检查。 缺失的
    required
    value、多余的 keys、错误的 types,以及悬空的 layout ids 或 conditions 全都可以保存;只有 UI 会执行表单规则。
  3. 仅保留
    form
    和
    init
    。
    settingsForm
    顶层的其他内容都会被静默丢弃。
  4. Values 直接位于
    settings
    下,并以
    name
    为 key。
    包含点号的
    name
    会被保存为嵌套对象,但重新加载时为空;任何使用其他 key 保存的 value 对字段都不可见。
  5. Steps 需要 scope key。
    {{settings.region}}
    会渲染为空;根据字段标签或 section 标题构建的路径同样如此。
  6. 单独运行的 step 只能看到自身 scope。 没有父级的 export 或 import invoke 会将
    settings.integration
    、
    settings.flowGrouping
    和
    settings.flow
    渲染为空——应将其作为 flow 中的 step 运行,或单独运行该 flow,才能看到这些值。
  7. 隐藏字段仍会被保存,除非它设置了
    omitWhenHidden
    。
  8. 引用缺失字段的 condition 会在可见性判断时 fail open。
    visibleWhen
    引用不存在的字段时,字段会始终可见;
    requiredWhen
    和
    disabledWhen
    则不会生效。
  9. Init script 会替代表单。 UI 显示函数返回的内容,而不是
    settingsForm.form
    ;发生异常时,错误信息会替代表单。
  10. iClient forms 不能使用 init,并且 iClient 保存时会接受任何
    _scriptId
    ,不会进行检查。
  11. exportSelect 最多列出预览中的 100 条 records。 缺少
    label
    和
    value
    的 records 无法形成可用选项;如果 HTTP export 未找到内容,则会显示错误,而不是空列表。
  12. 保留 steps 会读取的每个 value。 UI 保存时会根据表单字段写入
    settings
    ,因此通过 API 写入、但表单未定义的 key 不会在有人保存表单后继续保留。
  13. Integration App settings 是独立的。 App 的 settings form 位于其
    settings
    中(
    settings.general
    、
    settings.sections
    ),相关变更通过 app 自身的 save route 提交——参见 managing-integrations > Integration Apps。

Common Errors

常见错误

ErrorCauseFix
422 "Only a script owned by the user or a script shared by the Integration App can be used at settingsForm.init._scriptId"
init._scriptId
names a script this account doesn't have
Use the
_id
of one of the account's scripts
The form shows an error message instead of fieldsThe init function threw (or the form is on an iClient with an
init
)
Run the function through the JavaScript processor to see the error; remove
init
from an iClient
"requires export resource." under an exportSelectNo
resource.virtual
Add the inline export body
"expecting array. try transform?"The exportSelect's export found no records (an HTTP preview then has no
data
) or returned something other than a list
Preview the export with
test.limit
100; shape records into
label
/
value
with a
transform
An exportSelect's options are blank or can't be chosenThe records have no
label
/
value
Map them in the export's
transform
A field always shows despite
visibleWhen
The rule names a field id that isn't in
fieldMap
Name the controlling field's id
A field reloads empty after savingIts
name
contains a dot, or differs from the key steps read
Use one plain string for key,
id
, and
name
{{settings.<scope>.<name>}}
renders empty
No scope key, the wrong scope, the value isn't in
settings
, or the step ran outside its flow
Check the path against The Seven Scopes and read
settings
back
A whole form or
settings
disappeared
A full update left it outWrite it back from your copy or the resource's audit log (an update's
fieldChanges
keep the old values), and send complete documents
错误原因修复方法
422 “Only a script owned by the user or a script shared by the Integration App can be used at settingsForm.init._scriptId”
init._scriptId
引用了当前 account 中不存在的 script
使用当前 account 中某个 script 的
_id
表单显示错误消息而不是字段Init function 抛出异常,或者表单位于带
init
的 iClient 上
通过 JavaScript processor 运行该函数以查看错误;从 iClient 中移除
init
exportSelect 下显示“requires export resource.”缺少
resource.virtual
添加内联 export body
“expecting array. try transform?”exportSelect 的 export 未找到 records(HTTP preview 因而没有
data
),或返回了列表之外的内容
使用
test.limit
100 预览 export;通过
transform
将 records 转换为包含
label
/
value
的结构
exportSelect 的 options 为空或无法选择Records 缺少
label
/
value
在 export 的
transform
中映射这些字段
尽管设置了
visibleWhen
,字段始终显示
规则引用了
fieldMap
中不存在的 field id
引用控制字段的正确 id
保存后字段重新加载时为空其
name
包含点号,或与 steps 读取的 key 不一致
对 key、
id
和
name
使用同一个普通字符串
{{settings.<scope>.<name>}}
渲染为空
缺少 scope key、scope 错误、value 不在
settings
中,或 step 在 flow 之外运行
对照“七种 Scope”检查路径,并重新读取
settings
整个表单或
settings
消失
完整更新遗漏了这些内容从你的副本或资源 audit log 中恢复(更新的
fieldChanges
会保留旧值),然后发送完整 document