0
0
mirror of https://github.com/honojs/hono.git synced 2024-12-01 11:51:01 +01:00

feat(validator): support v.queries (#636)

This commit is contained in:
Yusuke Wada 2022-11-01 14:22:04 +09:00 committed by GitHub
parent 6a96ace96a
commit c14dd5ad43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 100 additions and 2 deletions

View File

@ -4,7 +4,7 @@ import { rule } from './rule.ts'
import { sanitizer } from './sanitizer.ts'
import type { Schema } from './schema.ts'
type Target = 'query' | 'header' | 'body' | 'json'
type Target = 'query' | 'queries' | 'header' | 'body' | 'json'
type Type = JSONPrimitive | JSONObject | JSONArray | File
type RuleFunc = (value: Type) => boolean
type Rule = {
@ -90,6 +90,7 @@ export class VArray<T extends Schema> extends VObjectBase<T> {
export class Validator {
isArray: boolean = false
query = (key: string): VString => new VString({ target: 'query', key: key })
queries = (key: string): VStringArray => new VStringArray({ target: 'queries', key: key })
header = (key: string): VString => new VString({ target: 'header', key: key })
body = (key: string): VString => new VString({ target: 'body', key: key })
json = (key: string) => {
@ -216,6 +217,9 @@ export abstract class VBase {
if (this.target === 'query') {
value = req.query(this.key)
}
if (this.target === 'queries') {
value = req.queries(this.key)
}
if (this.target === 'header') {
value = req.header(this.key)
}
@ -327,6 +331,7 @@ export abstract class VBase {
if (this._optional && typeof value === 'undefined') return true
if (Array.isArray(value)) {
if (value.length === 0 && !this._optional) return false
// Sanitize
for (const sanitizer of this.sanitizers) {
value = value.map((innerVal: any) => sanitizer(innerVal)) as JSONArray
@ -362,6 +367,9 @@ export abstract class VBase {
case 'query':
keyText = `the query parameter "${this.key}"`
break
case 'queries':
keyText = `the query parameters "${this.key}"`
break
case 'header':
keyText = `the request header "${this.key}"`
break

View File

@ -46,6 +46,43 @@ describe('Basic - query', () => {
})
})
describe('Basic - queries', () => {
const app = new Hono()
app.get(
'/',
validator((v) => ({
ids: v.queries('id').isNumeric().isRequired(),
tags: v.queries('tag').isOptional(),
})),
(c) => {
return c.text('Valid')
}
)
it('Should return 200 response', async () => {
const res = await app.request('http://localhost/?id=123&id=456')
expect(res.status).toBe(200)
})
it('Should return 400 response - id is not set', async () => {
const res = await app.request('http://localhost/')
expect(res.status).toBe(400)
const messages = [
'Invalid Value []: the query parameters "id" is invalid - isNumeric',
'Invalid Value []: the query parameters "id" is invalid - isRequired',
]
expect(await res.text()).toBe(messages.join('\n'))
})
it('Should return 400 response - is is not numeric', async () => {
const res = await app.request('http://localhost/?id=one')
expect(res.status).toBe(400)
const messages = ['Invalid Value ["one"]: the query parameters "id" is invalid - isNumeric']
expect(await res.text()).toBe(messages.join('\n'))
})
})
describe('Basic - body', () => {
const app = new Hono()

View File

@ -43,6 +43,51 @@ describe('Basic - query', () => {
})
})
describe('Basic - queries', () => {
const v = new Validator()
const req = new Request('http://localhost/?tag=foo&tag=bar&id=123&id=456')
it('Should be valid - tag', async () => {
const validator = v.queries('tag').isRequired()
const results = await validator.validate(req)
expect(results[0].isValid).toBe(true)
expect(results[0].message).toBeUndefined()
expect(results[0].value).toEqual(['foo', 'bar'])
expect(results[1].isValid).toBe(true)
expect(results[1].message).toBeUndefined()
})
it('Should be invalid - ids', async () => {
const validator = v.queries('ids').isRequired()
const results = await validator.validate(req)
console.log(results)
expect(results[0].isValid).toBe(true)
expect(results[1].isValid).toBe(false)
expect(results[1].message).toBe(
'Invalid Value []: the query parameters "ids" is invalid - isRequired'
)
})
it('Should be valid - id', async () => {
const validator = v.queries('id').isNumeric()
const results = await validator.validate(req)
expect(results[0].isValid).toBe(true)
expect(results[0].message).toBeUndefined()
expect(results[1].isValid).toBe(true)
expect(results[1].message).toBeUndefined()
})
it('Should be valid - comment is options', async () => {
const validator = v.queries('comment').isOptional()
const results = await validator.validate(req)
expect(results[0].isValid).toBe(true)
expect(results[0].message).toBeUndefined()
expect(results[1].isValid).toBe(true)
expect(results[1].message).toBeUndefined()
})
})
describe('Basic - header', () => {
const v = new Validator()

View File

@ -4,7 +4,7 @@ import { rule } from './rule'
import { sanitizer } from './sanitizer'
import type { Schema } from './schema'
type Target = 'query' | 'header' | 'body' | 'json'
type Target = 'query' | 'queries' | 'header' | 'body' | 'json'
type Type = JSONPrimitive | JSONObject | JSONArray | File
type RuleFunc = (value: Type) => boolean
type Rule = {
@ -90,6 +90,7 @@ export class VArray<T extends Schema> extends VObjectBase<T> {
export class Validator {
isArray: boolean = false
query = (key: string): VString => new VString({ target: 'query', key: key })
queries = (key: string): VStringArray => new VStringArray({ target: 'queries', key: key })
header = (key: string): VString => new VString({ target: 'header', key: key })
body = (key: string): VString => new VString({ target: 'body', key: key })
json = (key: string) => {
@ -216,6 +217,9 @@ export abstract class VBase {
if (this.target === 'query') {
value = req.query(this.key)
}
if (this.target === 'queries') {
value = req.queries(this.key)
}
if (this.target === 'header') {
value = req.header(this.key)
}
@ -327,6 +331,7 @@ export abstract class VBase {
if (this._optional && typeof value === 'undefined') return true
if (Array.isArray(value)) {
if (value.length === 0 && !this._optional) return false
// Sanitize
for (const sanitizer of this.sanitizers) {
value = value.map((innerVal: any) => sanitizer(innerVal)) as JSONArray
@ -362,6 +367,9 @@ export abstract class VBase {
case 'query':
keyText = `the query parameter "${this.key}"`
break
case 'queries':
keyText = `the query parameters "${this.key}"`
break
case 'header':
keyText = `the request header "${this.key}"`
break