Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notifications service: first batch of notifications #165

Merged
merged 40 commits into from
Jun 2, 2020
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
d507e79
notifications scaffolding
promaty May 7, 2020
588c3bf
typo
promaty May 7, 2020
baaf3e0
added not nullabel
promaty May 7, 2020
bae589e
added last notification date check
promaty May 7, 2020
7676876
Merge branch 'development' into notifications_service
promaty May 8, 2020
b796b75
added notification types table
promaty May 8, 2020
bd20b41
split into notification scanner and sender
promaty May 9, 2020
3a26717
notification service prototype
promaty May 12, 2020
ad42d38
renamed test-server to test since we are testing services as well
promaty May 12, 2020
187bbdb
changed test folder
promaty May 12, 2020
24622a5
added email template for subscription reminder
promaty May 14, 2020
e883365
Emails: moved emails from court-dashboard repo (#159)
promaty May 14, 2020
e0bb80a
Emails: missing template subject fix
promaty May 14, 2020
b0a27a8
Merge branch 'development' into notifications_service
promaty May 15, 2020
cf370be
Apply suggestions from code review
promaty May 15, 2020
05ec92e
minor suggested fixes
promaty May 15, 2020
e622805
added scan check
promaty May 16, 2020
55544a6
abstracted scanner check
promaty May 16, 2020
5030736
renamed scanner checkUser to shouldNotifyUser
promaty May 16, 2020
adc2c32
added some user abstractions
promaty May 16, 2020
d3443c4
typo typo typo
promaty May 16, 2020
6415f92
added more reliable test script
promaty May 18, 2020
8fce807
improved test errors
promaty May 18, 2020
83c6161
minor test fixes
promaty May 18, 2020
6e4e825
switched to logger error instead of throw
promaty May 18, 2020
ce9686d
removed $ from objection methods
promaty May 18, 2020
c230f0c
removed query() from some base methods
promaty May 18, 2020
a57156a
moved logs before cors
promaty May 18, 2020
e164e65
added JurorDrafted
promaty May 20, 2020
64ac3d2
tidying up test file
promaty May 20, 2020
af43335
removed subscription reminder for anj registrations
promaty May 20, 2020
d779cf6
send verification token for anj registrations
promaty May 20, 2020
6c844bd
added reminder email instructions
promaty May 20, 2020
12cbb58
Services: notifications service - subscription reminder (#155)
promaty May 20, 2020
b6036b4
Merge branch 'notifications_service' into notifications_service_drafted
promaty May 20, 2020
bc372e4
Merge branch 'development' into notifications_service_drafted
promaty May 20, 2020
65a07b4
fixed dispute URL
promaty May 23, 2020
1377152
renamed mock-utils to helpers
promaty May 23, 2020
6ca7bf8
Merge branch 'development' into notifications_service_drafted
promaty May 25, 2020
3e14c89
Notifications service: remaining first batch (#169)
promaty Jun 2, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ SESSION_SECRET=secret

# Email config
[email protected]
EMAIL_VERIFICATION_TARGET_HOST=https://court.aragon.org
POSTMARK_SERVER_API_TOKEN=POSTMARK_API_TEST
POSTMARK_TEMPLATE_ALIAS_VERIFY=court-email-verification
POSTMARK_TEMPLATE_ALIAS_VERIFY=email-verification
EMAIL_JWT_PRIVATE_KEY=test

# DB config
Expand Down Expand Up @@ -42,4 +41,5 @@ NETWORK=rpc
RPC=http://localhost:8545

# Court config
CLIENT_URL=https://court.aragon.org/
COURT_ADDRESS=0xD833215cBcc3f914bD1C9ece3EE7BF8B14f841bb
6 changes: 6 additions & 0 deletions .github/scripts/emails-sync.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh
export POSTMARK_SERVER_API_TOKEN="$1"
cd emails
yarn install
yarn sync:assets
yarn sync:templates
8 changes: 0 additions & 8 deletions .github/scripts/test-server/run.sh

This file was deleted.

11 changes: 11 additions & 0 deletions .github/scripts/test/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/bin/sh
set -e # immediately fail script on any command error
DIR=$(dirname "$0")
cp .env.sample $DIR/.env
cd $DIR
export SERVER_IMAGE="$1"
docker-compose up -d
docker-compose exec -T court_server /bin/sh -c "npx wait-on http://localhost:\$SERVER_METRICS_PORT --timeout 20000"
docker-compose exec -T court_server npm run test:server
docker-compose exec -T court_server npm run test:services
docker-compose down
2 changes: 1 addition & 1 deletion .github/workflows/dashboard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Dashboard CI/CD
on:
push:
branches:
- development
- master
paths:
- monitoring/grafana/provisioning/dashboards/court-backend.json

Expand Down
19 changes: 19 additions & 0 deletions .github/workflows/emails-staging.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Email templates/assets sync
on:
push:
branches:
- development
paths:
- 'emails/**'

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
project_id: ${{secrets.GCP_PROJECT_ID}}
service_account_key: ${{secrets.GCP_SA_KEY}}
export_default_credentials: true
- run: .github/scripts/emails-sync.sh ${{secrets.POSTMARK_SERVER_API_TOKEN_STAGING}}
19 changes: 19 additions & 0 deletions .github/workflows/emails.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Email templates/assets sync
on:
push:
branches:
- master
paths:
- 'emails/**'

jobs:
sync:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
with:
project_id: ${{secrets.GCP_PROJECT_ID}}
service_account_key: ${{secrets.GCP_SA_KEY}}
export_default_credentials: true
- run: .github/scripts/emails-sync.sh ${{secrets.POSTMARK_SERVER_API_TOKEN}}
6 changes: 3 additions & 3 deletions .github/workflows/mainnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ jobs:
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
- run: .github/scripts/docker-build.sh $REPO ${GITHUB_SHA}

test-server:
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
- run: .github/scripts/test-server/run.sh $REPO:${GITHUB_SHA}
- run: .github/scripts/test/run.sh $REPO:${GITHUB_SHA}

release:
runs-on: ubuntu-latest
needs: test-server
needs: test
steps:
- uses: actions/checkout@v2
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
Expand Down
9 changes: 6 additions & 3 deletions .github/workflows/testnet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ on:
# This is useful to see if tests are passing during PR review
- '**'
- '!master'
paths-ignore:
- 'monitoring/**'
- 'emails/**'
env:
# This is a base repository and we use ${GITHUB_SHA} to set the version of the container
REPO: docker.pkg.github.com/aragonone/court-backend/testnet
Expand All @@ -18,18 +21,18 @@ jobs:
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
- run: .github/scripts/docker-build.sh $REPO ${GITHUB_SHA}

test-server:
test:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v2
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
- run: .github/scripts/test-server/run.sh $REPO:${GITHUB_SHA}
- run: .github/scripts/test/run.sh $REPO:${GITHUB_SHA}

release:
if: github.ref == 'refs/heads/development'
runs-on: ubuntu-latest
needs: test-server
needs: test
steps:
- uses: actions/checkout@v2
- run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p ${{secrets.GITHUB_TOKEN}}
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ services:
volumes:
- ./packages/services/bin:/court-backend/packages/services/bin
- ./packages/services/src:/court-backend/packages/services/src
- ./packages/services/test:/court-backend/packages/services/test
env_file:
- .env
logging: *loki
Expand Down
2 changes: 2 additions & 0 deletions emails/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules/
dist/
73 changes: 73 additions & 0 deletions emails/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Emails

Email notification templates for use with Aragon Court (may be moved in the future to where the notification service lives).

Currently intended for use through any [Mustache](https://mustache.github.io/) compatible email service, including [Postmark](https://postmarkapp.com/).

## Generating templates

Install the dependencies in this directory via `yarn`, and then generate templates with `yarn build`.

To test each template, mock data has been included and fully rendered emails can be generated with `yarn build:mock`.

Two outputs are generated per template: one `.html` and one `.txt`. If the email service supports it, you should include both versions.

## Environment variables

### ASSETS_URL

Set this to the base URL of the assets. Defaults to `./assets`.

### MOCK_DATA

Set this to `1` to replace the mustache variables by the `mockData` provided in the template.

### PRINT_DATA_FOR

Set this to the name of the template you want to extract the data for. This is useful to generate the Postmark request that will fill the corresponding template.

### SYNC_TEMPLATES

Set this to `1` in order to sync templates to the postmark server.

### POSTMARK_SERVER_API_TOKEN

Required when `SYNC_TEMPLATES` is set to `1` in order to authenticate to postmark.

## Examples

To develop or test (e.g. on Litmus), use the `build:mock` script:

```console
yarn build:mock
```

When used with Litmus, remember to change the `ASSETS_URL` variable.

### Update Postmark templates

Use `build:postmark` to get this variable set on the URLs used with our Postmark templates:

```console
yarn build:postmark
```

After running this command, the generated .html and .txt templates are ready to be used on Postmark.

Use `PRINT_DATA_FOR` to get the data corresponding to a template:

```console
PRINT_DATA_FOR=generic yarn build
```

#### Automatic sync

This repo also automatically synchronizes templates to postmark server and pushes assets to the google storage bucket.

This flow is defined in: [../.github/workflows/emails.yml](../.github/workflows/emails.yml)

You can also execute the sync commands manually if you have the required postmark/google cloud credentials:
```console
yarn sync:assets
yarn sync:templates
```
Binary file added emails/assets/banner-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/banner-dispute0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/banner-precedence-restart.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/header-anj.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/header-dispute0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/header-logo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/icon-appeals-opened.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/icon-generic.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/icon-negative.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added emails/assets/icon-positive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
127 changes: 127 additions & 0 deletions emails/build-emails
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/usr/bin/env node

const { execSync } = require('child_process')
const fs = require('fs')
const path = require('path')
const prettier = require('prettier')
const handlebars = require('handlebars')
const postmark = require('postmark')
const {
MOCK_DATA,
PRINT_DATA_FOR,
SYNC_TEMPLATES,
POSTMARK_SERVER_API_TOKEN
} = require('./env')

const DIST_DIR = path.resolve('dist')

function renderTemplate(filename) {
const name = path.basename(filename, '.js')
const createTemplate = require(path.join(__dirname, 'templates', name))
let { subject, template, templateText, mockData } = createTemplate()

// Prettify templates
template = prettier.format(template, { parser: 'html', printWidth: 999 })
templateText = prettier.format(templateText, {
parser: 'markdown',
proseWrap: 'always',
printWidth: 80,
})

// Remove extra whitespaces in the text template
templateText = templateText.replace(/^ +/gm, '')

if (MOCK_DATA) {
template = handlebars.compile(template)(mockData)
templateText = handlebars.compile(templateText)(mockData)
}

return { subject, name, template, templateText }
}

async function syncPostmark(files) {
if (!POSTMARK_SERVER_API_TOKEN) {
throw 'You must specify a valid POSTMARK_SERVER_API_TOKEN environment variable.'
}
const client = new postmark.ServerClient(POSTMARK_SERVER_API_TOKEN)
for (const filename of files) {
const { subject, name, template, templateText } = renderTemplate(filename)
const params = {
Name: name,
Alias: name,
Subject: subject,
HtmlBody: template,
TextBody: templateText
}
try {
await client.getTemplate(name)
await client.editTemplate(name, params)
} catch {
await client.createTemplate(params)
}
}
}

function generateTemplates(files) {
execSync(`rm -rf "${DIST_DIR}"`)

fs.mkdirSync(DIST_DIR, { recursive: true })
files.forEach(filename => {
const { name, template, templateText } = renderTemplate(filename)
fs.writeFileSync(path.join(DIST_DIR, name + '.html'), template, 'utf8')
fs.writeFileSync(path.join(DIST_DIR, name + '.txt'), templateText, 'utf8')
})

execSync(`cp -r "${__dirname}/assets" "${DIST_DIR}/assets"`)
}

function printTemplateData(files, filename) {
// Tolerate .js suffix in the name passed to PRINT_DATA_FOR
const name = path.basename(filename, '.js')

const templateFile = files.find(filename => filename === name + '.js')

if (!templateFile) {
throw `Error: the template ${PRINT_DATA_FOR} couldn’t be found. ` +
`Please check the PRINT_DATA_FOR variable.`
}

const createTemplate = require(path.join(
__dirname,
'templates',
filename + '.js'
))
const template = createTemplate()

console.log('')
console.log(`Data for the template “${name}”:`)
console.log('')
console.log(JSON.stringify(template.mockData, null, 2))
console.log('')
}

async function main() {
const files = fs
.readdirSync(path.join(__dirname, 'templates'))
.filter(filename => filename.endsWith('.js'))

// Print template data
if (PRINT_DATA_FOR) {
printTemplateData(files, PRINT_DATA_FOR)
return
}

// sync templates to postmark
if (SYNC_TEMPLATES) {
await syncPostmark(files)
return
}

// Generate the templates
generateTemplates(files)
}

main().catch((error) => {
console.error(error)
process.exit(1)
})
14 changes: 14 additions & 0 deletions emails/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module.exports.ASSETS_URL =
process.env.ASSETS_URL === undefined ? './assets' : process.env.ASSETS_URL

module.exports.MOCK_DATA =
process.env.MOCK_DATA !== undefined && process.env.MOCK_DATA !== '0'

module.exports.PRINT_DATA_FOR =
process.env.PRINT_DATA_FOR === undefined ? null : process.env.PRINT_DATA_FOR

module.exports.SYNC_TEMPLATES =
process.env.SYNC_TEMPLATES !== undefined && process.env.SYNC_TEMPLATES !== '0'

module.exports.POSTMARK_SERVER_API_TOKEN =
process.env.POSTMARK_SERVER_API_TOKEN === undefined ? null : process.env.POSTMARK_SERVER_API_TOKEN
9 changes: 9 additions & 0 deletions emails/mock-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function accountData(address) {
return {
account: address,
accountStart: address.slice(0, 6),
accountEnd: address.slice(-4),
}
}

module.exports = { accountData }
Loading