Skip to content

Commit eb03405

Browse files
committed
Merge pull request #501 from flovilmart/app-agnostic-controllers
Refactors PushController and FilesController to support multiple apps
2 parents 8aaf8f2 + 3c4d515 commit eb03405

17 files changed

+310
-277
lines changed

src/Config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,9 @@ function Config(applicationId, mount) {
2424

2525
this.database = DatabaseAdapter.getDatabaseConnection(applicationId);
2626
this.filesController = cacheInfo.filesController;
27-
27+
this.pushController = cacheInfo.pushController;
2828
this.oauth = cacheInfo.oauth;
29+
2930
this.mount = mount;
3031
}
3132

src/Controllers/FilesController.js

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ export class FilesController {
1313
this._filesAdapter = filesAdapter;
1414
}
1515

16+
static getHandler() {
17+
return (req, res) => {
18+
let config = new Config(req.params.appId);
19+
return config.filesController.getHandler()(req, res);
20+
}
21+
}
22+
1623
getHandler() {
1724
return (req, res) => {
1825
let config = new Config(req.params.appId);
@@ -30,6 +37,13 @@ export class FilesController {
3037
};
3138
}
3239

40+
static createHandler() {
41+
return (req, res, next) => {
42+
let config = req.config;
43+
return config.filesController.createHandler()(req, res, next);
44+
}
45+
}
46+
3347
createHandler() {
3448
return (req, res, next) => {
3549
if (!req.body || !req.body.length) {
@@ -50,6 +64,7 @@ export class FilesController {
5064
return;
5165
}
5266

67+
const filesController = req.config.filesController;
5368
// If a content-type is included, we'll add an extension so we can
5469
// return the same content-type.
5570
let extension = '';
@@ -60,9 +75,9 @@ export class FilesController {
6075
}
6176

6277
let filename = randomHexString(32) + '_' + req.params.filename + extension;
63-
this._filesAdapter.createFile(req.config, filename, req.body).then(() => {
78+
filesController._filesAdapter.createFile(req.config, filename, req.body).then(() => {
6479
res.status(201);
65-
var location = this._filesAdapter.getFileLocation(req.config, filename);
80+
var location = filesController._filesAdapter.getFileLocation(req.config, filename);
6681
res.set('Location', location);
6782
res.json({ url: location, name: filename });
6883
}).catch((error) => {
@@ -72,6 +87,13 @@ export class FilesController {
7287
};
7388
}
7489

90+
static deleteHandler() {
91+
return (req, res, next) => {
92+
let config = req.config;
93+
return config.filesController.deleteHandler()(req, res, next);
94+
}
95+
}
96+
7597
deleteHandler() {
7698
return (req, res, next) => {
7799
this._filesAdapter.deleteFile(req.config, req.params.filename).then(() => {
@@ -114,9 +136,9 @@ export class FilesController {
114136
}
115137
}
116138

117-
getExpressRouter() {
139+
static getExpressRouter() {
118140
let router = express.Router();
119-
router.get('/files/:appId/:filename', this.getHandler());
141+
router.get('/files/:appId/:filename', FilesController.getHandler());
120142

121143
router.post('/files', function(req, res, next) {
122144
next(new Parse.Error(Parse.Error.INVALID_FILE_NAME,
@@ -127,14 +149,14 @@ export class FilesController {
127149
Middlewares.allowCrossDomain,
128150
BodyParser.raw({type: '*/*', limit: '20mb'}),
129151
Middlewares.handleParseHeaders,
130-
this.createHandler()
152+
FilesController.createHandler()
131153
);
132154

133155
router.delete('/files/:filename',
134156
Middlewares.allowCrossDomain,
135157
Middlewares.handleParseHeaders,
136158
Middlewares.enforceMasterKeyAccess,
137-
this.deleteHandler()
159+
FilesController.deleteHandler()
138160
);
139161

140162
return router;

src/Controllers/PushController.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ export class PushController {
3131
}
3232
});
3333
}
34-
35-
getExpressRouter() {
34+
35+
static getExpressRouter() {
3636
var router = new PromiseRouter();
3737
router.route('POST','/push', (req) => {
38-
return this.handlePOST(req);
38+
return req.config.pushController.handlePOST(req);
3939
});
4040
return router;
4141
}

src/PromiseRouter.js

Lines changed: 89 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// themselves use our routing information, without disturbing express
66
// components that external developers may be modifying.
77

8-
function PromiseRouter() {
8+
export default class PromiseRouter {
99
// Each entry should be an object with:
1010
// path: the path to route, in express format
1111
// method: the HTTP method that this route handles.
@@ -15,73 +15,102 @@ function PromiseRouter() {
1515
// status: optional. the http status code. defaults to 200
1616
// response: a json object with the content of the response
1717
// location: optional. a location header
18-
this.routes = [];
19-
}
20-
21-
// Global flag. Set this to true to log every request and response.
22-
PromiseRouter.verbose = process.env.VERBOSE || false;
23-
24-
// Merge the routes into this one
25-
PromiseRouter.prototype.merge = function(router) {
26-
for (var route of router.routes) {
27-
this.routes.push(route);
28-
}
29-
};
30-
31-
PromiseRouter.prototype.route = function(method, path, handler) {
32-
switch(method) {
33-
case 'POST':
34-
case 'GET':
35-
case 'PUT':
36-
case 'DELETE':
37-
break;
38-
default:
39-
throw 'cannot route method: ' + method;
18+
constructor() {
19+
this.routes = [];
20+
this.mountRoutes();
4021
}
22+
23+
// Leave the opportunity to
24+
// subclasses to mount their routes by overriding
25+
mountRoutes() {}
26+
27+
// Merge the routes into this one
28+
merge(router) {
29+
for (var route of router.routes) {
30+
this.routes.push(route);
31+
}
32+
};
33+
34+
route(method, path, handler) {
35+
switch(method) {
36+
case 'POST':
37+
case 'GET':
38+
case 'PUT':
39+
case 'DELETE':
40+
break;
41+
default:
42+
throw 'cannot route method: ' + method;
43+
}
4144

42-
this.routes.push({
43-
path: path,
44-
method: method,
45-
handler: handler
46-
});
47-
};
45+
this.routes.push({
46+
path: path,
47+
method: method,
48+
handler: handler
49+
});
50+
};
51+
52+
// Returns an object with:
53+
// handler: the handler that should deal with this request
54+
// params: any :-params that got parsed from the path
55+
// Returns undefined if there is no match.
56+
match(method, path) {
57+
for (var route of this.routes) {
58+
if (route.method != method) {
59+
continue;
60+
}
4861

49-
// Returns an object with:
50-
// handler: the handler that should deal with this request
51-
// params: any :-params that got parsed from the path
52-
// Returns undefined if there is no match.
53-
PromiseRouter.prototype.match = function(method, path) {
54-
for (var route of this.routes) {
55-
if (route.method != method) {
56-
continue;
57-
}
62+
// NOTE: we can only route the specific wildcards :className and
63+
// :objectId, and in that order.
64+
// This is pretty hacky but I don't want to rebuild the entire
65+
// express route matcher. Maybe there's a way to reuse its logic.
66+
var pattern = '^' + route.path + '$';
5867

59-
// NOTE: we can only route the specific wildcards :className and
60-
// :objectId, and in that order.
61-
// This is pretty hacky but I don't want to rebuild the entire
62-
// express route matcher. Maybe there's a way to reuse its logic.
63-
var pattern = '^' + route.path + '$';
68+
pattern = pattern.replace(':className',
69+
'(_?[A-Za-z][A-Za-z_0-9]*)');
70+
pattern = pattern.replace(':objectId',
71+
'([A-Za-z0-9]+)');
72+
var re = new RegExp(pattern);
73+
var m = path.match(re);
74+
if (!m) {
75+
continue;
76+
}
77+
var params = {};
78+
if (m[1]) {
79+
params.className = m[1];
80+
}
81+
if (m[2]) {
82+
params.objectId = m[2];
83+
}
6484

65-
pattern = pattern.replace(':className',
66-
'(_?[A-Za-z][A-Za-z_0-9]*)');
67-
pattern = pattern.replace(':objectId',
68-
'([A-Za-z0-9]+)');
69-
var re = new RegExp(pattern);
70-
var m = path.match(re);
71-
if (!m) {
72-
continue;
85+
return {params: params, handler: route.handler};
7386
}
74-
var params = {};
75-
if (m[1]) {
76-
params.className = m[1];
77-
}
78-
if (m[2]) {
79-
params.objectId = m[2];
87+
};
88+
89+
// Mount the routes on this router onto an express app (or express router)
90+
mountOnto(expressApp) {
91+
for (var route of this.routes) {
92+
switch(route.method) {
93+
case 'POST':
94+
expressApp.post(route.path, makeExpressHandler(route.handler));
95+
break;
96+
case 'GET':
97+
expressApp.get(route.path, makeExpressHandler(route.handler));
98+
break;
99+
case 'PUT':
100+
expressApp.put(route.path, makeExpressHandler(route.handler));
101+
break;
102+
case 'DELETE':
103+
expressApp.delete(route.path, makeExpressHandler(route.handler));
104+
break;
105+
default:
106+
throw 'unexpected code branch';
107+
}
80108
}
109+
};
110+
}
81111

82-
return {params: params, handler: route.handler};
83-
}
84-
};
112+
// Global flag. Set this to true to log every request and response.
113+
PromiseRouter.verbose = process.env.VERBOSE || false;
85114

86115
// A helper function to make an express handler out of a a promise
87116
// handler.
@@ -122,27 +151,3 @@ function makeExpressHandler(promiseHandler) {
122151
}
123152
}
124153
}
125-
126-
// Mount the routes on this router onto an express app (or express router)
127-
PromiseRouter.prototype.mountOnto = function(expressApp) {
128-
for (var route of this.routes) {
129-
switch(route.method) {
130-
case 'POST':
131-
expressApp.post(route.path, makeExpressHandler(route.handler));
132-
break;
133-
case 'GET':
134-
expressApp.get(route.path, makeExpressHandler(route.handler));
135-
break;
136-
case 'PUT':
137-
expressApp.put(route.path, makeExpressHandler(route.handler));
138-
break;
139-
case 'DELETE':
140-
expressApp.delete(route.path, makeExpressHandler(route.handler));
141-
break;
142-
default:
143-
throw 'unexpected code branch';
144-
}
145-
}
146-
};
147-
148-
module.exports = PromiseRouter;

src/Routers/AnalyticsRouter.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// AnalyticsRouter.js
2+
3+
var Parse = require('parse/node').Parse;
4+
5+
import PromiseRouter from '../PromiseRouter';
6+
7+
// Returns a promise that resolves to an empty object response
8+
function ignoreAndSucceed(req) {
9+
return Promise.resolve({
10+
response: {}
11+
});
12+
}
13+
14+
15+
export class AnalyticsRouter extends PromiseRouter {
16+
mountRoutes() {
17+
this.route('POST','/events/AppOpened', ignoreAndSucceed);
18+
this.route('POST','/events/:eventName', ignoreAndSucceed);
19+
}
20+
}

src/Routers/ClassesRouter.js

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ import rest from '../rest';
44

55
import url from 'url';
66

7-
export class ClassesRouter {
8-
// Returns a promise that resolves to a {response} object.
7+
export class ClassesRouter extends PromiseRouter {
8+
99
handleFind(req) {
1010
let body = Object.assign(req.body, req.query);
1111
let options = {};
@@ -97,15 +97,13 @@ export class ClassesRouter {
9797
return {response: {}};
9898
});
9999
}
100-
101-
getExpressRouter() {
102-
var router = new PromiseRouter();
103-
router.route('GET', '/classes/:className', (req) => { return this.handleFind(req); });
104-
router.route('GET', '/classes/:className/:objectId', (req) => { return this.handleGet(req); });
105-
router.route('POST', '/classes/:className', (req) => { return this.handleCreate(req); });
106-
router.route('PUT', '/classes/:className/:objectId', (req) => { return this.handleUpdate(req); });
107-
router.route('DELETE', '/classes/:className/:objectId', (req) => { return this.handleDelete(req); });
108-
return router;
100+
101+
mountRoutes() {
102+
this.route('GET', '/classes/:className', (req) => { return this.handleFind(req); });
103+
this.route('GET', '/classes/:className/:objectId', (req) => { return this.handleGet(req); });
104+
this.route('POST', '/classes/:className', (req) => { return this.handleCreate(req); });
105+
this.route('PUT', '/classes/:className/:objectId', (req) => { return this.handleUpdate(req); });
106+
this.route('DELETE', '/classes/:className/:objectId', (req) => { return this.handleDelete(req); });
109107
}
110108
}
111109

0 commit comments

Comments
 (0)