Skip to content

Commit 28fb0ba

Browse files
authoredApr 3, 2024··
Merge branch 'nestjs:master' into feature/add-more-apiresponse
2 parents 5a0020e + eedd401 commit 28fb0ba

File tree

4 files changed

+949
-922
lines changed

4 files changed

+949
-922
lines changed
 

‎lib/type-helpers/partial-type.helper.ts

+23-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Type } from '@nestjs/common';
22
import {
33
applyIsOptionalDecorator,
4+
applyValidateIfDefinedDecorator,
45
inheritPropertyInitializers,
56
inheritTransformationMetadata,
67
inheritValidationMetadata
@@ -15,7 +16,25 @@ import { clonePluginMetadataFactory } from './mapped-types.utils';
1516

1617
const modelPropertiesAccessor = new ModelPropertiesAccessor();
1718

18-
export function PartialType<T>(classRef: Type<T>): Type<Partial<T>> {
19+
export function PartialType<T>(
20+
classRef: Type<T>,
21+
/**
22+
* Configuration options.
23+
*/
24+
options: {
25+
/**
26+
* If true, validations will be ignored on a property if it is either null or undefined. If
27+
* false, validations will be ignored only if the property is undefined.
28+
* @default true
29+
*/
30+
skipNullProperties?: boolean;
31+
} = {}
32+
): Type<Partial<T>> {
33+
const applyPartialDecoratorFn =
34+
options.skipNullProperties === false
35+
? applyValidateIfDefinedDecorator
36+
: applyIsOptionalDecorator;
37+
1938
const fields = modelPropertiesAccessor.getModelProperties(classRef.prototype);
2039

2140
abstract class PartialTypeClass {
@@ -30,7 +49,7 @@ export function PartialType<T>(classRef: Type<T>): Type<Partial<T>> {
3049
if (keysWithValidationConstraints) {
3150
keysWithValidationConstraints
3251
.filter((key) => !fields.includes(key))
33-
.forEach((key) => applyIsOptionalDecorator(PartialTypeClass, key));
52+
.forEach((key) => applyPartialDecoratorFn(PartialTypeClass, key));
3453
}
3554

3655
inheritTransformationMetadata(classRef, PartialTypeClass);
@@ -48,7 +67,7 @@ export function PartialType<T>(classRef: Type<T>): Type<Partial<T>> {
4867
PartialTypeClass[METADATA_FACTORY_NAME]()
4968
);
5069
pluginFields.forEach((key) =>
51-
applyIsOptionalDecorator(PartialTypeClass, key)
70+
applyPartialDecoratorFn(PartialTypeClass, key)
5271
);
5372
}
5473

@@ -65,7 +84,7 @@ export function PartialType<T>(classRef: Type<T>): Type<Partial<T>> {
6584
required: false
6685
});
6786
decoratorFactory(PartialTypeClass.prototype, key);
68-
applyIsOptionalDecorator(PartialTypeClass, key);
87+
applyPartialDecoratorFn(PartialTypeClass, key);
6988
});
7089
}
7190
applyFields(fields);

‎package-lock.json

