Skip to content

Commit

Permalink
SaaS (#105)
Browse files Browse the repository at this point in the history
* Use multi db and multi service for SaaS

* Fix tests

* Fix tests running in docker

* Fix missed location group usage
Use location group in all tests

* Add request logging

* Debug pingeon

* Use node 7
Remove babel

* Pass pubsub config in response

Story https://www.pivotaltracker.com/story/show/144101103

* Fix error handler

* Version bump

* Fix mocha tests running

* Fix node version for test image

* Fix fanout error handler

* Update pubsub docs

* Version bump
  • Loading branch information
kostia-official authored Apr 27, 2017
1 parent 9cd44c9 commit bf23955
Show file tree
Hide file tree
Showing 73 changed files with 592 additions and 373 deletions.
6 changes: 2 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
FROM mhart/alpine-node:4
FROM node:7.9.0-alpine

# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY . /usr/src/app

RUN apk add --no-cache make gcc g++ python
RUN npm install
RUN npm prune --production
RUN npm install --production

EXPOSE 8080
CMD [ "npm", "start" ]
9 changes: 2 additions & 7 deletions _build/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,19 +1,14 @@
FROM musedlab/alpine-node
FROM node:7.9.0-alpine

MAINTAINER Vanya Andreychuk "[email protected]"

RUN apk add --update curl make gcc g++ python \
&& mkdir -p /var/www/html

WORKDIR /var/www/html

COPY .cache /var/www/html

COPY run.sh /root/run.sh

RUN npm install \
&& npm run build \
&& npm prune --production
RUN npm install --production

EXPOSE 8080

Expand Down
12 changes: 7 additions & 5 deletions apiary.apib
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ Notification Microservice.

{
"channel": "stream",
"message": {"text": "Hello!"}
"message": {"text": "Hello!"},
"service": {"name": "fanout", "id": "12ds54d", "key": "5d5v85d1f2s2d" }
}

+ Response 201
Expand All @@ -22,15 +23,16 @@ Notification Microservice.
"message": {"text": "Hello!"},
"statusCode": 200
}

## Send a pub/sub message to a recipient [/provider/pubsub/recipient]
### /provider/pubsub/recipient [POST]

+ Request (application/json)

{
"recipientId": "1",
"message": {"text": "Hello!"}
"message": {"text": "Hello!"},
"service": {"name": "fanout", "id": "12ds54d", "key": "5d5v85d1f2s2d" }
}

+ Response 201
Expand Down Expand Up @@ -181,7 +183,7 @@ Notification Microservice.
"firstName": "John",
"lastName": "Testerson"
}

+ Response 201
{
"id": "57472118fc2805aa3556770c",
Expand All @@ -190,7 +192,7 @@ Notification Microservice.
}

### Find all recipients [GET]

