Skip to content

Commit f671b62

Browse files
committed
Merge pull request #1 from startappdev/develop
First Implementation of Express Context Service
2 parents cf24d23 + e22c7b9 commit f671b62

9 files changed

Lines changed: 327 additions & 0 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

bin/express-context

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#!/usr/bin/env node
2+
3+
"use strict";
4+
var path = require('path');
5+
var fs = require('fs');
6+
var lib = path.join(path.dirname(fs.realpathSync(__filename)), '../lib');
7+
8+
require(lib+'/index.js');

lib/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
'use strict';
2+
3+
var contextMiddleware = require('./middleware/context.middleware'),
4+
contextService = require('./service/context.service');
5+
6+
/**
7+
* Express Middleware to handle context
8+
* @param req - {Request}
9+
* @param res - {Response}
10+
* @param next - {Function} next middleware
11+
*/
12+
module.exports.middleware = contextMiddleware.middleware;
13+
14+
/**
15+
* API to add a context manipulator
16+
* @param manipulatorFn - {Function} in the form of function(contextManipulator, req, next)
17+
* @returns {Number} id - unique to get the data with
18+
*/
19+
module.exports.addContextManipulator = contextService.addContextManipulator;
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
'use strict';
2+
3+
var contextService = require('../service/context.service');
4+
5+
/**
6+
* Express Middleware to handle context
7+
* @param req - {Request}
8+
* @param res - {Response}
9+
* @param next - {Function} next middleware
10+
*/
11+
module.exports.middleware = function(req,res,next){
12+
// Get a context after manipulation
13+
contextService.createContext(req, function(err, context){
14+
// Print error and continue
15+
if (err){
16+
console.log(err);
17+
}
18+
19+
// Piggy bag the context to the request
20+
req.getContext = function(){
21+
return context;
22+
};
23+
24+
next();
25+
})
26+
};

lib/model/Context.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
'use strict';
2+
3+
/**
4+
* Context object
5+
* @constructor
6+
*/
7+
function Context(){
8+
this._data = [];
9+
this._global = {};
10+
}
11+
12+
/**
13+
* API to get a specific data from a context
14+
* @param id - {Number} id returned by addData method
15+
* @returns - {Object} the data registered for this module
16+
*/
17+
Context.prototype.getData = function(id){
18+
return this._data[id];
19+
};
20+
21+
/**
22+
* API to get the global namespace object for the context
23+
* @returns {Object} global namespace
24+
*/
25+
Context.prototype.getGlobal = function(){
26+
return this._global;
27+
};
28+
29+
module.exports = Context;

lib/model/ContextManipulator.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
'use strict';
2+
3+
/**
4+
* Helper object to manipulate Context object
5+
* @param context - {Context} - to manipulate
6+
* @param req - {Request} - the request that been made
7+
* @constructor
8+
*/
9+
function ContextManipulator(context, req){
10+
this._id;
11+
this._context = context;
12+
this._request = req;
13+
}
14+
15+
/**
16+
* Sed Id of context namespace
17+
* @param id - {Number}
18+
* @private
19+
*/
20+
ContextManipulator.prototype._setId = function(id){
21+
this._id = id;
22+
};
23+
24+
/**
25+
* Adds a data to the Context object
26+
* @param data - {Object} to add to the private module namespace
27+
*/
28+
ContextManipulator.prototype.addData = function(data){
29+
this._context._data[this._id] = data;
30+
};
31+
32+
/**
33+
* Adds a data object at the specified key to the global namespace
34+
* @param key - {String} key to be saved at in the global namespace
35+
* @param data - {Object} data to save for the module
36+
*/
37+
ContextManipulator.prototype.addGlobal = function(key, data){
38+
this._context._global[key] = data;
39+
};
40+
41+
/**
42+
* Request Getter
43+
* @returns {Request}
44+
*/
45+
ContextManipulator.prototype.getRequest = function(){
46+
return this._request;
47+
};
48+
49+
50+
module.exports = ContextManipulator;

