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:
parent
6a96ace96a
commit
c14dd5ad43
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user