Gregivy / simpleddp

An easy to use DDP client library
MIT License
165 stars 19 forks source link

Browser DDP call promise never resolves #20

Open hems opened 4 years ago

hems commented 4 years ago

Good work on the package and plugins architecture ( :

Currently i tried:

But funny enough, when i tried to do a simple call to some method the returned promise never resolves.

I investigated a bit of the source code ( specifically the apply method ) and turns out:

Then basically the promise hangs forever and never resolves.

I have some Meteor "native code" using that same Meteor.method and it works normally, so i can confirm it's not a problem on the meteor side.

I'll let you know if I find any other clues about what might be causing that problem.

hems commented 4 years ago

It seems it has something to do with the way arguments are sent.

I tried

server.call("hello", 5) // never resolved, meteor method code never executed
server.call("hello", [5]) // did resolve, method executed
hems commented 4 years ago

The other problem i'm seeing is:

This works:

server.call('hello', [5] )


This does not work:
 - Meteor method gets called
 - Meteor method does some database work and returns an array of objects
 - promise never resolves
 - The method works as expected on the Meteor application, but not on simpleDDP
````js
Meteor.methods({
  hello (limit = 5) {
    // do some database work
    const things = Things.find(myQuery).fetch()
    // actually return an array of items from the database, DONT WORK
    return things
  }
})

server.call('hello', [5] )
hems commented 4 years ago

I found something that seems to nail what the problem is, although i don't know the solution yet:

This does not work:

server.call('hello', [5] )


This works on both, Meteor Application and simpleDDP.
````js
Meteor.methods({
  hello (limit = 5) {
    // do some database work
    const things = Things.find(myQuery).fetch()
    // actually return an array of items from the database, DONT WORK
    return JSON.parse(JSON.stringify(things))
  }
})

server.call('hello', [5] )

A funny thing is that, debugging ddp.js on simpleddp-core i found out that:

``` socket.on("message:in", function (message) {````

Won't receive a message with msg set to result on simpleDDP, while the Meteor application works anyway.

There is something wrong about the communication that for some reason the result don't get received by simpleDDP unless i do something funny with JSON on the server.

hems commented 4 years ago

I just tried exactly same code on node.js:

if i

JSON.parse(JSON.stringify(response))

Then it works as expected on node.js and on the browser.


The puzzling thing is that my Meteor application works as expected.

I'm using Meteor 1.8.1

Have you experienced anything similar?


I'll give up on debugging it further because I need to carry on with my app, let me know if you have experienced something similar or have an idea on how to fix it.

I can provide you with a sample app privately if you let me know your email.

Gregivy commented 4 years ago

Really strange issue with call method. If your Meteor method accepts one argument for example, and you pass it as [5], your Meteor method should receive it as [5]. About the second issue it seems a problem with EJSON. Please provide the browser and its version and the version of simpleDDP you are using.

Gregivy commented 4 years ago

You can create a secret gist of your Meteor method and your simpleDDP code then send me the link here gpoi.mail@gmail.com

dennishu001 commented 4 years ago

I've got the same problem. Fortunately for me, I only got problems when requesting certain documents. Upon comparing the difference with other document, I found the problematic ones got ObjectIDs from workers using mongoose outside of Meteor.

The DDP client seems have trouble parsing non standard data object, hence JSON.stringify/JSON.parse can mitigate the problem. You may want to check if this is the case.

Gregivy commented 4 years ago

It could be possible. Both Meteor DDP and simpleDDP uses EJSON not JSON (https://docs.meteor.com/api/ejson.html). However Mongo.ObjectID is not included in EJSON by default, so you should do it manually (read more here https://github.com/Gregivy/simpleddp/blob/master/custom_ejson.md):

import EJSON from 'ejson';

class MongoObjectId {
  constructor(value) {
    this.value = value;
  }

  // Convert our type to JSON.
  toJSONValue() {
    return this.value;
  }

  // Unique type name.
  typeName() {
    return 'oid';
  }
}

EJSON.addType('oid', function fromJSONValue(str) {
  return new MongoObjectId(str);
});

Do it before creating simpleDDP instance. If you also want to create MongoId on client and send it on server, the MongoObjectId class would become more complicated (see https://github.com/meteor/meteor/blob/d8042c8fa9dd0cc53604b2729ac33333a2ce5b73/packages/mongo-id/id.js)

hems commented 4 years ago

Really strange issue with call method. If your Meteor method accepts one argument for example, and you pass it as [5], your Meteor method should receive it as [5].

Turns out this was a bug on my abstraction ( as you could imagine ), so that's solved for me.

hems commented 4 years ago

It could be possible. Both Meteor DDP and simpleDDP uses EJSON not JSON (https://docs.meteor.com/api/ejson.html). However Mongo.ObjectID is not included in EJSON by default, so you should do it manually (read more here https://github.com/Gregivy/simpleddp/blob/master/custom_ejson.md):

Yes indeed it seems to be something related to this issue.

The funny part tough is that Meteor version of the DDP Client don't have this issue ( as i said, the same subscription that got stuck for me with DDPClient did not get stuck on the Meteor Client), i wonder if there is an easy way to "copy" the solution Meteor DDP uses and implement it on the DDP Client transparently?

Gregivy commented 4 years ago

@hems I think it will be easier if you publish here the structure with data types of the object because I am not sure about easy copying of all custom ejsons :(