From d4fb2eca6f19c9b914824249d1fc69911bce6ae2 Mon Sep 17 00:00:00 2001 From: jlenon7 Date: Sat, 11 May 2024 13:23:36 +0100 Subject: [PATCH] feat(rules): add custom rules --- package-lock.json | 4 +- package.json | 2 +- src/providers/ValidatorProvider.ts | 93 +++++++++++++++++++++++++++++- 3 files changed, 94 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index f4ad115..6beb087 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@athenna/validator", - "version": "1.0.0", + "version": "4.2.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@athenna/validator", - "version": "1.0.0", + "version": "4.2.0", "license": "MIT", "dependencies": { "@vinejs/vine": "^2.0.0" diff --git a/package.json b/package.json index 6f6c862..308fce7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@athenna/validator", - "version": "4.1.0", + "version": "4.2.0", "description": "Template for new projects.", "license": "MIT", "author": "João Lenon ", diff --git a/src/providers/ValidatorProvider.ts b/src/providers/ValidatorProvider.ts index 2b5976b..5b7d3df 100644 --- a/src/providers/ValidatorProvider.ts +++ b/src/providers/ValidatorProvider.ts @@ -9,12 +9,56 @@ import { sep } from 'node:path' import { Config } from '@athenna/config' -import { SimpleErrorReporter } from '#src' -import { Exec, Module, Path } from '@athenna/common' +import { Validate, SimpleErrorReporter } from '#src' +import { Is, Exec, Module, Path } from '@athenna/common' import { Annotation, ServiceProvider } from '@athenna/ioc' import { ValidatorImpl } from '#src/validator/ValidatorImpl' import { ValidationException } from '#src/exceptions/ValidationException' +type UniqueOptions = { + /** + * The table where the database will lookup for the data. + */ + table: string + + /** + * The column name in database. If not defined, the name + * of the field in the schema will be used. + * + * @default 'fieldNameInYourSchema' + */ + column?: string + + /** + * Use the max field to stablish a max limit for your validation. + * In some cases in your database you might have a max of 10 tuples + * with the same data. Use this option to validate that the number + * of fields registered in database cannot be bigger than the number + * defined on this option. + * + * @example + * ```ts + * const schema = this.validator.object({ + * name: this.validator.string().unique({ table: 'users', max: 10 }) + * }) + * + * const data = { name: 'lenon' } + * + * // Will throw if there are 10 users with name `lenon` + * // created in database + * await this.validator.validate({ schema: this.schema, data }) + * ``` + * @default undefined + */ + max?: number +} + +declare module '@vinejs/vine' { + interface VineString { + unique(options: UniqueOptions): this + } +} + class ErrorReporter extends SimpleErrorReporter { createError(): any { return new ValidationException(this.errors) @@ -32,6 +76,51 @@ export class ValidatorProvider extends ServiceProvider { await this.registerValidators() } + public async boot() { + if (!ioc.has('Athenna/Core/Database')) { + return + } + + const DB = ioc.safeUse('Athenna/Core/Database') + + Validate.extend().string('unique', async (value, options, field) => { + /** + * We do not want to deal with non-string + * values. The "string" rule will handle the + * the validation. + */ + if (!Is.String(value)) { + return + } + + if (!options.column) { + options.column = field.name as string + } + + if (options.max) { + const rows = await DB.table(options.table) + .select(options.column) + .where(options.column, value) + .findMany() + + if (rows.length > options.max) { + field.report('The {{ field }} field is not unique', 'unique', field) + } + + return + } + + const existsRow = await DB.table(options.table) + .select(options.column) + .where(options.column, value) + .exists() + + if (existsRow) { + field.report('The {{ field }} field is not unique', 'unique', field) + } + }) + } + public async registerValidators() { const validators = Config.get('rc.validators', [])