Skip to content

Commit

Permalink
DOC: add Email API documentation
Browse files Browse the repository at this point in the history
added info on how the email api works and how to add new actions to stackstorm using the workflow/workflow_actions method
  • Loading branch information
anish-mudaraddi committed Nov 29, 2023
1 parent f9ec0e7 commit 1dd4be0
Show file tree
Hide file tree
Showing 3 changed files with 276 additions and 0 deletions.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ A Stackstorm pack for running Openstack scripts built for the STFC Cloud team
- [Contributing](docs/CONTRIBUTING.md)
- [Developer Notes](docs/DEVELOPER_NOTES.md)

#### References
- [email_api](docs/EMAIL_API.md)

#### How Tos
- [Add an Action to the pack](docs/ADDING_ACTIONS.md)

### Pack Features

Expand Down
50 changes: 50 additions & 0 deletions docs/ADDING_ACTIONS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Adding Actions

**NOTE: Many of our actions need to be refactored to use this guide**

This guide will go through how to add actions using `workflows` module and
`workflow_actions` as the entrypoint for your action


## 1. Write The Action script in Workflows

We recommend writing the script in `lib/workflows/email`
- `lib/workflows` contains scripts that validate and handle actions.
- These scripts will make use of specific API modules also located in `lib`

- this is much easier to test than if this was handled in the actions layer.

The script's `main` function - the one that the Action will call must be named the same as the file itself
See example scripts in `lib/workflows/` to get an idea of how they work


## 2. Writing the Action config

