From 1f7588fa4989f07a33bc5b89bdaf928be7120558 Mon Sep 17 00:00:00 2001 From: Nilan Marktanner Date: Wed, 1 Nov 2017 22:08:12 +0100 Subject: [PATCH] Add SES to templates/messaging (#118) * Add AWS SES to templates/messaging * Clean up some code * Remove some deps and general clean up * Add Promise, remove a couple imports, and cleanup * Refactor Promise to be correct syntax * Remove name, version, & description from package.json * Add readme to SES template * Update readme for SES template * Update readme for SES template * clean up graphcool.yml * clean up types, payload and fields * clean up env vars * clean up promise * code style * rehaul README --- .gitignore | 1 + messaging/ses/README.md | 93 +++++++++++++++++++++++++++++ messaging/ses/graphcool.yml | 13 ++++ messaging/ses/package.json | 9 +++ messaging/ses/src/sendEmail.graphql | 13 ++++ messaging/ses/src/sendEmail.ts | 83 +++++++++++++++++++++++++ messaging/ses/types.graphql | 0 7 files changed, 212 insertions(+) create mode 100644 messaging/ses/README.md create mode 100644 messaging/ses/graphcool.yml create mode 100644 messaging/ses/package.json create mode 100644 messaging/ses/src/sendEmail.graphql create mode 100644 messaging/ses/src/sendEmail.ts create mode 100644 messaging/ses/types.graphql diff --git a/.gitignore b/.gitignore index 0127b16..521d3c8 100644 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ fastlane/screenshots jest_* dist +.env .envrc .graphcoolrc diff --git a/messaging/ses/README.md b/messaging/ses/README.md new file mode 100644 index 0000000..c8f08c5 --- /dev/null +++ b/messaging/ses/README.md @@ -0,0 +1,93 @@ +# ses + +Send emails with AWS SES in your Graphcool project 🎁 + +## Getting Started + +### 1. Add the template to your Graphcool service + +```sh +graphcool add-template graphcool/templates/messaging/ses +``` + +### 2. Uncomment lines in `graphcool.yml` and `types.graphql` + +The [`add-template`](https://docs-next.graph.cool/reference/graphcool-cli/commands-aiteerae6l#graphcool-add-template) command is performing three major steps: + +1. Download the source files from the [`src`](./src) directory and put them into your service's `src` directory (into a subdirectory called `ses`). +2. Download the contents from [`graphcool.yml`](./graphcool.yml) and append them as comments to your service's `graphcool.yml`. +3. Download the contents from [`types.graphql`](./types.graphql) and append them as comments to your service's `types.graphql`. + +In order for the changes to take effect, you need to manually uncomment all the lines that have been added by the `add-template` command. + +### 3. Setup AWS SES credentials + +You need to configure these credentials as environment variables: + +In your base project, you need to configure the following **environment variables**. + +- `ACCESS_KEY_ID`: AWS Access Key ID +- `SECRET_ACCESS_KEY`: AWS Secret Access Key +- `REGION`: AWS Region (example: us-west-2) + +You can read more about managing your Access Keys [in the AWS docs](https://docs.aws.amazon.com/general/latest/gr/managing-aws-access-keys.html). Note: You'll need to create an account. + +An easy way to setup environment variables is using [direnv](https://direnv.net/). +To use `direnv`, put the following into `.envrc` in you project root: + +```sh +export ACCESS_KEY_ID=xxx +export SECRET_ACCESS_KEY=xxx +export REGION=xxx +``` + +### 4. Deploy the service + +Finally, you need to install the [node dependencies](./package.json#L2) and apply all the changes you just made by deploying the service: + +```sh +npm install +graphcool deploy +``` + +## Test the Code + +Use the `sendSesEmail` mutation to send emails according to its parameters: + +* `from: String!`: sender email +* `to: [String!]!`: a list of recipient emails +* `subject: String!`: the email subject +* `text: String!`: the text body of the email +* `html: String!`: the html body of the email + +**Important Note:** You can only use verified emails to send and receive. You can learn more [from the AWS docs](https://docs.aws.amazon.com/ses/latest/DeveloperGuide/verify-email-addresses.html) + +Go to the Graphcool Playground: + +```sh +graphcool playground +``` + +Hook into the function logs: + +```sh +graphcool logs -f sendEmail --tail +``` + +Run this mutation to send a new email: + +```graphql +mutation { + sendSesEmail( + from: "__SENDER_EMAIL__" + to: "__RECIPIENT_EMAIL__" + subject: "A new email from the Graphcool SES template!" + html: "This is your first email from the Graphcool SES template!" + text: "This is your first email from the Graphcool SES template!" + ) { + success + } +} +``` + +![](http://i.imgur.com/5RHR6Ku.png) diff --git a/messaging/ses/graphcool.yml b/messaging/ses/graphcool.yml new file mode 100644 index 0000000..7eb2080 --- /dev/null +++ b/messaging/ses/graphcool.yml @@ -0,0 +1,13 @@ +types: ./types.graphql + +functions: + sendEmail: + handler: + code: + src: src/sendEmail.ts + environment: + ACCESS_KEY_ID: ${env:ACCESS_KEY_ID} + SECRET_ACCESS_KEY: ${env:SECRET_ACCESS_KEY} + REGION: ${env:REGION} + type: resolver + schema: src/sendEmail.graphql diff --git a/messaging/ses/package.json b/messaging/ses/package.json new file mode 100644 index 0000000..f23bc34 --- /dev/null +++ b/messaging/ses/package.json @@ -0,0 +1,9 @@ +{ + "dependencies": { + "aws-sdk": "^2.135.0", + "graphcool-lib": "^0.1.3" + }, + "devDependencies": { + "@types/node": "^8.0.45" + } +} diff --git a/messaging/ses/src/sendEmail.graphql b/messaging/ses/src/sendEmail.graphql new file mode 100644 index 0000000..add4e29 --- /dev/null +++ b/messaging/ses/src/sendEmail.graphql @@ -0,0 +1,13 @@ +type SesEmailPayload { + success: Boolean! +} + +extend type Mutation { + sendSesEmail( + from: String! + to: [String!]! + subject: String! + text: String! + html: String! + ): SesEmailPayload! +} \ No newline at end of file diff --git a/messaging/ses/src/sendEmail.ts b/messaging/ses/src/sendEmail.ts new file mode 100644 index 0000000..e37432f --- /dev/null +++ b/messaging/ses/src/sendEmail.ts @@ -0,0 +1,83 @@ +import * as aws from 'aws-sdk' +import { FunctionEvent } from 'graphcool-lib' + +const ses = new aws.SES({ + region: process.env.REGION, + accessKeyId: process.env.ACCESS_KEY_ID, + secretAccessKey: process.env.SECRET_ACCESS_KEY, +}) + + +interface EventData { + from: string + to: [string] + subject: string + text: string + html: string +} + +export default async (event: FunctionEvent) => { + console.log(event) + + console.log(process.env) + + if (!process.env['ACCESS_KEY_ID']) { + console.log('Please provide a valid AWS Access Key ID!') + return { error: 'Module not configured correctly.' } + } + + if (!process.env['SECRET_ACCESS_KEY']) { + console.log('Please provide a valid AWS Secret Access Key!') + return { error: 'Module not configured correctly.' } + } + + if (!process.env['REGION']) { + console.log(`Please provide a valid AWS region, for example 'us-east-2'!`) + return { error: 'Module not configured correctly.' } + } + + try { + + const { to, from, subject, text, html } = event.data + + const params = { + Destination: { + ToAddresses: to + }, + Message: { + Body: { + Html: { + Charset: 'UTF-8', + Data: html + }, + Text: { + Charset: 'UTF-8', + Data: text + }, + }, + Subject: { + Charset: 'UTF-8', + Data: subject + } + }, + ReturnPath: from, + Source: from + } + + await new Promise((resolve, reject) => { + ses.sendEmail(params, (err) => { + if (err) { + throw new Error(JSON.stringify(err)) + } else { + resolve({}) + } + }) + }) + + return { data: { success: true } } + } catch(e) { + console.log(`Email could not be sent because an error occured:`) + console.log(e) + return { error: 'An unexpected error occured while sending email.' } + } +} diff --git a/messaging/ses/types.graphql b/messaging/ses/types.graphql new file mode 100644 index 0000000..e69de29