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

[BUG] DynamicRef serializes to [object Object] #1767

Closed
1 task
ochrstn opened this issue Feb 17, 2022 · 11 comments
Closed
1 task

[BUG] DynamicRef serializes to [object Object] #1767

ochrstn opened this issue Feb 17, 2022 · 11 comments

Comments

@ochrstn
Copy link
Contributor

ochrstn commented Feb 17, 2022

Information

  • Version: 6.102.1
  • Packages: mongoose

I can at least say that it worked in 5.59.2 (thats where I upgraded from 😅 )

Example

for full example, see: https://github.com/ochrstn/tsed/blob/fix-dynamicref-serialize-object/packages/orm/mongoose/test/dynamicRef.integration.spec.ts

describe("DynamicRef Integration", () => {
  @Model()
  class ClickedLinkEventModel {
    @ObjectID("id")
    _id: string;

    @Required()
    url: string;
  }

  @Model()
  class SignedUpEventModel {
    @ObjectID("id")
    _id: string;

    @Required()
    user: string;
  }

  @Model()
  class EventModel {
    @ObjectID("id")
    _id: string;

    @DynamicRef("eventType")
    event: Ref<ClickedLinkEventModel | SignedUpEventModel>;

    @Enum("ClickedLinkEventModel", "SignedUpEventModel")
    eventType: "ClickedLinkEventModel" | "SignedUpEventModel";
  }

  beforeEach(TestMongooseContext.bootstrap(Server));
  afterEach(TestMongooseContext.clearDatabase);
  afterEach(TestMongooseContext.reset);

  it("should serialize", async () => {
    const EventRepository = TestMongooseContext.get<MongooseModel<EventModel>>(EventModel);
    const ClickedEventRepository = TestMongooseContext.get<MongooseModel<ClickedLinkEventModel>>(ClickedLinkEventModel);
    const SignedUpEventRepository = TestMongooseContext.get<MongooseModel<SignedUpEventModel>>(SignedUpEventModel);

    const clickedEvent = await new ClickedEventRepository({url: "https://www.tsed.io"}).save();
    const event1 = await new EventRepository({eventType: "ClickedLinkEventModel", event: clickedEvent}).save();
    expect(serialize(event1, {type: EventModel})).toStrictEqual({
      id: event1.id,
      eventType: "ClickedLinkEventModel",
      event: {id: clickedEvent.id, url: "https://www.tsed.io"}
    });
    // bug: event serializes to "[object Object]"

    const signedUpEvent = await new SignedUpEventRepository({user: "test"}).save();
    const event2 = await new EventRepository({eventType: "SignedUpEventModel", event: clickedEvent}).save();
    expect(serialize(event2, {type: EventModel})).toStrictEqual({
      id: event2.id,
      eventType: "SignedUpEventModel",
      event: {id: signedUpEvent.id, user: "test"}
    });
    // bug: event serializes to "[object Object]"
  });
});

Acceptance criteria

  • DynamicRef should serialize the (populated) object
Romakita added a commit that referenced this issue Feb 23, 2022
@Romakita
Copy link
Collaborator

Hello @ochrstn

I created a pr to fix this issue. But I'm not sure about the result:
#1777

One thing that is strange, is about the @Property(String) used in th DynamicRef. This decorator wasn't added here for nothing. DynamicRef is specific kinf of Ref right ? So DymanicRef can return an object or a string, if the populare method is called or not.

Another things, is about the result. the id keep the _id. It's normal, because mongoose doesn't give a real class Object. and there is no metadata to retrieve the correct class according to the type field. So the question is, how it's work in v5 (I'm not sure that this point worked before or not).

See you
Romain

@ochrstn
Copy link
Contributor Author

ochrstn commented Feb 25, 2022

I created a pr to fix this issue. But I'm not sure about the result:
#1777

Thanks for looking into it.

DynamicRef is specific kinf of Ref right ? So DymanicRef can return an object or a string, if the populare method is called or not.

Correct

Another things, is about the result. the id keep the _id. It's normal, because mongoose doesn't give a real class Object. and there is no metadata to retrieve the correct class according to the type field. So the question is, how it's work in v5 (I'm not sure that this point worked before or not).

I just tried that in the old version and (to my own surprise) it does work. _id of the dynamic reference serializes to id

@Romakita
Copy link
Collaborator

Arff… so I don’t why it doesn’t work as expected now…

@ochrstn
Copy link
Contributor Author

ochrstn commented Feb 25, 2022

I did some debugging and in v5.x the actual class behind the populated dynamic ref is known at this point:

type: getClass(target),

and therefore can be serialized correctly

Romakita added a commit that referenced this issue Mar 10, 2022
@Romakita
Copy link
Collaborator

I don't know how is it possible... Mongoose doesn't return the class behind the object:

Capture d’écran 2022-03-10 à 07 50 26

In my capture, the EventModel is here, but we see that the event instance value isn't an instance of SignedUpEventRepository, but it's an object. I haven't metadata to infer correctly the model. If it's works in v5, it was probably a mistake...

@ochrstn
Copy link
Contributor Author

ochrstn commented Mar 10, 2022

How it works to my understanding in 5.x (not sure about my wording 😅 ):

When the @Model() get evaluated the target gets captured in the target.prototype.serialize closure. That function is then later called for serialization.

target.prototype.serialize = function (options: IConverterOptions, converter: ConverterService) {
const {checkRequiredValue, ignoreCallback, withIgnoredProps = true} = options;
return converter.serializeClass(this, {
type: getClass(target),
checkRequiredValue,
ignoreCallback,
withIgnoredProps
});
};

Like you said, the instance obtained by mongoose isn't an instance of the class but calling this.converterService.serialize(mongooseResult) calls the serialize function shown above which has the class information.

Where does the serialize function of then mongoose object come from? I thought from a custom transform function toObject.transform set on the schema (https://mongoosejs.com/docs/guide.html#toObject https://mongoosejs.com/docs/api.html#document_Document-toObject) but I couldn't find it in the code being used

@Romakita
Copy link
Collaborator

I finally found a better way to fix that :)

@Romakita
Copy link
Collaborator

Here is the PR:
#1777

using OnDeserialize and OnSerialize, it works and you retrieve the correct typings + the JSONSchema is aligned with the models declaration.

I'll merge it
Romain

@Romakita
Copy link
Collaborator

Sorry for the delay @ochrstn ;) this issue wasn't easy ^^

@Romakita
Copy link
Collaborator

🎉 This issue has been resolved in version 6.105.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

@Romakita
Copy link
Collaborator

🎉 This issue has been resolved in version 7.0.0-beta.4 🎉

The release is available on:

Your semantic-release bot 📦🚀

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

No branches or pull requests

2 participants