4.9 KiB
Writing a Connector
Connectors are API endpoints that can easily be used within SpiffArena to allow your workflows to communicate with external systems via Service Tasks
.
In this tutorial we will walk through the steps required to create a new SMTP Connector, integrate it with a Connector Proxy
and finally test within SpiffArena
.
While the code will implement an SMTP Connector, the principles can be applied to create a Connector for other external systems.
Anatomy of a Connector
Since we will be leveraging SpiffWorkflow Proxy Blueprint, our Connector must respect the conventions that are used for discoverability and routing. The convention comes in two parts - the directory/file structure on disk and the Connector class structure.
Directory/File structure
A Connector project looks like:
src/connector_NAME:
total 8
drwxrwxr-x 3 jon jon 4096 Jun 29 15:26 commands
-rw-rw-r-- 1 jon jon 0 Jun 29 13:05 __init__.py
src/connector_NAME/commands:
total 12
-rw-rw-r-- 1 jon jon 0 Jun 29 13:47 __init__.py
-rw-rw-r-- 1 jon jon 1463 Jun 29 15:26 myCommand.py
Connectors can have multiple commands.
The HTTP Connector
for example has getRequest.py
and postRequest.py
.
Class structure
Each Connector command file contains a class structured like the following:
class MyCommand:
def __init__(self,
some_str_param: str,
some_int_param: int,
some_optional_str_param: Optional[str] = None,
):
...
def execute(self, config, task_data):
...
response = {"result": 42}
return {
"response": json.dumps(response),
"status": 200,
"mimetype": "application/json",
}
The parameters defined in the constructor will be exposed in the diagram editor of SpiffArena.
The executre method will be called when a Service Task
is executed within a running diagram.
Creating the SMTP Connector
Directory Setup
Our new SMTP Connector is going to start life with one SendEmail
command.
In a new git repository initialize poetry and create the folder structures:
src/connector_smtp:
total 8
drwxrwxr-x 3 jon jon 4096 Jun 29 15:26 commands
-rw-rw-r-- 1 jon jon 0 Jun 29 13:05 __init__.py
src/connector_smtp/commands:
total 12
-rw-rw-r-- 1 jon jon 0 Jun 29 13:47 __init__.py
-rw-rw-r-- 1 jon jon 1463 Jun 29 15:26 sendEmail.py
Don't forget the __init__.py
files or your Connector will not be discovered properly.
Your pyproject.toml file should look something like:
[tool.poetry]
name = "connector-smtp"
version = "0.1.0"
description = "Make SMTP Requests available to SpiffWorkflow Service Tasks"
authors = ["My Name <my.name@yahoo.com>"]
readme = "README.md"
packages = [{include = "connector_smtp", from = "src" }]
[tool.poetry.dependencies]
python = "^3.9"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Implement the class
Our SendEmail
Connector is going to start quite simple.
We will take all configuration parameters required to send the email from a running workflow.
This keeps the Connector stateless and greatly simplifies the code.
Our SendEmail
Connector looks like:
import json
from email.message import EmailMessage
from smtplib import SMTP
from typing import Optional
class SendEmail:
def __init__(self,
smtp_host: str,
smtp_port: int,
email_subject: str,
email_body: str,
email_to: str,
email_from: str,
smtp_user: Optional[str] = None,
smtp_password: Optional[str] = None,
):
self.smtp_host = smtp_host
self.smtp_port = smtp_port
self.smtp_user = smtp_user
self.smtp_password = smtp_password
self.email_subject = email_subject
self.email_body = email_body
self.email_to = email_to
self.email_from = email_from
def execute(self, config, task_data):
message = EmailMessage()
message.set_content(self.email_body)
message["Subject"] = self.email_subject
message["From"] = self.email_from
message["To"] = self.email_to
response = {}
should_login = self.smtp_user and self.smtp_password
try:
with SMTP(self.smtp_host, self.smtp_port) as smtp:
if should_login:
smtp.login(self.smtp_user, self.smtp_password)
smtp.send_message(message)
except Exception as e:
response["error"] = str(e)
return {
"response": json.dumps(response),
"status": 200,
"mimetype": "application/json",
}
Any exception will be returned to the workflow along with a 200 response. This allows the BPMN author the opportunity to inspect the result and take appropriate actions in their diagram.