-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Deprecate getCurrentContext() #1676
Comments
I'm concerned about this, as I have been using getCurrentContext extensively. And without any issues thusfar. I'm on mobile right now, so I don't have access to the whole discussion(s) - IIRC mainly regarding unreliable availability on different platforms - but what's the reasoning here? Can we have a flag to silence the deprecation warning? ;-) |
I abandoned getCurrentContext pretty quickly since it led to unpredictable behavior. Notably, retrieving the Access Token using something like |
I was thinking about the problems of getCurrentContext we are experiencing. It seems to me that most of the issues are caused by the fact that any connector using connection pool must be CLS aware and include few lines of code to switch to the right CLS context. I am wondering we can add this small piece into loopback-datasource-juggler, so that we don't depend on connector implementation anymore? An example to illustrate my point: DAO.create = function(data, options, cb) {
// setup instance, call hooks, etc.
clsCtx = // save current CLS context;
connector.create(/*args*/, function(err, result) {
// restore CLS context from clsCtx
// continue as before
});
}; If that's not a viable approach, then I support @fabien's request for a feature flag. This flag can have three states:
Thoughts? |
I don't think fixing the connector boundary and lack of CLS support in the connectors is the only issue. I've seen issues from our usage of I'm OK with a flag too. Opt-in and with a bit of warning in the documentation. As far as regular API - I still think we should deprecate it.
The states LGTM. |
+1 for flag, please |
loopback.getCurrentContext() has given lot of hard time while developing unit test cases so far. We had to setup dummy context before executing tests. However today we faced one major issue where loopback.getCurrentContext () is returned as null when a async callback function gets called. We are making an external HTTP call from within loopback API in async manner. loopback.getCurrnetContext () is consistently coming as null in the callback made by that http request. The loopback api call is returned by that time. Is there any way to completely avoid loopback.getCurrentContext() and still make context information available everywhere without explicitly passing it around? |
Don't think so. On Wed, 23 Dec 2015 at 17:42, Sachin Mane notifications@github.com wrote:
|
Any progress about |
…d override the checkAccess method and look at the request to identify the context and whether the current (token) user can access the target data strongloop/loopback#1676
Another bug in node-continuation-local-storage: It looks like |
I also was playing around with a solution. I would love someone from Strongloop team or the community to chime in about the roleResolver https://gist.github.com/dancingshell/26b8101e0620b27244d63b71f2b8f55a |
@dancingshell I think I've seen you mention this solution before, but somehow it slipped my mind while churning away on things. Your solution seems to work pretty well for your use-case, as a workaround it's perfect. However, I think it's less suited as a general solution. Sometimes, My main concern is that in the case of a static method, you still need to hijack one of the In fact, I think most of what this 'roleResolver' does, can also be done in a remote before hook, since you'd have access to One thing that came to mind is a more barebones/integrated CLS-like solution, where
I don't know if I'm making any sense here, but I'd love to hear some opinions on this. |
@fabien thanks for posting your gist. Do you think we should replace my original solution with a link to your gist? I know you took care to make it a general solution, and it looks much more complete than what I suggested a while ago.
I think argument injection is the only bullet proof solution to this problem. It is not perfect either, but it is predictable. This is what I'd prefer: MyModel.myMethod = function(params, cb) {
// params.userId => injected by user code
// params.foo => mapped by system
};
MyModel.remoteMethod('myMethod', {
params: { // alternative behavior to 'accepts'
userId: {
type: 'number',
required: true,
http: function(ctx) {
return ctx.user.id;
}
},
foo: {type: 'number'}
}
}); Which solves a couple of things. One being the argument position issue you ran into when having to add the |
The other approach is to introduce request-scoped model repositories (one instance per request) so that a repository instance itself can carry context information as its state. Please note that LoopBack models are singletons as of today and they are tied with attached datasources. With model repository, LoopBack runtime can create a new instance (or acquire an instance from a pool) per request: Conceptually, model repository = request context + model definition + attached data source(s)
To navigate over relations, |
@ritch if you have a chance to try my gist first, then yes, please link to it. I am using this code as part of a larger codebase, so some things might be different if you run it on plain LB. I really like your use of having a single @raymondfeng I'm not sure I follow you when you say models are singletons as of today - does this mean that |
@raymondfeng do I understand correctly that essentially model.js files would then get executed per-request, to ensure that the model handle used by all methods is the one tied to the context-linked repository? And thus, all hooks (etc etc) get initialized per request as well. |
@fabien I don't get it, tx for you reply, I don't know loopback a lot, but I think your gist can save me :) |
Thank you @fabien for posting a detailed comment about your experience and taking the time to extract your code into a gist that can be used by general audience. The solution you proposed looks reasonable to me at high level 👍 I especially like the ability to configure options-injection at model setting level, i.e. by editing Model JSON file. What I personally/subjectively don't like that much, is a "big" I'd also like to correct few points you made in your comment.
The solution proposed in my patch #2762 uses custom
Because the |
As I mentioned in my previous comment, I personally want remote methods to be regular javascript functions that can be called without strong-remoting too. Using
This is a very similar concept to what I proposed for Controllers. The proposal is unfortunately buried in our internal repository (https://github.com/strongloop-internal/scrum-loopback/issues/614) , below is an excerpt:
Let me illustrate this on an example from apiary's Example Poll API. The API defines an HTTP endpoint "Vote on a Choice" exposed at What I would like to see, is a controller that defines "Vote on a Choice" method and tells strong-remoting's REST adapter to use // server/controllers/question-ctrl.js
// I'm using ES6 class syntax and Promises,
// but this can be rewritten in ES5 with callbacks too.
module.exports = class QuestionController {
constructor(app, remotingCtx) {
this.Question = app.models.Question;
this.Choice = app.models.Choice;
this.ctx = remotingCtx;
}
vote(questionId, choiceId) {
return this.Question.findById(questionId)
.then(function(question) {
return question.choices.findOne({ where: { choiceId: choiceId } } ;
})
.then(function(choice) {
// TODO check that choice exists
choice.count++;
return choice.save();
});
}
}; Now let's add some remoting metadata using LoopBack JSON files similar to what we have here, the code here is a mockup // server/controllers/question-ctrl.json
{
"methods": {
"vote": {
"accepts": [
{ "arg": "question_id", ... },
{ "arg": "choice_id", ... },
],
// ...
"http": { "verb": "post", "path": "/questions/:question_id/choices/:choice_id" }
}
}
} |
I cannot speak for @raymondfeng, but here is what I think he meant: Right now, you have only one global entity for each model, which is the model constructor. When you invoke a static method like Compare this with my proposal for Controllers above, where we would create new controller instance for each request, and thus we would be able to attach extra data to this per-request controller instance. As for the second part of your comment, we don't have any plans to stop supporting prototype model methods, or at least I am personally not aware of any. A change like this is definitely out of scope of v3. LoopBack 3.0 is in a release-candidate mode right now (see the announcement blog post) and we don't anticipate any new breaking changes, with the exception of dropping support for Node v0.10/v0.12 as discussed in #2807. |
@bajtos Please note ES6 supports destructuring for parameters, for example:
@doublemarked @bajtos There are different ways to have isolated instances for the model per request. We can rebuild it per request (which is expensive). It also be done by something like:
The idea is to move away from singletons if needed so that we can carry states for such objects per request. |
@fabien Would you be able to share an example of how to do this? I'm trying to extend the context in a boot script to include the current user object. Thanks! |
@fabien how can i get the current connected user inside a model using your solution. Can you please share an example. Do you have any other solution to get current user in models. Thanks |
@fabien, thanks for sharing this, it seems we are a number of folks out there currently building custom solutions to solve context-passing related issues... I like the general idea of your solution (
Maybe we could we think of a generic nested ctx object format with generic getters/setters methods (as you provided, great) that the user could customize to its tastes by setting any property required? ++ We could also argue the concept of method and contextProperties whitelist, vs. blacklists, as things may break with new loopback releases (for exemple new method names in loopback 3) ps. really smart the optional per-model |
Please, see my revised version of @fabien 's gist. in a nutshell
|
Hey @ebarault, thanks for the gist! However, my I've got your gist in a boot script and it's being run before all other boot scripts. The Any idea how to get the context hook running first? |
@bostondv : so yes, I've no clean way to do this right now (there might be a better way, but i took the shortest track for my project to move on). So here it is in the bootscript (cf. gist), i register the beforeRemote for ctx injection on ** :
now, you need to know that the ** beforeRemote hooks are actually executed after all per-method beforeRemote registered hooks. So first, you need to register a similar ** hook in your Models if you want to get a chance for it to be executed after the ctx injection hook. secondly, you still need to make sure that the actual registration of the ** hook in your model is done after the ctx injection one, otherwise your hook might still be executed before the ctx injection one. Depending on your application it can be done in a number of ways with emitted events so you wait for the models to be fully loaded before registering your beforeRemote hooks. Usually you can get it done with:
if you don't do this the result can be random as the user defined Models are loaded in lexicographical order Remember: you can still discriminate the method called in the |
@ebarault thanks so much, that's exactly what I needed! |
all: some archeology to excavate an old debate: #1362 around the ctx propagation question |
I was having issues with remote context a while back, especially after wrapping a remote method call in a third party library callback (particularly jsdom). I have been trying to find some practical working example of this aside from the examples that have been provided in this thread. Is it safe to say that https://github.com/snowyu/loopback-component-remote-ctx.js should pretty much address this issue? If anyone knows of a non-coffee script package, references would be greatly appreciated. |
@nidhhoggr : i just went through the code from @snowyu component. It is essentially a port to coffee script of one of the general idea developed in this thread (and this one) about passing the context as an argument injected in the methods definitions ( If you want a pure node implem which is also making sure the context is available the same way in methods definition, remote methods hooks and operation hooks, please see my revision of @fabien's optionnated implementation here |
@ebarault: I can confirm @snowyu implementation is working and your snippet does give me a better understanding of what is happening with method injection etc. I see that your RemoteContext has a prototype method for setting the access token. Ideally would this be set in some middleware? The legacy implementation using getCurrentContext looked something like this:
So my question, is there a way to set the accessToken of the remote context that is injected into each of the method with middleware as the previous implementation is doing above, or is it best to just access the accesstoken from the context request headers within that method? |
@nidhhoggr : there's actually a method for getting the accessToken, not for setting it.
the accessToken is always available in the remotingContext, in the |
@ebarault Okay I see now, so it's being set from the items in contextProperties array. Thanks you Sir! |
Hello, the official implementation of "options" injection has been landed via #3023 (3.x) and #3048 (2.x, behind a feature flag, disabled by default for backwards compatibility) and released in 2.37.0 and 3.2.0. The documentation will be updated soon, see ttps://github.com/loopbackio/loopback.io/pull/227. I am closing this issue as done. |
Well its been fun. And by fun I mean: not so fun. We've tried to create a simple method for getting at contextual information no matter where your code is executing. We did this by depending on a sophisticated module called continuation-local-storage .
Someday there will be a more reliable mechanism than the userland cls module, and we can re-add first class support for
getCurrentContext()
. But until then I am proposing we deprecate this method with the standard warning message.What does this mean for users of
getCurrentContext()
? You will get an annoying message (configurable) every time you use the method. We will still support it, and we won't break semver. But we strongly encourage you to refactor.For an immediate work around, take a look at #1495. A more sustainable workaround will be added as a feature to the framework.
/cc @bajtos @raymondfeng @superkhau @piscisaureus
The text was updated successfully, but these errors were encountered: