diff --git a/config/param-validation.js b/config/param-validation.js index c47a3b9..af21c0f 100644 --- a/config/param-validation.js +++ b/config/param-validation.js @@ -29,7 +29,13 @@ export default { 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(), + errorType: Joi.string().required().valid([ + 'auth', + 'other' + ]), + errorData: Joi.object().required().keys({ + message: Joi.string().required() + }) } }, register: Joi.object().required().keys(), diff --git a/server/helpers/InfluxErrorInterface.js b/server/helpers/InfluxErrorInterface.js index f0b8ed3..6a5522a 100644 --- a/server/helpers/InfluxErrorInterface.js +++ b/server/helpers/InfluxErrorInterface.js @@ -1,5 +1,25 @@ +import _ from 'lodash'; import { FieldType, escape } from 'influx'; import InfluxInterface from './InfluxInterface'; +import paramValidation from '../../config/param-validation'; + +const fieldList = Object.keys(paramValidation.error.body); +fieldList.splice(fieldList.indexOf('errorData'), 1); + +Object.keys(paramValidation.error.body.errorData).forEach((key) => { + const inner = paramValidation.error.body.errorData[key]; + + if (inner && inner.children) { + inner.children.forEach((child) => { + fieldList.push(child.key); + }); + } +}); + +const fields = {}; +Array.from(new Set(fieldList)).forEach((field) => { + fields[field] = FieldType.STRING; +}); const databaseName = 'errorsDb'; @@ -8,18 +28,18 @@ export default class InfluxErrorInterface extends InfluxInterface { super(databaseName, [ { measurement: 'errors', - fields: { - errorData: FieldType.STRING - }, + fields, tags: [] } ]); } write(topic, errorData, callback) { + _.merge(errorData, errorData.errorData); + delete errorData.errorData; // eslint-disable-line no-param-reassign this.influx.writeMeasurement('errors', [ { - fields: { errorData: JSON.stringify(errorData) } + fields: { ...errorData } } ]) .then(() => { diff --git a/tests/server/errors/error.test.js b/tests/server/errors/error.test.js index cc7e846..92d9f3d 100644 --- a/tests/server/errors/error.test.js +++ b/tests/server/errors/error.test.js @@ -12,8 +12,9 @@ describe('## Basic Error APIs', () => { eventApiEnv: 'production', deviceType: 'API', errorTime: new Date().getTime(), + errorType: 'auth', errorData: { - errorType: 'Unauthorized' + message: 'Unauthorized user' } }; request(app) @@ -33,7 +34,10 @@ describe('## Basic Error APIs', () => { deviceType: 'Browser', eventApiEnv: 'production', errorTime: new Date().getTime(), - errorData: {} + errorType: 'auth', + errorData: { + message: 'Unauthorized user' + } }; request(app) .post('/api/v1/error') @@ -52,8 +56,9 @@ describe('## Basic Error APIs', () => { clientId: '1234567', eventApiEnv: 'production', errorTime: new Date().getTime(), + errorType: 'other', errorData: { - userId: '3462562' + message: 'Error sending mailchimp email' } }; request(app) @@ -73,8 +78,9 @@ describe('## Basic Error APIs', () => { clientId: '1234567', eventApiEnv: 'production', deviceType: 'API', + errorType: 'other', errorData: { - userId: '3462562' + message: 'Username not found: Andrew' } }; request(app) @@ -94,7 +100,8 @@ describe('## Basic Error APIs', () => { clientId: '1234567', eventApiEnv: 'production', deviceType: 'API', - errorTime: new Date().getTime() + errorTime: new Date().getTime(), + errorType: 'other', }; request(app) .post('/api/v1/error') @@ -111,11 +118,12 @@ describe('## Basic Error APIs', () => { it('errors when no eventApiEnv is sent', (done) => { const error = { clientId: '1234567', + deviceType: 'API', + errorTime: new Date().getTime(), + errorType: 'auth', errorData: { - userId: '3462562' + message: 'User not allowed to perform function' }, - deviceType: 'API', - errorTime: new Date().getTime() }; request(app) .post('/api/v1/error') @@ -133,8 +141,9 @@ describe('## Basic Error APIs', () => { const error = { clientId: '1234567', eventApiEnv: 'prod', + errorType: 'other', errorData: { - userId: '3462562' + message: 'Username not found' }, deviceType: 'API', errorTime: new Date().getTime() @@ -150,4 +159,48 @@ describe('## Basic Error APIs', () => { }) .catch(done); }); + + it('errors when errorType sent is wrong type', (done) => { + const error = { + clientId: '1234567', + eventApiEnv: 'production', + errorType: '404', + errorData: { + message: 'Web page not found' + }, + 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('"errorType" must be one of [auth, other]'); //eslint-disable-line + done(); + }) + .catch(done); + }); + + it('errors when errorData sent has no message', (done) => { + const error = { + clientId: '1234567', + eventApiEnv: 'production', + errorType: 'other', + errorData: {}, + 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('"message" is required'); //eslint-disable-line + done(); + }) + .catch(done); + }); });