diff --git a/spec/LoggerController.spec.js b/spec/LoggerController.spec.js index f23004ab..3475495e 100644 --- a/spec/LoggerController.spec.js +++ b/spec/LoggerController.spec.js @@ -2,53 +2,85 @@ var LoggerController = require('../src/Controllers/LoggerController').LoggerCont var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; describe('LoggerController', () => { - it('can check valid master key of request', (done) => { + it('can check process a query witout throwing', (done) => { // Make mock request - var request = { - auth: { - isMaster: true - }, - query: {} + var query = {}; + + var loggerController = new LoggerController(new FileLoggerAdapter()); + + expect(() => { + loggerController.getLogs(query).then(function(res) { + expect(res.length).toBe(0); + done(); + }) + }).not.toThrow(); + }); + + it('properly validates dateTimes', (done) => { + expect(LoggerController.validDateTime()).toBe(null); + expect(LoggerController.validDateTime("String")).toBe(null); + expect(LoggerController.validDateTime(123456).getTime()).toBe(123456); + expect(LoggerController.validDateTime("2016-01-01Z00:00:00").getTime()).toBe(1451606400000); + done(); + }); + + it('can set the proper default values', (done) => { + // Make mock request + var result = LoggerController.parseOptions(); + expect(result.size).toEqual(10); + expect(result.order).toEqual('desc'); + expect(result.level).toEqual('info'); + + done(); + }); + + it('can process a query witout throwing', (done) => { + // Make mock request + var query = { + from: "2016-01-01Z00:00:00", + until: "2016-01-01Z00:00:00", + size: 5, + order: 'asc', + level: 'error' + }; + + var result = LoggerController.parseOptions(query); + + expect(result.from.getTime()).toEqual(1451606400000); + expect(result.until.getTime()).toEqual(1451606400000); + expect(result.size).toEqual(5); + expect(result.order).toEqual('asc'); + expect(result.level).toEqual('error'); + + done(); + }); + + it('can check process a query witout throwing', (done) => { + // Make mock request + var query = { + from: "2015-01-01", + until: "2016-01-01", + size: 5, + order: 'desc', + level: 'error' }; var loggerController = new LoggerController(new FileLoggerAdapter()); expect(() => { - loggerController.handleGET(request); + loggerController.getLogs(query).then(function(res) { + expect(res.length).toBe(0); + done(); + }) }).not.toThrow(); - done(); }); - - it('can check invalid construction of controller', (done) => { - // Make mock request - var request = { - auth: { - isMaster: true - }, - query: {} - }; + + it('should throw without an adapter', (done) => { var loggerController = new LoggerController(); expect(() => { - loggerController.handleGET(request); - }).toThrow(); - done(); - }); - - it('can check invalid master key of request', (done) => { - // Make mock request - var request = { - auth: { - isMaster: false - }, - query: {} - }; - - var loggerController = new LoggerController(new FileLoggerAdapter()); - - expect(() => { - loggerController.handleGET(request); + loggerController.getLogs(); }).toThrow(); done(); }); diff --git a/spec/LogsRouter.spec.js b/spec/LogsRouter.spec.js new file mode 100644 index 00000000..a8ef8b25 --- /dev/null +++ b/spec/LogsRouter.spec.js @@ -0,0 +1,67 @@ +var LogsRouter = require('../src/Routers/LogsRouter').LogsRouter; +var LoggerController = require('../src/Controllers/LoggerController').LoggerController; +var FileLoggerAdapter = require('../src/Adapters/Logger/FileLoggerAdapter').FileLoggerAdapter; + +const loggerController = new LoggerController(new FileLoggerAdapter()); + +describe('LogsRouter', () => { + it('can check valid master key of request', (done) => { + // Make mock request + var request = { + auth: { + isMaster: true + }, + query: {}, + config: { + loggerController: loggerController + } + }; + + var router = new LogsRouter(); + + expect(() => { + router.handleGET(request); + }).not.toThrow(); + done(); + }); + + it('can check invalid construction of controller', (done) => { + // Make mock request + var request = { + auth: { + isMaster: true + }, + query: {}, + config: { + loggerController: undefined // missing controller + } + }; + + var router = new LogsRouter(); + + expect(() => { + router.handleGET(request); + }).toThrow(); + done(); + }); + + it('can check invalid master key of request', (done) => { + // Make mock request + var request = { + auth: { + isMaster: false + }, + query: {}, + config: { + loggerController: loggerController + } + }; + + var router = new LogsRouter(); + + expect(() => { + router.handleGET(request); + }).toThrow(); + done(); + }); +}); diff --git a/src/Config.js b/src/Config.js index ed3d5aa7..4859c09c 100644 --- a/src/Config.js +++ b/src/Config.js @@ -27,6 +27,7 @@ export class Config { this.database = DatabaseAdapter.getDatabaseConnection(applicationId); this.filesController = cacheInfo.filesController; this.pushController = cacheInfo.pushController; + this.loggerController = cacheInfo.loggerController; this.oauth = cacheInfo.oauth; this.mount = mount; diff --git a/src/Controllers/LoggerController.js b/src/Controllers/LoggerController.js index d0b8bb28..fe89446c 100644 --- a/src/Controllers/LoggerController.js +++ b/src/Controllers/LoggerController.js @@ -1,35 +1,55 @@ import { Parse } from 'parse/node'; import PromiseRouter from '../PromiseRouter'; -import rest from '../rest'; const Promise = Parse.Promise; -const INFO = 'info'; -const ERROR = 'error'; const MILLISECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; -// only allow request with master key -let enforceSecurity = (auth) => { - if (!auth || !auth.isMaster) { - throw new Parse.Error( - Parse.Error.OPERATION_FORBIDDEN, - 'Clients aren\'t allowed to perform the ' + - 'get' + ' operation on logs.' - ); - } +export const LogLevel = { + INFO: 'info', + ERROR: 'error' } -// check that date input is valid -let isValidDateTime = (date) => { - if (!date || isNaN(Number(date))) { - return false; - } +export const LogOrder = { + DESCENDING: 'desc', + ASCENDING: 'asc' } export class LoggerController { - - constructor(loggerAdapter) { + + constructor(loggerAdapter, loggerOptions) { this._loggerAdapter = loggerAdapter; } + + // check that date input is valid + static validDateTime(date) { + if (!date) { + return null; + } + date = new Date(date); + + if (!isNaN(date.getTime())) { + return date; + } + + return null; + } + + static parseOptions(options = {}) { + let from = LoggerController.validDateTime(options.from) || + new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY); + let until = LoggerController.validDateTime(options.until) || new Date(); + let size = Number(options.size) || 10; + let order = options.order || LogOrder.DESCENDING; + let level = options.level || LogLevel.INFO; + + return { + from, + until, + size, + order, + level, + }; + } // Returns a promise for a {response} object. // query params: @@ -38,41 +58,21 @@ export class LoggerController { // until (optional) End time for the search. Defaults to current time. // order (optional) Direction of results returned, either “asc” or “desc”. Defaults to “desc”. // size (optional) Number of rows returned by search. Defaults to 10 - handleGET(req) { + getLogs(options= {}) { if (!this._loggerAdapter) { throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, 'Logger adapter is not availabe'); } let promise = new Parse.Promise(); - let from = (isValidDateTime(req.query.from) && new Date(req.query.from)) || - new Date(Date.now() - 7 * MILLISECONDS_IN_A_DAY); - let until = (isValidDateTime(req.query.until) && new Date(req.query.until)) || new Date(); - let size = Number(req.query.size) || 10; - let order = req.query.order || 'desc'; - let level = req.query.level || INFO; - enforceSecurity(req.auth); - this._loggerAdapter.query({ - from, - until, - size, - order, - level, - }, (result) => { - promise.resolve({ - response: result - }); + + options = LoggerController.parseOptions(options); + + this._loggerAdapter.query(options, (result) => { + promise.resolve(result); }); return promise; } - - getExpressRouter() { - let router = new PromiseRouter(); - router.route('GET','/logs', (req) => { - return this.handleGET(req); - }); - return router; - } } export default LoggerController; diff --git a/src/Routers/LogsRouter.js b/src/Routers/LogsRouter.js new file mode 100644 index 00000000..abd57944 --- /dev/null +++ b/src/Routers/LogsRouter.js @@ -0,0 +1,60 @@ +import { Parse } from 'parse/node'; +import PromiseRouter from '../PromiseRouter'; + +// only allow request with master key +let enforceSecurity = (auth) => { + if (!auth || !auth.isMaster) { + throw new Parse.Error( + Parse.Error.OPERATION_FORBIDDEN, + 'Clients aren\'t allowed to perform the ' + + 'get' + ' operation on logs.' + ); + } +} + +export class LogsRouter extends PromiseRouter { + + mountRoutes() { + this.route('GET','/logs', (req) => { + return this.handleGET(req); + }); + } + + // Returns a promise for a {response} object. + // query params: + // level (optional) Level of logging you want to query for (info || error) + // from (optional) Start time for the search. Defaults to 1 week ago. + // until (optional) End time for the search. Defaults to current time. + // order (optional) Direction of results returned, either “asc” or “desc”. Defaults to “desc”. + // size (optional) Number of rows returned by search. Defaults to 10 + handleGET(req) { + if (!req.config || !req.config.loggerController) { + throw new Parse.Error(Parse.Error.PUSH_MISCONFIGURED, + 'Logger adapter is not availabe'); + } + + let promise = new Parse.Promise(); + let from = req.query.from; + let until = req.query.until; + let size = req.query.size; + let order = req.query.order + let level = req.query.level; + enforceSecurity(req.auth); + + const options = { + from, + until, + size, + order, + level, + } + + return req.config.loggerController.getLogs(options).then((result) => { + return Promise.resolve({ + response: result + }); + }) + } +} + +export default LogsRouter; diff --git a/src/index.js b/src/index.js index dd671353..6ff5bab7 100644 --- a/src/index.js +++ b/src/index.js @@ -30,6 +30,7 @@ import { SchemasRouter } from './Routers/SchemasRouter'; import { IAPValidationRouter } from './Routers/IAPValidationRouter'; import { PushRouter } from './Routers/PushRouter'; import { FilesRouter } from './Routers/FilesRouter'; +import { LogsRouter } from './Routers/LogsRouter'; import { FileLoggerAdapter } from './Adapters/Logger/FileLoggerAdapter'; import { LoggerController } from './Controllers/LoggerController'; @@ -168,7 +169,7 @@ function ParseServer({ new FunctionsRouter(), new SchemasRouter(), new PushRouter(), - new LoggerController(loggerAdapter).getExpressRouter(), + new LogsRouter(), new IAPValidationRouter() ];