-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add routing, guard, decorators, hono typing
- Loading branch information
Showing
20 changed files
with
346 additions
and
18 deletions.
There are no files selected for viewing
5 changes: 5 additions & 0 deletions
5
packages/hono-openapi-adapter/src/constants/reflector.constant.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export const SERVER_TARGET = {}; | ||
export const SERVER = 'server'; | ||
export const GUARD = 'guard'; | ||
export const PATH_METADATA = 'path'; | ||
export const CONTAINER = 'container'; |
Empty file.
29 changes: 29 additions & 0 deletions
29
packages/hono-openapi-adapter/src/decorators/guard.decorator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import type * as hono from 'hono'; | ||
import type { Container } from 'inversify'; | ||
import { CONTAINER, GUARD, SERVER_TARGET } from 'src/constants/reflector.constant'; | ||
import type { GuardsType } from 'src/types/guards'; | ||
|
||
export function guardHandler(guards: GuardsType[], ctx?: hono.Context): void { | ||
const container: Container = Reflect.getMetadata(CONTAINER, SERVER_TARGET); | ||
for (const guard of guards) { | ||
const resolvedGuard = container.get(guard); | ||
resolvedGuard.run(ctx); | ||
} | ||
} | ||
|
||
function createFunctionParameters() { | ||
return (...guard: GuardsType[]) => { | ||
return (target: any, propertyKey: string, descriptor: PropertyDescriptor): void | TypedPropertyDescriptor<any> => { | ||
const original = target[propertyKey]; | ||
Reflect.defineMetadata(GUARD, guard, original); | ||
return { | ||
...descriptor, | ||
value() { | ||
return original.call(this); | ||
}, | ||
}; | ||
}; | ||
}; | ||
} | ||
|
||
export const Guards = createFunctionParameters(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export * from './parameter.decorator'; | ||
export * from './controller.decorator'; | ||
export * from './guard.decorator'; |
76 changes: 76 additions & 0 deletions
76
packages/hono-openapi-adapter/src/decorators/parameter.decorator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import { createRoute } from '@hono/zod-openapi'; | ||
import { StatusCodes } from 'http-status-codes'; | ||
import { GUARD, PATH_METADATA, SERVER, SERVER_TARGET } from 'src/constants/reflector.constant'; | ||
import { RequestMethod } from 'src/enums/request-method'; | ||
import { guardMiddleware } from 'src/middlewares/guards.middleware'; | ||
import type { Server } from 'src/server'; | ||
import type { RouteParameters } from 'src/types/custom-hono-zod'; | ||
import type { GuardsType } from 'src/types/guards'; | ||
|
||
function controllerHandler(options: RouteParameters, requestType: RequestMethod, target: any, guards: GuardsType[], thisArg: any) { | ||
const server: Server = Reflect.getMetadata(SERVER, SERVER_TARGET); | ||
console.log(server); | ||
// Define route | ||
const { ...routeMetadata } = options; | ||
const finalRouteMetadata = Object.assign(routeMetadata, { | ||
method: requestType, | ||
}); | ||
const route = createRoute(finalRouteMetadata); | ||
|
||
if (guards && guards.length > 0) { | ||
guardMiddleware(guards, requestType, finalRouteMetadata.path); | ||
} | ||
|
||
const openapi = server.hono.openapi( | ||
route, | ||
async (ctx) => { | ||
return target.call(thisArg, ctx); | ||
}, | ||
(result, c) => { | ||
if (!result.success) { | ||
console.error(result.error); | ||
return c.json( | ||
{ | ||
code: StatusCodes.BAD_REQUEST, | ||
message: result.error, | ||
}, | ||
StatusCodes.BAD_REQUEST, | ||
); | ||
} | ||
}, | ||
); | ||
|
||
return openapi; | ||
} | ||
|
||
/** | ||
* To understand how this function work | ||
* @link https://stackoverflow.com/a/70910553/15431338 | ||
*/ | ||
export function createFunctionParameters(type: RequestMethod) { | ||
return (routeParameters: RouteParameters) => { | ||
return (target: any, propertyKey: string, descriptor: PropertyDescriptor): void | TypedPropertyDescriptor<any> => { | ||
// Add path to reflection | ||
Reflect.defineMetadata(PATH_METADATA, routeParameters.path, target); | ||
const original = target[propertyKey]; | ||
return { | ||
...descriptor, | ||
value() { | ||
const guards = Reflect.getMetadata(GUARD, original); | ||
// At the moment controller function cannot have other parameter than ctx | ||
return controllerHandler(routeParameters, type, original, guards, this); | ||
}, | ||
}; | ||
}; | ||
}; | ||
} | ||
|
||
export const Get = createFunctionParameters(RequestMethod.GET); | ||
|
||
export const Post = createFunctionParameters(RequestMethod.POST); | ||
|
||
export const Put = createFunctionParameters(RequestMethod.PUT); | ||
|
||
export const Patch = createFunctionParameters(RequestMethod.PATCH); | ||
|
||
export const Delete = createFunctionParameters(RequestMethod.DELETE); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
export enum RequestMethod { | ||
GET = 'get', | ||
POST = 'post', | ||
PUT = 'put', | ||
DELETE = 'delete', | ||
PATCH = 'patch', | ||
HEAD = 'head', | ||
OPTIONS = 'options', | ||
TRACE = 'trace', | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,7 @@ | ||
import 'reflect-metadata'; | ||
|
||
export { HonoFactory } from './start/hono-adapter'; | ||
export * from './server'; | ||
export * from './guards'; | ||
export * from './decorators'; | ||
export * as hono from 'hono'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,9 @@ | ||
import { Server } from '@server/server'; | ||
import type { Container } from 'inversify'; | ||
import { Server } from '../server'; | ||
|
||
/** | ||
* Bind all classes to container | ||
*/ | ||
export const bindToContainers = (container: Container): void => { | ||
container.bind('Server').to(Server).inSingletonScope(); | ||
container.bind(Server).toSelf().inSingletonScope(); | ||
}; |
42 changes: 42 additions & 0 deletions
42
packages/hono-openapi-adapter/src/middlewares/guards.middleware.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { SERVER, SERVER_TARGET } from 'src/constants/reflector.constant'; | ||
import { guardHandler } from 'src/decorators'; | ||
import { RequestMethod } from 'src/enums/request-method'; | ||
import type { Server } from 'src/server'; | ||
import type { GuardsType } from 'src/types/guards'; | ||
import { serializeRoutePath } from 'src/utils/utils'; | ||
|
||
/** | ||
* Set middleware for path and method | ||
*/ | ||
export function guardMiddleware(guards: GuardsType[], requestType: RequestMethod, path: string): void { | ||
if (guards && guards.length > 0) { | ||
const middlewareDefinition = getMiddlewareByDefinition(requestType); | ||
if (middlewareDefinition) { | ||
middlewareDefinition(serializeRoutePath(path), async (ctx, next) => { | ||
guardHandler(guards, ctx); | ||
await next(); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* https://hono.dev/guides/middleware#definition-of-middleware | ||
*/ | ||
function getMiddlewareByDefinition(type: RequestMethod) { | ||
const appServer: Server = Reflect.getMetadata(SERVER, SERVER_TARGET); | ||
switch (type) { | ||
case RequestMethod.GET: | ||
return appServer.hono.get; | ||
case RequestMethod.POST: | ||
return appServer.hono.post; | ||
case RequestMethod.PATCH: | ||
return appServer.hono.patch; | ||
case RequestMethod.DELETE: | ||
return appServer.hono.delete; | ||
case RequestMethod.PUT: | ||
return appServer.hono.put; | ||
case RequestMethod.OPTIONS: | ||
return appServer.hono.options; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,20 @@ | ||
import type { Container } from 'inversify'; | ||
import { CONTAINER, SERVER, SERVER_TARGET } from 'src/constants/reflector.constant'; | ||
import { bindToContainers } from '../ioc'; | ||
import type { Server } from '../server'; | ||
import { Server } from '../server'; | ||
|
||
class HonoAdapter { | ||
public listen(port: number, container: Container) { | ||
bindToContainers(container); | ||
const app = container.get<Server>('Server'); | ||
public listen(port: number, container: Container) { | ||
bindToContainers(container); | ||
const app = container.get(Server); | ||
Reflect.defineMetadata(SERVER, app, SERVER_TARGET); | ||
Reflect.defineMetadata(CONTAINER, container, SERVER_TARGET); | ||
|
||
return { | ||
port, | ||
fetch: app.hono.fetch, | ||
}; | ||
} | ||
return { | ||
port, | ||
fetch: app.hono.fetch, | ||
}; | ||
} | ||
} | ||
|
||
export const HonoFactory = new HonoAdapter(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import type { RouteConfig } from './hono-zod'; | ||
|
||
export type RouteParameters = RouteConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import type { GuardAbstract } from 'src/guards'; | ||
|
||
export type GuardsType<T extends GuardAbstract = any> = new () => T; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
/** | ||
* This class export the type of @hono/zod-openapi openapi-regristry.d.ts | ||
* Because we need to use this to type our custom controller and it's not exported by the library | ||
*/ | ||
|
||
import type { | ||
EncodingObject as EncodingObject30, | ||
ExamplesObject as ExamplesObject30, | ||
HeadersObject as HeadersObject30, | ||
LinksObject as LinksObject30, | ||
OperationObject as OperationObject30, | ||
ReferenceObject as ReferenceObject30, | ||
SchemaObject as SchemaObject30, | ||
} from 'openapi3-ts/oas30'; | ||
|
||
import type { | ||
EncodingObject as EncodingObject31, | ||
ExamplesObject as ExamplesObject31, | ||
HeadersObject as HeadersObject31, | ||
LinksObject as LinksObject31, | ||
OperationObject as OperationObject31, | ||
ReferenceObject as ReferenceObject31, | ||
SchemaObject as SchemaObject31, | ||
} from 'openapi3-ts/oas31'; | ||
import type { AnyZodObject, ZodType } from 'zod'; | ||
|
||
declare type EncodingObject = EncodingObject30 | EncodingObject31; | ||
declare type ExamplesObject = ExamplesObject30 | ExamplesObject31; | ||
declare type HeadersObject = HeadersObject30 | HeadersObject31; | ||
declare type LinksObject = LinksObject30 | LinksObject31; | ||
declare type OperationObject = OperationObject30 | OperationObject31; | ||
declare type ReferenceObject = ReferenceObject30 | ReferenceObject31; | ||
declare type SchemaObject = SchemaObject30 | SchemaObject31; | ||
declare type Method = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'head' | 'options' | 'trace'; | ||
interface ZodMediaTypeObject { | ||
schema: ZodType<unknown> | SchemaObject | ReferenceObject; | ||
examples?: ExamplesObject; | ||
example?: any; | ||
encoding?: EncodingObject; | ||
} | ||
|
||
interface ZodContentObject { | ||
[mediaType: string]: ZodMediaTypeObject; | ||
} | ||
|
||
interface ZodRequestBody { | ||
description?: string; | ||
content: ZodContentObject; | ||
required?: boolean; | ||
} | ||
|
||
interface ResponseConfig { | ||
description: string; | ||
headers?: AnyZodObject | HeadersObject; | ||
links?: LinksObject; | ||
content?: ZodContentObject; | ||
} | ||
export declare type RouteConfig = Omit<OperationObject, 'responses'> & { | ||
// Removed because we inject it in background | ||
// method: Method; | ||
path: string; | ||
request?: { | ||
body?: ZodRequestBody; | ||
params?: AnyZodObject; | ||
query?: AnyZodObject; | ||
cookies?: AnyZodObject; | ||
headers?: AnyZodObject | ZodType<unknown>[]; | ||
}; | ||
responses: { | ||
[statusCode: string]: ResponseConfig; | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import { describe, expect, it } from 'bun:test'; | ||
import { serializeRoutePath } from './utils'; | ||
|
||
describe('Util', () => { | ||
it('Should remove custom param to *', () => { | ||
const path = serializeRoutePath('/user/{userId}'); | ||
expect(path).toEqual('/user/*'); | ||
}); | ||
|
||
it('Should remove multiple custom param', () => { | ||
const path = serializeRoutePath('/user/{userId}/site/{siteId}'); | ||
expect(path).toEqual('/user/*/site/*'); | ||
}); | ||
|
||
it('Should return initial value', () => { | ||
const path = serializeRoutePath('/user/register'); | ||
expect(path).toEqual('/user/register'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
export function serializeRoutePath(routePath: string): string { | ||
// To find all string with pattern {...} to after replace them by * | ||
const regex = /\{[^}]+\}/g; | ||
const serializedPath = routePath.replace(regex, '*'); | ||
|
||
return serializedPath; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Get } from '@cosmosjs/hono-openapi'; | ||
import type { hono } from '@cosmosjs/hono-openapi'; | ||
import { injectable } from 'inversify'; | ||
|
||
@injectable() | ||
export class ControllerRoot { | ||
public setup(): void { | ||
this.helloWorld(); | ||
} | ||
|
||
@Get({ | ||
path: '/', | ||
responses: {}, | ||
}) | ||
private helloWorld(ctx?: hono.Context): unknown { | ||
if (ctx) { | ||
return ctx.json('Hello world, back is working fine'); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,16 @@ | ||
import { IocContainer } from '@cosmosjs/core'; | ||
import { Validator } from './validator'; | ||
import { inject, injectable } from 'inversify'; | ||
|
||
export default () => { | ||
console.log('cc') | ||
} | ||
const test = IocContainer.container.get(Test); | ||
}; | ||
|
||
@injectable() | ||
export class Test { | ||
constructor(@inject(Validator) private readonly validator: Validator) { } | ||
|
||
public validate() { | ||
console.log(this.validator); | ||
} | ||
} |
Oops, something went wrong.