Skip to content

Commit b8e6553

Browse files
Merge branch 'FionaLovett-validate-transform' into 5.6.0
2 parents 6204fe1 + 2f888d3 commit b8e6553

4 files changed

Lines changed: 97 additions & 30 deletions

File tree

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,21 @@
55
"scripts": {
66
"coverage": "nyc report --reporter=text-lcov | coveralls",
77
"precommit": "lint-staged",
8-
"test":
9-
"nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --require 'node_modules/reflect-metadata/Reflect.js'",
10-
"integration-test":
11-
"mocha integration/**/*.spec.ts --reporter spec --require ts-node/register --require 'node_modules/reflect-metadata/Reflect.js'",
12-
"lint":
13-
"tslint -p tsconfig.json -c tslint.json \"packages/**/*.ts\" -e \"*.spec.ts\"",
14-
"format":
15-
"prettier **/**/*.ts --ignore-path ./.prettierignore --write && git status",
8+
"test": "nyc --require ts-node/register mocha packages/**/*.spec.ts --reporter spec --require 'node_modules/reflect-metadata/Reflect.js'",
9+
"integration-test": "mocha integration/**/*.spec.ts --reporter spec --require ts-node/register --require 'node_modules/reflect-metadata/Reflect.js'",
10+
"lint": "tslint -p tsconfig.json -c tslint.json \"packages/**/*.ts\" -e \"*.spec.ts\"",
11+
"format": "prettier **/**/*.ts --ignore-path ./.prettierignore --write && git status",
1612
"clean": "gulp clean:bundle",
1713
"build": "npm run clean && gulp build",
1814
"prebuild:dev": "rm -rf node_modules/@nestjs",
1915
"build:dev": "gulp build --dist node_modules/@nestjs && gulp move",
2016
"postinstall": "opencollective",
2117
"prerelease": "gulp copy-misc && gulp build --dist node_modules/@nestjs",
22-
"publish":
23-
"npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --force-publish --exact -m \"chore(@nestjs) publish %s release\"",
24-
"publish:rc":
25-
"npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=rc -m \"chore(@nestjs) publish %s release\"",
26-
"publish:next":
27-
"npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=next --skip-git -m \"chore(@nestjs) publish %s release\"",
28-
"publish:beta":
29-
"npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=beta -m \"chore(@nestjs) publish %s release\"",
30-
"publish:test":
31-
"npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --force-publish --npm-tag=test --skip-git -m \"chore(@nestjs) publish %s release\""
18+
"publish": "npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --force-publish --exact -m \"chore(@nestjs) publish %s release\"",
19+
"publish:rc": "npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=rc -m \"chore(@nestjs) publish %s release\"",
20+
"publish:next": "npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=next --skip-git -m \"chore(@nestjs) publish %s release\"",
21+
"publish:beta": "npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --npm-tag=beta -m \"chore(@nestjs) publish %s release\"",
22+
"publish:test": "npm run prerelease && npm run build && ./node_modules/.bin/lerna publish --force-publish --npm-tag=test --skip-git -m \"chore(@nestjs) publish %s release\""
3223
},
3324
"engines": {
3425
"node": ">= 8.9.0"
@@ -141,7 +132,9 @@
141132
}
142133
},
143134
"nyc": {
144-
"include": ["packages/**/*.ts"],
135+
"include": [
136+
"packages/**/*.ts"
137+
],
145138
"exclude": [
146139
"node_modules/",
147140
"packages/**/*.spec.ts",
@@ -161,13 +154,23 @@
161154
"packages/common/serializer/**/*",
162155
"packages/common/services/logger.service.ts"
163156
],
164-
"extension": [".ts"],
165-
"require": ["ts-node/register"],
166-
"reporter": ["text-summary", "html"],
157+
"extension": [
158+
".ts"
159+
],
160+
"require": [
161+
"ts-node/register"
162+
],
163+
"reporter": [
164+
"text-summary",
165+
"html"
166+
],
167167
"sourceMap": true,
168168
"instrument": true
169169
},
170170
"lint-staged": {
171-
"packages/**/*.{ts,json}": ["npm run format", "git add"]
171+
"packages/**/*.{ts,json}": [
172+
"npm run format",
173+
"git add"
174+
]
172175
}
173176
}

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: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
1-
import * as sinon from 'sinon';
21
import { expect } from 'chai';
2+
import { Exclude, Expose } from 'class-transformer';
3+
import { IsOptional, IsString } from 'class-validator';
34
import { ArgumentMetadata } from '../../interfaces';
4-
import { IsString } from 'class-validator';
55
import { ValidationPipe } from '../../pipes/validation.pipe';
66

7+
@Exclude()
8+
class TestModelInternal {
9+
constructor() {}
10+
@Expose()
11+
@IsString()
12+
public prop1: string;
13+
14+
@Expose()
15+
@IsString()
16+
public prop2: string;
17+
18+
@Expose({ groups: ['internal'] })
19+
@IsString()
20+
@IsOptional()
21+
public propInternal: string;
22+
}
23+
724
class TestModel {
825
constructor() {}
926
@IsString() public prop1: string;
@@ -18,6 +35,11 @@ describe('ValidationPipe', () => {
1835
metatype: TestModel,
1936
data: '',
2037
};
38+
const transformMetadata: ArgumentMetadata = {
39+
type: 'body',
40+
metatype: TestModelInternal,
41+
data: '',
42+
};
2143

2244
describe('transform', () => {
2345
describe('when validation passes', () => {
@@ -68,8 +90,40 @@ describe('ValidationPipe', () => {
6890
expect(target.transform(testObj, metadata)).to.eventually.throw;
6991
});
7092
});
93+
describe('when transformation is internal', () => {
94+
it('should return a TestModel with internal property', async () => {
95+
target = new ValidationPipe({
96+
transform: true,
97+
transformOptions: { groups: ['internal'] },
98+
});
99+
const testObj = {
100+
prop1: 'value1',
101+
prop2: 'value2',
102+
propInternal: 'value3',
103+
};
104+
expect(
105+
await target.transform(testObj, transformMetadata),
106+
).to.have.property('propInternal');
107+
});
108+
});
109+
describe('when transformation is external', () => {
110+
it('should return a TestModel without internal property', async () => {
111+
target = new ValidationPipe({
112+
transform: true,
113+
transformOptions: { groups: ['external'] },
114+
});
115+
const testObj = {
116+
prop1: 'value1',
117+
prop2: 'value2',
118+
propInternal: 'value3',
119+
};
120+
expect(
121+
await target.transform(testObj, transformMetadata),
122+
).to.not.have.property('propInternal');
123+
});
124+
});
71125
});
72-
describe("when validation doesn't transform", () => {
126+
describe('when validation doesn\'t transform', () => {
73127
describe('when validation strips', () => {
74128
it('should return a plain object without extra properties', async () => {
75129
target = new ValidationPipe({ transform: false, whitelist: true });

0 commit comments

Comments
 (0)