-
Notifications
You must be signed in to change notification settings - Fork 5.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
Add DDP._CurrentPublicationInvocation and DDP._CurrentMethodInvocation #8629
Conversation
Added a test to make sure that methods called from a publication have access to the connection. |
randomSeed = DDPCommon.makeRpcSeed(currentInvocation, name); | ||
} else if (currentPublicationInvocation) { | ||
userId = currentPublicationInvocation.userId; | ||
setUserId = function(userId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if this should be set in this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How would we handle the case when a method wanna change the userId
of the connection
when the method was invoked by a publication
?
Print the same kind of error like we do when setUserId
is invoked in a method initiated from server-code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What would be the potential downsides of changing the connection's userId
in this case?
Will this result in security flaws or something else maybe?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. You are doing the same as what initial method invocation is doing.
Can you make some tests which make sure |
@@ -5,4 +5,5 @@ LivedataTest.SUPPORTED_DDP_VERSIONS = DDPCommon.SUPPORTED_DDP_VERSIONS; | |||
// This is private but it's used in a few places. accounts-base uses | |||
// it to get the current user. Meteor.setTimeout and friends clear | |||
// it. We can probably find a better way to factor this. | |||
DDP._CurrentInvocation = new Meteor.EnvironmentVariable; | |||
DDP._CurrentMethodInvocation = DDP._CurrentInvocation = new Meteor.EnvironmentVariable; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would split this into two lines. One creating DDP._CurrentMethodInvocation
and the other setting DDP._CurrentInvocation
and commenting that this for backwards compatibility since X version because it was used as a public API by many packages.
We should maybe also change everywhere else in the core code to use DDP._CurrentMethodInvocation
. But maybe somebody else from MDG could comment on this first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, will fix that.
In the case when a method is called from a publication both And with the merge of #8031, there will be a test that |
Oh, you are right. Before it was the same, only that |
You should probably prepare a |
I'll hold off on working more on this until we get an "official" stamp of approval. |
I don't really see this as a breaking change as |
It is a breaking change because now |
Yes, we should probably change those tests to make sure that |
if (!currentInvocation) | ||
throw new Error("Meteor.userId can only be invoked in method calls. Use this.userId in publish functions."); | ||
throw new Error("Meteor.userId can only be invoked in method calls or publications."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you also change "This function only works if called inside a method." comment above. And maybe change the rest of the comment to warn that if you use Meteor.user()
(not Meteor.userId()
) outside of the reactive context on the server the data returned by Meteor.user()
will not change reactivelly. I mean, to me this is pretty obvious, so maybe we should just remove this comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll try and generalize this comment a bit as this holds true for methods as well, as far as I can tell.
i.e. Meteor.user()
won't be reactive on server-side either in a method or a publication.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I think the argument here is that publications are longer active, so there is more chance that content returned by user
changes.
BTW, I have a package which gives server-side reactivity inside publish functions, so then Meteor.user()
is reactive. :-)
// _setUserId is normally called from a Meteor method with | ||
// DDP._CurrentInvocation set. But DDP._CurrentInvocation is not expected | ||
// to be set inside a publish function, so we temporary unset it. | ||
// Otherwise, this can lead to a problem that if a publish function is |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would remove the part of the comment from here on, because it is not anymore true. connection
will now be always set in server-side methods, but we do not want DDP._CurrentInvocation
to be set for those who are accessing it directly to check if they are inside a method call.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Somewhere along the line I guess I merged #8031 and pushed to this PR which I didn't want to do. I'm thinking I'll rebase this branch of your PR and update the comments and tests to work with my changes. I don't really see these changes making sense without the changes in your PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you do not have to rebase, you merged and we can merge this one together. And we can just close that other one. I do not mind.
}); | ||
|
||
Tinytest.addAsync( | ||
"livedata server - no connection in a method called from a publish function", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be probably renamed.
if (! methodCallResults[connectionId]) { | ||
methodCallResults[connectionId] = []; | ||
} | ||
methodCallResults[connectionId].push(Meteor.call('livedata_server_test_inner')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here DDP._CurrentInvocation.get()
shoud be pushed to methodCallResults
now, no?
There is one breaking change here. |
I rebased this PR from #8031, so we should be able to just merge this PR and skip that one. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zimme Sorry for the delay in getting to this and thank you very much for visiting both this and #8031 and the additional insight/rational on both. This appears to be headed in the correct direction, and addresses my concerns with #8031 while still implementing the very important fix that @mitar provided there.
I'm not too concerned about the one breaking change given that the result of a pub => method
will be a more-controlled permission set, and we can make this clear in History.md
– though I do think we need a very clear call-to-action, for example:
If you
Method.call
from within aMeteor.publish
and rely on the value ofthis.connection
within those invoked methods... ... ...
@mitar I believe your (valid) comments above have been addressed after the recent round of commits, did you want to take another pass at it?
Excited to get this wrapped up!
// in a method or publish function is to do Meteor.find(this.userId).observe | ||
// and recompute when the user record changes. | ||
var currentInvocation = DDP._CurrentPublicationInvocation.get(); | ||
currentInvocation = currentInvocation || DDP._CurrentMethodInvocation.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe avoid the reassignment here and change currentInvocation
to a const
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it make sense to reverse the order of the fallback here so that _CurrentMethodInvocation
falls back to _CurrentPublicationInvocation
(instead of publication falling back to method) since _CurrentMethodInvocation
is (temporarily) unset within the publication?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this would make sense as it's more likely that DDP._CurrentMethodInvocation
would be set. If that isn't set, we're in a publish function and we fall back to DDP._CurrentPublicationInvocation
.
So given the first comment I would change this to const currentInvocation = DDP._CurrentMethodInvocation.get() || DDP._CurrentPublicationInvocation.get();
.
setUserId = function(userId) { | ||
currentInvocation.setUserId(userId); | ||
currentPublicationInvocation._session._setUserId(userId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there another way to handle this? Or should this be more defensive? In other words, could currentPublicationInvocation._session
ever be undefined
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From what I can tell a Subscription
always have _session
which is a Session
and _setUserId
on Session
is what's used by a methodInvocation
to set it's connections userId
. We need this to be called properly so that publications are re-run when changing the userId
.
I haven't found a different way of doing this, however maybe we could cc someone at MDG that knows more about the DDP protocol implementation to verify that _session
is always available before merging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also looked into this because it was suspicious to me, but I could not find a cleaner way either. It seems the best and most stable one.
Maybe we should make a test which makes sure you call setUserId
from inside a method called from publish function to make sure this will be always available. It would also in general be interesting to see what happens if you call setUserId
from inside a publish function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
from what I can tell there is no setUserId
on the publication context, that's why we need to reach into _session
to get that function. Calling _session._setUserId
from inside a publish function should just change the current connections userId
and the publication should re-run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just make a test that setUserId
can be called from a method called from a publish function. To catch if _session
will be renamed at any point in the future or anything like that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually already do this. Here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are great! I have no further comments.
I have found myself writing Maybe this is an indicator that we should switch from |
History.md
Outdated
re-run after a user logged in. This would cause method calls from within | ||
publish functions to unexpectedly having `this.connection` available. | ||
* Support `DDP._CurrentPublicationInvocation` and `DDP._CurrentMethodInvocation` | ||
, `DDP._CurrentInvocation` is kept for backwards-compatibility. This change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange wrapping?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just a line length thing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, but comma could be in the previous line, no?
I went through and it looks OK with my comments addressed, but I didn't do it as thoroughly as previously. |
}, | ||
|
||
livedata_server_test_outer: function () { | ||
return Meteor.call('livedata_server_test_inner'); | ||
}, | ||
livedata_server_test_setuserid: function (userId) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably a newline above that. Just a nitpick. ;-)
There, I believe all the current comments have been addressed. I'm not sure if anything else is needed to get this merged, but I don't think I'll have time to work a lot on this until next week again. |
I think we should add tests (and potential code improvements) which make sure that In a way, you want Maybe that already happens because they are run inside a fiber, but it would still be good to test and assure that this is really so. |
That's a valid point, I just recently fixed a bug in However, I do believe this is another issue in itself. Becuase from what I can tell from working on that issue on this issue this is the way it currently work and to get these changes in quicker I would suggest we open a new issue for binding the environments of the callbacks. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great! Thank you, everyone, for all the hard work that went into this. This will come out in 1.5.1 due to the number of packages affected and its ties to the meteor
package.
Some formatting changes to (try! and) match existing `History.md` formatting.
Improving History.md entries for meteor#8629 (meteor#8736)
I realized that we haven't bumped the versions of the packages this PR touches. Thinking that |
Shall we update documents to use Meteor.user() / .userId() in publications? |
Where in the docs are you talking about? |
I mean, for example, http://docs.meteor.com/api/pubsub.html#Subscription-userId and some other places use The real problem is, should we recommend |
@laosb, I'm thinking that we shouldn't recommend I don't think it belongs in the Subscriptions section and looking through the Methods section there is no mention of But maybe we should make a note about all of this in the |
* Major version bump for `ddp-server` * Minor version bumps for: - `accounts-base` - `allow-deny` - `ddp-client` - `ddp-common` - `meteor` - `mongo`
* Major version bump for `ddp-server` * Minor version bumps for: - `accounts-base` - `allow-deny` - `ddp-client` - `ddp-common` - `meteor` - `mongo`
BTW, how does one now know if a method is called from an existing server-side method, or directly from a client side? |
With the new code one would check if |
Ooh, I misread that quite a lot. How would you do it before? I guess that would still hold true? Also, what's the use-case? |
One way would be adding an extra |
Before you checked that The use case is having a Meteor method which can have a more permissive API if called from the server side. For example, you can create a document without linking it to the current user (because you are calling it from the server side and you know that you will in the next step assign custom user as an author). So I can have Meteor method Maybe the solution is to refactor this code and move most of the method into an importable function, and then inside Meteor method just check if |
If a method is called from server-side code it won't have I would go with the latter suggestion. |
Yes, before calling a method from a method called from the client side behaved like a server-side call. |
I guess we should probably open some PRs with the Docs and Guide to make it clear that it's no longer necessary to use |
This pull-request is related to issue #8031. Please see this comment for more information.
Tests are still needed before we can merge this, but I thought I should try and get the ball rolling based on the clarification and discussion in #8031.
#8031 should probably be merged before this one if we decide to go down this road./cc @abernix, @mitar