Skip to content
This repository has been archived by the owner on Dec 23, 2021. It is now read-only.

Commit

Permalink
feat: validate enum type
Browse files Browse the repository at this point in the history
  • Loading branch information
Soontao committed Nov 4, 2020
1 parent e715619 commit f2abdb9
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 5 deletions.
27 changes: 25 additions & 2 deletions src/type/decorators/assert.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import { isArray } from '@newdash/newdash';
import { isClass } from '@newdash/newdash/isClass';
import { isPlainObject } from '@newdash/newdash/isPlainObject';
import { ODataMethod } from '@odata/parser';
import BigNumber from 'bignumber.js';
import 'reflect-metadata';
import * as validate from 'validate.js';
import { getEnumValues } from '../utils';
import { EColumnOptions } from './odata';


interface BigNumberValidateOptions {
integerOnly?: boolean;
precision?: number;
Expand Down Expand Up @@ -43,12 +47,18 @@ export interface ConstraintOption {
* The inclusion validator is useful for validating input from a dropdown for example.
* It checks that the given value exists in the list given by the `within` option.
*/
inclusion?: any[];
inclusion?: {
within: any[],
message?: string;
}
/**
* The exclusion validator is useful for restriction certain values.
* It checks that the given value is not in the list given by the within option.
*/
exclusion?: any[];
exclusion?: {
within: any[],
message?: string;
}
/**
* The format validator will validate a value against a regular expression of your choosing.
*/
Expand Down Expand Up @@ -140,6 +150,19 @@ export function columnToValidateRule(
cOption.presence = {}; // mandatory
}

if (typeof options.enum === 'object') {
let enumValues = options.enum;
if (isPlainObject(enumValues)) {
enumValues = getEnumValues(enumValues);
}
if (isArray(enumValues)) {
cOption.inclusion = {
within: enumValues,
message: '^value \'%{value}\' is not in enum values'
};
}
}

switch (options.type) {
case 'date':
case 'nvarchar':
Expand Down
22 changes: 22 additions & 0 deletions src/type/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Returns available values of an enum
*
* @example
* ```
* enum LambdaEnum {
* NONE = 0,
* A = 1,
* B = 2,
* }
* // become => { "0": "NONE", "1": "A", "2": "B", "NONE": 0, "A": 1, "B": 2}
*
* const values = getEnumValues(LambdaEnum); // returns [ 0, 1, 2 ]
* ```
* @param enumObj
* @returns The values of the enum
*/
export function getEnumValues<T>(enumObj: T): Array<T[keyof T]> {
return Object.keys(enumObj)
.filter((key) => isNaN(parseInt(key, 10)))
.map((key) => (enumObj as any)[key]);
}
4 changes: 2 additions & 2 deletions src/type/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ export function applyValidate(entityType: Class, input: any, method: ODataMethod
const errors = validateJs.validate(input, columnRules);
if (errors != undefined) {
Object.entries(errors).forEach(([key, value]) => {
msgs.push(`Property '${key}' ${value}`);
msgs.push(`property '${key}' ${value}`);
});
}
}
if (customRules) {
const errors = validateJs.validate(input, customRules);
if (errors != undefined) {
Object.entries(errors).forEach(([key, value]) => {
msgs.push(`Property '${key}' ${value}`);
msgs.push(`property '${key}' ${value}`);
});
}
}
Expand Down
48 changes: 47 additions & 1 deletion test/type/entity.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { lazyRef } from '@newdash/inject';
import { ODataFilter } from '@odata/parser';
import BigNumber from 'bignumber.js';
import { Edm, InjectedTypedService, KeyProperty, ODataAction, ODataFunction, ODataModel, oInject, Property, UUIDKeyProperty, withEntitySetName } from '../../src';
import { Edm, InjectedTypedService, KeyProperty, ODataAction, ODataFunction, ODataModel, oInject, OptionalProperty, Property, UUIDKeyProperty, withEntitySetName } from '../../src';
import { createServerAndClient, createTmpConnection } from './utils';


Expand Down Expand Up @@ -127,4 +127,50 @@ describe('Entity Type Test Suite', () => {

});

it('should support enum types as column', async () => {

enum ValueType1 {
v1 = 1,
v2 = 3
}
enum ValueType2 {
v1 = '1',
v2 = '3'
}

@withEntitySetName('EnumEntities')
@ODataModel()
class EnumEntity {
@UUIDKeyProperty() id: string;
@OptionalProperty({ enum: ValueType1 }) t1: ValueType1
@OptionalProperty({ enum: ValueType2 }) t2: ValueType2
@OptionalProperty({ enum: ['v1', 'v2'] }) t3: string

}

const conn = await createTmpConnection({
name: 'entity_test_03',
entityPrefix: 'entity_test_03',
entities: [EnumEntity]
});

const { client, shutdownServer } = await createServerAndClient(conn);

try {

const set = client.getEntitySet<EnumEntity>('EnumEntities');
await set.create({ t1: ValueType1.v1, t2: ValueType2.v2 });
await set.create({ t1: ValueType1.v2, t2: ValueType2.v1 });

await expect(() => set.create({ t1: 5, t2: '124' }))
.rejects
.toThrow("property 't1' value '5' is not in enum values, property 't2' value '124' is not in enum values");
await expect(() => set.create({ t3: '123123' }))
.rejects
.toThrow("property 't3' value '123123' is not in enum values");
} finally {
await shutdownServer();
}
});

});

0 comments on commit f2abdb9

Please sign in to comment.