Skip to content

Commit

Permalink
Merge pull request #39 from AndreyWV/issue-37/serializable-validation…
Browse files Browse the repository at this point in the history
…-errors

Validation errors are serializable.
  • Loading branch information
AndreyWV committed Apr 25, 2024
2 parents e6b638b + e00ce6a commit 3022a7b
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 9 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.1.0 (2024-04-25)

- Feature: Issue 38 - Serializable validation errors

## 1.0.1 (2024-03-26)

- Bugfix: Issue 36 - Empty object while serializing array items without serializable type
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# typescript-object-serializer
Typescript library to convert javascript object to typescript class and vice versa

[CHANGELOG](CHANGELOG.md)

## Installation and configuration
```sh
npm install typescript-object-serializer
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "typescript-object-serializer",
"version": "1.0.1",
"version": "1.1.0",
"description": "Powerful Scalable Typescript object serializer",
"scripts": {
"build": "TS_NODE_PROJECT=\"webpack.tsconfig.json\" webpack",
Expand Down
11 changes: 6 additions & 5 deletions src/validators/methods/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export function validate<T>(ctor: Constructor<T>, data: any | any[]): Validation
.map(
(validationErrors, index) => {
return validationErrors.map(
validationError => ({
...validationError,
path: `[${index}]${PATH_SEPARATOR}${validationError.path}`,
}),
validationError => {
validationError.path = `[${index}]${PATH_SEPARATOR}${validationError.path}`;
return validationError;
},
);
},
)
Expand Down Expand Up @@ -88,7 +88,8 @@ export function validate<T>(ctor: Constructor<T>, data: any | any[]): Validation
.map((itemErrors, itemIndex) => {
return itemErrors.map(
error => {
error.path = `${extractionResult?.path}${PATH_SEPARATOR}[${itemIndex}]${PATH_SEPARATOR}${error.path}`;
error.path =
`${extractionResult?.path}${PATH_SEPARATOR}[${itemIndex}]${PATH_SEPARATOR}${error.path}`;
return error;
},
);
Expand Down
16 changes: 15 additions & 1 deletion src/validators/types/validation-error.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,27 @@
import { property } from '../../decorators/property';

/**
* @class ValidationError Instance contains validation message and full path to invalid property.
* Important: ValidationError is not inherited from js Error!
*/
export class ValidationError {

@property()
public path: string;

constructor(
@property()
public message: string,
public path: string,
@property()
path: string,
) {
this.path = ValidationError.clearErrorPath(path);
}

protected static clearErrorPath(path: string): string {
return path
.replace(/\.\./g, '.')
.replace(/(^\.|\.$)/g, '');
}

}
78 changes: 78 additions & 0 deletions tests/validators/validate.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
OverrideNameExtractor,
property,
propertyType,
serialize,
SnakeCaseExtractor,
} from '../../src';
import { Constructor } from '../../src/base-types/constructor';
Expand Down Expand Up @@ -106,6 +107,61 @@ describe('validate', () => {

});

it('should return serializable errors', () => {

const validationResult = validate(Test, {});
const serializedError = serialize(validationResult[0]);
expect(serializedError).toEqual({
message: 'Property is required',
path: 'property'
});

});

it('should return serializable errors if property is array of serializable items', () => {

class TestProperty {
@property()
@propertyValidators([RequiredValidator])
public deepProperty: string;
}

class Test {
@property()
@propertyType(TestProperty)
public property: TestProperty[];
}

const validationResult = validate(Test, {
property: [
{
deepProperty: 'test1',
},
{},
{},
{
deepProperty: 'test2',
},
],
});

expect(validationResult[0]).toBeInstanceOf(ValidationError);
expect(validationResult[1]).toBeInstanceOf(ValidationError);

const serializedError1 = serialize(validationResult[0]);
expect(serializedError1).toEqual({
message: 'Property is required',
path: 'property.[1].deepProperty'
});

const serializedError2 = serialize(validationResult[1]);
expect(serializedError2).toEqual({
message: 'Property is required',
path: 'property.[2].deepProperty'
});

});

});

describe('multiple validators', () => {
Expand Down Expand Up @@ -305,6 +361,28 @@ describe('validate', () => {

});

it('should clear error path if it has some extraction conditions', () => {

class TestValidator extends Validator {
public validate(value: any, path: string): ValidationError | undefined {
return new ValidationError(
'Property is always invalid',
'..property1..[0].property2..',
);
}
}

class Test {
@property()
@propertyValidators([TestValidator])
public property: any;
}

const result = validate(Test, {});
expect(result[0].path).toBe('property1.[0].property2')

});

});

it('should validate by all validators of current class and all it\'s parent', () => {
Expand Down

0 comments on commit 3022a7b

Please sign in to comment.