See [Stackstorm Docs](https://docs.stackstorm.com/actions.html) for guidance on writing actions.
Action YAML configuration should be written to `actions` folder.


You need to set the entry_point for your Action to point to `src/workflow_actions.py`
And add two immutable parameters pointing to the submodule and file where your script is located
see example:


```yaml
...
entry_point: src/workflow_actions.py # setting entry point
...
parameters:
...

submodule_name: # points to the submodule your script is located in `lib/workflows`
default: email # here we're using the email submodule
immutable: true
type: string

action_name: # point to the file that contains code to handle the action
default: send_decom_flavor_email
immutable: true
type: string
```
**NOTE: use of submodules may be deprecated soon: https://github.com/stfc/st2-cloud-pack/pull/186**. In this case - you won't need to include `submodule_name`
221 changes: 221 additions & 0 deletions docs/EMAIL_API.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# Email API
The emailing part of the pack is handled in the email_api submodule.

Emailing works by using an SMTP relay service. When the user wishes to send
emails via any of the pack email actions - our StackStorm service attempts to
connect to the relay service.

Our action builds up our email payload using templates (see below) and sends it to the service

## Templates

Since our pack is designed to send standardised emails to users - like set notifications/warnings.
We have designed templates that our emails can be constructed from.

We use [jinja2](https://jinja.palletsprojects.com/en/3.0.x/intro/) templating language to build our email templates.

Templates are stored in `email_templates`. Each template contains two versions
- in HTML format (stored in `html` directory)
- in plaintext format (stored in `plaintext` directory)

An email can be composed of one or more of these templates - but they should all be a single format - either HTML or
plaintext.


## Creating a New Template

When creating a template, you can choose whether it's HTML or plaintext or both. Its recommended
to add both so there are both options in case one of them doesn't work for whatever reason

### 1. Write the plaintext template (Recommended)

Though this is optional if you just want a HTML version - it's recommended to start with plaintext
before you write the html template

create a new file in `email_templates/plaintext/<template-name>.txt.j2`

**NOTE: remember to include the `.j2` extension so that it will be treated as a jinja2 template**

Write the body of the email. Since this is jinja2 - you can include some of the features that jinja2 gives you
see [Jinja2 Templates](https://jinja.palletsprojects.com/en/3.0.x/templates/)

One of the main features you will be using is the ability to include variables - like so:

e.g. we'll create a file called - `example.txt.j2` in `email_templates/plaintext` and write in it
```
Dear Cloud User {{username}},
Test Email
```
the `{{username}}` - refers to a variable called `username` that the template expects before being rendered. The template
will replace any instance of `{{username}}` with the value of what's stored in given `username` variable

### 2. Write the html template

You can write a html email template by writing raw HTML.

create a new file in `email_templates/html/<template-name>.html.j2`
- remember to include the `.j2` extension so that it will be treated as a jinja2 template

e.g. for `example.html.j2`
```
Dear Cloud User {{username}}; <br /><br />
Test Email
```

notice that [Jinja2 Templates](https://jinja.palletsprojects.com/en/3.0.x/templates/) will work in the same way here
- **NOTE - you'll need to make sure any variables can be rendered into proper html **

**NOTE: Working on adding CSS and styling [191](https://github.com/stfc/st2-cloud-pack/issues/191)**

### 3. Add Schema Entry

Now that the email template has been written - we need to add a reference to it in the template schema file.

The template schema file holds:
- filepaths to the plaintext and html template files we've written
- what variables the template expects,
- any default values for the variables to send if not given.

The email template schema file is located in `/lib/email_api/email_template_schemas.yaml`

To add a new template, you must add another entry like so:

```yaml

...

example: # unique name of template to refer to it in code
schema: # all variables are defined in schema

# variables are listed as key-value pairs
# unique variable name as key, default value as value
username: null

# relative filepaths using email_templates as root directory
# to both html and plaintext templates - you must provide at least one - recommended both
html_filepath: "html/test.html.j2"
plaintext_filepath: "plaintext/test.txt.j2"

```

Now you can refer to the template when writing a new Email Action or editing an existing one.


## Interfacing with Email API

When you need to write a script to send an Email, you will need to use the Email API.

We need the following information before we try to send the email:

1. Information that make up an email:
- email subject (Required)
- email to send to (Required)
- email to send from (Required)
- whether email body is HTML or plaintext format (Required)
- email(s) to cc in (Optional)
- files to attach (Optional)

2. Information about the SMTP relay service
- Information to connect to the SMTP relay service - which Mailbox to use and authentication info

3. Email Templates to use to make up the email
- A list of jinja templates, including the variables they expect


### 1. Information that make up an email

This information is usually given by the user via the StackStorm Web UI when running the Action - usually this
information requires some validation or pre-processing - how this is done is largely left to the developer's discretion


### 2. Information about the SMTP relay service

This information should already be configured via StackStorm pack configuration.

When the action is called, a dataclass SMTPAccount is automatically created and passed through as `smtp_account`.
make sure that your Email Action YAML configuration contains the following entry to enable this. See below

```yaml
smtp_account_name:
type: string
description: "Name of SMTP Account to use. Must be configured in the pack settings."
required: true
default: "default"
```
This entry allows the user to define the SMTP Account to use.
StackStorm will try to find any stored pack info under the name given.
We usually leave this blank so it uses the configuration set under `default`.
See our confluence documentation on setting up the pack config

### 3. Email Template Information

This is the part that will likely change the most between Email Actions - which templates should be used to build up the email.
Usually you would write new templates for new Actions. See above about creating templates.

Once a template is created and available to use - you must define it's parameters using a dataclass called `EmailTemplateDetails`

e.g. for `example` template you define a dataclass object like so:
```python
body = EmailTemplateDetails(
template_name="example", # set the template name
# This sets the variables
# if any variables that are required are not specified here - the default will be used
# the default is specified in the schema file
template_params={
"username": "foo",
},
)
```

when the email is sent, the body will contain the rendered template
```
Dear Cloud User foo,

Test Email
```
You can define more than one template this way - like so:
```python
footer = EmailTemplateDetails(
template_name="footer"
# footer doesn't require any variables
template_params={}
)
```

### Tying it all-together

Once all these are created and ready to be used as stated aboce.
You must create a dataclass called `EmailParams` like so:

```python
email_info = EmailParams(
# we pass a list of EmailTemplateDetails dataclasses that make up the email body
# NOTE: this will be rendered in order - here body will be rendered first, then footer
email_templates=[body, footer],

"subject": "subject1", # email subject
"email_from": "[email protected]", # email address which will appear as the sender
"email_to": "[email protected]", # email address to send to
"email_cc": ["[email protected]", "[email protected]"], # email addresses to cc in
"attachment_filepaths": ["path/to/file1", "path/to/file2"], # filepaths to attach (NEEDS TESTING)
"as_html" # which template to use - if True - html, if False - plaintext (all in templates list must support given format)
)
```

Once this is created. You can send the email.
To send emails, we use the `Emailer` class.

```python

# Emailer requires a dataclass SMTPAccount in order to be setup - this is passed automatically as a parameter
# if you're creating an action using `workflow_actions.py` as your entrypoint
# see lib/structs/email/smtp_account.py
emailer = Emailer(smtp_account)

# this send_emails method accepts a list of EmailParam dataclass objects and will iteratively send emails
# here we provide information to send a single email - hence a singleton list being used
emailer.send_emails([email_info])
```

0 comments on commit 1dd4be0

Please sign in to comment.