From 4d7ef02795697c8d96a1dbec4217357caf21fc59 Mon Sep 17 00:00:00 2001 From: Pham Anh Tuan Date: Mon, 1 Dec 2014 17:36:34 +0700 Subject: [PATCH 1/5] MongoDB - loopback.getCurrentContext() return null loopback.getCurrentContext() return null after MongoDB call returns. wrap up @Ichenay's patch https://github.com/lchenay/loopback/commit/f075c79 --- server/middleware/context.js | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/server/middleware/context.js b/server/middleware/context.js index c86903a06..42827c422 100644 --- a/server/middleware/context.js +++ b/server/middleware/context.js @@ -2,6 +2,7 @@ var loopback = require('../../lib/loopback'); var juggler = require('loopback-datasource-juggler'); var remoting = require('strong-remoting'); var cls = require('continuation-local-storage'); +var domain = require('domain'); module.exports = context; @@ -44,6 +45,13 @@ function context(options) { var scope = options.name || name; var enableHttpContext = options.enableHttpContext || false; var ns = createContext(scope); + + var currentDomain = process.domain = domain.create(); + currentDomain.oldBind = currentDomain.bind; + currentDomain.bind = function(callback, context) { + return currentDomain.oldBind(ns.bind(callback, context), context); + }; + // Return the middleware return function contextHandler(req, res, next) { if (req.loopbackContext) { @@ -53,13 +61,19 @@ function context(options) { // Bind req/res event emitters to the given namespace ns.bindEmitter(req); ns.bindEmitter(res); + + currentDomain.add(req); + currentDomain.add(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(); + currentDomain.run(function() { + 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(); + }); }); }; } @@ -115,4 +129,4 @@ function chain(child) { } else { child.getCurrentContext = loopback.getCurrentContext; } -} +} \ No newline at end of file From 501a79393e131d3fdbe71b65bfa93c98487d5994 Mon Sep 17 00:00:00 2001 From: Pham Anh Tuan Date: Mon, 15 Dec 2014 16:07:29 +0700 Subject: [PATCH 2/5] test script to check if context available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test script for issue: “MongoDB - loopback.getCurrentContext() return null” https://github.com/strongloop/loopback/pull/885 --- test/context.test.js | 81 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 test/context.test.js diff --git a/test/context.test.js b/test/context.test.js new file mode 100644 index 000000000..eda36ecfa --- /dev/null +++ b/test/context.test.js @@ -0,0 +1,81 @@ +var loopback = require('loopback'); +var request = require('supertest'); +var Domain = require('domain'); +var assert = require('assert'); +var EventEmitter = require("events").EventEmitter; + +describe('check loopback.getCurrentContext', function() { + var app = loopback(); + var runInOtherDomain; + var runnerInterval; + + before(function(){ + var called = false; + app.use(loopback.rest()); + app.dataSource('db', { connector: 'memory' }); + app.dataSource('mongox', { + "host": "localhost", + "port": 27017, + "database": "api", + "username": "root", + "password": "123456", + "name": "mongodb", + "connector": "mongodb" + }); + + var TestModel = loopback.createModel({name: 'TestModel', properties: {name: String}, options: { base: 'Model'}}); + app.model(TestModel, {dataSource: "db", public: true}); + + var emitterInOtherDomain = new EventEmitter(); + Domain.create().add(emitterInOtherDomain); + + runInOtherDomain = function(fn) { + emitterInOtherDomain.once('run', fn); + } + + runnerInterval = setInterval(function() { emitterInOtherDomain.emit('run'); }, 10); + + // function for remote method + TestModel.test = function(inst, cb) { + tmpCtx = loopback.getCurrentContext(); + if (tmpCtx) tmpCtx.set('data', 'test'); + called = true; + if (process.domain) cb = process.domain.bind(cb); // IMPORTANT + runInOtherDomain(cb); + }; + + // remote method + TestModel.remoteMethod('test', { + accepts: {arg: 'inst', type: 'TestModel'}, + returns: {root: true}, + http: {path: '/test', verb: 'get'} + }); + + // after remote hook + TestModel.afterRemote('**', function(ctxx, inst, next){ + ctxx.result.called = called; + tmpCtx = loopback.getCurrentContext(); + if (tmpCtx) { + ctxx.result.data = tmpCtx.get('data'); + }else { + ctxx.result.data = ""; + } + next(); + }); + }); + + after(function tearDownRunInOtherDomain() { + clearInterval(runnerInterval); + }); + + it('should fail without the patch and it should pass once the patch is applied', function(done) { + request(app) + .get('/TestModels/test') + .end(function(err, res) { + if (err) return done(err); + assert.equal(res.body.called, true); + assert.equal(res.body.data, 'test'); + done(); + }); + }); +}); \ No newline at end of file From bb5fcf17ff6692b0359a2136a7824ca50e23cf1a Mon Sep 17 00:00:00 2001 From: Pham Anh Tuan Date: Mon, 15 Dec 2014 16:17:53 +0700 Subject: [PATCH 3/5] remove 'mongox' db definition --- test/context.test.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/test/context.test.js b/test/context.test.js index eda36ecfa..8d534d884 100644 --- a/test/context.test.js +++ b/test/context.test.js @@ -13,15 +13,6 @@ describe('check loopback.getCurrentContext', function() { var called = false; app.use(loopback.rest()); app.dataSource('db', { connector: 'memory' }); - app.dataSource('mongox', { - "host": "localhost", - "port": 27017, - "database": "api", - "username": "root", - "password": "123456", - "name": "mongodb", - "connector": "mongodb" - }); var TestModel = loopback.createModel({name: 'TestModel', properties: {name: String}, options: { base: 'Model'}}); app.model(TestModel, {dataSource: "db", public: true}); From c6ee7901e496497afe56b886f8e21b41f429ad32 Mon Sep 17 00:00:00 2001 From: Pham Anh Tuan Date: Wed, 7 Jan 2015 00:55:03 +0700 Subject: [PATCH 4/5] add loopback.getCurrentContext to loopback.test.js --- test/context.test.js | 72 ------------------------------------------- test/loopback.test.js | 65 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 72 deletions(-) delete mode 100644 test/context.test.js diff --git a/test/context.test.js b/test/context.test.js deleted file mode 100644 index 8d534d884..000000000 --- a/test/context.test.js +++ /dev/null @@ -1,72 +0,0 @@ -var loopback = require('loopback'); -var request = require('supertest'); -var Domain = require('domain'); -var assert = require('assert'); -var EventEmitter = require("events").EventEmitter; - -describe('check loopback.getCurrentContext', function() { - var app = loopback(); - var runInOtherDomain; - var runnerInterval; - - before(function(){ - var called = false; - app.use(loopback.rest()); - app.dataSource('db', { connector: 'memory' }); - - var TestModel = loopback.createModel({name: 'TestModel', properties: {name: String}, options: { base: 'Model'}}); - app.model(TestModel, {dataSource: "db", public: true}); - - var emitterInOtherDomain = new EventEmitter(); - Domain.create().add(emitterInOtherDomain); - - runInOtherDomain = function(fn) { - emitterInOtherDomain.once('run', fn); - } - - runnerInterval = setInterval(function() { emitterInOtherDomain.emit('run'); }, 10); - - // function for remote method - TestModel.test = function(inst, cb) { - tmpCtx = loopback.getCurrentContext(); - if (tmpCtx) tmpCtx.set('data', 'test'); - called = true; - if (process.domain) cb = process.domain.bind(cb); // IMPORTANT - runInOtherDomain(cb); - }; - - // remote method - TestModel.remoteMethod('test', { - accepts: {arg: 'inst', type: 'TestModel'}, - returns: {root: true}, - http: {path: '/test', verb: 'get'} - }); - - // after remote hook - TestModel.afterRemote('**', function(ctxx, inst, next){ - ctxx.result.called = called; - tmpCtx = loopback.getCurrentContext(); - if (tmpCtx) { - ctxx.result.data = tmpCtx.get('data'); - }else { - ctxx.result.data = ""; - } - next(); - }); - }); - - after(function tearDownRunInOtherDomain() { - clearInterval(runnerInterval); - }); - - it('should fail without the patch and it should pass once the patch is applied', function(done) { - request(app) - .get('/TestModels/test') - .end(function(err, res) { - if (err) return done(err); - assert.equal(res.body.called, true); - assert.equal(res.body.data, 'test'); - done(); - }); - }); -}); \ No newline at end of file diff --git a/test/loopback.test.js b/test/loopback.test.js index 8f217f6a3..cd6a8b25b 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -1,4 +1,5 @@ var it = require('./util/it'); +var describe = require('./util/describe'); describe('loopback', function() { var nameCounter = 0; @@ -388,4 +389,68 @@ describe('loopback', function() { }); }); }); + + describe.onServer('loopback.getCurrentContext', function() { + var app = loopback(); + var runInOtherDomain; + var runnerInterval; + + before(function() { + + app.use(loopback.rest()); + app.dataSource('db', { connector: 'memory' }); + + var TestModel = loopback.createModel({ name: 'TestModel' }); + app.model(TestModel, {dataSource: "db", public: true}); + + var emitterInOtherDomain = new EventEmitter(); + Domain.create().add(emitterInOtherDomain); + + runInOtherDomain = function(fn) { + emitterInOtherDomain.once('run', fn); + } + + runnerInterval = setInterval(function() { emitterInOtherDomain.emit('run'); }, 10); + + // function for remote method + TestModel.test = function(inst, cb) { + tmpCtx = loopback.getCurrentContext(); + if (tmpCtx) tmpCtx.set('data', 'test'); + if (process.domain) cb = process.domain.bind(cb); // IMPORTANT + runInOtherDomain(cb); + }; + + // remote method + TestModel.remoteMethod('test', { + accepts: {arg: 'inst', type: uniqueModelName}, + returns: {root: true}, + http: {path: '/test', verb: 'get'} + }); + + // after remote hook + TestModel.afterRemote('**', function(ctxx, inst, next) { + tmpCtx = loopback.getCurrentContext(); + if (tmpCtx) { + ctxx.result.data = tmpCtx.get('data'); + }else { + ctxx.result.data = ""; + } + next(); + }); + }); + + after(function tearDownRunInOtherDomain() { + clearInterval(runnerInterval); + }); + + it('should fail without the patch and it should pass once the patch is applied', function(done) { + request(app) + .get('/TestModels/test') + .end(function(err, res) { + if (err) return done(err); + assert.equal(res.body.data, 'test'); + done(); + }); + }); + }); }); From 0bec30ae5fa1761ee2d280f5efe75b9bbbe57f20 Mon Sep 17 00:00:00 2001 From: Pham Anh Tuan Date: Wed, 7 Jan 2015 09:00:19 +0700 Subject: [PATCH 5/5] fixed linter issue --- test/loopback.test.js | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/test/loopback.test.js b/test/loopback.test.js index cd6a8b25b..a943d8ad3 100644 --- a/test/loopback.test.js +++ b/test/loopback.test.js @@ -1,5 +1,7 @@ var it = require('./util/it'); var describe = require('./util/describe'); +var Domain = require('domain'); +var EventEmitter = require('events').EventEmitter; describe('loopback', function() { var nameCounter = 0; @@ -401,20 +403,23 @@ describe('loopback', function() { app.dataSource('db', { connector: 'memory' }); var TestModel = loopback.createModel({ name: 'TestModel' }); - app.model(TestModel, {dataSource: "db", public: true}); + app.model(TestModel, {dataSource: 'db', public: true}); var emitterInOtherDomain = new EventEmitter(); Domain.create().add(emitterInOtherDomain); runInOtherDomain = function(fn) { emitterInOtherDomain.once('run', fn); - } + }; - runnerInterval = setInterval(function() { emitterInOtherDomain.emit('run'); }, 10); + runnerInterval = setInterval( + function() { + emitterInOtherDomain.emit('run'); + }, 10); // function for remote method TestModel.test = function(inst, cb) { - tmpCtx = loopback.getCurrentContext(); + var tmpCtx = loopback.getCurrentContext(); if (tmpCtx) tmpCtx.set('data', 'test'); if (process.domain) cb = process.domain.bind(cb); // IMPORTANT runInOtherDomain(cb); @@ -429,11 +434,11 @@ describe('loopback', function() { // after remote hook TestModel.afterRemote('**', function(ctxx, inst, next) { - tmpCtx = loopback.getCurrentContext(); + var tmpCtx = loopback.getCurrentContext(); if (tmpCtx) { ctxx.result.data = tmpCtx.get('data'); }else { - ctxx.result.data = ""; + ctxx.result.data = ''; } next(); }); @@ -443,7 +448,7 @@ describe('loopback', function() { clearInterval(runnerInterval); }); - it('should fail without the patch and it should pass once the patch is applied', function(done) { + it('passed if the patch is applied, otherwise failed', function(done) { request(app) .get('/TestModels/test') .end(function(err, res) {