refactors LoggerController with LogsRouter

This commit is contained in:
Florent Vilmart
2016-02-20 13:02:22 -05:00
parent 305879a251
commit fbb5e448e6
6 changed files with 242 additions and 81 deletions

View File

@@ -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();
});

67
spec/LogsRouter.spec.js Normal file
View File

@@ -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();
});
});

View File

@@ -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;

View File

@@ -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;

60
src/Routers/LogsRouter.js Normal file
View File

@@ -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;

View File

@@ -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()
];