Messages and Forms
Cloudomation executions can generate messages and forms for Cloudomation users to read and respond.
Use Cases
Use messages to
- provide status updates about active executions
- log processing steps
- store reports or results
Use forms to
- query parameters from Cloudomation users
- wait for confirmation by Cloudomation users
Concept
Usage
Messages and forms are created by executions. In the Cloudomation user interface messages are accessible via the bell icon in the top menu bar.
The messages button
The bell icon can contain a badge with the number of messages which are waiting for a response.
Clicking on a message subject in the "Latest messages" popover will open the message and display the message form.
If an execution is currently waiting for a response of a message, the message form is also embedded within the execution screen.
Simple informational message
To create a simple informational message use the message
argument of the this.message()
call.
The message will be rendered as markdown and include an "OK" button.
Create an informational message
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution):
this.message(
subject='Information message',
message=(
'''
# Message body
with **Markdown** formatting!
'''
),
)
return this.success('all done')
The execution always waits until an informational message has been acknowledged by a
user clicking the OK button. To skip waiting for a message response use the form this.message(..., wait=False)
.
Simple request form
To create a simple request form use the request
argument of the this.message()
call.
The message will include one string input field and an "OK" button. The value entered by the user
is accessible in the string
key of the response dictionary.
Request one simple input from a user
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution):
message = this.message(
subject='Input request',
request='Please enter some value',
)
# retreive the value which was entered
# the value of a "request" message is always of type string
# and stored in the key `string`
value = message.get('response')['string']
this.log(value=value)
return this.success('all done')
Wait Timeout
Executions can be configured to wait for a message response with a timeout. If the message was not submitted within the timeout, a MessageResponseTimeoutError
will be thrown.
Use a message wait timeout
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution):
try:
message = this.message(
subject='Input request',
request='Please enter some value',
wait_timeout=60, # allow 60 seconds to answer the message
)
except MessageResponseTimeoutError:
this.log('Message was not responded within 60 seconds')
else:
# retreive the value which was entered
# the value of a "request" message is always of type string
# and stored in the key `string`
value = message.get('response')['string']
this.log(value=value)
return this.success('all done')
Deleting
When deleting an execution which currently is waiting for a message response, the message will be archived.
When deleting a message which is currently waited for by an execution, the execution will be notified and raise an ResourceNotFoundError
.
Advanced message form
To create advanced message forms use the body
argument of the this.message()
call.
The message can contain multiple informational fields, input fields, and submit buttons.
The structure of the response dictionary is configured by the names of the fields.
The value of the body
argument is validated with SCHEMA_MESSAGE_BODY
which is outlined below:
SCHEMA_MESSAGE_FIELD = {
'type': 'object',
'properties': {
# the form element to use
'element': {
'type': 'string',
'enum': [
'string', # string input field
'number', # number input field
'password', # password input field
'date', # date picker
# returned as string in the format YYYY-mm-dd
'time', # time input field or picker (browser dependent)
# returned as string in the format HH:MM
'date-time', # date-time picker
# returned as string in the format YYYY-mm-ddTHH:MM
'markdown', # markdown output
'toggle', # toggle button
# returned as boolean
'submit', # submit button
# the button which was pressed
# will return boolean `True`
]
},
# datatype of the value in the response dictionary
'type': {
'type': 'string',
'enum': ['string', 'number', 'boolean']
},
# label for the element. if unset, the name of the field is used.
'label': {
'type': 'string'
},
# the "placeholder" value, displayed when the input field is empty
'example': {
'type': ['string', 'number', 'null']
},
# the order in which the element will appear in the form
'order': {
'type': 'integer'
},
# a default value which is pre-filled
'default': {
'type': ['string', 'number', 'boolean', 'null']
},
# an additional format validator.
# see https://json-schema.org/understanding-json-schema/reference/string.html#format
'format': {
'type': 'string'
},
# the markdown content
'docs': {
'type': 'string'
},
},
'required': [
'element',
'type',
'order',
],
'additionalProperties': False,
"if": {
"properties": { "element": { "const": "submit" } }
},
"then": {
"properties": { "type": { "const": "boolean", } }
},
"else": {
}
}
SCHEMA_MESSAGE_PROPERTIES = {
'type': 'object',
'patternProperties': {
# field names must match this pattern
'^[a-zA-Z0-9_]+$': SCHEMA_MESSAGE_FIELD,
},
}
SCHEMA_MESSAGE_BODY = {
'type': 'object',
'properties': {
# always "object"
'type': {'type': 'string', 'enum': ['object']},
'properties': SCHEMA_MESSAGE_PROPERTIES,
# a list of field names which must be entered by the user
'required': {'type': 'array', 'items': {'type': 'string'}},
},
'required': ['type', 'properties'],
}
Naming an input field cloudomation_fe_timezone
will cause an error message!
All message forms return the timezone of the user's browser when filling in the form.
You can retrieve it from the response with the key cloudomation_fe_timezone
.
Create a message form to query several inputs from a user:
import flow_api
def handler(system: flow_api.System, this: flow_api.Execution):
message = this.message(
subject='Message form',
body={
'type': 'object',
'properties': {
# the key in the response dictinary where the value will be stored
'string-field': {
'element': 'string',
'type': 'string',
'example': 'enter a string',
'order': 1,
'label': 'string-field-label',
},
'number-field': {
'element': 'number',
'type': 'number',
'example': 'enter a number',
'order': 2,
},
'date-field': {
'element': 'date',
'type': 'string',
'format': 'date',
'order': 3,
},
'time-field': {
'element': 'time',
'type': 'string',
'format': 'time',
'order': 4,
},
'date-time-field': {
'element': 'date-time',
'type': 'string',
'format': 'date-time',
'order': 5,
},
# the markdown field is only informational and does
# not produce a key in the response dictionary
'markdown-field': {
'element': 'markdown',
'docs': '### Markdown\n\n- item 1\n- item 2\n- item3',
'order': 6,
},
'toggle-button': {
'element': 'toggle',
'type': 'boolean',
'order': 7,
},
'submit-button': {
'element': 'submit',
'type': 'boolean',
'order': 8,
},
'alternative submit-button': {
'element': 'submit',
'type': 'boolean',
'order': 9,
},
},
'required': [
'string-field',
'time-field',
],
},
)
# retreive all values which were entered
response = message.get('response')
this.log(response=response)
return this.success('all done')