Skip to content

feat: add a useResponseClassTransformer global option #329

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 8 commits into from
Jul 21, 2020

Conversation

epiphone
Copy link

Hi, first of all thanks for a great library!

By default response values are coerced to plain objects with class-transformer. When returning large objects (#226) or values with complex serialization logic (e.g. Mongoose documents #149) you might opt for the default toJSON handler instead.

This PR adds a global useResponseClassTransformer option parameter for toggling response transformation. It defaults to true, and is overridden by classTransformer: false.

Also related to #179.

@@ -129,14 +135,14 @@ export abstract class BaseDriver {

protected transformResult(result: any, action: ActionMetadata, options: Action): any {
// check if we need to transform result
const shouldTransform = (this.useClassTransformer && result != null) // transform only if enabled and value exist
const shouldTransform = (this.useClassTransformer && this.useResponseClassTransformer && result != null) // transform only if enabled and value exist
Copy link
Contributor

Choose a reason for hiding this comment

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

not updated comment

@MichalLytek
Copy link
Contributor

Isn't that the feature that @NoNameProvided promised to implement a few months ago? 😉

@NoNameProvided
Copy link
Member

Yes, it was. Shame I haven't had the time. I haven't reviewed this either because I think we had agreed on a slightly different approach, but I am not sure, and I don't have time to read it back now. I have a lot of thing on my table now, so I struggle to find any time to spend on my hobbies (including this lib) right now.

@epiphone
Copy link
Author

epiphone commented Dec 4, 2017

Hi, I updated the PR to better match with @NoNameProvided's solution outlined here, i.e. added transformRequest and transformResponse options to controller and route decorators. Gotta agree this approach seems more future-proof 😄

What do you think?

Copy link
Contributor

@MichalLytek MichalLytek left a comment

Choose a reason for hiding this comment

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

Please refactor your test entities to make sure that class-transformer option works - use @Exclude and @Expose decorator on class properties instead of toJSON.

/**
* Extra options that apply to each controller action.
*/
export interface ControllerOptions extends TransformationOptions {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that you should implement TransformationOptions, not extending it - it would be ready for future extension/changes because you can't extend multiple interfaces.

assertRequest([3001, 3002], "post", "default", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => {
expect(initializedUser).to.be.instanceOf(User);
expect(response).to.have.status(200);
expect(response.body.lastName).to.be.defined;
Copy link
Contributor

Choose a reason for hiding this comment

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

You've set transformResponse: false but you expect lastName to be defined even if lastName is excluded when class-transformer is disabled... I don't get it 😕

}

@JsonController("/transform", {transformRequest: true, transformResponse: true})
class NoResponseTransformController {
Copy link
Contributor

Choose a reason for hiding this comment

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

NoResponseTransformController? No? 😆

assertRequest([3001, 3002], "post", "default", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => {
expect(initializedUser).to.be.instanceOf(User);
expect(response).to.have.status(200);
expect(response.body.lastName).to.be.defined;
Copy link
Contributor

@MichalLytek MichalLytek Dec 4, 2017

Choose a reason for hiding this comment

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

  • default: ...lastName).to.be.defined;
  • enable: ...lastName).not.to.be.undefined;
    Please be consistent with assertion, don't negate the negative expectation 😉

assertRequest([3001, 3002], "post", "override", { firstName: "Umed", lastName: "Khudoiberdiev" }, response => {
expect(initializedUser).not.to.be.instanceOf(User);
expect(response).to.have.status(200);
expect(response.body.lastName).to.be.defined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't you use .exist here? I can't see defined in chai documentation 😕

@epiphone
Copy link
Author

epiphone commented Dec 5, 2017

Thanks for the feedback @19majkel94, lots of good points in there 👍 . I refactored the tests, let me know if there's anything else you'd like to see changed.

I did have some trouble with class-transformer's global metadata storage: calling defaultMetadataStorage.clear() (here for example) deletes the class-transformer metadata of all defined classes, so I had to move the User class definitions inside a before-block and assign it to a variable in the outer scope. The end result is still reasonably clean, I think, but let me know if you'd have a better solution.

@MichalLytek
Copy link
Contributor

MichalLytek commented Dec 5, 2017

Yeah, it clears because you call clear 😆 But I don't see the need to do change this - pleerock's configuration worked well, all metadata was keeped, only after all tests the storage was cleared. Why you had to change it?

@epiphone
Copy link
Author

epiphone commented Dec 6, 2017

Because the way it was set up, after a test module finished it would not only clear the metadata of its own test classes, but also of all the classes declared in the following tests. For example after class-transformer-options.ts finished, it wiped out User's metadata in global-options.spec.ts which would break the test. That's why I ended up delaying the class declarations until the before block; I'd be happy to refactor it though if you have an alternative solution in mind.

@MichalLytek
Copy link
Contributor

So the problem was that pleerock uses the class-transformer only in one test so clearing metadata wasn't affect other tests but now you have tests also in other files so it clears the metadata of all tests, right? 😄

@epiphone
Copy link
Author

epiphone commented Dec 7, 2017

Exactly 👍 . I took a peek at class-transformer tests, seems like they're handling things in a similar way, declaring classes in the upper describe and it blocks.

@MichalLytek
Copy link
Contributor

Ok, you have user.lastName || "default"; in your tests but I can't see a test case that is using it. Please make a one more test case that have transformRequest: true and transformResponse: false to see that it's working and we are ready to merge 😉

@epiphone
Copy link
Author

epiphone commented Dec 26, 2017

Hey, sorry for the delay, got kinda busy over the holidays! I added the test case and removed user.lastName = "default"; where it's unused.

Copy link
Contributor

@MichalLytek MichalLytek left a comment

Choose a reason for hiding this comment

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

LGTM!

@MichalLytek
Copy link
Contributor

I'm not sure if @pleerock will like the options object inside decorator so I will wait for his review before merging this PR 😉

@vekexasia
Copy link

Hello, i'm jumping here to understand if this was holded back due to an alternative way to use both JSON controllers and "Raw" controllers that returns buffers

@epiphone
Copy link
Author

I'm also curious about what's holding this back. Care to comment @NoNameProvided ?

@f4z3k4s
Copy link

f4z3k4s commented Jan 17, 2019

@pleerock, @NoNameProvided could you please have a look at this? This is still an existing issue :(

@kunokdev
Copy link

Is there anything happening regarding this?

@CSLTech
Copy link

CSLTech commented Jun 29, 2019

Is there still something holding this back @NoNameProvided @pleerock ?

@github-actions
Copy link

Stale pull request message

@jotamorais jotamorais added this to the 0.8.x release milestone Feb 12, 2020
@MohammedSiddiqui
Copy link

What's the update on this ? 😅

@hallsamuel90
Copy link

Also curious about the status of this?

@jotamorais
Copy link
Member

@hallsamuel90, @MohammedSiddiqui @epiphone, @CSLTech, @kunokdev, @f4z3k4s, @vekexasia, @MichalLytek.

I'm planning to merge this PR and release this tomorrow night among some other fixes:

@yurkagon
Copy link

yurkagon commented Jul 7, 2020

Still waiting :)

@jotamorais jotamorais merged commit cd26ae1 into typestack:master Jul 21, 2020
@NoNameProvided NoNameProvided changed the title Add a useResponseClassTransformer global option feat: add a useResponseClassTransformer global option Aug 9, 2020
@typestack typestack locked as resolved and limited conversation to collaborators Aug 9, 2020
# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
None yet
Development

Successfully merging this pull request may close these issues.