Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .istanbul.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
verbose: false
instrumentation:
excludes: ['dist/**', 'coverage/**', 'gulpfile.babel.js', 'api/**', 'cli/**']
excludes: ['dist/**', 'coverage/**', 'gulpfile.babel.js', 'api/**', 'cli/**', 'config/coverageReporter.js']
include-all-sources: true
reporting:
print: summary
Expand Down
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ script:
- npm run lint
- npm run test:check-coverage
after_script:
# - npm run report-coverage
- npm run report-coverage
19 changes: 8 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
[![logo](https://i.imgur.com/3OtP3p8.png)](https://softwareengineeringdaily.com/)

# SEDaily Event Stream Processor
# SEDaily Logging, Monitoring, and Analytics

The real time event processing infrastructure gateway server for the Software Engineering Daily [Android](https://github.com/SoftwareEngineeringDaily/SEDaily-Android), [iOS](https://github.com/SoftwareEngineeringDaily/se-daily-iOS), and [web front end](https://github.com/SoftwareEngineeringDaily/sedaily-front-end). The SEDaily event stream is responsible for authenticating connecting clients and validating event payload schemas before putting the event on the SED event bus. Interested clients can subscribe to events on the stream.
The real time event processing infrastructure gateway server for the Software Engineering Daily [Android](https://github.com/SoftwareEngineeringDaily/SEDaily-Android), [iOS](https://github.com/SoftwareEngineeringDaily/se-daily-iOS), and [web front end](https://github.com/SoftwareEngineeringDaily/sedaily-front-end). The SEDaily event stream is responsible for validating event payload schemas before putting the event into InfluxDB. The resulting database is queried using Grafana to get up to the second analytics reporting.


<a href="https://travis-ci.org/SoftwareEngineeringDaily/sedaily-devops"><img src="https://travis-ci.org/SoftwareEngineeringDaily/sedaily-devops.svg?branch=develop"></img></a>
<div>
<a href="https://travis-ci.org/SoftwareEngineeringDaily/sedaily-devops"><img src="https://travis-ci.org/SoftwareEngineeringDaily/sedaily-devops.svg?branch=develop"></img></a>
<img src="https://wt-1364ed7c3f0364e6608ffd3c14d53518-0.run.webtask.io/coverage-badge?service=github" hspace="10"></img>
</div>

## Getting Started
```sh
$ git clone https://github.com/SoftwareEngineeringDaily/sedaily-event-stream.git
$ cd sedaily-event-stream
$ git clone https://github.com/SoftwareEngineeringDaily/sedaily-devops.git
$ cd sedaily-devops
```

### Set up (local)
<!-- Temporarily commented out until Redis Streams is in merged into the master branch - Install and run a local [Redis](https://github.com/antirez/redis) client -->
- Install and run a local [InfluxDB](https://github.com/influxdata/influxdb) client
- `cp .env.local_example .env`
- `npm install` or `yarn install`
Expand All @@ -26,10 +27,6 @@ $ cd sedaily-event-stream
- Run `docker-compose up`
- If dependencies are updated in package.json, run `docker-compose down` and then `docker-compose up --build`. This will remove the old container and rebuild the API image which installs the new dependencies.

## Current State

The current state of the SEDaily event stream is analytics focused. The event stream backend relies on Redis Streams, which is only currently available in the [`unstable`](https://github.com/antirez/redis/tree/unstable) branch. Until it is stable, the SEDaily event stream will only directly input events into InfluxDB. Data analytics can be run against queries on the InfluxDB events database.

## Contributing
We use the develop branch to perform work in. Fork the project and clone it, create a branch off of develop and perform your changes. Then submit a pull request to merge your branch into the develop branch here. We have an active Slack community that you can reach out to for more information or just to chat with anyone. Check out the [<img src="https://upload.wikimedia.org/wikipedia/commons/7/76/Slack_Icon.png" alt="Slack Channel" width="20px"/> SED app development](https://softwaredaily.slack.com/app_redirect?channel=sed_app_development) slack channel. Also see the [Open Source Guide](https://softwareengineeringdaily.github.io/).

Expand Down
32 changes: 32 additions & 0 deletions config/coverageReporter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const request = require('request');
const lcov2badge = require('lcov2badge');

const filePath = process.argv[2];

const options = {
filePath: filePath || './coverage/lcov.info',
warnThreshold: 80, // default is 80
koThreshold: 60, // default is 60
};

function sendCoverageBadge(coverageBadge) {
const requestOptions = {
method: 'post',
body: { coverageBadge, coverageBadgeToken: process.env.COVERAGE_BADGE_TOKEN },
json: true,
url: process.env.COVERAGE_BADGE_URL
};
request(requestOptions, (err, res, body) => {
if (err) {
throw err;
} else {
console.log(body.result); // eslint-disable-line no-console
}
});
}


lcov2badge.badge(options, (err, svgBadge) => {
if (err) throw err;
sendCoverageBadge(svgBadge);
});
2 changes: 2 additions & 0 deletions config/param-validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default {
body: {
clientId: Joi.string().required(),
deviceType: Joi.string().required().valid(['iOS', 'Android', 'Browser', 'API']),
eventApiEnv: Joi.string().required().valid(['production', 'test']),
eventTime: Joi.date().timestamp('unix').required(),
eventData: Joi.object().required(),
eventType: Joi.string().required().valid([
Expand All @@ -26,6 +27,7 @@ export default {
body: {
clientId: Joi.string().required(),
deviceType: Joi.string().required().valid(['iOS', 'Android', 'Browser', 'API']),
eventApiEnv: Joi.string().required().valid(['production', 'test']),
errorTime: Joi.date().timestamp('unix').required(),
errorData: Joi.object().required(),
}
Expand Down
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test:watch": "npm run test -- --watch",
"test:coverage": "cross-env NODE_ENV=test ./node_modules/.bin/istanbul cover _mocha -- --ui bdd --reporter spec --colors --compilers js:babel-core/register tests --recursive",
"test:check-coverage": "npm run test:coverage && istanbul check-coverage",
"report-coverage": "coveralls < ./coverage/lcov.info"
"report-coverage": "node config/coverageReporter.js"
},
"repository": {
"type": "git",
Expand Down Expand Up @@ -69,6 +69,7 @@
"joi": "10.6.0",
"jsonwebtoken": "8.0.0",
"kafka-node": "2.2.3",
"lcov2badge": "^0.1.0",
"lodash": "4.17.4",
"method-override": "2.3.9",
"mocha": "3.5.0",
Expand All @@ -79,6 +80,7 @@
"passport-facebook-token": "3.3.0",
"raccoon": "0.2.8",
"redis": "^2.8.0",
"request": "^2.83.0",
"run-sequence": "2.1.0",
"sinon": "^4.1.2",
"supertest": "3.0.0",
Expand Down
48 changes: 48 additions & 0 deletions tests/server/errors/error.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('## Basic Error APIs', () => {
it('sending valid error is successful', (done) => {
const error = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'API',
errorTime: new Date().getTime(),
errorData: {
Expand All @@ -30,6 +31,7 @@ describe('## Basic Error APIs', () => {
it('errors when no clientId is sent', (done) => {
const error = {
deviceType: 'Browser',
eventApiEnv: 'production',
errorTime: new Date().getTime(),
errorData: {}
};
Expand All @@ -48,6 +50,7 @@ describe('## Basic Error APIs', () => {
it('errors when no deviceType is sent', (done) => {
const error = {
clientId: '1234567',
eventApiEnv: 'production',
errorTime: new Date().getTime(),
errorData: {
userId: '3462562'
Expand All @@ -68,6 +71,7 @@ describe('## Basic Error APIs', () => {
it('errors when no errorTime is sent', (done) => {
const error = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'API',
errorData: {
userId: '3462562'
Expand All @@ -88,6 +92,7 @@ describe('## Basic Error APIs', () => {
it('errors when no errorData is sent', (done) => {
const error = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'API',
errorTime: new Date().getTime()
};
Expand All @@ -102,4 +107,47 @@ describe('## Basic Error APIs', () => {
})
.catch(done);
});

it('errors when no eventApiEnv is sent', (done) => {
const error = {
clientId: '1234567',
errorData: {
userId: '3462562'
},
deviceType: 'API',
errorTime: new Date().getTime()
};
request(app)
.post('/api/v1/error')
.send(error)
.expect(httpStatus.BAD_REQUEST)
.then((res) => {
expect(res.body).to.exist; //eslint-disable-line
expect(res.body.message).to.equal('"eventApiEnv" is required')
done();
})
.catch(done);
});

it('errors when eventApiEnv sent is wrong type', (done) => {
const error = {
clientId: '1234567',
eventApiEnv: 'prod',
errorData: {
userId: '3462562'
},
deviceType: 'API',
errorTime: new Date().getTime()
};
request(app)
.post('/api/v1/error')
.send(error)
.expect(httpStatus.BAD_REQUEST)
.then((res) => {
expect(res.body).to.exist; //eslint-disable-line
expect(res.body.message).to.equal('"eventApiEnv" must be one of [production, test]'); //eslint-disable-line
done();
})
.catch(done);
});
});
55 changes: 54 additions & 1 deletion tests/server/events/event.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('## Basic Event APIs', () => {
it('sending valid event is successful', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'test',
deviceType: 'iOS',
eventTime: new Date().getTime(),
eventType: 'login',
Expand Down Expand Up @@ -40,6 +41,7 @@ describe('## Basic Event APIs', () => {
it('errors with invalid deviceType', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'Windows phone',
eventTime: new Date().getTime(),
eventType: 'login',
Expand All @@ -62,6 +64,7 @@ describe('## Basic Event APIs', () => {
it('errors with invalid timestamp', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'Browser',
eventTime: new Date(),
eventType: 'login',
Expand Down Expand Up @@ -89,7 +92,7 @@ describe('## Basic Event APIs', () => {
.expect(httpStatus.BAD_REQUEST)
.then((res) => {
expect(res.body).to.exist; //eslint-disable-line
expect(res.body.message).to.equal('"clientId" is required and "deviceType" is required and "eventTime" is required and "eventData" is required and "eventType" is required'); //eslint-disable-line
expect(res.body.message).to.equal('"clientId" is required and "deviceType" is required and "eventApiEnv" is required and "eventTime" is required and "eventData" is required and "eventType" is required'); //eslint-disable-line
done();
})
.catch(done);
Expand All @@ -98,6 +101,7 @@ describe('## Basic Event APIs', () => {
it('errors when no clientId sent', (done) => {
const event = {
deviceType: 'Browser',
eventApiEnv: 'production',
eventTime: new Date().getTime(),
eventType: 'login',
eventData: {
Expand All @@ -119,6 +123,7 @@ describe('## Basic Event APIs', () => {
it('errors when no deviceType sent', (done) => {
const event = {
clientId: '45426562',
eventApiEnv: 'production',
eventTime: new Date().getTime(),
eventType: 'login',
eventData: {
Expand All @@ -140,6 +145,7 @@ describe('## Basic Event APIs', () => {
it('errors when no eventData sent', (done) => {
const event = {
clientId: '45426562',
eventApiEnv: 'production',
deviceType: 'API',
eventTime: new Date().getTime(),
eventType: 'login'
Expand All @@ -159,6 +165,7 @@ describe('## Basic Event APIs', () => {
it('errors when no eventType sent', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'Android',
eventTime: new Date().getTime(),
eventData: {
Expand All @@ -180,6 +187,7 @@ describe('## Basic Event APIs', () => {
it('errors when no eventTime sent', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'iOS',
eventType: 'login',
eventData: {
Expand All @@ -197,4 +205,49 @@ describe('## Basic Event APIs', () => {
})
.catch(done);
});

it('errors when no eventApiEnv sent', (done) => {
const event = {
clientId: '1234567',
deviceType: 'iOS',
eventTime: new Date().getTime(),
eventType: 'login',
eventData: {
userId: '8675309'
}
}
request(app)
.post('/api/v1/event')
.send(event)
.expect(httpStatus.BAD_REQUEST)
.then((res) => {
expect(res.body).to.exist; //eslint-disable-line
expect(res.body.message).to.equal('"eventApiEnv" is required'); //eslint-disable-line
done();
})
.catch(done);
});

it('errors when eventApiEnv sent is wrong type', (done) => {
const event = {
clientId: '1234567',
deviceType: 'iOS',
eventApiEnv: 'testing',
eventTime: new Date().getTime(),
eventType: 'login',
eventData: {
userId: '8675309'
}
}
request(app)
.post('/api/v1/event')
.send(event)
.expect(httpStatus.BAD_REQUEST)
.then((res) => {
expect(res.body).to.exist; //eslint-disable-line
expect(res.body.message).to.equal('"eventApiEnv" must be one of [production, test]'); //eslint-disable-line
done();
})
.catch(done);
});
});
1 change: 1 addition & 0 deletions tests/server/events/register.event.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('## Register Events', () => {
it('sending valid register event is successful', (done) => {
const event = {
clientId: '1234567',
eventApiEnv: 'production',
deviceType: 'Android',
eventTime: new Date().getTime(),
eventType: 'register',
Expand Down