In our previous tutorials, only users interacted with the process. An important feature of Live Contract is the ability to interact with both people and systems.
This tutorial will start after the introduction and exchange of the recipients e-mail address. The initiator is organizing a meetup and wants to invite the people he met at the conference via e-mail.
Create a new subdirectory named invitation and copy main.feature and scenario.yml from the condition subdirectory.
We'll only focus on the situation where the recipient gives her e-mail address and the process is completed successfully.
Feature: Initiator invites the recipient to a meetup.
Background:
Given a chain is created by "Joe"
Given "Joe" creates the "main" process using the "invitation" scenario
And "Joe" is the "initiator" actor of the "main" process
And "Jane" is the "recipient" actor of the "main" process
Scenario:
When "Joe" runs the "introduce" action of the "main" process with:
| name | Joe Smith |
| organization | LTO Network |
Then the "initiator" actor of the "main" process has:
| name | Joe Smith |
| organization | LTO Network |
And the "main" process is in the "initial" state
When "Jane" runs the "introduce" action of the "main" process with:
| name | Jane Wong |
| organization | Acme Inc |
Then the "recipient" actor of the "main" process has:
| name | Jane Wong |
| organization | Acme Inc |
And the "main" process is in the "wait_on_initiator" state
When "Joe" runs the "request_email" action of the "main" process
Then the "main" process is in the "wait_on_recipient" state
When "Jane" runs the "reply" action of the "main" process with:
| email | jane.wong@example.com |
Then the "recipient" actor of the "main" process has:
| name | Jane Wong |
| organization | Acme Inc |
| email | jane.wong@example.com |
And the "main" process is in the "pending_invitation" state
When "Joe" runs the "invite" action of the "main" process
Then the "main" process is completed
In that case Joe needs to manually invite Jane (and all other invitees) by e-mail. Luckily, the workflow engine supports running actions in an automated manner.
The invite action
An important aspect of a Live Contract is that it only defines what should be done and not how it must be done. In this case we'll define that the initiator should send an e-mail. The way that's done is up to the actor that performs the action.
actions:introduce:actors: - initiator - recipientcondition:!evalcurrent.actor.name == nullupdate:current.actorrequest_email:actor:initiatorreply:actor:recipientupdate:actors.recipientinvite:$schema:http://exmple.com/schemas/invite/schema.json#actor:initiatorinvitee:!refactors.recipientmessage:Please come to great Meetup next week.states:initial:action:introducetransitions: - transition:wait_on_initiatorcondition:!evalactors.initiator.name != null && actors.recipient.name != nullwait_on_initiator:action:request_emailtransition:wait_on_recipientwait_on_recipient:action:replytransitions: - transition:pending_invitationcondition:!evalactors.recipient.email != null - transition::cancelledcondition:!evalactors.recipient.email == nullpending_invitation:action:invitetransition::success
Up until now, we've only used the default schema for an action. Actions that may be automated should contain all information required to run them. The structure of such an action must be defined in a custom JSON Schema.
The invite action uses a custom JSON Schema, specified with $schema. The schema may be publicly available at the specified URL or can be distributed as part of the Live Contract.
It's recommended, though not required, that every actor in the process has a copy of the JSON Schema. Actors that may perform the action do require the schema.
Mailgun e-mail service
For this tutorial we'll send an e-mail through Mailgun using their REST API.
Add your e-mail address to 'Authorized Recipients' and verify it.
This tutorial uses sandbox000.mailgun.org as endpoint, a dummy API key and jane.wong@example.com as e-mail address. Replace that with the (long) sandbox URL, your Mailgun API key and your own e-mail address.
Trigger
A node can define a trigger for an action schema. If an actor on the node is allowed to perform the action the workflow engine will automatically do so.
Triggers are defined through the configuration of the workflow engine. In order to use a custom configuration, please stop docker and than create and link a new directory workflow-settings.
$ docker-compose stop
In the workflow-settings directory, create a settings.yml file with custom configuration settings. In that file, we'll define a custom trigger for the invite schema.
triggers:invite:type:http$schema:http://exmple.com/schemas/invite/schema.json#url:sandbox000.mailgun.orgmethod:POSTauth:username:apipassword:key-00000000000000000000000000000000headers:Content-Type:application/x-www-form-urlencodeddata:from:joe.smith@example.comsubject:You are invited to our Meetupprojection:"{ data: { to: join('', [invitee.name, ' <', invitee.email, '>']), text: message } }"
The projection is a JMESPath expression that converts the action into an object that's expected by the HTTP trigger.
The data will be encoded and send as request body. The data parameters are described by the Mailgun API. The data in the trigger is merged with the data from the projection.
Triggers are configured by each party on their own node. This node will be configured to send an e-mail using Mailgun, but any other service could be used or an entirely different action could be taken to invite the recipient.
Testing the HTTP request
Enable outgoing capturing HTTP requests in workflow-settings/settings.yml by adding the following setting
http_request_log:true
In the test, we can check if the workflow engine has send a specific HTTP request.
Feature: Initiator invites the recipient to a meetup.
Background:
Given a chain is created by "Joe"
Given "Joe" creates the "main" process using the "handshake" scenario
And "Joe" is the "initiator" actor of the "main" process
And "Jane" is the "recipient" actor of the "main" process
Scenario:
When "Joe" runs the "introduce" action of the "main" process with:
| name | Joe Smith |
| organization | LTO Network |
Then the "initiator" actor of the "main" process has:
| name | Joe Smith |
| organization | LTO Network |
And the "main" process is in the "initial" state
When "Jane" runs the "introduce" action of the "main" process with:
| name | Jane Wong |
| organization | Acme Inc |
Then the "recipient" actor of the "main" process has:
| name | Jane Wong |
| organization | Acme Inc |
And the "main" process is in the "wait_on_initiator" state
When "Joe" runs the "request_email" action of the "main" process
Then the "main" process is in the "wait_on_recipient" state
When "Jane" runs the "reply" action of the "main" process with:
| email | jane.wong@example.com |
Then the "recipient" actor of the "main" process has:
| name | Jane Wong |
| organization | Acme Inc |
| email | jane.wong@example.com |
And a "POST" request has been send to "https://api.mailgun.net/v3/sandbox000.mailgun.org/messages" with:
| from | info@example.com |
| to | Jane Wong <jane.wong@example.com> |
| subject | You are invited to our Meetup |
| text | Please come to great Meetup next week. |
And that request received a "200" response with:
| message | Queued. Thank you. |
Then the "main" process is completed
The workflow engine can only capture outbound HTTP requests. It can't mock requests, but needs to work with an actual service or you need to setup a custom mocked service yourself.