Skip to content

Commit

Permalink
feat: extend validator by adding unique and exists rules
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Mar 16, 2020
1 parent 0e2f6ee commit 4768c3c
Show file tree
Hide file tree
Showing 7 changed files with 516 additions and 40 deletions.
1 change: 1 addition & 0 deletions adonis-typings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@
/// <reference path="./schema.ts" />
/// <reference path="./migrator.ts" />
/// <reference path="./relations.ts" />
/// <reference path="./validator.ts" />
28 changes: 28 additions & 0 deletions adonis-typings/validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

declare module '@ioc:Adonis/Core/Validator' {
import { Rule } from '@ioc:Adonis/Core/Validator'

export interface Rules {
exists (options: {
table: string,
column: string,
connection?: string,
constraints?: { [key: string]: any } | { [key: string]: any }[],
}): Rule

unique (options: {
table: string,
column: string,
connection?: string,
constraints?: { [key: string]: any } | { [key: string]: any }[],
}): Rule
}
}
81 changes: 44 additions & 37 deletions package-lock.json

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

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,10 @@
"@adonisjs/core": ">=5.0.0-preview"
},
"devDependencies": {
"@adonisjs/ace": "^6.8.3",
"@adonisjs/ace": "^6.8.4",
"@adonisjs/application": "^1.3.9",
"@adonisjs/core": "^5.0.0-preview.2",
"@adonisjs/fold": "^6.3.3",
"@adonisjs/core": "^5.0.0-preview.5",
"@adonisjs/fold": "^6.3.4",
"@adonisjs/mrm-preset": "^2.2.4",
"@poppinss/dev-utils": "^1.0.4",
"@types/dotenv": "^8.2.0",
Expand Down
5 changes: 5 additions & 0 deletions providers/DatabaseProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Database } from '../src/Database'
import { Adapter } from '../src/Orm/Adapter'
import { OrmConfig } from '../src/Orm/Config'
import { BaseModel } from '../src/Orm/BaseModel'
import { extendValidator } from '../src/Bindings/Validator'

import {
column,
Expand Down Expand Up @@ -72,5 +73,9 @@ export default class DatabaseServiceProvider {
this.$container.with(['Adonis/Core/HealthCheck', 'Adonis/Lucid/Database'], (HealthCheck) => {
HealthCheck.addChecker('lucid', 'Adonis/Lucid/Database')
})

this.$container.with(['Adonis/Core/Validator', 'Adonis/Lucid/Database'], (Validator, Db) => {
extendValidator(Validator.validator, Db)
})
}
}
144 changes: 144 additions & 0 deletions src/Bindings/Validator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
/*
* @adonisjs/lucid
*
* (c) Harminder Virk <virk@adonisjs.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

import { Exception } from '@poppinss/utils'
import { DatabaseContract } from '@ioc:Adonis/Lucid/Database'
import { DatabaseQueryBuilderContract } from '@ioc:Adonis/Lucid/DatabaseQueryBuilder'
import { validator as validatorStatic, ValidationRuntimeOptions } from '@ioc:Adonis/Core/Validator'

/**
* Checks for database rows for `exists` and `unique` rule.
*/
class DbRowCheck {
constructor (private ruleName: 'exists' | 'unique', private database: DatabaseContract) {
}

/**
* Applies user defined constraints on the query builder
*/
private applyConstraints (query: DatabaseQueryBuilderContract, constraints: any[]) {
if (constraints.length > 1) {
query.where((builder) => {
constraints.forEach((constraint) => builder.orWhere(constraint))
})
} else {
constraints.forEach((constraint) => query.where(constraint))
}
}

/**
* Compile validation options
*/
public compile (options) {
/**
* Ensure options are defined with table and column name
*/
if (!options || !options.table || !options.column) {
throw new Exception(`"${this.ruleName}" rule expects a "table" and a "column" name`)
}

/**
* Normalize where constraints
*/
let constraints: { [key: string]: any }[] = []
if (options.constraints && Array.isArray(options.constraints)) {
constraints = options.constraints
} else if (options.constraints && typeof (options.constraints) === 'object' && options.constraints !== null) {
constraints = [options.constraints]
}

return {
table: options.table,
column: options.column,
connection: options.connection,
constraints: constraints,
}
}

/**
* Validate value
*/
public async validate (
value: any,
{ table, column, constraints, connection }: any,
{ pointer, errorReporter, arrayExpressionPointer }: ValidationRuntimeOptions,
) {
const query = this.database.connection(connection).query().from(table).where(column, value)
this.applyConstraints(query, constraints)

const row = await query.first()
if (this.ruleName === 'exists') {
if (!row) {
errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer)
}
return
}

if (this.ruleName === 'unique') {
if (row) {
errorReporter.report(pointer, this.ruleName, `${this.ruleName} validation failure`, arrayExpressionPointer)
}
return
}
}
}

/**
* Extends the validator by adding `unique` and `exists`
*/
export function extendValidator (
validator: typeof validatorStatic,
database: DatabaseContract,
) {
/**
* Exists rule to ensure the value exists in the database
*/
const existsChecker = new DbRowCheck('exists', database)
validator.addRule('exists', {
compile (_, __, args) {
const compiledOptions = existsChecker.compile(args[0])
return {
async: true,
allowUndefineds: false,
name: 'exists',
compiledOptions: compiledOptions,
}
},
async validate (value, compiledOptions, options) {
try {
await existsChecker.validate(value, compiledOptions, options)
} catch (error) {
options.errorReporter.report(options.pointer, 'exists', error.message, options.arrayExpressionPointer)
}
},
})

/**
* Unique rule to check if value is unique or not
*/
const uniqueChecker = new DbRowCheck('unique', database)
validator.addRule('unique', {
compile (_, __, args) {
const compiledOptions = uniqueChecker.compile(args[0])
return {
async: true,
allowUndefineds: false,
name: 'unique',
compiledOptions: compiledOptions,
}
},
async validate (value, compiledOptions, options) {
try {
await uniqueChecker.validate(value, compiledOptions, options)
} catch (error) {
options.errorReporter.report(options.pointer, 'unique', error.message, options.arrayExpressionPointer)
}
},
})
}
Loading

0 comments on commit 4768c3c

Please sign in to comment.