lib/service/context.service.js

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
'use strict';
2+
3+
var Q = require('q'),
4+
Context = require('../model/Context'),
5+
ContextManipulator = require('../model/ContextManipulator');
6+
7+
var contextManipulators = [];
8+
9+
/**
10+
* API to add a context manipulator
11+
* @param manipulatorFn - {Function} in the form of function(contextManipulator, req)
12+
* @returns {Number} id - unique to get the data with
13+
*/
14+
exports.addContextManipulator = function(manipulatorFn){
15+
if (typeof manipulatorFn === 'function') {
16+
contextManipulators.push(manipulatorFn);
17+
18+
return contextManipulators.length - 1;
19+
} else {
20+
return -1;
21+
}
22+
};
23+
24+
/**
25+
* Create a context object
26+
* @param req - {Request}
27+
* @param callback - {Function} in the form of callback(err, context)
28+
* err - Error
29+
* context - {Context}
30+
*/
31+
exports.createContext = function(req, callback){
32+
var context = new Context();
33+
var contextManipulator = new ContextManipulator(context, req);
34+
35+
var promises = [];
36+
37+
// For Each one in the context manipulation list
38+
for (var i in contextManipulators){
39+
40+
// create a new promise
41+
var promise = Q.defer();
42+
promises.push(promise);
43+
44+
// Set his internal ID
45+
contextManipulator._setId(i);
46+
47+
// Run the manipulator
48+
try {
49+
contextManipulators[i](contextManipulator, function () {
50+
// Manipulation done
51+
promise.resolve('');
52+
});
53+
} catch (e) {
54+
// Error in manipulation
55+
promise.reject(e);
56+
console.log('ERR: ');
57+
console.error(e);
58+
}
59+
}
60+
61+
// when all manipulation are done
62+
Q.allSettled(promises).then(function(results){
63+
for (var i in results){
64+
var isError = false;
65+
var error = '';
66+
67+
// Add errors if exists
68+
if (results[i].state !== "fulfilled") {
69+
isError = true;
70+
error += '[ ' + results[i].reason + '] ';
71+
}
72+
73+
// continue with a context
74+
callback(isError ? new Error(error) : null, context);
75+
}
76+
});
77+
};

package.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"author": "Yarin Podoler",
3+
"name": "express-context",
4+
"description": "",
5+
"version": "0.1.0",
6+
"repository": {
7+
"type": "git",
8+
"url": "git@github.com:startappdev/express-context.git"
9+
},
10+
"main": "./lib/index.js",
11+
"keywords": [],
12+
"bin": {
13+
"express-context": "./bin/express-context"
14+
},
15+
"dependencies": {
16+
"q": "^1.0.1"
17+
},
18+
"engines": {
19+
"node": "*"
20+
},
21+
"scripts": {
22+
"test": "jasmine-node --verbose test/"
23+
},
24+
"devDependencies": {
25+
"jasmine-node": "^1.14.5"
26+
}
27+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict';
2+
3+
describe('Unit Test Context MiddleWare', function(){
4+
var expressContext = require('../../lib/index');
5+
6+
var uniqueId1, uniqueId2;
7+
8+
it ('Should verify all API\'s', function(){
9+
expect(typeof expressContext.sessionContext).toEqual('function');
10+
expect(typeof expressContext.addContextManipulator).toEqual('function');
11+
});
12+
13+
it ('Should successfully add context manipulator for namespaces', function(){
14+
uniqueId1 = expressContext.addContextManipulator(function(contextManipulator, next){
15+
contextManipulator.addData({
16+
UNIQUE1 : 'UNIQUE1'
17+
});
18+
19+
next();
20+
});
21+
22+
uniqueId2 = expressContext.addContextManipulator(function(contextManipulator, next){
23+
contextManipulator.addData({
24+
UNIQUE2 : 'UNIQUE2'
25+
});
26+
27+
next();
28+
});
29+
30+
expressContext.addContextManipulator(function(contextManipulator, next){
31+
contextManipulator.addGlobal('test', {
32+
GLOBAL : 'GLOBAL'
33+
});
34+
35+
next();
36+
});
37+
38+
expect(uniqueId1).toEqual(jasmine.any(Number));
39+
expect(uniqueId1).toBeGreaterThan(-1);
40+
expect(uniqueId2).toEqual(jasmine.any(Number));
41+
expect(uniqueId2).toBeGreaterThan(-1);
42+
});
43+
44+
it ('Should successfully get the context on the request', function(done){
45+
var req = {};
46+
47+
expressContext.sessionContext(req, {}, function(){
48+
expect(typeof req.getContext).toEqual('function');
49+
50+
done();
51+
});
52+
});
53+
54+
it ('Should successfully get the global data from the context', function(done){
55+
var req = {};
56+
57+
expressContext.sessionContext(req, {}, function(){
58+
expect(req.getContext().getData(uniqueId1)).toEqual({
59+
UNIQUE1 : 'UNIQUE1'
60+
});
61+
62+
expect(req.getContext().getData(uniqueId2)).toEqual({
63+
UNIQUE2 : 'UNIQUE2'
64+
});
65+
66+
expect(req.getContext().getGlobal().test).toEqual({
67+
GLOBAL : 'GLOBAL'
68+
});
69+
70+
done();
71+
});
72+
});
73+
74+
it ('should successfully get Request from the ContextManipulator', function(done){
75+
var req = {
76+
TEST : 'TEST'
77+
};
78+
79+
expressContext.addContextManipulator(function(contextManipulator, next){
80+
expect(typeof contextManipulator.getRequest).toEqual('function');
81+
expect(contextManipulator.getRequest().TEST).toEqual('TEST');
82+
83+
next();
84+
});
85+
86+
expressContext.sessionContext(req, {}, function(){
87+
done();
88+
});
89+
});
90+
});

0 commit comments

Comments
 (0)