In our applications, we have been using Meteor.EnvironmentVariable to handle cross-function calls. I find a discrepancy between how 2.14 and earlier work and 3.0. Here is a simple method that simulates the problem:
Method call user 5na9a9daGL3nf7KS8
Link context { idd: 'QERNEZZXqBvH2nH8X' }
Method call user inner 5na9a9daGL3nf7KS8
Whereas in 3.0 I get the error:
Method invocation user 5na9a9daGL3nf7KS8
Link context { idd: 'kwN57Kqi6uTEMGtLD' }
meteor://💻app/packages/accounts-base.js:762
if (!currentInvocation) throw new Error("Meteor.userId can only be invoked in method calls or publications.");
Error: Meteor.userId can only be invoked in method calls or publications.
at AccountsServer.userId (packages/accounts-base/accounts_server.js:124:13)
at Object.Meteor.userId (packages/accounts-base/accounts_common.js:425:32)
at server/main.js:32:64
at EnvironmentVariableAsync.<anonymous> (packages/meteor.js:1241:23)
at packages/meteor.js:740:19
at AsyncLocalStorage.run (node:async_hooks:346:14)
at Object.Meteor._runAsync (packages/meteor.js:737:37)
at EnvironmentVariableAsync.withValue (packages/meteor.js:1236:19)
at MethodInvocation.test (server/main.js:30:21)
at maybeAuditArgumentChecks (packages/ddp-server/livedata_server.js:1990:12)
at packages/ddp-server/livedata_server.js:1899:11
at EnvironmentVariableAsync.<anonymous> (packages/meteor.js:1241:23)
at packages/meteor.js:740:19
at AsyncLocalStorage.run (node:async_hooks:346:14)
at Object.Meteor._runAsync (packages/meteor.js:737:37)
at EnvironmentVariableAsync.withValue (packages/meteor.js:1236:19)
From a rough reconstruction, it seems that the local EnvironmentVariable overwrites the _CurrentMethodInvocation. Is there an alternative way to do this in the framework?
I’d definitely say yes, due to the fact that the I can confirm exact behavior with my own server function calls consuming Meteor.userId on the server from within publication and also this is what breaks meteor-collection-hooks for me on 3.0
Hello @denyhs, @grubba. Is there any confirmation or denial of the reported bug? Are any changes planned to make the EnvironmentVariable work the same in 3.0 as it did in 2.x?
Hi @denyhs, I tried but I have another problem. It seems that nested calls are not handled correctly, below is a simple example:
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
const linkContext1 = new Meteor.EnvironmentVariable();
const linkContext2 = new Meteor.EnvironmentVariable();
linkContext1.withValue({ idd: Random.id() }, async () => {
await linkContext2.withValue({ idd: Random.id() }, async () => {
await linkContext2.withValue({ idd: Random.id() }, async () => {
console.log(' Link context 2', linkContext2.get())
})
console.log(' Link context 2', linkContext2.get())
})
console.log('Link context 1', linkContext1.get())
console.log('Link context 2', linkContext2.get())
});
The result is:
Link context 2 { idd: 'nai9jtXk7mAdrQq5N' }
Link context 2 { idd: 'Di6aKku65s2pdne5L' }
Link context 1 { idd: 'E97X6ZwjM6cEjbxpN' }
**Link context 2 { idd: 'Di6aKku65s2pdne5L' } // Should be undefined but we have the last id**
As a curiosity, do you see any contraindications in using AsyncLocalStorage directly for some properties without going through Meteor.EnvironmentVariable?
Hey! I’ll check if @denyhs can make us a card for that. If I understood correctly, is the value not cleaned at the end?
For now, while we are working on this, maybe we could think of a workaround, I believe this snippet should do the trick(have not tested it but I’m almost sure that it works):
const withContext = async (ctx, value, fn) => {
await ctx.withValue(value, async () => {
await fn();
// this example I only clean objects
if( typeof value === 'object' && value !== null){
for (const member in value) delete value[member];
}
});
}