+874-901
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+17-17
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@nestjs/swagger",
3-
"version": "7.3.0",
3+
"version": "7.3.1",
44
"description": "Nest - modern, fast, powerful node.js web framework (@swagger)",
55
"author": "Kamil Mysliwiec",
66
"license": "MIT",
@@ -32,32 +32,32 @@
3232
"swagger-ui-dist": "5.11.2"
3333
},
3434
"devDependencies": {
35-
"@commitlint/cli": "18.6.0",
36-
"@commitlint/config-angular": "18.6.0",
37-
"@fastify/static": "7.0.1",
38-
"@nestjs/common": "10.3.2",
39-
"@nestjs/core": "10.3.2",
40-
"@nestjs/platform-express": "10.3.2",
41-
"@nestjs/platform-fastify": "10.3.2",
35+
"@commitlint/cli": "19.2.1",
36+
"@commitlint/config-angular": "19.1.0",
37+
"@fastify/static": "7.0.2",
38+
"@nestjs/common": "10.3.7",
39+
"@nestjs/core": "10.3.7",
40+
"@nestjs/platform-express": "10.3.7",
41+
"@nestjs/platform-fastify": "10.3.7",
4242
"@types/jest": "29.5.12",
4343
"@types/js-yaml": "4.0.9",
44-
"@types/lodash": "4.14.202",
45-
"@types/node": "20.11.17",
46-
"@typescript-eslint/eslint-plugin": "6.21.0",
47-
"@typescript-eslint/parser": "6.21.0",
44+
"@types/lodash": "4.17.0",
45+
"@types/node": "20.12.3",
46+
"@typescript-eslint/eslint-plugin": "7.5.0",
47+
"@typescript-eslint/parser": "7.5.0",
4848
"class-transformer": "0.5.1",
4949
"class-validator": "0.14.1",
50-
"eslint": "8.56.0",
50+
"eslint": "8.57.0",
5151
"eslint-config-prettier": "9.1.0",
5252
"eslint-plugin-import": "2.29.1",
53-
"express": "4.18.2",
54-
"husky": "9.0.10",
53+
"express": "4.19.2",
54+
"husky": "9.0.11",
5555
"jest": "29.7.0",
5656
"lint-staged": "15.2.2",
5757
"openapi-types": "12.1.3",
5858
"prettier": "2.8.8",
59-
"reflect-metadata": "0.2.1",
60-
"release-it": "17.0.3",
59+
"reflect-metadata": "0.2.2",
60+
"release-it": "17.1.1",
6161
"supertest": "6.3.4",
6262
"swagger-parser": "10.0.3",
6363
"ts-jest": "29.1.2",

‎test/type-helpers/partial-type.helper.spec.ts

+35
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,44 @@ describe('PartialType', () => {
2121
describe('Validation metadata', () => {
2222
it('should apply @IsOptional to properties reflected by the plugin', async () => {
2323
const updateDto = new UpdateUserDto();
24+
updateDto.firstName = null;
2425
const validationErrors = await validate(updateDto);
2526
expect(validationErrors).toHaveLength(0);
2627
});
28+
29+
it('should apply @IsOptional to properties reflected by the plugin if option `skipNullProperties` is true', async () => {
30+
class UpdateUserWithNullableDto extends PartialType(CreateUserDto, {
31+
skipNullProperties: true
32+
}) {}
33+
const updateDto = new UpdateUserWithNullableDto();
34+
updateDto.firstName = null;
35+
const validationErrors = await validate(updateDto);
36+
expect(validationErrors).toHaveLength(0);
37+
});
38+
39+
it('should apply @IsOptional to properties reflected by the plugin if option `skipNullProperties` is undefined', async () => {
40+
class UpdateUserWithoutNullableDto extends PartialType(
41+
CreateUserDto,
42+
{}
43+
) {}
44+
const updateDto = new UpdateUserWithoutNullableDto();
45+
updateDto.firstName = null;
46+
const validationErrors = await validate(updateDto);
47+
expect(validationErrors).toHaveLength(0);
48+
});
49+
50+
it('should apply @ValidateIf to properties reflected by the plugin if option `skipNullProperties` is false', async () => {
51+
class UpdateUserWithoutNullableDto extends PartialType(CreateUserDto, {
52+
skipNullProperties: false
53+
}) {}
54+
const updateDto = new UpdateUserWithoutNullableDto();
55+
updateDto.firstName = null;
56+
const validationErrors = await validate(updateDto);
57+
expect(validationErrors).toHaveLength(1);
58+
expect(validationErrors[0].constraints).toEqual({
59+
isString: 'firstName must be a string'
60+
});
61+
});
2762
});
2863
describe('OpenAPI metadata', () => {
2964
it('should return partial class', async () => {

0 commit comments

Comments
 (0)
Please sign in to comment.