Workflow bots: data extraction & patching
This family covers two kinds of low-level data tools:
JP*— raw JSON Patch (RFC 6902) on a channel, property or user. These accept any op on any path, with no whitelist. They are powerful but sharp — for everyday edits prefer thePB*equivalents (PBUpdateChannel,PBUpdateProperty,PBUpdateUser), which validate and are easier to read.ST*— survey tools that read a survey answer and produce a formatted string. Handy for building display labels and formatting dates from answers.
All JP* bots call their patch endpoint with admin: 'true' and share the same two branches: DEFAULT on success, ERROR on API failure. Note that a patch failure does not throw — it just returns ERROR with no further detail.
JPChannel
RFC 6902 JSON Patch on a channel. Any op (add/remove/replace/move/copy) on any path.
Key parameters: channelId, body (array of { op, path, value?, from? }).
- key: s1
name: JPChannel
data:
channelId: "{{task.channel}}"
body:
- op: "add"
path: "/propertyIds/-"
value: "5d2fd97cb448357a12fe59ac"
next:
DEFAULT: ""
ERROR: ""
For convenience over flexibility, use PBUpdateChannel (replaces lists wholesale).
JPProperty
RFC 6902 JSON Patch on a property. No path whitelist.
Key parameters: propertyId, body.
- key: s1
name: JPProperty
data:
propertyId: "5d2fd97cb448357a12fe59ac"
body:
- op: "add"
path: "/subproperty/-"
value: "5e7548ae1d8897202c6668b7"
next:
DEFAULT: ""
ERROR: ""
PBUpdateProperty (3.0.0) is the safer, whitelisted alternative for /display, /schemaInstance, /subproperty.
JPUser
RFC 6902 JSON Patch on a user. Any op on any path.
Key parameters: userId, body.
- key: s1
name: JPUser
data:
userId: "{{user._id}}"
body:
- op: "add"
path: "/accessRoles/-"
value: "5d2fd97cb448357a12fe59ac"
next:
DEFAULT: ""
ERROR: ""
For a field-level, validated alternative use PBUpdateUser.
STDatetimeString
Formats a datetime answer (or a raw epoch dateNumber) into a string according to dateFormat, timeFormat and dateSeparator. Always returns DEFAULT (errors call back with {}).
Key parameters: answer + identifier (for a +datetime question) OR dateNumber (epoch ms as a string); dateFormat (yyyy/mm/dd default, dd/mm/yyyy, mm/dd/yyyy, dd/mm/yy, mm/dd/yy, D-ESmonthWord); timeFormat (null default, military, ampm); dateSeparator (- default, /).
- key: s1
name: STDatetimeString
data:
answer: "{{answer._id}}"
identifier: "due_date"
dateFormat: "dd/mm/yyyy"
dateSeparator: "/"
timeFormat: "military"
next:
DEFAULT: ""
Gotchas:
- Use
dateSeparator(the legacy annotations misspell itdateSepator, but the runtime readsdateSeparator). - Known bug for
dd/mm/yyyy: the implementation builds${dateNumber}${sep}${dateNumber}${sep}${fullYear}(day repeated). Verify the output before relying on it. - For
+datetimeanswers, the value is read fromdata.process[0]as an epoch string.
STPropertyNames
Given a +property answer, fetches each selected property and returns its name.display (default) or name.code. Always returns DEFAULT.
Key parameters: answer, identifier (a +property question), mode (display default, code), asStringSeparatedBy (optional; also returns a joined resultString).
- key: s1
name: STPropertyNames
data:
answer: "{{answer._id}}"
identifier: "category_selector"
mode: "display"
asStringSeparatedBy: ", "
next:
DEFAULT: ""
Gotchas:
- Rejects if the question's
contentTypeis notapplication/vnd.cotalker.survey+property. - For compositions combining several fields per property, use
STPropertyStrComposer.
STPropertyStrComposer
For each already-loaded COTProperty, walks a commandArray and builds a string by appending literal text or property fields (via %<path> accessors). Returns the array and optionally a joined string. Always returns DEFAULT.
Key parameters: properties (array of already-loaded COTProperty objects — the bot does not fetch them), commandArray (command strings), joinBy (optional joiner), printDebug (optional).
Command rules: anything not starting with % is appended literally; %<path> walks the property with .split('.') (e.g. %name.display, %extra.taxId) and appends the resolved value.
- key: s1
name: STPropertyStrComposer
data:
properties: "{{stages.s_load.properties}}"
commandArray:
- "%name.display"
- " ("
- "%name.code"
- ")"
joinBy: ", "
next:
DEFAULT: ""
Gotchas:
propertiesmust contain already-loaded objects — load them in a prior stage.- A missing/undefined path resolves to the literal string
"undefined"(the accessor usesString(path)).