233 lines
6.0 KiB
TypeScript
233 lines
6.0 KiB
TypeScript
import type { MiddlewareHandler } from 'hono'
|
|
import type { Equal, Expect } from 'hono/utils/types'
|
|
import type { MiddlewareToHandlerType, OfHandlerType, RouteHandler } from '../src'
|
|
|
|
import { OpenAPIHono, createRoute, z } from '../src'
|
|
|
|
describe('supports async handler', () => {
|
|
const route = createRoute({
|
|
method: 'get',
|
|
path: '/users',
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({
|
|
id: z.string(),
|
|
}),
|
|
},
|
|
},
|
|
description: 'Retrieve the user',
|
|
},
|
|
},
|
|
})
|
|
|
|
test('argument of openapi method', () => {
|
|
const hono = new OpenAPIHono()
|
|
|
|
hono.openapi(route, (c) => {
|
|
return c.json({
|
|
id: '123',
|
|
})
|
|
})
|
|
|
|
hono.openapi(route, async (c) => {
|
|
return c.json({
|
|
id: '123',
|
|
})
|
|
})
|
|
})
|
|
|
|
test('RouteHandler type', () => {
|
|
const handler: RouteHandler<typeof route> = (c) => {
|
|
return c.json({
|
|
id: '123',
|
|
})
|
|
}
|
|
|
|
const asyncHandler: RouteHandler<typeof route> = async (c) => {
|
|
return c.json({
|
|
id: '123',
|
|
})
|
|
}
|
|
|
|
const hono = new OpenAPIHono()
|
|
hono.openapi(route, handler)
|
|
hono.openapi(route, asyncHandler)
|
|
})
|
|
|
|
test('RouteHandler infers env type from middleware', () => {
|
|
type CustomEnv = { Variables: { customKey: string } }
|
|
|
|
const customMiddleware: MiddlewareHandler<CustomEnv> = (c, next) => {
|
|
c.set('customKey', 'customValue')
|
|
return next()
|
|
}
|
|
|
|
const routeWithMiddleware = createRoute({
|
|
method: 'get',
|
|
path: '/users',
|
|
middleware: [customMiddleware] as const,
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({
|
|
id: z.string(),
|
|
}),
|
|
},
|
|
},
|
|
description: 'Retrieve the user',
|
|
},
|
|
},
|
|
})
|
|
|
|
const handler: RouteHandler<typeof routeWithMiddleware> = (c) => {
|
|
return c.json({
|
|
id: c.get('customKey'),
|
|
})
|
|
}
|
|
|
|
const hono = new OpenAPIHono()
|
|
hono.openapi(routeWithMiddleware, handler)
|
|
})
|
|
|
|
test('RouteHandler infers complex objects from multiple middleware handlers', () => {
|
|
// https://github.com/honojs/middleware/issues/847
|
|
type CustomEnv = { Variables: { session: { id: string; createdAt: Date } } }
|
|
|
|
const setSessionMiddleware: MiddlewareHandler<CustomEnv> = (c, next) => {
|
|
c.set('session', { id: '8e760fe8-f064-4929-b632-737f88213e57', createdAt: new Date() })
|
|
return next()
|
|
}
|
|
|
|
const validateSessionMiddleware: MiddlewareHandler<CustomEnv> = async (c, next) => {
|
|
const session = c.get('session')
|
|
if ((new Date().getTime() - session.createdAt.getTime()) / 1000 / 60 / 60 > 1) {
|
|
return c.json({ message: 'Unauthorized' }, 401)
|
|
}
|
|
return await next()
|
|
}
|
|
|
|
type Example = MiddlewareToHandlerType<
|
|
[typeof setSessionMiddleware, typeof validateSessionMiddleware]
|
|
>
|
|
|
|
// ensure the first defined env does not lose its type in multi-middleware handler
|
|
type Verify = Expect<
|
|
Equal<
|
|
OfHandlerType<Example>['env'],
|
|
{
|
|
Variables: CustomEnv['Variables']
|
|
}
|
|
>
|
|
>
|
|
})
|
|
|
|
test('Route accepts a response other than the response from c.json() and c.text()', () => {
|
|
const route = createRoute({
|
|
method: 'get',
|
|
path: '/html',
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
'text/html': {
|
|
schema: z.string(),
|
|
},
|
|
},
|
|
description: 'Return HTML',
|
|
},
|
|
},
|
|
})
|
|
|
|
const handler: RouteHandler<typeof route> = (c) => {
|
|
return c.html('<h1>Hello from html</h1>')
|
|
}
|
|
|
|
const hono = new OpenAPIHono()
|
|
hono.openapi(route, handler)
|
|
})
|
|
|
|
test('handler should support default response for unspecified status codes', () => {
|
|
const routeWithDefault = createRoute({
|
|
method: 'get',
|
|
path: '/users',
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({ id: z.string() }),
|
|
},
|
|
},
|
|
description: 'Success response',
|
|
},
|
|
default: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({ error: z.string() }),
|
|
},
|
|
},
|
|
description: 'Error response',
|
|
},
|
|
},
|
|
})
|
|
|
|
const errorHandler: RouteHandler<typeof routeWithDefault> = (c) => {
|
|
return c.json({ error: 'Server Error' }, 500)
|
|
}
|
|
|
|
const hono = new OpenAPIHono()
|
|
hono.openapi(routeWithDefault, errorHandler)
|
|
})
|
|
|
|
test('handler should respect explicitly defined status codes with default fallback', () => {
|
|
const routeWithDefault = createRoute({
|
|
method: 'get',
|
|
path: '/users',
|
|
responses: {
|
|
200: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({ id: z.string() }),
|
|
},
|
|
},
|
|
description: 'Success response',
|
|
},
|
|
404: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({ message: z.string() }),
|
|
},
|
|
},
|
|
description: 'Not found response',
|
|
},
|
|
default: {
|
|
content: {
|
|
'application/json': {
|
|
schema: z.object({ error: z.string() }),
|
|
},
|
|
},
|
|
description: 'Error response',
|
|
},
|
|
},
|
|
})
|
|
|
|
const successHandler: RouteHandler<typeof routeWithDefault> = (c) => {
|
|
return c.json({ id: '123' }, 200)
|
|
}
|
|
|
|
const notFoundHandler: RouteHandler<typeof routeWithDefault> = (c) => {
|
|
return c.json({ message: 'Not Found' }, 404)
|
|
}
|
|
|
|
const errorHandler: RouteHandler<typeof routeWithDefault> = (c) => {
|
|
return c.json({ error: 'Server Error' }, 500)
|
|
}
|
|
|
|
const hono = new OpenAPIHono()
|
|
hono.openapi(routeWithDefault, successHandler)
|
|
hono.openapi(routeWithDefault, notFoundHandler)
|
|
hono.openapi(routeWithDefault, errorHandler)
|
|
})
|
|
})
|