Skip to content

Commit

Permalink
Implements simple microservice for moving data from SendGrid events t…
Browse files Browse the repository at this point in the history
…o Mixpanel
  • Loading branch information
weilandia committed Nov 17, 2017
1 parent b5ce41a commit 898046d
Show file tree
Hide file tree
Showing 8 changed files with 511 additions and 272 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.serverless
serverless.env.yml
node_modules
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SendGrid Event Webhook to Mixpanel Lambda Function

A simple lambda function that can be used to transfer SendGrid event data to Mixpanel pings.

## Getting Started

1. To get started, you'll need the [Serverless Framework](https://serverless.com/framework/docs/providers/aws/guide/quick-start/) installed. You'll also need your environment configured with [AWS credentials](https://serverless.com/framework/docs/providers/aws/guide/credentials/).

2. Recommended: Map relevant Mixpanel characteristics to [SendGrid SMTP API unique_args](https://sendgrid.com/docs/API_Reference/SMTP_API/unique_arguments.html) for the emails you are sending through SendGrid.

3. Add the MIXPANEL_TOKEN env var for each environment to `serverless.env.yml`.

``` yml
dev:
MIXPANEL_TOKEN: "DEV TOKEN"
staging:
MIXPANEL_TOKEN: "STAGING TOKEN"
prod:
MIXPANEL_TOKEN: "PROD TOKEN"
```
4. Deploy
``` sh
sls deploy --stage prod
```

5. [Setup the SendGrid Event Webhook](https://sendgrid.com/docs/API_Reference/Webhooks/event.html) with the lambda endpoint.

```
HTTP POST URL
https://EXAMPLE.execute-api.us-east-1.amazonaws.com/dev/mixpanel_sendgrid_events
```

6. 🎉 SendGrid event data is in Mixpanel and I don't have to run the webhook through our app!
9 changes: 0 additions & 9 deletions index.js

This file was deleted.

24 changes: 24 additions & 0 deletions mpTrackSendGrid.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
const serverless = require('serverless-http');

const Mixpanel = require('mixpanel');
const mixpanel = Mixpanel.init(process.env.MIXPANEL_TOKEN);

function handler(event, context, callback) {
let body = JSON.parse(event.body);
let eventsTracked = 0;

body.forEach((sendGridEvent) => {
mixpanel.track('SendGrid: ' + sendGridEvent.event, sendGridEvent);
eventsTracked += 1;
});

const response = {
statusCode: 200,
eventsTracked: eventsTracked
};

callback(null, response);
}

module.exports.mixpanel = mixpanel;
module.exports.handler = handler;
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
"author": "Nick Weiland <[email protected]>",
"license": "MIT",
"dependencies": {
"express": "^4.16.2",
"mixpanel": "^0.7.0",
"serverless-http": "^1.5.2"
},
"devDependencies": {
"serverless-offline": "^3.16.0"
"chai": "^4.1.2",
"lambda-local": "^1.4.4",
"mocha": "^4.0.1",
"serverless-offline": "^3.16.0",
"sinon": "^4.1.2"
}
}
13 changes: 10 additions & 3 deletions serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,16 @@ provider:
stage: dev
region: us-east-1
profile: weilandia
environment:
MIXPANEL_TOKEN: ${file(./serverless.env.yml):${self:provider.stage}.MIXPANEL_TOKEN}

functions:
app:
handler: index.handler
trackEvents:
handler: mpTrackSendGrid.handler
events:
- http: 'POST /mixpanel_sendgrid_event'
- http:
path: mixpanel_sendgrid_events
method: post

plugins:
- serverless-offline
89 changes: 89 additions & 0 deletions test/mpTrackSendGrid.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
process.env.MIXPANEL_TOKEN = "test_token";
const expect = require('chai').expect;
const sinon = require('sinon');

const lambdaLocal = require('lambda-local');
const mpTrackSendGrid = require('../mpTrackSendGrid');

let sendGridPayload = {
body: JSON.stringify([
{
"distinct_id": 123,
"organization_id": 32,
"organization_size": "enterprise",
"user_email": "[email protected]",
"timestamp": 1508542936,
"smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
"event": "processed",
"category": "cat facts",
"sg_event_id": "M0KaRo92wGpS1clK5ox6gg==",
"sg_message_id": "14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0"
},
{
"distinct_id": 456,
"organization_id": 122,
"organization_size": "smb",
"user_email": "[email protected]",
"timestamp": 1508542936,
"smtp-id": "<14c5d75ce93.dfd.64b469@ismtpd-555>",
"event": "deferred",
"category": "cat facts",
"sg_event_id": "W5aNQjYjn5JzWnrfQ6tGuw==",
"sg_message_id": "14c5d75ce93.dfd.64b469.filter0001.16648.5515E0B88.0",
"response": "400 try again later",
"attempt": "5"
}
])
};

let res;
let err;
let mixpanelSpy;

before(function(cb) {
mixpanelSpy = sinon.spy(mpTrackSendGrid.mixpanel, 'track');
lambdaLocal.execute({
event: sendGridPayload,
lambdaFunc: mpTrackSendGrid
}).then(function(_res) {
res = _res;
}).catch(function(_err) {
err = _err;
});

cb();
});

describe('mpTrackSendGrid', function() {
it('returns a 200 status', function() {
expect(res.statusCode).to.equal(200);
});

it('counts tracked events', function() {
expect(res.eventsTracked).to.equal(2);
});

it('asks mixpanel to track the right number of events', function() {
expect(mixpanelSpy.callCount).to.equal(res.eventsTracked);
});

it('asks mixpanel to track the right data', function() {
let event_1 = JSON.parse(sendGridPayload.body)[0];
let args_1 = mixpanelSpy.firstCall.args;
let props_1 = args_1[1];

expect(args_1[0]).to.equal("SendGrid: " + event_1.event);
expect(props_1.category).to.equal(event_1.category);
expect(props_1.sg_event_id).to.equal(event_1.sg_event_id);
expect(props_1.sg_message_id).to.equal(event_1.sg_message_id);

let event_2 = JSON.parse(sendGridPayload.body)[1];
let args_2 = mixpanelSpy.secondCall.args;
let props_2 = args_2[1];

expect(args_2[0]).to.equal("SendGrid: " + event_2.event);
expect(props_2.category).to.equal(event_2.category);
expect(props_2.sg_event_id).to.equal(event_2.sg_event_id);
expect(props_2.sg_message_id).to.equal(event_2.sg_message_id);
});
});
Loading

0 comments on commit 898046d

Please sign in to comment.