add constraint
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
ValidationArguments,
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
ValidationOptions,
|
||||
registerDecorator,
|
||||
} from 'class-validator';
|
||||
import { ObjectType, Repository, DataSource } from 'typeorm';
|
||||
|
||||
type Condition = {
|
||||
entity: ObjectType<any>;
|
||||
map?: string;
|
||||
};
|
||||
|
||||
@ValidatorConstraint({ name: 'dataExist', async: true })
|
||||
@Injectable()
|
||||
export class DataExistConstraint implements ValidatorConstraintInterface {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
async validate(value: any, validationArguments?: ValidationArguments) {
|
||||
let repo: Repository<any>;
|
||||
if (!value) {
|
||||
return true;
|
||||
}
|
||||
let map = 'id';
|
||||
if ('entity' in validationArguments.constraints[0]) {
|
||||
map = validationArguments.constraints[0].map ?? 'id';
|
||||
repo = this.dataSource.getRepository(validationArguments.constraints[0].entitiy);
|
||||
} else {
|
||||
repo = this.dataSource.getRepository(validationArguments.constraints[0]);
|
||||
}
|
||||
const item = await repo.findOne({ where: { [map]: value } });
|
||||
return !!item;
|
||||
}
|
||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||
if (!validationArguments.constraints[0]) {
|
||||
return 'Model not been specified!';
|
||||
}
|
||||
return `All instance of ${validationArguments.constraints[0].name} must been exists in databse!`;
|
||||
}
|
||||
}
|
||||
|
||||
function IsDataExist(
|
||||
entity: ObjectType<any>,
|
||||
validationOptions?: ValidationOptions,
|
||||
): (object: RecordAny, propertyName: string) => void;
|
||||
|
||||
function IsDataExist(
|
||||
condition: Condition,
|
||||
validationOptions?: ValidationOptions,
|
||||
): (object: RecordAny, propertyName: string) => void;
|
||||
|
||||
function IsDataExist(
|
||||
condition: Condition | ObjectType<any>,
|
||||
validationOptions?: ValidationOptions,
|
||||
): (object: RecordAny, propertyName: string) => void {
|
||||
return (object: RecordAny, propertyName: string) => {
|
||||
registerDecorator({
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
constraints: [condition],
|
||||
validator: DataExistConstraint,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export { IsDataExist };
|
||||
@@ -0,0 +1,5 @@
|
||||
export * from './data.exist.constraint';
|
||||
export * from './tree.unique.constraint';
|
||||
export * from './tree.unique.exist.constraint';
|
||||
export * from './unique.constraint';
|
||||
export * from './unique.exist.constraint';
|
||||
@@ -0,0 +1,87 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
ValidationArguments,
|
||||
registerDecorator,
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
import { merge, isNil } from 'lodash';
|
||||
import { DataSource, ObjectType } from 'typeorm';
|
||||
|
||||
type Condition = {
|
||||
entity: ObjectType<any>;
|
||||
|
||||
parentKey?: string;
|
||||
|
||||
property?: string;
|
||||
};
|
||||
|
||||
@ValidatorConstraint({ name: 'treeDataUnique', async: true })
|
||||
@Injectable()
|
||||
export class TreeUniqueConstraint implements ValidatorConstraintInterface {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
async validate(value: any, args: ValidationArguments) {
|
||||
// 获取要验证的模型和字段
|
||||
const config: Omit<Condition, 'entity'> = {
|
||||
parentKey: 'parent',
|
||||
property: args.property,
|
||||
};
|
||||
const condition = ('entity' in args.constraints[0]
|
||||
? merge(config, args.constraints[0])
|
||||
: {
|
||||
...config,
|
||||
entity: args.constraints[0],
|
||||
}) as unknown as Required<Condition>;
|
||||
if (!condition.entity) {
|
||||
return false;
|
||||
}
|
||||
if (isNil(value)) {
|
||||
return true;
|
||||
}
|
||||
const argsObj = args.object as any;
|
||||
try {
|
||||
// 查询是否存在数据,如果已经存在则验证失败
|
||||
const repo = this.dataSource.getTreeRepository(condition.entity);
|
||||
const collections = await repo.find({
|
||||
where: {
|
||||
parent: !argsObj[condition.parentKey]
|
||||
? null
|
||||
: { id: argsObj[condition.parentKey] },
|
||||
},
|
||||
});
|
||||
return collections.every((item) => item[condition.property] !== value);
|
||||
} catch (err) {
|
||||
// 如果数据库操作异常则验证失败
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
const { entity, property } = args.constraints[0];
|
||||
const queryProperty = property ?? args.property;
|
||||
if (!(args.object as any).getManager) {
|
||||
return 'getManager function not been found!';
|
||||
}
|
||||
if (!entity) {
|
||||
return 'Model not been specified!';
|
||||
}
|
||||
return `${queryProperty} of ${entity.name} must been unique!`;
|
||||
}
|
||||
}
|
||||
|
||||
export function IsTreeUnique(
|
||||
params: ObjectType<any> | Condition,
|
||||
validationOptions?: ValidationOptions,
|
||||
) {
|
||||
return (object: Record<string, any>, propertyName: string) => {
|
||||
registerDecorator({
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
constraints: [params],
|
||||
validator: TreeUniqueConstraint,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
} from 'class-validator';
|
||||
import { merge } from 'lodash';
|
||||
import { DataSource, ObjectType } from 'typeorm';
|
||||
|
||||
type Condition = {
|
||||
entity: ObjectType<any>;
|
||||
|
||||
ignore?: string;
|
||||
|
||||
ignoreKey?: string;
|
||||
|
||||
property?: string;
|
||||
};
|
||||
|
||||
@ValidatorConstraint({ name: 'treeDataUniqueExist', async: true })
|
||||
@Injectable()
|
||||
export class TreeUniqueExistContraint implements ValidatorConstraintInterface {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
async validate(value: any, args: ValidationArguments) {
|
||||
const config: Omit<Condition, 'entity'> = {
|
||||
ignore: 'id',
|
||||
property: args.property,
|
||||
};
|
||||
const condition = ('entity' in args.constraints[0]
|
||||
? merge(config, args.constraints[0])
|
||||
: {
|
||||
...config,
|
||||
entity: args.constraints[0],
|
||||
}) as unknown as Required<Condition>;
|
||||
if (!condition.entity) {
|
||||
return false;
|
||||
}
|
||||
if (!condition.ignoreKey) {
|
||||
condition.ignoreKey = condition.ignore;
|
||||
}
|
||||
const argsObj = args.object as any;
|
||||
// 在传入的dto数据中获取需要忽略的字段的值
|
||||
const ignoreValue = argsObj[condition.ignore];
|
||||
const findValue = argsObj[condition.ignoreKey];
|
||||
if (!ignoreValue || !findValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 通过entity获取repository
|
||||
const repo = this.dataSource.getRepository(condition.entity);
|
||||
// 查询忽略字段之外的数据是否对queryProperty的值唯一
|
||||
const item = await repo.findOne({
|
||||
where: {
|
||||
[condition.ignoreKey]: findValue,
|
||||
},
|
||||
relations: ['parent'],
|
||||
});
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
const rows = await repo.find({
|
||||
where: { parent: item.parent ? { id: item.parent.id } : null },
|
||||
withDeleted: true,
|
||||
});
|
||||
return !rows.find(
|
||||
(row) => row[condition.property] === value && row[condition.ignore] !== ignoreValue,
|
||||
);
|
||||
}
|
||||
|
||||
defaultMessage(args: ValidationArguments) {
|
||||
const { entity, property } = args.constraints[0];
|
||||
const queryProperty = property ?? args.property;
|
||||
if (!(args.object as any).getManager) {
|
||||
return 'getManager function not been found!';
|
||||
}
|
||||
if (!entity) {
|
||||
return 'Model not been specified!';
|
||||
}
|
||||
return `${queryProperty} of ${entity.name} must been unique!`;
|
||||
}
|
||||
}
|
||||
|
||||
export function IsTreeUniqueExist(
|
||||
params: ObjectType<any> | Condition,
|
||||
validationOptions?: ValidationOptions,
|
||||
) {
|
||||
return (object: Record<string, any>, propertyName: string) => {
|
||||
registerDecorator({
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
constraints: [params],
|
||||
validator: TreeUniqueExistContraint,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
ValidationOptions,
|
||||
} from 'class-validator';
|
||||
import { isNil, merge } from 'lodash';
|
||||
import { DataSource, ObjectType } from 'typeorm';
|
||||
|
||||
type Condition = {
|
||||
entity: ObjectType<any>;
|
||||
property?: string;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
@ValidatorConstraint({ name: 'dataUnique', async: true })
|
||||
export class UniqueConstraint implements ValidatorConstraintInterface {
|
||||
constructor(private dataSource: DataSource) {}
|
||||
|
||||
async validate(value: any, validationArguments?: ValidationArguments): Promise<boolean> {
|
||||
const config: Omit<Condition, 'entity'> = { property: validationArguments.property };
|
||||
const condition = ('entity' in validationArguments.constraints[0]
|
||||
? merge(config, validationArguments.constraints[0])
|
||||
: {
|
||||
...config,
|
||||
entity: validationArguments.constraints[0],
|
||||
}) as unknown as Required<Condition>;
|
||||
if (!condition.entity) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const repo = this.dataSource.getRepository(condition.entity);
|
||||
return isNil(
|
||||
await repo.findOne({ where: { [condition.property]: value }, withDeleted: true }),
|
||||
);
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
defaultMessage?(validationArguments?: ValidationArguments): string {
|
||||
const { entity, property } = validationArguments.constraints[0];
|
||||
const queryProperty = property ?? validationArguments.property;
|
||||
if (!(validationArguments.object as any).getManager) {
|
||||
return 'getManager function not been found!';
|
||||
}
|
||||
if (!entity) {
|
||||
return 'Model not been specified!';
|
||||
}
|
||||
return `${queryProperty} of ${entity.name} must been unique!`;
|
||||
}
|
||||
}
|
||||
|
||||
export function IsUnique(
|
||||
params: ObjectType<any> | Condition,
|
||||
validationOptions: ValidationOptions,
|
||||
) {
|
||||
return (object: RecordAny, propertyName: string) => {
|
||||
registerDecorator({
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options: validationOptions,
|
||||
constraints: [params],
|
||||
validator: UniqueConstraint,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
registerDecorator,
|
||||
ValidationArguments,
|
||||
ValidationOptions,
|
||||
ValidatorConstraint,
|
||||
ValidatorConstraintInterface,
|
||||
} from 'class-validator';
|
||||
import { isNil, merge } from 'lodash';
|
||||
import { DataSource, Not, ObjectType } from 'typeorm';
|
||||
|
||||
type Condition = {
|
||||
entity: ObjectType<any>;
|
||||
|
||||
ignore?: string;
|
||||
|
||||
ignoreKey?: string;
|
||||
|
||||
property?: string;
|
||||
};
|
||||
@Injectable()
|
||||
@ValidatorConstraint({ name: 'dataUniqueExist', async: true })
|
||||
export class UniqueExistConstraint implements ValidatorConstraintInterface {
|
||||
constructor(protected dataSource: DataSource) {}
|
||||
|
||||
async validate(value: any, args?: ValidationArguments): Promise<boolean> {
|
||||
const config: Omit<Condition, 'entity'> = {
|
||||
ignore: 'id',
|
||||
property: args.property,
|
||||
};
|
||||
const condition = ('entity' in args.constraints[0]
|
||||
? merge(config, args.constraints[0])
|
||||
: { ...config, entity: args.constraints[0] }) as unknown as Required<Condition>;
|
||||
if (!condition.entity) {
|
||||
return false;
|
||||
}
|
||||
const ignoreValue = (args.object as any)[
|
||||
isNil(condition.ignoreKey) ? condition.ignore : condition.ignoreKey
|
||||
];
|
||||
if (ignoreValue === undefined) {
|
||||
return false;
|
||||
}
|
||||
const repo = this.dataSource.getRepository(condition.entity);
|
||||
return isNil(
|
||||
await repo.findOne({
|
||||
where: { [condition.property]: value, [condition.ignore]: Not(ignoreValue) },
|
||||
withDeleted: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
defaultMessage?(args?: ValidationArguments): string {
|
||||
const { entity, property } = args.constraints[0];
|
||||
const queryProperty = property ?? args.property;
|
||||
if (!(args.object as any).getManager) {
|
||||
return 'getManager function not been found!';
|
||||
}
|
||||
if (!entity) {
|
||||
return 'Model not been specified!';
|
||||
}
|
||||
return `${queryProperty} of ${entity.name} must been unique!`;
|
||||
}
|
||||
}
|
||||
|
||||
export function IsUniqueExist(params: ObjectType<any> | Condition, options?: ValidationOptions) {
|
||||
return (object: RecordAny, propertyName: string) => {
|
||||
registerDecorator({
|
||||
target: object.constructor,
|
||||
propertyName,
|
||||
options,
|
||||
constraints: [params],
|
||||
validator: UniqueExistConstraint,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -5,11 +5,13 @@ import { DataSource, ObjectType } from 'typeorm';
|
||||
|
||||
import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants';
|
||||
|
||||
import { DataExistConstraint } from '../core/constraints/data.exist.constraint';
|
||||
import { TreeUniqueConstraint } from '../core/constraints/tree.unique.constraint';
|
||||
import { TreeUniqueExistContraint } from '../core/constraints/tree.unique.exist.constraint';
|
||||
import { UniqueConstraint } from '../core/constraints/unique.constraint';
|
||||
import { UniqueExistConstraint } from '../core/constraints/unique.exist.constraint';
|
||||
import {
|
||||
DataExistConstraint,
|
||||
TreeUniqueConstraint,
|
||||
TreeUniqueExistContraint,
|
||||
UniqueConstraint,
|
||||
UniqueExistConstraint,
|
||||
} from './constraints';
|
||||
|
||||
@Module({})
|
||||
export class DatabaseModule {
|
||||
|
||||
Reference in New Issue
Block a user