You are reading the documentation for an outdated Corteza release. 2023.9 is the latest stable Corteza release.

Examples

Add an attachment to an email

For this example, let’s assume we want to send out customer a quote for the new product they’ve ordered. The quote will be provided by our template system.

In order to render templates as PDF documents, PDF rendering must be setup.

workflow
Figure 1. The screenshot outlines the workflow that can be used to send an attachment via email.

The source code for the workflow example.

Workflow step details:
  1. (1) System; onManual:

    • resource: System

    • event: onManual

    • enabled: checked

  2. (2) Render quote:

    • type: Template render

    • arguments:

      • lookup:

        • type: Handle

        • value type: constant

        • value: quote

      • documentName:

        • value type: constant

        • value: quote

      • documentType:

        • value type: constant

        • value: application/pdf

      • options:

        • value type: constant

        • value: { "documentSize": "A4", "contentScale": "1", "orientation": "portrait", "margin": "0.3" }

    • results:

      • document target: quote

  3. (3) Render email:

    • type: Template render

    • arguments:

      • lookup:

        • type: Handle

        • value type: constant

        • value: content

      • documentType:

        • value type: constant

        • value: text/html

    • results:

      • document target: content

  4. (4) Build base email:

    • type: Email builder

    • arguments:

      • subject:

        • type: String

        • value type: constant

        • value: Quote for your product

      • to:

        • type: String

        • value type: constant

        • value: example@mail.tld

      • html:

        • type: Reader

        • value type: expression

        • value: content.document

    • results:

      • message target: email

  5. (5) Attach rendered template:

    • type: Email embedded attachment

    • arguments:

      • message:

        • type: EmailMessage

        • value type: expression

        • value: email

      • content:

        • type: Reader

        • value type: expression

        • value: quote.document

      • name:

        • type: String

        • value type: constant

        • value: quote.pdf

  6. (6) Send email:

    • type: Email sender

    • arguments:

      • message:

        • type: EmailMessage

        • value type: expression

        • value: email

  7. (7) Done

Make sure that the name parameter of the email embedded attachment uses a correct extension, such as .txt for plain text and .pdf for PDF. If the extension is omitted or incorrect, some email clients may display the attachment incorrectly or ignore it altogether.

The resulting email message includes the quote PDF as an attachment.

email mime contents
Figure 2. The screenshot outlines the raw received email message.

Example templates

The above example uses two templates: quote and content

template base content
Figure 3. The screenshot outlines the basic email content template used in the example.
The source code for the email content template:
<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <title>Quote for our services</title>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
</head>
<body>
  <h1>Hello!</h1>
  <p>
    Attached is the quote for our services.
  </p>
  <p>
    Best, Team ltd
  </p>
</body>
</html>
template base quote
Figure 4. The screenshot outlines the basic quote template used in the example.
The source code for the quote template:
<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <title>Quote#012356</title>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
</head>
<body>
  <h1>Quote#012356</h1>
  <p>
    Service quote for our services
  </p>
</body>
</html>

Parse Integration Gateway Request

For this example, let’s parse a request sent to the Integration Gateway

parse gateway payload
Figure 5. The screenshot outlines the workflow that can be used to process the request.

The source code for the workflow example.

Workflow step details:
  1. (1) System; onManual:

    • resource: System

    • event: onManual

    • enabled: checked

  2. (2) Parse request payload:

    • type: Process arbitrary data in jsenv

    • arguments:

      • scope:

        • type: Any

        • value type: expression

        • value: payload

      • source: refer below

    • results:

      • resultAny: parsedPayload

  3. (3) Debug state

  4. (7) Done

Integration gateway configuration

Table 1. Below are the configuration parameters for the corresponding integration gateway:

Endpoint

/examples/payload

Prefilter

Header: Token == "SOME_SECRET_TOKEN"

Processing

Workflow processer: On request payload notify user

Postfilter

Default JSON Response

cURL request

Below is an example of the cURL request that invokes the integration gateway and workflow:
curl -X POST "$CORTEZA_BASE/api/gateway/examples/payload" \
  -H "Content-Type: application/json" \
  -H 'Token: SOME_SECRET_TOKEN' \
  -d '{"name":"Peter","id":123}';

JSEnv parsing function

