Automation
Create api request instruction configurations that can be either triggered based on events or timed triggers. Automation instructions are json structures where we use the same templating functionalities that we use to generate pages to generate json instead.
Automation configuration is on the ‘Automation’ tab on the right side menu, and it is only available for ‘page’ or ‘automation’ type templates. Page types are deprecated for automations, they will still work but syntax corrections and new features will not be supported on that type.
As of 1.228+ automation no longer use jwt’s to operate. Sessions are used instead, this also means that regular user permissions are being used. Automations use a dedicated user group called ‘automation-group’, if some of the api’s are giving access errors (example: 1060 on erply api) then the rights on this group need to be adjusted according to the api calls used.
The user group will be generated automatically once a template is either saved or installed.
Workflow
Automation templates are processed twice, first when forming the json and second when the post operations are being triggered.
- Template formation step, on this step the json instruction is formed. All preset calls are processed and can be used in
the json instruction generation.
- Calls defined in the url configuration presets are triggered on this step
- All template functions will run
- Result needs to be clean and valid json
- Post operations trigger step, on this step the api calls defined in the post operations are triggered.
- Calls made in the url configuration cannot be chained to the post operation calls, but the values can just be printed to the values as they are triggered in different steps
- Stage call step. Read staged automation at the end of this page for more information.
Automation triggers
Automation triggering uses hmac (sha256) keys for authentication. Each automation page has a separate unique key that is occasionally refreshed (refresh will also re-register all webhook triggers).
By default, the key is not visible. Installation, store update or manual triggering will generate one in the ui.
Structure
Basic structure consists of ‘postOperations’ and ’enabled’ keys. Post operations is a key/value set of parameters similar to what is being used on templates. Enabled flag can be used to toggle the automation feature on and off.
{
"postOperations": [
{
"call": "value"
}
],
"enabled": true
}
Post operations
Post operations are input parameters on what should be done. These are the same instructions that are being used on regular html templates. Dynamic api instructions are also supported.
{
"postOperations": [
{
"ErplyApi.Api.Post.createProduct": "saveProduct",
"ErplyApi.Api.PostParam.createProduct.groupID": "1",
"ErplyApi.Api.PostParam.createProduct.code": "Code",
"ErplyApi.Api.PostParam.createProduct.nameENG": "Name"
}
],
"enabled": true
}
Sets of instructions can be separated by bucketing the instructions, to make the run on separate times. Dynamic api chaining is also supported here.
Separate sets here cannot access data between themselves. For example dynamic api chains cannot chain data from an api call of a separate instruction set. See staging instead for more complex automations.
{
"postOperations": [
{
"ErplyApi.Api.Post.createProduct1": "saveProduct",
"ErplyApi.Api.PostParam.createProduct1.groupID": "1",
"ErplyApi.Api.PostParam.createProduct1.code": "Code 1",
"ErplyApi.Api.PostParam.createProduct1.nameENG": "Name 1"
},
{
"ErplyApi.Api.Post.createProduct2": "saveProduct",
"ErplyApi.Api.PostParam.createProduct2.groupID": "1",
"ErplyApi.Api.PostParam.createProduct2.code": "Code 2",
"ErplyApi.Api.PostParam.createProduct2.nameENG": "Name 2"
}
],
"enabled": true
}
Url configuration
Url configuration’s static presets work the same way on automations as they do on regular templates. These calls are processed when the instruction is generated and cannot be chained into the post operation calls. To use these values in post operations, we can safely just print the values of the calls.
All calls both preset and post operations however are available for stages to be read under the ParentRequests map.
Reading webhook data
Webhook data can be read the limited model based support:
- wm_sales_document -> .Data.Automation.SalesDocumentList
- wm_customer -> .Data.Automation.PaymentList
- wm_payment -> .Data.Automation.PaymentList
In this data the model data matches that of the data source naming for Erply Api
Supports limited data.
{
"postOperations": [
{{ range $index, $customer := .Data.Automation.CustomerList }}
{
"EMSApi.SendEmailInput.Subject": "Customer {{ $customer.CustomerID }} created",
"EMSApi.SendEmailInput.To": "{{ $customer.Email }}",
"EMSApi.SendEmailInput.Content": "<encode><h1>Hello {{ $customer.FullName }}</h1></encode>",
"EMSApi.SendEmailInput.ContentType": "html"
}
{{ end }}
],
"enabled": true
}
Or all possible values using the json parameter reading (supports all the same functionality as dynamic api parameter reading).
Supports all data.
{
"postOperations": [
{{ range $customer := (.Data.Automation.Request.Get "items").Array }}
{
"EMSApi.SendEmailInput.Subject": "Customer {{ $customer.Get "CustomerID" }} created",
"EMSApi.SendEmailInput.To": "{{ $customer.Get "Email" }}",
"EMSApi.SendEmailInput.Content": "<encode><h1>Hello {{ $customer.Get "FullName" }}</h1></encode>",
"EMSApi.SendEmailInput.ContentType": "html"
}
{{ end }}
],
"enabled": true
}
Testing & custom webhooks
As of 1.288+ the new automation types have the ‘TEST’ button in the editor instead of the preview. This test allows us to define the hook data (not used for timed triggers) and test the execution of the automation without the need to construct the hmac ourselves. Output will contain details if it was successful.
In order to test the functionality without using an actual webhook we can construct the webhook data and send it against the automation endpoint using a generated hmac key.
To use the following features please make sure that a key has been generated for the page (use the refresh button if not). Automation will still work if a key is not present (as one will be automatically generated for it) but in order to test that everything works it’s always best to generate it manually first.
Custom webhook
For custom webhooks use the page trigger key and generate a hmac for the entire json body. Add the key as a header ’trigger-key’ to the request and send it to the appropriate cluster automation endpoint.
/api/automation/{YOUR_CLIENTCODE}/automation-demo-page
Generate the hmac key with the trigger key from automation tab as a secret using sha256 method. Use the entire json body as content.
The same method can be used for any custom webhook triggers.
Erply webhook
Erply webhooks use a specific structure.
```json
{
"id": "{HOOK_ID}",
"hmac": "{HMAC}",
"clientCode": "{CLIENT_CODE}",
"table": "wm_customer",
"action": "update",
"eventCount": 1,
"items": [
{
"rowId": 18651,
"timestamp": "2022-05-18 15:10:46",
"data": {
"id": 18651,
"code": "Auto-Prod-Code-3",
"name": "Generated by automation 3"
}
}
]
}
For these use the trigger key from the automation tab as secret using sha256 method. Use the id of the webhook as content.
Chaining automation data into api calls
We can also chain automation webhook data into api requests, using the following url configuration as a sample
ServiceApi.Api.Get.getWorkorders: api/v1/workorder
ServiceApi.Api.Query.getWorkorders.<-id: Automation.items.0.id
We can also use a special helper modifier function to get the oldest date out of the webhook items. The argument ‘2006-01-02T15:04:05Z07:00’ is the format that the date should be converted to (depends on the api used).
The function will return the oldest date in the items and then the used api should filter items based on that.
ServiceApi.Api.Get.getWorkorders: api/v1/workorder
ServiceApi.Api.Query.getWorkorders.<-createdAtStart: Automation.items|@getOldestItemDate:2006-01-02T15:04:05Z07:00
Automation page could look something like this then
{
"postOperations": [
{{ range .Data.ServiceApi.Api.Requests.getWorkorders.Response.Array }}
{
"test": "{{ .Get "id" }}"
},{{ end }}],
"enabled": true
}
Logs
From 1.228+ the automations that have been linked to an application will log the failures in the application logs. Similarly, the debug flag for automation logs works with automations as well.
Staging
Only available for the ‘automation’ file types.
This allows us to make separate stages to automations (up to 5). Useful for cases where we need to perhaps construct data for the final operations, and we need access data from the original postOperations api calls.
For example api calls defined in postOperations the sample below are executed at the end of the automation, so logic to read the data cannot be added.
{
"postOperations": [
{
"ErplyApi.Api.Post.createProduct1": "saveProduct",
"ErplyApi.Api.PostParam.createProduct1.groupID": "1",
"ErplyApi.Api.PostParam.createProduct1.code": "Code 1",
"ErplyApi.Api.PostParam.createProduct1.nameENG": "Name 1"
}
],
"enabled": true
}
Define a stage
To define a second stage we use the stageTo instruction, name here is the name of the automation to use and data is representation of the data that we access from .Data.Automation.Request. The data here needs to be a json string.
We can use the jsonSet, jsonSetObj and jsonDel helpers to manipulate possible json for this input.
The entire automation will complete and then when successful it will execute the second stage when defined. Note that the second stages can access all the api requests made in the first automation without the need to pass that data specifically to the next.
{
"postOperations": [
{
"ErplyApi.Api.Post.createProduct1": "saveProduct",
"ErplyApi.Api.PostParam.createProduct1.groupID": "1",
"ErplyApi.Api.PostParam.createProduct1.code": "Code 1",
"ErplyApi.Api.PostParam.createProduct1.nameENG": "Name 1"
}
],
"stageTo": {
"name": "da-at2-automation",
"data": "{}"
},
"enabled": true
}
Optionally we can also define an execution behaviour (runBehaviour) for the stage, available options are:
- skip - stage will not be triggered if any of the postOperations on the parent automation fail (default value)
- always - stage will always be triggered, and it does not matter if the parent stage is successful or fails
- fail - stage will only be triggered if the parent automation fails
The field is optional and will use ‘skip’ by default.
...,
"stageTo": {
"name": "da-at2-automation",
"data": "{}",
"runBehaviour": "always"
},
...
If the parent does indeed fail then we can access the failure message in the stages request data object,
.Data.Automation.Request.Get "parentError"
Define multiple stages
There is option to define multiple stages in array, but only one will be executed based on the
runBehaviour
of the stage and the result of the parent.
{
"stageToOneOf": [
{
"name": "pg-aut2-automation"
},
{
"name": "pg-autFail-automation",
"runBehaviour": "fail"
}
]
}
Construct and read data for second stage
Maximum amount of stages is 10
Adding items the data object
{{ $myJson := `{}` }}
<!-- Add to array -->
{{ $myJson = jsonSet $myJson "myArr." "test-1" }}
{{ $myJson = jsonSet $myJson "myArr. "test-2" }}
<!-- Add entire json strings (arrays or objects) -->
{{ $myJson = jsonSetObj $myJson "myData2" `[5,6]` }}
{
"postOperations": [
{
"CaFaApi.Api.Get.getConfig": "configuration",
"CaFaApi.Api.Query.getConfig.application": "GOERP"
}
],
"stageTo": {
"name": "da-at2-automation",
"data": {{ $myJson }}
},
"enabled": true
}
To read the api calls from the parent stage we can use the parentRequests data element. Note that all calls from the parent are accessible like this both calls made with presets and post operations.
If the parent defines multiple calls with the same name then only the last one called is accessible (for example in a loop). Loop index can be used to make unique names for the requests.
{
"postOperations": [
{{ range $index, $key := (.Data.Automation.Request.Get "myArr").Array }}
{
"Test": "{{ $key }}",
"Test2": "{{ $.Data.Automation.ParentRequests.CaFaApi.getConfig.Response.Get "0.name" }}"
},
{{ end }}
],
"enabled": true
}