+ Response 200
[{
"id": "57472118fc2805aa3556770c``",
Expand Down
35 changes: 12 additions & 23 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "pingeon",
"description": "",
"version": "1.1.0",
"version": "1.2.1",
"homepage": "",
"main": "src/",
"keywords": [
Expand All @@ -15,73 +15,62 @@
"contributors": [],
"bugs": {},
"engines": {
"node": "4.4.4"
"node": "7.9.0"
},
"scripts": {
"ci": "npm run lint && npm test",
"ci:docker": "./test/docker/run-tests.sh",
"lint": "eslint --ignore-path .gitignore ./",
"start": "forever -m 5 --spinSleepTime 30000 --minUptime 1000 -af dist/",
"start:dev": "DEBUG=app* babel-node src/",
"server:mocked": "node dist/mocked.bundle.js",
"start": "forever -m 5 --spinSleepTime 30000 --minUptime 1000 -af src/",
"dev": "DEBUG=app* npm start",
"test": "NODE_ENV=test mocha test/*.test.js",
"dredd": "dredd",
"build": "redrun clean babel",
"clean": "del ./dist",
"babel": "babel --source-maps --copy-files -d dist/ src/",
"heroku-postbuild": "npm run build",
"deploy": "node ./deploy.js",
"deploy:staging": "docker build -t outfit/pingeon:staging . && docker push outfit/pingeon:staging",
"deploy:production": "docker build -t outfit/pingeon:production . && docker push outfit/pingeon:production"
},
"dependencies": {
"ajv": "4.0.6",
"aws-sdk": "2.3.3",
"babel-runtime": "6.22.0",
"bluebird": "3.4.6",
"body-parser": "1.15.0",
"compression": "1.6.1",
"cors": "2.7.1",
"debug": "2.2.0",
"deep-extend": "0.4.1",
"express-mw-correlation-id": "3.0.0",
"fanoutpub": "1.0.3",
"faye": "1.1.2",
"faye": "1.2.4",
"feathers": "2.0.1",
"feathers-authentication": "0.7.0",
"feathers-errors": "2.1.0",
"feathers-hooks": "1.5.3",
"feathers-hooks": "1.8.1",
"feathers-lg-multi-service-mongoose": "0.1.1",
"feathers-mongoose": "3.3.7",
"feathers-rest": "1.3.0",
"feathers-rest": "1.7.2",
"forever": "0.15.2",
"http-errors": "1.5.0",
"lodash": "4.16.3",
"mandrill-api": "1.0.45",
"mongoose": "4.4.10",
"mongoose": "4.6.5",
"mongoose-multi-connect": "0.6.1",
"mongoose-rename-id": "1.0.2",
"node-helpers": "~1.0.8",
"postmark": "1.2.1",
"promdash": "1.1.0",
"raven": "0.12.1",
"serve-favicon": "2.3.0",
"simple-express-logger": "2.0.0",
"smart-config": "0.7.4",
"source-map-support": "0.4.2",
"worque": "0.9.3"
},
"devDependencies": {
"ava": "0.17.0",
"babel-cli": "6.23.0",
"babel-core": "6.23.1",
"babel-eslint": "6.0.2",
"babel-plugin-transform-runtime": "6.23.0",
"babel-preset-es2015": "6.22.0",
"babel-preset-stage-0": "6.22.0",
"babel-runtime": "6.6.1",
"chai": "3.5.0",
"clear-require": "1.0.1",
"del-cli": "0.2.0",
"eslint": "2.6.0",
"eslint-config-airbnb": "6.2.0",
"hooks": "0.3.2",
"if-env": "1.0.0",
"mocha": "2.4.5",
"mockery": "1.4.1",
Expand Down
25 changes: 12 additions & 13 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
const compress = require('compression');
const cors = require('cors');
const { locationGroupHeader } = require('feathers-lg-multi-service-mongoose');
const beforeRequest = require('./middleware/before-request');
const afterRequest = require('./middleware/after-request');
const db = require('./db');
const feathers = require('feathers');
const hooks = require('feathers-hooks');
const rest = require('feathers-rest');
const bodyParser = require('body-parser');
const middleware = require('./middleware');
const services = require('./services');
const sentry = require('./helpers/sentry');

const app = feathers();
app.use(compress())
.options('*', cors())
.use(cors())
.use(bodyParser.json())
.use(bodyParser.urlencoded({ extended: true }))
.use(sentry.requestHandler)

app
.configure(beforeRequest)
.configure(hooks())
.configure(rest())
.configure(db)
.use(locationGroupHeader(
{ defaultLocationGroup: '' }
))
.configure(services)
.configure(middleware)
.use(sentry.errorHandler);
.configure(afterRequest);

module.exports = app;
17 changes: 17 additions & 0 deletions src/db/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
const multiConnect = require('mongoose-multi-connect');
const mongoose = require('mongoose');
const Promise = require('bluebird');
const requireDir = require('require-dir');
const renameId = require('mongoose-rename-id');
const { db: { url } } = require('smart-config');
const { mapValues } = require('lodash');

const schemas = mapValues(requireDir('./schemas'), (schema) => {
schema.plugin(renameId({ mongoose, newIdName: 'id' }));
return schema;
});

mongoose.Promise = Promise;
multiConnect.init({ mongoose, url, schemas });

module.exports = () => multiConnect;
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import renameId from 'mongoose-rename-id';
import mongoose, { Schema } from 'mongoose';
const { Schema } = require('mongoose');

const schema = new Schema({
module.exports = new Schema({
recipientId: String,
address: {
type: String
Expand All @@ -19,8 +18,6 @@ const schema = new Schema({
sendDate: Date,
received: Boolean,
receiveStatus: Object,
error: Object
});
schema.plugin(renameId({ newIdName: 'id', mongoose }));

module.exports = mongoose.model('notification', schema);
error: Object,
locationGroup: String
}, { versionKey: false });
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import renameId from 'mongoose-rename-id';
import mongoose, { Schema } from 'mongoose';
const { Schema } = require('mongoose');

const schema = new Schema({
module.exports = new Schema({
recipientId: {
type: String,
required: true
Expand All @@ -25,7 +24,4 @@ const schema = new Schema({
deviceId: String,
app: Object,
token: String
});
schema.plugin(renameId({ newIdName: 'id', mongoose }));

module.exports = mongoose.model('recipientProfile', schema);
}, { versionKey: false });
12 changes: 12 additions & 0 deletions src/db/schemas/recipients.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const { Schema } = require('mongoose');

module.exports = new Schema({
firstName: String,
lastName: String,
gender: {
type: String,
enum: ['male', 'female']
},
lastActivity: Date,
appId: Schema.Types.ObjectId
}, { versionKey: false });
5 changes: 3 additions & 2 deletions src/helpers/aws-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ const _ = require('lodash');
const config = require('smart-config');
const { title, appsArns, defaultApp } = config.get('push');
const debug = require('debug')('app:helpers:aws-utils');
const RecipientProfile = require('../services/recipient-profile/model');
const multiDB = require('mongoose-multi-connect');

function getPushMessage({ platform, message, payload }) {
const pushMessage = {};
Expand Down Expand Up @@ -36,8 +36,9 @@ function isOldToken(error) {
return error && error.message === 'Invalid parameter: This endpoint is already registered with a different token.';
}

async function deleteOldToken(token) {
async function deleteOldToken(token, locationGroup) {
try {
const RecipientProfile = multiDB.getModel('recipientprofiles', locationGroup);
await RecipientProfile.remove({ token });
debug('Deleted old token', token);
} catch (err) {
Expand Down
5 changes: 3 additions & 2 deletions src/helpers/email/get-recipient-name-parts.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const Recipient = require('../../services/recipient/model');
const multiDB = require('mongoose-multi-connect');

module.exports = async function (recipientId) {
module.exports = async function (recipientId, locationGroup) {
try {
const Recipient = multiDB.getModel('recipients', locationGroup);
const { firstName, lastName } = await Recipient.findOne({ _id: recipientId });
return { firstName, lastName };
} catch (err) {
Expand Down
14 changes: 6 additions & 8 deletions src/helpers/email/get-template-vars.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
const defaultVars = require('smart-config').get('email.defaultVars') || {};
const getRecipientNameParts = require('./get-recipient-name-parts');

module.exports = async function ({ vars = {}, recipientId, toEmail }) {
const nameParts = await getRecipientNameParts(recipientId);
const generatedVars = {
currentYear: new Date().getFullYear(),
...nameParts, toEmail
};
return { ...defaultVars, ...generatedVars, ...vars };
module.exports = async function ({ vars = {}, recipientId, toEmail, locationGroup }) {
const nameParts = await getRecipientNameParts(recipientId, locationGroup);
const generatedVars = Object.assign({
currentYear: new Date().getFullYear(), toEmail
}, nameParts);
return Object.assign({}, defaultVars, generatedVars, vars);
};

22 changes: 9 additions & 13 deletions src/helpers/email/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,31 @@ const getTemplateId = require('./get-template-id');
const getTemplateVars = require('./get-template-vars');
const client = promisifyAll(new postmark.Client(emailConfig.key));

const send = async({ email, from, to, cc, bcc, subject, message, template, vars, recipientId }) => {
const send = async ({ email, from, to, cc, bcc, subject, message, template, vars, recipientId, locationGroup }) => {
try {
const toEmail = email || to;
const options = {
From: from || emailConfig.from,
To: toEmail,
Cc: cc,
Bcc: bcc
To: toEmail, Cc: cc, Bcc: bcc
};

if (template) {
const templateId = getTemplateId(template);
const resultVars = await getTemplateVars({ vars, toEmail, recipientId });
const resultVars = await getTemplateVars({ vars, toEmail, recipientId, locationGroup });

return await client.sendEmailWithTemplateAsync({
return await client.sendEmailWithTemplateAsync(Object.assign({
TemplateId: templateId,
TemplateModel: resultVars,
...options
});
}, options));
}

return await client.sendEmailAsync({
return await client.sendEmailAsync(Object.assign({
TextBody: message,
Subject: subject,
...options
});
Subject: subject
}, options));

} catch (err) {
debug(err);
debug(err, { locationGroup });
return err;
}
};
Expand Down
8 changes: 8 additions & 0 deletions src/helpers/get-schema-info.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
const { db: { url } } = require('smart-config');
const _ = require('lodash');

module.exports = function (schemaPath) {
const schema = require(schemaPath);
const collectionName = _.last(schemaPath.split('/'));
return { schema, collectionName, dbUrl: url };
};
Loading

0 comments on commit bf23955

Please sign in to comment.