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

Call 'toJSON' if present for ID and String serialize #1520

Merged
merged 1 commit into from
Oct 11, 2018

Conversation

IvanGoncharov
Copy link
Member

@IvanGoncharov IvanGoncharov commented Sep 11, 2018

Fixes #1518

class ObjectId doesn't have valueOf only toString and toJSON:
https://github.com/mongodb/js-bson/blob/5acdebf7a63ed02d3bc1eb8481cdcb709fb5c886/lib/objectid.js#L213-L215

We can't use toString because:

class A {}
const a = new A();
a.toString()
// '[object Object]'

So I added support for toJSON.

@IvanGoncharov
Copy link
Member Author

@alexmcmillan I don't have access to MongoDB so I can't run: https://github.com/alexmcmillan/graphql-mongodb-query-fail-demo

Can you please test it with attached package:
graphql-14.0.2.zip
Just unpack it inside node_modules.

@alexmcmillan
Copy link

@IvanGoncharov Can confirm the package you attached does resolve the problem. Thank you - I hope this gets merged soon!

@IvanGoncharov
Copy link
Member Author

@alexmcmillan Thanks 👍
@mjmahone Can you please review?
mongoose is a pretty popular library so I think it's worth to make 14.1.0.
What do you think?

Copy link
Contributor

@mjmahone mjmahone left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good to me, thanks for fixing it!

const result =
value && typeof value.valueOf === 'function' ? value.valueOf() : value;
// Support serializing objects with custom toJSON() or valueOf() functions -
// a common way to represent an complex value which can be represented as
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

grammar nit: "a complex value" instead of "an complex value"

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks 👍
Fixed

@mjmahone
Copy link
Contributor

mjmahone commented Sep 14, 2018

Prior to doing the bump to 14.1.0 I think we'd want @leebyron to help fix our NPM push process: I don't want to accidentally mess up another release, and I don't want Lee to be on the hook for overseeing and validating each patch. Can the version bump wait until the next WG meeting? It's probably worth figuring out with everyone in the same (virtual) room, a process that makes sense. I want us to be able to make fast decisions on situations like this, but for instance I'm not sure why this would be a minor release rather than a patch, and I don't currently have the right framework for figuring out that answer. On the other hand, @IvanGoncharov, you may have that framework well thought-out, which is why I'd like to make sure we can make these decisions quickly, without appealing to "higher authorities".

@IvanGoncharov
Copy link
Member Author

Prior to doing the bump to 14.1.0 I think we'd want @leebyron to help fix our NPM push process: I don't want to accidentally mess up another release, and I don't want Lee to be on the hook for overseeing and validating each patch

Agree, we definitely need to improve bus factor 👍

Can the version bump wait until the next WG meeting?

Yes 👍

I'm not sure why this would be a minor release rather than a patch, and I don't currently have the right framework for figuring out that answer.

AFAIK, it covered by semver:
https://semver.org/#spec-item-7

It MAY be incremented if substantial new functionality or improvements are introduced within the private code.

My personal rule of thumb: if you add something new that clients explicitly depend on it means you need to do minor release. So if someone write the resolvers that depend on toJSON that mean this will work only on >= 14.1.0 and if for somereason we release 14.0.3 it wouldn't contain this feature.

It also important for 3rd tools like graphql-tools and other graphql middlewares since they wrapping resolvers and may be surprised by object with toJSON so they have an option to support only 14.0.x and be shielded from new features.

So for me this change is not a bug fix but a new functionality especially since 0.13.x and previous version didn't use toJSON (they used toString).

// a common way to represent a complex value which can be represented as
// a string (ex: MongoDB id objects).
function serializeObject(value: mixed): mixed {
if (typeof value === 'object' && value !== null) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!= nulll

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

> typeof undefined
'undefined'

@ruiaraujo So it will fail on the first check.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed, missed that.

// a string (ex: MongoDB id objects).
function serializeObject(value: mixed): mixed {
if (typeof value === 'object' && value !== null) {
if (typeof value.toJSON === 'function') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is actually breaking - there may be objects which define both valueOf and toJSON which provide different values. If those objects previously worked correctly, this PR would cause them to work incorrectly.

If the goal is to also support objects which define toJSON but do not define valueOf then this could be easily solved by just moving this new addition to below the existing check for valueOf

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did that because:

> typeof ({}).valueOf
'function'

But I agree it breaking so I updated this PR to have this instead:

    if (typeof value.valueOf === 'function') {
      const valueOfResult = value.valueOf();
      if (typeof valueOfResult !== 'object') {
        return valueOfResult;
      }
    }
    if (typeof value.toJSON === 'function') {
      return value.toJSON();
    }

I also changed tests to explicitly test precidence.
Can you please review new code and tests?

@IvanGoncharov IvanGoncharov mentioned this pull request Oct 3, 2018
@IvanGoncharov IvanGoncharov added this to the v14.1.0 milestone Oct 3, 2018
@IvanGoncharov
Copy link
Member Author

@mjmahone Since you reviewed this PR and I already addressed @leebyron comments I merging this issue, so people can atleast use our npm branch.
Would be great to disscuss the scope for 14.1.0 release, can you please comment in #1543 ?

@IvanGoncharov IvanGoncharov merged commit 3aef662 into graphql:master Oct 11, 2018
@IvanGoncharov IvanGoncharov deleted the scalarsToJSON branch October 11, 2018 21:16
@xabra
Copy link

xabra commented Oct 14, 2018

I am having this problem also. Using "graphql": "^14.0.2"
"ID cannot represent value: { _bsontype: "ObjectID"
In a query resolver such as below, can someone explain what I do to fix it?
Thanks

    user: async (parent, args, context, info) => {
      let user = await context.UserModel.findOne(args);
      return user;
    }

@frankRose1
Copy link

I am having this problem also. Using "graphql": "^14.0.2"
"ID cannot represent value: { _bsontype: "ObjectID"
In a query resolver such as below, can someone explain what I do to fix it?
Thanks

    user: async (parent, args, context, info) => {
      let user = await context.UserModel.findOne(args);
      return user;
    }

I know its a bit of a late response but In regards to xabra's post try this:

user: async(parent, args, ctx, info){
     let user = await ctx.UserModel.findOne(args);
     return { ...user._doc, _id: user._id.toString() };
}

@EmiyaYang
Copy link

EmiyaYang commented Jan 12, 2020

Well, I have no problems to query the _id field. But the same error happens while I try to populate sth.

  function findOne(id: string) {
    const res = this.nodeModel.findOne({ id }).populate('parent');

    return res;
  }

Finally I have this problem solved. The main issue is that Document.prototype.toJSON() return an object instead of a string. REF: document_Document-toJSON

As a workaround, set a transformer like this:

NodeSchema.set('toJSON', {
  transform(doc, ret) {
    return JSON.stringify(ret);
  },
});

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

Successfully merging this pull request may close these issues.

Unable to query for MongoDB ObjectIDs
9 participants