swagger icon indicating copy to clipboard operation
swagger copied to clipboard

feat(IntersectionType): add support for composing multiple classes

Open wagerfield opened this issue 2 years ago • 2 comments

PR Checklist

Please check if your PR fulfills the following requirements:

  • [x] The commit message follows our guidelines: https://github.com/nestjs/nest/blob/master/CONTRIBUTING.md
  • [x] Tests for the changes have been added (for bug fixes / features)
  • [ ] Docs have been added / updated (for bug fixes / features)
    • Potentially the OpenAPI / Mapped Types / Intersection docs and example could be updated to show that the updated IntersectionType mapper now supports multiple classes.
    • Happy to open another PR for the docs.nestjs.com repo if that would be desirable.

PR Type

What kind of change does this PR introduce?

  • [ ] Bugfix
  • [x] Feature
  • [ ] Code style update (formatting, local variables)
  • [x] Refactoring (no functional changes, no api changes)
  • [ ] Build related changes
  • [ ] CI related changes
  • [ ] Other... Please describe:

What is the current behavior?

Issue Number: N/A

The current Swagger IntersectionType mapper only supports passing 2 classes eg.

class CreateUserDto {
  @ApiProperty({ required: true })
  @IsEmail()
  login: string

  @ApiProperty({ required: true })
  @MinLength(8)
  password: string
}

class UserDto {
  @ApiProperty({ required: true })
  @IsString()
  firstName: string

  @ApiProperty({ required: true })
  @IsString()
  lastName: string
}

class UpdateUserDto extends IntersectionType(UserDto, CreateUserDto)

You cannot pass more than 2 classes eg.

class MappedClass1 extends IntersectionType(Class1, Class2, Class3) // Expected 2 arguments, but got 3.
class MappedClass2 extends IntersectionType(Class1, Class2, Class3, Class4) // Expected 2 arguments, but got 4.

What is the new behavior?

  1. The updated Swagger IntersectionType mapper supports 2-8 classes
class MappedClass1 extends IntersectionType(Class1, Class2) // sure
class MappedClass2 extends IntersectionType(Class1, Class2, Class3) // nice
class MappedClass3 extends IntersectionType(Class1, Class2, Class3, Class4) // keep going
class MappedClass4 extends IntersectionType(Class1, Class2, Class3, Class4, Class5, Class6, Class7, Class8) // no sweat

The implementation actually supports an infinite number of classes (just like @nestjs/mapped-types does) ...however I have only added interfaces for up to 8 classes...hopefully that will be suffice!

Does this PR introduce a breaking change?

  • [ ] Yes
  • [x] No

Other information

I have leveraged the IntersectionType mapper function from @nestjs/mapped-types rather than rewriting that logic as it had been implemented previously.

The upside to this approach is it reduces the surface area of the Swagger IntersectionType mapper—leveraging the existing implementation and test suite.

The downside is that there are a couple of loops over the passed classes—one for the call to the mapped-types IntersectionType mapper and another to add the Swagger OpenAPI decorators in this IntersectionType mapper.

However this really is a moot point given that the number of classes will be at most 8 😅

Essentially this solution just augments the target class with the Swagger/OpenAPI related decorators. It leaves the validation and transformation inheritance to the IntersectionType function from mapped-types.

Finally, I have added another test to validate that the class-validator decorators are working as expected when composing 3 classes together. This is somewhat duplicative of the tests in mapped-types but I don't think it hurts to be extra safe!

wagerfield avatar May 25 '22 10:05 wagerfield

Would this be the same as IntersectionType(IntersectionType(Class1, Class2), Class3) etc?

jmcdo29 avatar May 25 '22 15:05 jmcdo29

@jmcdo29 correct ✅

wagerfield avatar May 25 '22 17:05 wagerfield

https://github.com/nestjs/swagger/pull/2227

kamilmysliwiec avatar Feb 06 '23 10:02 kamilmysliwiec