diff --git a/Gruntfile.js b/Gruntfile.js index 756bede3..25b0a880 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -44,9 +44,6 @@ module.exports = function(grunt) { common: { src: ['common/**/*.js'] }, - browser: { - src: ['browser/**/*.js'] - }, server: { src: ['server/**/*.js'] }, @@ -59,7 +56,6 @@ module.exports = function(grunt) { lib: ['lib/**/*.js'], common: ['common/**/*.js'], server: ['server/**/*.js'], - browser: ['browser/**/*.js'], test: ['test/**/*.js'] }, watch: { diff --git a/browser/current-context.js b/browser/current-context.js deleted file mode 100644 index 9963a528..00000000 --- a/browser/current-context.js +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright IBM Corp. 2015,2016. All Rights Reserved. -// Node module: loopback -// This file is licensed under the MIT License. -// License text available at https://opensource.org/licenses/MIT - -var g = require('strong-globalize')(); - -module.exports = function(loopback) { - loopback.getCurrentContext = function() { - return null; - }; - - loopback.runInContext = - loopback.createContext = function() { - throw new Error(g.f('Current context is not supported in the browser.')); - }; -}; diff --git a/lib/current-context.js b/lib/current-context.js new file mode 100644 index 00000000..185b7b16 --- /dev/null +++ b/lib/current-context.js @@ -0,0 +1,78 @@ +// Copyright IBM Corp. 2015,2016. All Rights Reserved. +// Node module: loopback +// This file is licensed under the MIT License. +// License text available at https://opensource.org/licenses/MIT + +var juggler = require('loopback-datasource-juggler'); +var remoting = require('strong-remoting'); +var LoopBackContext = require('loopback-context'); +var deprecated = require('depd')('loopback'); +var g = require('strong-globalize')(); + +module.exports = function(loopback) { + + /** + * Get the current context object. The context is preserved + * across async calls, it behaves like a thread-local storage. + * + * @returns {ChainedContext} The context object or null. + */ + loopback.getCurrentContext = function() { + // NOTE(bajtos) LoopBackContext.getCurrentContext is overriden whenever + // the context changes, therefore we cannot simply assign + // LoopBackContext.getCurrentContext() to loopback.getCurrentContext() + deprecated(g.f('%s is deprecated. See %s for more details.', + 'loopback.getCurrentContext()', + 'https://docs.strongloop.com/display/APIC/Using%20current%20context')); + return LoopBackContext.getCurrentContext(); + }; + + juggler.getCurrentContext = + remoting.getCurrentContext = loopback.getCurrentContext; + + /** + * Run the given function in such way that + * `loopback.getCurrentContext` returns the + * provided context object. + * + * **NOTE** + * + * The method is supported on the server only, it does not work + * in the browser at the moment. + * + * @param {Function} fn The function to run, it will receive arguments + * (currentContext, currentDomain). + * @param {ChainedContext} context An optional context object. + * When no value is provided, then the default global context is used. + */ + loopback.runInContext = function(fn, ctx) { + deprecated(g.f('%s is deprecated. See %s for more details.', + 'loopback.runInContext()', + 'https://docs.strongloop.com/display/APIC/Using%20current%20context')); + return LoopBackContext.runInContext(fn, ctx); + }; + + /** + * Create a new LoopBackContext instance that can be used + * for `loopback.runInContext`. + * + * **NOTES** + * + * At the moment, `loopback.getCurrentContext` supports + * a single global context instance only. If you call `createContext()` + * multiple times, `getCurrentContext` will return the last context + * created. + * + * The method is supported on the server only, it does not work + * in the browser at the moment. + * + * @param {String} scopeName An optional scope name. + * @return {ChainedContext} The new context object. + */ + loopback.createContext = function(scopeName) { + deprecated(g.f('%s is deprecated. See %s for more details.', + 'loopback.createContext()', + 'https://docs.strongloop.com/display/APIC/Using%20current%20context')); + return LoopBackContext.createContext(scopeName); + }; +}; diff --git a/lib/loopback.js b/lib/loopback.js index 656db41c..3b355539 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -222,7 +222,7 @@ loopback.template = function(file) { }); }; -require('../server/current-context')(loopback); +require('../lib/current-context')(loopback); /** * Create a named vanilla JavaScript class constructor with an attached diff --git a/package.json b/package.json index c4534e18..f17cf8c0 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,6 @@ "bcryptjs": "^2.1.0", "body-parser": "^1.12.0", "canonical-json": "0.0.4", - "continuation-local-storage": "^3.1.3", "cookie-parser": "^1.3.4", "debug": "^2.1.2", "depd": "^1.0.0", @@ -45,6 +44,7 @@ "express": "^4.12.2", "inflection": "^1.6.0", "loopback-connector-remote": "^1.0.3", + "loopback-context": "^1.0.0", "loopback-phase": "^1.2.0", "nodemailer": "^2.5.0", "nodemailer-stub-transport": "^1.0.0", @@ -100,7 +100,6 @@ "browser": { "express": "./lib/browser-express.js", "./lib/server-app.js": "./lib/browser-express.js", - "./server/current-context.js": "./browser/current-context.js", "connect": false, "nodemailer": false, "supertest": false, diff --git a/server/current-context.js b/server/current-context.js deleted file mode 100644 index 4da00bf4..00000000 --- a/server/current-context.js +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright IBM Corp. 2015,2016. All Rights Reserved. -// Node module: loopback -// This file is licensed under the MIT License. -// License text available at https://opensource.org/licenses/MIT - -var juggler = require('loopback-datasource-juggler'); -var remoting = require('strong-remoting'); -var cls = require('continuation-local-storage'); -var domain = require('domain'); - -module.exports = function(loopback) { - - /** - * Get the current context object. The context is preserved - * across async calls, it behaves like a thread-local storage. - * - * @returns {ChainedContext} The context object or null. - */ - loopback.getCurrentContext = function() { - // A placeholder method, see loopback.createContext() for the real version - return null; - }; - - /** - * Run the given function in such way that - * `loopback.getCurrentContext` returns the - * provided context object. - * - * **NOTE** - * - * The method is supported on the server only, it does not work - * in the browser at the moment. - * - * @param {Function} fn The function to run, it will receive arguments - * (currentContext, currentDomain). - * @param {ChainedContext} context An optional context object. - * When no value is provided, then the default global context is used. - */ - loopback.runInContext = function(fn, context) { - var currentDomain = domain.create(); - currentDomain.oldBind = currentDomain.bind; - currentDomain.bind = function(callback, context) { - return currentDomain.oldBind(ns.bind(callback, context), context); - }; - - var ns = context || loopback.createContext('loopback'); - - currentDomain.run(function() { - ns.run(function executeInContext(context) { - fn(ns, currentDomain); - }); - }); - }; - - /** - * Create a new LoopBackContext instance that can be used - * for `loopback.runInContext`. - * - * **NOTES** - * - * At the moment, `loopback.getCurrentContext` supports - * a single global context instance only. If you call `createContext()` - * multiple times, `getCurrentContext` will return the last context - * created. - * - * The method is supported on the server only, it does not work - * in the browser at the moment. - * - * @param {String} scopeName An optional scope name. - * @return {ChainedContext} The new context object. - */ - loopback.createContext = function(scopeName) { - // Make the namespace globally visible via the process.context property - process.context = process.context || {}; - var ns = process.context[scopeName]; - if (!ns) { - ns = cls.createNamespace(scopeName); - process.context[scopeName] = ns; - // Set up loopback.getCurrentContext() - loopback.getCurrentContext = function() { - return ns && ns.active ? ns : null; - }; - - chain(juggler); - chain(remoting); - } - return ns; - }; - - /** - * 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 - * @private - */ - 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; - } - } -}; diff --git a/server/middleware/context.js b/server/middleware/context.js index 73948bd7..4c5fe7b0 100644 --- a/server/middleware/context.js +++ b/server/middleware/context.js @@ -3,55 +3,13 @@ // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT -var loopback = require('../../lib/loopback'); +var deprecated = require('depd')('loopback'); +var g = require('strong-globalize')(); +var perRequestContext = require('loopback-context').perRequest; -module.exports = context; - -var name = 'loopback'; - -/** - * Context middleware. - * ```js - * var app = loopback(); - * app.use(loopback.context(options); - * app.use(loopback.rest()); - * app.listen(); - * ``` - * @options {Object} [options] Options for context - * @property {String} name Context scope name. - * @property {Boolean} enableHttpContext Whether HTTP context is enabled. Default is false. - * @header loopback.context([options]) - */ - -function context(options) { - options = options || {}; - var scope = options.name || name; - var enableHttpContext = options.enableHttpContext || false; - var ns = loopback.createContext(scope); - - // Return the middleware - return function contextHandler(req, res, next) { - if (req.loopbackContext) { - return next(); - } - - loopback.runInContext(function processRequestInContext(ns, domain) { - req.loopbackContext = ns; - - // Bind req/res event emitters to the given namespace - ns.bindEmitter(req); - ns.bindEmitter(res); - - // Add req/res event emitters to the current domain - domain.add(req); - domain.add(res); - - // Run the code in the context of the namespace - if (enableHttpContext) { - // Set up the transport context - ns.set('http', {req: req, res: res}); - } - next(); - }); - }; -} +module.exports = function() { + deprecated(g.f('%s middleware is deprecated. See %s for more details.', + 'loopback#context', + 'https://docs.strongloop.com/display/APIC/Using%20current%20context')); + return perRequestContext.apply(this, arguments); +}; diff --git a/test/access-token.test.js b/test/access-token.test.js index 690b1953..aec2b86a 100644 --- a/test/access-token.test.js +++ b/test/access-token.test.js @@ -477,7 +477,8 @@ describe('app.enableAuth()', function() { it('stores token in the context', function(done) { var TestModel = loopback.createModel('TestModel', { base: 'Model' }); TestModel.getToken = function(cb) { - cb(null, loopback.getCurrentContext().get('accessToken') || null); + var ctx = loopback.getCurrentContext(); + cb(null, ctx && ctx.get('accessToken') || null); }; TestModel.remoteMethod('getToken', { returns: { arg: 'token', type: 'object' },