Below is the extracted source code which should be used in the JSEnv function step:
var inputData;

try {
    inputData = JSON.parse(input)
} catch (e) {
    throw new Error('could not parse input payload: ' + e);
}

if (!inputData.name) {
    throw new Error('could not parse input payload, name missing');
}

return {
    id: inputData.id,
    name: inputData.name
}

Interval

For this example, let’s assume we want to send an email to all users every Christmas at midnight. For this, we use a trigger of type onInterval with the interval 0 0 25 12 *

workflow example interval

Workflow step details:
  1. (1) Test trigger:

    • resource: System

    • event: onInterval

    • enabled: checked

    • constraints:

      • interval: 0 0 25 12 *

  2. (2) Iterate over Users:

    • type: Users

    • results:

      • user target: user

  3. (3) Send Email:

    • type: Email

    • arguments:

      • subject:

        • value type: constant

        • value: Merry christmas

      • to:

        • value type: expression

        • value: user.email

      • plain:

        • value type: constant

        • value: Merry christmas

  4. (4) Done:

Template rendering

In this example, we’ll take a look at how to render different templates that were made in our template system.

To start, create a template of type HTML in our templating system located in the admin area.

Rendering an HTML template

Create a workflow similar to the one below.

Make sure to look at the step details of steps (2) and (3).

html workflow
Figure 6. The screenshot outlines the workflow that renders an HTML template and sends it as an email.

The source code for the workflow can be found here.

Workflow step details:
  1. (1) System; onManual:

    • resource: System

    • event: onManual

    • enabled: checked

  2. (2) Render template:

    • type: Template render

    • arguments:

      • lookup:

        • type: Handle

        • value type: constant

        • value: email-template

      • documentName:

        • value type: constant

        • value: Email template

      • documentType:

        • value type: constant

        • value: text/html

    • results:

      • document target: renderedTemplate

  3. (3) Send email:

    • type: Email

    • arguments:

      • subject:

        • type: String

        • value type: constant

        • value: Email template

      • from:

        • type: String

        • value type: constant

        • value: demo@mail.com

      • to:

        • type: String

        • value type: constant

        • value: test@mail.com

      • html:

        • type: Reader

        • value type: expression

        • value: renderedTemplate.document

    • results:

      • document target: content

Make sure that the documentType parameter of the (2) Render template is of value text/html.

Rendering a PDF template

The difference between HTML and PDF rendering is that the documentType parameter changes to application/pdf.

With PDFs you can also adjust the render options to modify how the final PDF is rendered.

In order to render templates as PDF documents, you need to setup PDF rendering.

pdf workflow
Figure 7. The screenshot outlines the workflow that renders a PDF template and sends it as an email attachment.

The source code for the workflow can be found here.

Workflow step details:
  1. (1) System; onManual:

    • resource: System

    • event: onManual

    • enabled: checked

  2. (2) Render template:

    • type: Template render

    • arguments:

      • lookup:

        • type: Handle

        • value type: constant

        • value: pdf-template

      • documentName:

        • value type: constant

        • value: PDF template

      • documentType:

        • value type: constant

        • value: application/pdf

      • options:

        • type: renderOptions

        • value type: expression

        • value: { "documentSize": "A4", "contentScale": "1", "orientation": "portrait", "margin": "0.3" }

    • results:

      • document target: renderedTemplate

  3. (3) Build email:

    • type: Email builder

    • arguments:

      • subject:

        • type: String

        • value type: constant

        • value: PDF template

      • from:

        • type: String

        • value type: constant

        • value: test@mail.com

      • to:

        • type: String

        • value type: constant

        • value: example@mail.com

      • html:

        • type: Reader

        • value type: expression

        • value: content.document

    • results:

      • message target: email

  4. (4) Attach PDF template:

    • type: Attach PDF template

    • arguments:

      • message:

        • type: EmailMessage

        • value type: expression

        • value: email

      • content:

        • type: Reader

        • value type: expression

        • value: renderedTemplate.document

      • name:

        • type: String

        • value type: constant

        • value: PDF template.pdf

  5. (5) Send email:

    • type: Email sender

    • arguments:

      • message:

        • type: EmailMessage

        • value type: expression

        • value: email

You can combine both types of templates to render dynamic emails with PDF attachments.

The example for the workflow can be found here

