Skip to content
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

this.userId is null on server when called from the client #185

Open
wreiske opened this issue Oct 13, 2024 · 1 comment
Open

this.userId is null on server when called from the client #185

wreiske opened this issue Oct 13, 2024 · 1 comment

Comments

@wreiske
Copy link

wreiske commented Oct 13, 2024

Describe the bug
I'm running into an issue where this.userId is null when called from a Meteor.subscribe on the client, but is NOT null when using this.subscribe inside fast-render server side.

Expected behavior
this.userId should always include the current logged in userId calling the subscription either called server side (like in fast-render) or client side (using Meteor.subscribe).

Example

Client Side

FlowRouter.route('/billing', {
  name: 'BillingHome',
  waitOn: function () {
    console.log('Meteor userId is ', Meteor.userId()); // This outputs the ID correctly!
    return [
      import('../../ui/billing/billing.js'),
      Meteor.subscribe('UserData'),
      Meteor.subscribe('BillingPlans', 'client')
    ];
  },
  action() {
    renderSecuredRoute(this, 'BillingHome');
  }
});

Fast Render

Using communitypackages:fast-render@5.0.0-beta.0

FastRender.route('/billing', async function () {
  if (!this.userId) {
    return;
  }
  await this.subscribe('BillingPlans', 'server');
});

Server Side Publication


publishComposite('BillingPlans', async function (optional) {
  return {
    async find() {

      console.log('optional is =>', optional);

      console.log('before return, billingplans this.userId', this.userId);
      console.log('before return, billingplans Meteor.userId()', Meteor.userId());

      if (!this.userId) {
        console.error('billing plans not authorized');
        throw new Meteor.Error('not-authorized');
      }
      return BillingPlans.find({
        visible: { $ne: false }
      });
    }, children: [
      {
        find(plan) {
          return BillingSubscriptions.find({
            planId: plan._id,
            userId: this.userId
          });
        }
      }
    ]
  };
});

Current Behavior ec9f077

I20241013-20:00:10.686(0)? publishComposite called with userId: FQZPiRk46xdECpDpN
I20241013-20:00:10.686(0)? optional is => server
I20241013-20:00:10.686(0)? before return, billingplans this.userId FQZPiRk46xdECpDpN
I20241013-20:00:10.686(0)? before return, billingplans Meteor.userId() FQZPiRk46xdECpDpN
I20241013-20:00:11.606(0)? publishComposite called with userId: null
I20241013-20:00:11.606(0)? optional is => client
I20241013-20:00:11.606(0)? before return, billingplans this.userId null
I20241013-20:00:11.606(0)? before return, billingplans Meteor.userId() null
W20241013-20:00:11.607(0)? (STDERR) billing plans not authorized

With Meteor.bindEnvironment

import { Meteor } from 'meteor/meteor'

import Publication from './publication'
import Subscription from './subscription'
import { debugLog, enableDebugLogging } from './logging'

function publishComposite(name, options) {

  return Meteor.publish(name, function publish(...args) {
    const subscription = new Subscription(this);

    console.log('publishComposite called with userId:', this.userId); // Debug log for userId

    const boundEnvironment = Meteor.bindEnvironment(async () => {
      const instanceOptions = await prepareOptions.call(this, options, args);
      const publications = [];

      for (const opt of instanceOptions) {
        const pub = new Publication(subscription, opt);
        await pub.publish();
        publications.push(pub);
      }

      this.onStop(() => {
        for (const pub of publications) {
          pub.unpublish();
        }
      });

      await Promise.all(publications.map(p => p.awaitPromises()));
      debugLog('Meteor.publish', 'ready');
      this.ready();
    });

    // Invoke the async code within the bound environment
    boundEnvironment();
  });
}

// For backwards compatibility
Meteor.publishComposite = publishComposite;

async function prepareOptions(options, args) {
  let preparedOptions = options;

  if (typeof preparedOptions === 'function') {
    preparedOptions = await preparedOptions.apply(this, args);
  }

  if (!preparedOptions) {
    return [];
  }

  if (!Array.isArray(preparedOptions)) {
    preparedOptions = [preparedOptions];
  }

  return preparedOptions;
}

export {
  enableDebugLogging,
  publishComposite
}

Notice that the 'client' calls twice now. The second time it has the correct userId!!!!

I20241013-20:02:16.492(0)? publishComposite called with userId: FQZPiRk46xdECpDpN
I20241013-20:02:16.493(0)? optional is => server
I20241013-20:02:16.494(0)? before return, billingplans this.userId FQZPiRk46xdECpDpN
I20241013-20:02:16.494(0)? before return, billingplans Meteor.userId() FQZPiRk46xdECpDpN
I20241013-20:02:17.328(0)? publishComposite called with userId: null
I20241013-20:02:17.328(0)? optional is => client
I20241013-20:02:17.329(0)? before return, billingplans this.userId null
I20241013-20:02:17.329(0)? before return, billingplans Meteor.userId() null
W20241013-20:02:17.329(0)? (STDERR) billing plans not authorized
I20241013-20:02:17.401(0)? Exception in callback of async function: errorClass [Error]: [not-authorized]
I20241013-20:02:17.401(0)?     at Subscription.find (api/billing/server/publications.js:26:15)
I20241013-20:02:17.401(0)?     at Publication._getCursor (packages/reywood:publish-composite/lib/publication.js:95:36)
I20241013-20:02:17.401(0)?     at Publication.publish (packages/reywood:publish-composite/lib/publication.js:26:30)
I20241013-20:02:17.401(0)?     at packages/reywood:publish-composite/lib/publish_composite.js:20:19
I20241013-20:02:17.401(0)?     at processTicksAndRejections (node:internal/process/task_queues:95:5) {
I20241013-20:02:17.402(0)?   isClientSafe: true,
I20241013-20:02:17.402(0)?   error: 'not-authorized',
I20241013-20:02:17.402(0)?   reason: undefined,
I20241013-20:02:17.402(0)?   details: undefined,
I20241013-20:02:17.402(0)?   errorType: 'Meteor.Error'
I20241013-20:02:17.402(0)? }
I20241013-20:02:17.501(0)? publishComposite called with userId: FQZPiRk46xdECpDpN
I20241013-20:02:17.502(0)? optional is => client
I20241013-20:02:17.502(0)? before return, billingplans this.userId FQZPiRk46xdECpDpN
I20241013-20:02:17.502(0)? before return, billingplans Meteor.userId() FQZPiRk46xdECpDpN

IMO, Somewhere there is a bug that is causing this behavior... I am also running on about an hour of sleep so I may also be overlooking something simple.

Copy link

Thank you for submitting this issue!

We, the Members of Meteor Community Packages take every issue seriously.
Our goal is to provide long-term lifecycles for packages and keep up
with the newest changes in Meteor and the overall NodeJs/JavaScript ecosystem.

However, we contribute to these packages mostly in our free time.
Therefore, we can't guarantee your issues to be solved within certain time.

If you think this issue is trivial to solve, don't hesitate to submit
a pull request, too! We will accompany you in the process with reviews and hints
on how to get development set up.

Please also consider sponsoring the maintainers of the package.
If you don't know who is currently maintaining this package, just leave a comment
and we'll let you know

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant