-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9bfad7f
commit 2ce2810
Showing
22 changed files
with
5,766 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,150 @@ | ||
# notification-microservice | ||
A highly scalable microservice to handle WhatsApp, SMS, Email and Firebase notifications. | ||
# Notification Micro-service | ||
|
||
![https://img.shields.io/badge/version-v1.0.0-green](https://img.shields.io/badge/version-v1.0.0-green) | ||
|
||
## Introduction | ||
|
||
This project is a highly scalable micro-service to handle all the notifications (Email/SMS/WhatsApp) that you would require to schedule for contact from your small-business. It uses Amazon's SQS queuing service, SendGrid's email API and Twilio WhatsApp and SMS API to schedule notifications. Support would be added for other service-providers as the project evolves. | ||
|
||
## Features | ||
|
||
- Simple/Bulk WhatsApp notification through a single API call. | ||
- Simple/Bulk SMS notification through a single API call. | ||
- Simple/Bulk rich text Email through a single API call. | ||
- Rate limiter for streamlining bulk notification requests. | ||
|
||
## QuickStart | ||
- [Setup SendGrid](https://sendgrid.com/solutions/email-api/) and generate an API key | ||
- [Setup Twilio](https://www.twilio.com/docs/usage/tutorials/how-to-use-your-free-trial-account), register a number, generate API key and setup SMS and WhatsApp. | ||
- [Setup SQS](https://vivekkaushal.com/node-sqs/) | ||
- [Install Docker](https://docs.docker.com/engine/install/) | ||
- Clone this project | ||
```bash | ||
git clone https://github.com/kaushalvivek/notification-microservice.git | ||
cd notification-microservice | ||
``` | ||
- Create environment file | ||
```bash | ||
vim .env | ||
``` | ||
Provide all the values as specified in `sample_env.sh` | ||
- Start the micro-service | ||
``` | ||
sudo docker-compose up -d | ||
``` | ||
|
||
## Usage | ||
|
||
### Sending Emails | ||
POST request to : `host:PORT/api/v1/notify/email` | ||
Request body: | ||
```json | ||
{ | ||
"data":[ | ||
{ | ||
"content":{ | ||
"subject" : "Sample Email's Subject", | ||
"html" : "<p> Sample email's body</p>" | ||
}, | ||
"targets":[ | ||
"[email protected]", | ||
"[email protected]" | ||
] | ||
} | ||
{ | ||
"content":{ | ||
"subject" : "Sample Email 2's Subject", | ||
"html" : "<p> Sample email 2's body</p>" | ||
}, | ||
"targets":[ | ||
"[email protected]" | ||
] | ||
} | ||
] | ||
} | ||
|
||
``` | ||
|
||
### Sending WhatsApp messages | ||
POST request to : `host:PORT/api/v1/notify/whatsapp` | ||
Request body: | ||
```json | ||
{ | ||
"data":[ | ||
{ | ||
"content":{ | ||
"body" : "This is a sample message!" | ||
}, | ||
"targets":[ | ||
{ | ||
"countryCode":"+1", | ||
"phone":"123456789" | ||
}, | ||
{ | ||
"countryCode":"+91", | ||
"phone":"9876543211" | ||
} | ||
] | ||
} | ||
{ | ||
"content":{ | ||
"body" : "This is another sample message!" | ||
}, | ||
"targets":[ | ||
{ | ||
"countryCode":"+1", | ||
"phone":"123456789" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
|
||
``` | ||
|
||
|
||
### Sending SMS | ||
POST request to : `host:PORT/api/v1/notify/sms` | ||
Request body: | ||
```json | ||
{ | ||
"data":[ | ||
{ | ||
"content":{ | ||
"body" : "This is a sample message!" | ||
}, | ||
"targets":[ | ||
{ | ||
"countryCode":"+1", | ||
"phone":"123456789" | ||
}, | ||
{ | ||
"countryCode":"+91", | ||
"phone":"9876543211" | ||
} | ||
] | ||
} | ||
{ | ||
"content":{ | ||
"body" : "This is another sample message!" | ||
}, | ||
"targets":[ | ||
{ | ||
"countryCode":"+1", | ||
"phone":"123456789" | ||
} | ||
] | ||
} | ||
] | ||
} | ||
|
||
``` | ||
|
||
## Contributing | ||
Feel free to create issues in this repository for contribution. Contributions guidelines would evolve along with the project. Tests for integration would be added soon. | ||
|
||
## License | ||
|
||
[MIT License](https://raw.githubusercontent.com/kaushalvivek/notification-microservice/main/LICENSE) | ||
|
||
Copyright (c) 2021 Vivek Kaushal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
.dockerignore | ||
Dockerfile | ||
.git | ||
*.csv | ||
*.dat | ||
*.iml | ||
*.log | ||
*.out | ||
*.pid | ||
*.seed | ||
*.sublime-* | ||
*.swo | ||
*.swp | ||
*.tgz | ||
*.xml | ||
.DS_Store | ||
.idea | ||
.project | ||
.vscode | ||
npm-debug.log | ||
node_modules | ||
**/.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
FROM node:12.13.0-alpine | ||
ENV NODE_ENV=production | ||
WORKDIR /app | ||
COPY ["package.json", "package-lock.json*", "./"] | ||
RUN npm install --production | ||
COPY . . | ||
EXPOSE ${PORT} | ||
CMD [ "node", "server.js" ] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
const express = require('express') | ||
const app = express() | ||
const BodyParser = require('body-parser') | ||
const notificationRoutes = require('./routes/notification.route') | ||
|
||
// parse application/json | ||
app.use(BodyParser.json()) | ||
|
||
app.use('/api/v1/notify', notificationRoutes) | ||
|
||
app.get('/health', (req, res) => { | ||
res.send({ status: 'OK' }) | ||
}) | ||
|
||
module.exports = app |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
const notificationService = require('../services/notification.service') | ||
|
||
exports.email = async function (req, res) { | ||
const data = req.body.data | ||
try { | ||
await notificationService.email(data) | ||
return res.status(201).json({ status: 201, message: 'Email queued successfully.' }) | ||
} catch (e) { | ||
return res.status(400).json({ status: 400, message: e.message }) | ||
} | ||
} | ||
|
||
exports.whatsapp = async function (req, res) { | ||
const data = req.body.data | ||
try { | ||
await notificationService.whatsapp(data) | ||
return res.status(201).json({ status: 201, message: 'whatsapp message queued successfully.' }) | ||
} catch (e) { | ||
return res.status(400).json({ status: 400, message: e.message }) | ||
} | ||
} | ||
|
||
exports.sms = async function (req, res) { | ||
const data = req.body.data | ||
try { | ||
await notificationService.sms(data) | ||
return res.status(201).json({ status: 201, message: 'SMS queued successfully.' }) | ||
} catch (e) { | ||
return res.status(400).json({ status: 400, message: e.message }) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
const express = require('express') | ||
const router = express.Router() | ||
const notificationController = require('../controllers/notification.controller') | ||
|
||
router.post('/email', notificationController.email) | ||
router.post('/whatsapp', notificationController.whatsapp) | ||
router.post('/sms', notificationController.sms) | ||
|
||
module.exports = router |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const aws = require('aws-sdk') | ||
const uuid = require('uuid') | ||
const splitArray = require('split-array') | ||
const config = require('../../config') | ||
|
||
const sqsConfig = { | ||
apiVersion: config.aws.apiVersion, | ||
accessKeyId: config.aws.accessKeyId, | ||
secretAccessKey: config.aws.secretAccessKey, | ||
region: config.aws.region | ||
} | ||
aws.config.update(sqsConfig) | ||
|
||
const sqs = new aws.SQS({ apiVersion: config.aws.apiVersion }) | ||
|
||
exports.queueMessages = async function (messages, queueUrl) { | ||
try { | ||
const splittedArray = splitArray(messages, 10) | ||
for (const arr of splittedArray) { | ||
const params = { | ||
QueueUrl: queueUrl, | ||
Entries: [] | ||
} | ||
arr.forEach(message => { | ||
params.Entries.push({ | ||
Id: uuid.v4(), | ||
MessageBody: JSON.stringify(message) | ||
}) | ||
}) | ||
await sqs.sendMessageBatch(params).promise() | ||
}; | ||
return (201) | ||
} catch (e) { | ||
throw new Error(e.message) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
|
||
const config = require('../../config') | ||
const helper = require('./helper.service') | ||
|
||
exports.email = async function (data) { | ||
try { | ||
const messages = [] | ||
data.forEach(item => { | ||
if (!(item.content && item.targets && item.content.html && item.content.subject)) { | ||
throw new Error('Invalid request format, check API documentation.') | ||
}; | ||
messages.push(item) | ||
}) | ||
console.log(JSON.stringify(messages)) | ||
await helper.queueMessages(messages, config.aws.queueUrls.email) | ||
} catch (e) { | ||
throw new Error(e.message) | ||
} | ||
} | ||
|
||
exports.whatsapp = async function (data) { | ||
try { | ||
const messages = [] | ||
data.forEach(item => { | ||
if (!(item.content && item.targets && item.content.body)) { | ||
throw new Error('Invalid request format, check API documentation.') | ||
}; | ||
item.targets.forEach(target => { | ||
if (!(target.countryCode && target.phone)) { | ||
throw new Error('Invalid request format, check API documentation.') | ||
} | ||
if (isNaN(target.phone) || isNaN(target.countryCode.substring(1))) { | ||
throw new Error(`Invalid phone number in ${target}`) | ||
} | ||
}) | ||
messages.push(item) | ||
}) | ||
console.log(JSON.stringify(messages)) | ||
await helper.queueMessages(messages, config.aws.queueUrls.whatsapp) | ||
} catch (e) { | ||
throw new Error(e.message) | ||
} | ||
} | ||
|
||
exports.sms = async function (data) { | ||
try { | ||
const messages = [] | ||
data.forEach(item => { | ||
if (!(item.content && item.targets && item.content.body)) { | ||
throw new Error('Invalid request format, check API documentation.') | ||
}; | ||
item.targets.forEach(target => { | ||
if (!(target.countryCode && target.phone)) { | ||
throw new Error('Invalid request format, check API documentation.') | ||
} | ||
if (isNaN(target.phone) || isNaN(target.countryCode.substring(1))) { | ||
throw new Error('Invalid phone number or country code') | ||
} | ||
}) | ||
messages.push(item) | ||
}) | ||
console.log(JSON.stringify(messages)) | ||
await helper.queueMessages(messages, config.aws.queueUrls.sms) | ||
} catch (e) { | ||
throw new Error(e.message) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
require('dotenv').config() | ||
|
||
exports.aws = { | ||
region: process.env.AWS_REGION, | ||
apiVersion: process.env.AWS_API_VERSION, | ||
accessKeyId: process.env.AWS_ACCESS_KEY_ID, | ||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, | ||
queueUrls: { | ||
sms: process.env.SMS_QUEUE_URL, | ||
firebase: process.env.FIREBASE_QUEUE_URL, | ||
whatsapp: process.env.WHATSAPP_QUEUE_URL, | ||
email: process.env.EMAIL_QUEUE_URL | ||
} | ||
} | ||
|
||
exports.port = process.env.PORT |
Oops, something went wrong.