Skip to content

Commit

Permalink
Merge pull request #830 from Bendakh/feature/OverrideIsEnum
Browse files Browse the repository at this point in the history
[FEAT] Improve error message of isEnum validator
  • Loading branch information
g-ongenae authored Dec 11, 2023
2 parents abe09f8 + d084e4b commit 787895d
Show file tree
Hide file tree
Showing 8 changed files with 143 additions and 3 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ A collection of [NestJS](https://docs.nestjs.com) components. This repository is
- [NestJS Google Cloud PubSub MicroService](#nestjs-google-cloud-pubsub-microservice)
- [NestJS Google Cloud PubSub Client Proxy](#nestjs-google-cloud-pubsub-client-proxy)
- [NestJS custom decorators](#nestjs-custom-decorators)
- [NestJS class validators](#nestjs-class-validators)

## NestJS Pagination

Expand Down Expand Up @@ -57,6 +58,12 @@ A set of custom decorators for NestJS.

See [the documentation here](./packages/custom-decorators).

## NestJS class validators

A package containing overriden class validators.

See [the documentation here](./packages/class-validators).

# Contribution

This repository is managed by [Lerna.js](https://lerna.js.org). If you want to contribute, you need to follow these instructions:
Expand Down
7 changes: 5 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@algoan/nestjs-google-pubsub-microservice": "file:packages/google-pubsub-microservice",
"@algoan/nestjs-http-exception-filter": "file:packages/http-exception-filter",
"@algoan/nestjs-logging-interceptor": "file:packages/logging-interceptor",
"@algoan/nestjs-pagination": "file:packages/pagination"
"@algoan/nestjs-pagination": "file:packages/pagination",
"@algoan/nestjs-class-validators": "file:packages/class-validators"
}
}
21 changes: 21 additions & 0 deletions packages/class-validators/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Nestjs class validators

A set of class validators based on the class-validator package (https://www.npmjs.com/package/@nestjs/class-validator/v/0.13.1).

## Installation

```bash
npm install --save @algoan/nestjs-class-validators
```

## IsEnum

A class validator that validates the enum type.

### Usage

```ts
@IsEnum(UserType)
public userType: UserType;
```

14 changes: 14 additions & 0 deletions packages/class-validators/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "@algoan/nestjs-class-validators",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"class-validator": "^0.14.0"
}
}
2 changes: 2 additions & 0 deletions packages/class-validators/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { IsEnum } from './is-enum.decorator';
export * from 'class-validator';
13 changes: 13 additions & 0 deletions packages/class-validators/src/is-enum.decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { IsEnum as OriginalIsEnum, ValidationOptions } from 'class-validator';

/**
* Checks if a given value is the member of the provided enum.
*
* This an override for the class-validator IsEnum validator.
* The error message is enhanced with the invalid value
*/
export const IsEnum = (entity: object, validationOptions?: ValidationOptions): PropertyDecorator =>
OriginalIsEnum(entity, {
message: '$property has the value $value but must be one of the following values: $constraint2',
...validationOptions,
});
79 changes: 79 additions & 0 deletions packages/class-validators/test/is-enum-decorator.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { IsEnum } from '../src/is-enum.decorator';
import { Validator, ValidatorOptions } from 'class-validator';

function validateValues(
object: { testProperty: any },
values: any[],
validatorOptions?: ValidatorOptions,
): Promise<any> {
const validator = new Validator();
const promises = values.map((value) => {
object.testProperty = value;
return validator.validate(object, validatorOptions).then((errors) => {
expect(errors.length).toEqual(0);
if (errors.length > 0) {
console.log(`Unexpected errors: ${JSON.stringify(errors)}`);
throw new Error('Unexpected validation errors');
}
});
});

return Promise.all(promises);
}

function checkReturnedError(
object: { testProperty: any },
values: any[],
validationType: string,
messages: string[],
validatorOptions?: ValidatorOptions,
): Promise<any> {
let messagesIncrementor: number = 0;
const validator = new Validator();
const promises = values.map((value) => {
object.testProperty = value;
return validator.validate(object, validatorOptions).then((errors) => {
expect(errors.length).toEqual(1);
expect(errors[0].target).toEqual(object);
expect(errors[0].property).toEqual('testProperty');
expect(errors[0].constraints).toEqual({ [validationType]: messages[messagesIncrementor] });
expect(errors[0].value).toEqual(value);
messagesIncrementor++;
});
});

return Promise.all(promises);
}

describe('Tests related to the custom IsEnum Decorator', () => {
enum CustomEnum {
FIRST_ITEM = 'first-item',
SECOND_ITEM = 'second-item',
}

class TestClass {
@IsEnum(CustomEnum)
testProperty: CustomEnum = CustomEnum.FIRST_ITEM;
}

it('should validate the correct values', () => {
return validateValues(new TestClass(), [
CustomEnum.FIRST_ITEM,
CustomEnum.SECOND_ITEM,
'first-item',
'second-item',
]);
});

it('should not validate invalid values and return the correct errors', () => {
const validationType = 'isEnum';
const firstMessage =
'testProperty has the value false-value-1 but must be one of the following values: first-item, second-item';
const secondMessage =
'testProperty has the value false-value-2 but must be one of the following values: first-item, second-item';
return checkReturnedError(new TestClass(), ['false-value-1', 'false-value-2'], validationType, [
firstMessage,
secondMessage,
]);
});
});

0 comments on commit 787895d

Please sign in to comment.