-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #337 from strongloop/feature/context-propagation
Add context propagation middleware
- Loading branch information
Showing
5 changed files
with
261 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
var loopback = require('../../'); | ||
var app = loopback(); | ||
|
||
// Create a LoopBack context for all requests | ||
app.use(loopback.context()); | ||
|
||
// Store a request property in the context | ||
app.use(function saveHostToContext(req, res, next) { | ||
var ns = loopback.getCurrentContext(); | ||
ns.set('host', req.host); | ||
next(); | ||
}); | ||
|
||
app.use(loopback.rest()); | ||
|
||
var Color = loopback.createModel('color', { 'name': String }); | ||
Color.beforeRemote('**', function (ctx, unused, next) { | ||
// Inside LoopBack code, you can read the property from the context | ||
var ns = loopback.getCurrentContext(); | ||
console.log('Request to host', ns && ns.get('host')); | ||
next(); | ||
}); | ||
|
||
app.dataSource('db', { connector: 'memory' }); | ||
app.model(Color, { dataSource: 'db' }); | ||
|
||
app.listen(3000, function() { | ||
console.log('A list of colors is available at http://localhost:3000/colors'); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
var loopback = require('../loopback'); | ||
var juggler = require('loopback-datasource-juggler'); | ||
var remoting = require('strong-remoting'); | ||
var cls = require('continuation-local-storage'); | ||
|
||
module.exports = context; | ||
|
||
var name = 'loopback'; | ||
|
||
function createContext(scope) { | ||
// Make the namespace globally visible via the process.context property | ||
process.context = process.context || {}; | ||
var ns = process.context[scope]; | ||
if (!ns) { | ||
ns = cls.createNamespace(scope); | ||
process.context[scope] = ns; | ||
// Set up loopback.getCurrentContext() | ||
loopback.getCurrentContext = function() { | ||
return ns; | ||
}; | ||
|
||
chain(juggler); | ||
chain(remoting); | ||
} | ||
return ns; | ||
} | ||
|
||
function context(options) { | ||
options = options || {}; | ||
var scope = options.name || name; | ||
var enableHttpContext = options.enableHttpContext || false; | ||
var ns = createContext(scope); | ||
// Return the middleware | ||
return function contextHandler(req, res, next) { | ||
if (req.loopbackContext) { | ||
return next(); | ||
} | ||
req.loopbackContext = ns; | ||
// Bind req/res event emitters to the given namespace | ||
ns.bindEmitter(req); | ||
ns.bindEmitter(res); | ||
// Create namespace for the request context | ||
ns.run(function processRequestInContext(context) { | ||
// Run the code in the context of the namespace | ||
if (enableHttpContext) { | ||
ns.set('http', {req: req, res: res}); // Set up the transport context | ||
} | ||
next(); | ||
}); | ||
}; | ||
} | ||
|
||
/** | ||
* Create a chained context | ||
* @param {Object} child The child context | ||
* @param {Object} parent The parent context | ||
* @private | ||
* @constructor | ||
*/ | ||
function ChainedContext(child, parent) { | ||
this.child = child; | ||
this.parent = parent; | ||
} | ||
|
||
/** | ||
* Get the value by name from the context. If it doesn't exist in the child | ||
* context, try the parent one | ||
* @param {String} name Name of the context property | ||
* @returns {*} Value of the context property | ||
*/ | ||
ChainedContext.prototype.get = function(name) { | ||
var val = this.child && this.child.get(name); | ||
if (val === undefined) { | ||
return this.parent && this.parent.get(name); | ||
} | ||
}; | ||
|
||
ChainedContext.prototype.set = function(name, val) { | ||
if (this.child) { | ||
return this.child.set(name, val); | ||
} else { | ||
return this.parent && this.parent.set(name, val); | ||
} | ||
}; | ||
|
||
ChainedContext.prototype.reset = function(name, val) { | ||
if (this.child) { | ||
return this.child.reset(name, val); | ||
} else { | ||
return this.parent && this.parent.reset(name, val); | ||
} | ||
}; | ||
|
||
function chain(child) { | ||
if (typeof child.getCurrentContext === 'function') { | ||
var childContext = new ChainedContext(child.getCurrentContext(), | ||
loopback.getCurrentContext()); | ||
child.getCurrentContext = function() { | ||
return childContext; | ||
}; | ||
} else { | ||
child.getCurrentContext = loopback.getCurrentContext; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters