Skip to content

Commit b49a9a1

Browse files
committed
feature(common): add transformOptions to ValidationPipeOptions
allows plainToClass to expose class properties to defined groups as per issue nestjs#1374
1 parent ff2e310 commit b49a9a1

2 files changed

Lines changed: 55 additions & 5 deletions

File tree

packages/common/pipes/validation.pipe.ts

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import {
66
ValidationError,
77
} from '../index';
88
import { ValidatorOptions } from '../interfaces/external/validator-options.interface';
9+
import { ClassTransformOptions } from '../interfaces/external/class-transform-options.interface';
910
import { PipeTransform } from '../interfaces/features/pipe-transform.interface';
1011
import { loadPackage } from '../utils/load-package.util';
1112
import { isNil } from '../utils/shared.utils';
1213

1314
export interface ValidationPipeOptions extends ValidatorOptions {
1415
transform?: boolean;
1516
disableErrorMessages?: boolean;
17+
transformOptions?: ClassTransformOptions;
1618
exceptionFactory?: (errors: ValidationError[]) => any;
1719
}
1820

@@ -24,13 +26,20 @@ export class ValidationPipe implements PipeTransform<any> {
2426
protected isTransformEnabled: boolean;
2527
protected isDetailedOutputDisabled?: boolean;
2628
protected validatorOptions: ValidatorOptions;
29+
protected transformOptions: ClassTransformOptions;
2730
protected exceptionFactory: (errors: ValidationError[]) => any;
2831

2932
constructor(@Optional() options?: ValidationPipeOptions) {
3033
options = options || {};
31-
const { transform, disableErrorMessages, ...validatorOptions } = options;
34+
const {
35+
transform,
36+
disableErrorMessages,
37+
transformOptions,
38+
...validatorOptions
39+
} = options;
3240
this.isTransformEnabled = !!transform;
3341
this.validatorOptions = validatorOptions;
42+
this.transformOptions = transformOptions;
3443
this.isDetailedOutputDisabled = disableErrorMessages;
3544
this.exceptionFactory =
3645
options.exceptionFactory ||
@@ -52,6 +61,7 @@ export class ValidationPipe implements PipeTransform<any> {
5261
const entity = classTransformer.plainToClass(
5362
metatype,
5463
this.toEmptyIfNil(value),
64+
this.transformOptions,
5565
);
5666
const errors = await classValidator.validate(entity, this.validatorOptions);
5767
if (errors.length > 0) {
@@ -60,8 +70,8 @@ export class ValidationPipe implements PipeTransform<any> {
6070
return this.isTransformEnabled
6171
? entity
6272
: Object.keys(this.validatorOptions).length > 0
63-
? classTransformer.classToPlain(entity)
64-
: value;
73+
? classTransformer.classToPlain(entity, this.transformOptions)
74+
: value;
6575
}
6676

6777
private toValidate(metadata: ArgumentMetadata): boolean {

packages/common/test/pipes/validation.pipe.spec.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,22 @@ import { expect } from 'chai';
33
import { ArgumentMetadata } from '../../interfaces';
44
import { IsString } from 'class-validator';
55
import { ValidationPipe } from '../../pipes/validation.pipe';
6+
import { Exclude, Expose } from 'class-transformer';
67

8+
@Exclude()
79
class TestModel {
810
constructor() {}
9-
@IsString() public prop1: string;
11+
@Expose()
12+
@IsString()
13+
public prop1: string;
1014

11-
@IsString() public prop2: string;
15+
@Expose()
16+
@IsString()
17+
public prop2: string;
18+
19+
@Expose({ groups: ['internal'] })
20+
@IsString()
21+
public propInternal: string;
1222
}
1323

1424
describe('ValidationPipe', () => {
@@ -68,6 +78,36 @@ describe('ValidationPipe', () => {
6878
expect(target.transform(testObj, metadata)).to.eventually.throw;
6979
});
7080
});
81+
describe('when transformation is internal', () => {
82+
it('should return a TestModel with internal property', async () => {
83+
target = new ValidationPipe({
84+
transformOptions: { groups: ['internal'] },
85+
});
86+
const testObj = {
87+
prop1: 'value1',
88+
prop2: 'value2',
89+
propInternal: 'value3',
90+
};
91+
expect(await target.transform(testObj, metadata)).to.have.property(
92+
'propInternal',
93+
);
94+
});
95+
});
96+
describe('when transformation is external', () => {
97+
it('should return a TestModel without internal property', async () => {
98+
target = new ValidationPipe({
99+
transformOptions: { groups: ['external'] },
100+
});
101+
const testObj = {
102+
prop1: 'value1',
103+
prop2: 'value2',
104+
propInternal: 'value3',
105+
};
106+
expect(
107+
await target.transform(testObj, metadata),
108+
).to.not.have.property('propInternal');
109+
});
110+
});
71111
});
72112
describe("when validation doesn't transform", () => {
73113
describe('when validation strips', () => {

0 commit comments

Comments
 (0)