Timestamp

For this example, let’s assume we want to wish all users a Happy new year. All the emails need to be sent out at exactly midnight. For this, we use a trigger of type onTimestamp with the timestamp of 2021-01-01T00:00:00Z

workflow example timestamp

Workflow step details:
  1. (1) Test trigger:

    • resource: System

    • event: onTimestamp

    • enabled: checked

    • constraints:

      • timestamp: 2021-01-01T00:00:00Z

  2. (2) Iterate over Users:

    • type: Users

    • results:

      • user target: user

  3. (3) Send Email:

    • type: Email

    • arguments:

      • subject:

        • value type: constant

        • value: Happy new year

      • to:

        • value type: expression

        • value: user.email

      • plain:

        • value type: constant

        • value: Happy new year

  4. (4) Done:

Workfing with Records

The section outlines some tips and tricks you can use when working with records.

Checking for existence

In case where you wish to perform some task depending on the existence of records, you can use either of the following approaches.

Both approaches are valid and there is no benefit between using one or the other. Decide based on your preferences/context.

Approach A

When searching for records, tick the incTotal option and assign the total result value to a variable.

Inside the gateway, check if the total value is greater then 0.

The screenshot outlines a basic example of checking for existence.
Figure 8. The screenshot outlines a basic example of checking for existence.

You can find the source code for the workflow here.

Approach B

Inside the gateway, check if the count(items) value is greater then 0.

The screenshot outlines a basic example of checking for existence.
Figure 9. The screenshot outlines a basic example of checking for existence.

You can find the source code for the workflow here.

Create or update

When creating the record you need to call the compose record create function, and when updating the record, you need to call the compose record update function.

Only the highlighted portion performs the create/update check; the rest is boilerplate to get it to the desired state.

If you need to call one or the other on-the-fyly, you can use the following approach. You can use the record.recordID != "0" to determine if the record needs to be updated — the default recordID value is "0".

The screenshot outlines a basic example of checking for existence.
Figure 10. The screenshot outlines a basic example of checking for existence.

You can find the source code for the workflow here.

Removing the value

To remove some record value, use an expression step to set the value in question to an empty Any.

The screenshot outlines a basic example of removing record values.
Figure 11. The screenshot outlines a basic example of removing record values.

You can find the source code for the workflow here.

Handling missing values

To use the default value in case the record value does not exist, you need to use the ?? operator.

To exemplify, using a ?? b will return a if it exists or b if it does not.

The below example uses a variable as the default value. You can use a constant such as "something string" or 42.

The screenshot outlines a basic example of using default values.
Figure 12. The screenshot outlines a basic example of using default values.

You can find the source code for the workflow here.

Parallelism

This section provides some examples on how you should perform tasks in parallel.

Unconditional parallelism

Unconditional parallelism should be used when two or more branches of execution should be done in parallel regardless of the state.

To achieve this, use the fork gateway gatewayParallel where each outbound branch defines one branch of parallel execution.

If any of the branches defines the termination step termination, the entire workflow will be terminated.

The screenshot outlines a basic example of unconditional parallel execution.
Figure 13. The screenshot outlines a basic example of unconditional parallel execution.

You can find the source code for the workflow here.

Unconditional parallel segment

A parallel segment is where the workflow transitions from sequential execution into parallel execution and back into sequential execution.

Unconditional parallelism should be used when two or more branches of execution should be done in parallel regardless of the state.

To achieve this, use the fork gateway gatewayParallel where each outbound branch defines one branch of parallel execution. Terminate the parallel segment by using the join gateway gatewayParallel.

If any of the branches defines the termination step termination, the entire workflow will be terminated.

The screenshot outlines a basic example of unconditional parallel execution.
Figure 14. The screenshot outlines a basic example of unconditional parallel execution.

You can find the source code for the workflow here.

Conditional parallelism

conditional parallelism should be used when two or more branches of execution should be done in parallel regardless of the state.

To achieve this, use the fork gateway gatewayParallel where each outbound branch defines one branch of parallel execution.

If any of the branches defines the termination step termination, the entire workflow will be terminated.

The screenshot outlines a basic example of conditional parallel execution.
Figure 15. The screenshot outlines a basic example of conditional parallel execution.

You can find the source code for the workflow here.

Conditional parallel segment

A parallel segment is where the workflow transitions from sequential execution into parallel execution and back into sequential execution.

conditional parallelism should be used when two or more branches of execution should be done in parallel regardless of the state.

To achieve this, use the fork gateway gatewayParallel where each outbound branch defines one branch of parallel execution. Terminate the parallel segment by using the join gateway gatewayParallel.

If any of the branches defines the termination step termination, the entire workflow will be terminated.

The screenshot outlines a basic example of conditional parallel execution.
Figure 16. The screenshot outlines a basic example of conditional parallel execution.

You can find the source code for the workflow here.

Dynamic Configuration

When defining automation which needs to interact with external systems or you need to make the workflow execution configurable, static workflows may prove challenging to use.

You can define a settings module where you can define all of the configurable parameters your automation requires. This can be anything from URL addresses to login credentials and access tokens.

When storing access tokens and other credentials, make sure to properly configure access control.

The screenshot outlines a basic example of the `settings` module.
Figure 17. The screenshot outlines a basic example of the settings module.

Inside your workflow, simply fetch the record from the settings module and configure your execution using it’s values.

The screenshot outlines a basic example of a workflow that utilizes the `settings` module.
Figure 18. The screenshot outlines a basic example of a workflow that utilizes the settings module.

You can find the source code for the workflow here.

Execution timeout

Corteza workflows' execution does not timeout by default (the workflow can run indefinitely).

If your use-case requires you to define a timeout, you can implement this manually.

The below example does two things:
  • Schedule a timeout after 10sec of execution,

  • repeat the iterator indefinitely.

manual timeout
Workflow step details:
  1. (1) Test trigger:

    • resource: System

    • event: onManual

    • enabled: checked

  2. (2) Fork:

    • gateway: Inclusive

    • conditions:

      • true

      • true

  3. (3) Wait 10sec:

    • offset: 10

  4. (4) Log context timeout:

    • type: Log debug message

    • arguments:

      • message:

        • value type: constant

        • value: Context timeout

  5. (5) Context timeout: /

  6. (6) Repeat indefinitely:

    • type: Condition

    • arguments:

      • while:

        • value type: expression

        • value: true

  7. (7) Log iteration:

    • type: Log debug message

    • arguments:

      • message:

        • value type: constant

        • value: Iterator loop

  8. (8) Done: /

  9. (9) Completed: /

Messagebus Queue

Messagebus queue mechanism complements the workflows in a way that any asynchronous computation can be offloaded to another workflow or workflow-extendable process. More info on the described topics can be found at Workflows, Integration Gateway Route Profiler and the Messagebus subsystem.

In this example we will:
  • create a queue

  • create an endpoint

  • write to queue via workflow

  • read from queue via workflow

    • send a payload to an endpoint

  • view endpoint payload in profiler

Create a new Queue

The Consumer here should be Eventbus, since this is how the internal mechanism provides the payload data to the Corteza subsystems, including Workflows.

queue create
Figure 19. The screenshot shows the creation of the queue in UI.

Write to Queue

This is a workflow we will use just to manually trigger the write to queue.

You can either use any of the automation buttons to trigger this workflow from the UI. Usually the case would be after creating a record and then sending the new recordID to the queue.

The payload is defined as an Array that is then stringified

toJSON([
  {"key":"foo", "value": "bar"},
  {"key":"bar", "value": "baz"}
])
queue wf add
Figure 20. A screenshot of the add to queue workflow.

The source code for the workflow example.

Read from Queue

Here we parse the queue payload and create a custom HTTP request to an outside, ie. Kafka (in our example an endpoint we will create for this purpose only).

queue wf read
Figure 21. An example of reading the data from queue.

The source code for the workflow example.

Create a mock service endpoint

What we need now is to trigger the Queue Write workflow and a request will be sent to our local server on the /api/gateway/example_kafka endpoint.

The resulting request can be seen on the Integration Gateway profiler (if you have it enabled, see more at Route Profiler).

queue endpoint
Figure 22. Adding a new endpoint to the Integration Gateway.

Preview the request in the Profiler

This preview is for example only, a way to use the profiler to debug any sending payloads to production / external services.

queue profiler 1
Figure 23. Preview the request headers and other metadata.
queue profiler 2
Figure 24. Preview the request payload.