-
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.
- Loading branch information
Showing
19 changed files
with
508 additions
and
67 deletions.
There are no files selected for viewing
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
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 |
---|---|---|
@@ -1,9 +1,9 @@ | ||
// BackendAppConfig must be imported first in order to be used in other files. | ||
export * from "./BackendAppConfig"; | ||
export * from "./swagger"; | ||
|
||
// Variables used in BackendAppConfig come second. | ||
export * from "./vars"; | ||
|
||
export * from "./ioc"; | ||
export * from "./logger"; | ||
export * from "./swagger"; |
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 |
---|---|---|
@@ -1 +1,2 @@ | ||
export * from "./swagger"; | ||
export * from "./types"; |
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,27 +1,99 @@ | ||
import { Class } from "@org/shared"; | ||
import swaggerJsdoc from "swagger-jsdoc"; | ||
//import PackageJson from "./../../../package.json"; | ||
import { $BackendAppConfig } from "../BackendAppConfig"; | ||
import { RoutesMetaService } from "./../../meta"; | ||
import { | ||
SwaggerDefinition, | ||
SwaggerPath, | ||
SwaggerRequestMapping, | ||
SwaggerTag, | ||
} from "./types"; | ||
|
||
const packageRoot = (path: string) => `packages/backend/${path}`; | ||
|
||
const options: swaggerJsdoc.Options = { | ||
definition: { | ||
openapi: "3.0.0", | ||
info: { | ||
title: "REST API", | ||
version: "0.0.1", | ||
description: "This is a dynamically generated Swagger API documentation", | ||
const DEFAULT_SWAGGER_DEFINITION: SwaggerDefinition = { | ||
openapi: "3.0.0", | ||
info: { | ||
title: "REST API", | ||
license: { | ||
name: "MIT", | ||
url: "https://spdx.org/licenses/MIT.html", | ||
}, | ||
termsOfService: "http://swagger.io/terms/", | ||
contact: { | ||
email: "", | ||
name: "", | ||
url: "", | ||
}, | ||
components: { | ||
securitySchemas: { | ||
bearerAuth: { | ||
type: "http", | ||
scheme: "bearer", | ||
bearerFormat: "JWT", | ||
}, | ||
version: $BackendAppConfig.env.PACKAGE_JSON_VERSION, | ||
description: "This is a dynamically generated Swagger API documentation", | ||
}, | ||
components: { | ||
securitySchemas: { | ||
bearerAuth: { | ||
type: "http", | ||
scheme: "bearer", | ||
bearerFormat: "JWT", | ||
}, | ||
}, | ||
security: [{ bearerAuth: [] }], | ||
}, | ||
apis: [packageRoot("src/controllers/*.ts")], | ||
security: [{ bearerAuth: [] }], | ||
tags: [], | ||
paths: {}, | ||
}; | ||
|
||
export const swaggerSpec = swaggerJsdoc(options); | ||
export class SwaggerManager { | ||
#definition: SwaggerDefinition; | ||
#controllerClasses: Class[]; | ||
|
||
get definition() { | ||
return this.#definition; | ||
} | ||
|
||
get controllerClasses() { | ||
return this.#controllerClasses; | ||
} | ||
|
||
get tags() { | ||
return this.#definition.tags; | ||
} | ||
|
||
constructor() { | ||
this.#definition = DEFAULT_SWAGGER_DEFINITION; | ||
this.#controllerClasses = []; | ||
} | ||
|
||
registerTag(tagData: SwaggerTag & { constructor: Class }) { | ||
if (this.tags.some((t) => t.name === tagData.name)) return; | ||
const { constructor, ...tag } = tagData; | ||
this.controllerClasses.push(constructor); | ||
this.tags.push(tag); | ||
} | ||
|
||
registerPath( | ||
path: string, | ||
requestMapping: SwaggerRequestMapping, | ||
data: SwaggerPath | ||
) { | ||
if (!this.definition.paths[path]) this.definition.paths[path] = {}; | ||
this.definition.paths[path][requestMapping] = data; | ||
} | ||
|
||
#registerPaths() { | ||
$SwaggerManager.controllerClasses.forEach((controllerClass) => { | ||
const meta = RoutesMetaService.from(controllerClass).value; | ||
meta.routes.forEach((route) => { | ||
const fullPath = `${meta.basePath}${route.path}`; | ||
const swagger = route.swagger ?? {}; | ||
swagger.tags = [String(controllerClass.name)]; | ||
$SwaggerManager.registerPath(fullPath, route.method, swagger); | ||
}); | ||
}); | ||
} | ||
|
||
buildSpec(): object { | ||
this.#registerPaths(); | ||
return swaggerJsdoc({ definition: this.definition, apis: [] }); | ||
} | ||
} | ||
|
||
export const $SwaggerManager = new SwaggerManager(); |
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,100 @@ | ||
import HttpStatus from "http-status"; | ||
import swaggerJsdoc from "swagger-jsdoc"; | ||
|
||
type HttpStatusConverter<T> = { | ||
[K in keyof T]: T[K] extends number ? T[K] : never; | ||
}; | ||
|
||
type HttpStatusMain = Purify<HttpStatusConverter<typeof HttpStatus>>; | ||
|
||
type HttpStatusNumeric = Values<HttpStatusMain>; | ||
|
||
/** | ||
* A type that extracts the values from the properties of an object type `T`. | ||
* @typeParam T - An object type. | ||
*/ | ||
export type Values<T> = T[keyof T]; | ||
|
||
/** | ||
* A type that excludes properties with values of type `TExclude` from `TParent`. | ||
* @typeParam TParent - The parent type. | ||
* @typeParam TExclude - The type to exclude from `TParent`. | ||
*/ | ||
export type Exclude<TParent, TExclude> = Pick< | ||
TParent, | ||
Values<{ | ||
[Prop in keyof TParent]: [TParent[Prop]] extends [TExclude] ? never : Prop; | ||
}> | ||
>; | ||
|
||
/** | ||
* A type that removes properties with values of type `never` from `T`. | ||
* @typeParam T - The type to purify. | ||
*/ | ||
export type Purify<T> = Exclude<T, never>; | ||
|
||
export type SwaggerExternalDocs = { | ||
description: string; | ||
url: string; | ||
}; | ||
|
||
export type SwaggerTag = { | ||
name: string; | ||
description?: string; | ||
externalDocs?: SwaggerExternalDocs; | ||
}; | ||
|
||
export type SwaggerPath = { | ||
tags?: string[]; | ||
summary?: string; | ||
description?: string; | ||
operationId?: string; | ||
requestBody?: SwaggerRequestBody; | ||
responses?: SwaggerResponse; | ||
}; | ||
|
||
export type SwaggerPaths = Record< | ||
string, | ||
Partial<Record<SwaggerRequestMapping, SwaggerPath>> | ||
>; | ||
|
||
export type SwaggerRequestMapping = | ||
| "put" | ||
| "get" | ||
| "post" | ||
| "delete" | ||
| "patch" | ||
| "options" | ||
| "head"; | ||
|
||
export type SwaggerResponse = Partial< | ||
Record< | ||
HttpStatusNumeric, | ||
{ | ||
description?: string; | ||
content?: SwaggerRequestContent; | ||
} | ||
> | ||
>; | ||
|
||
export type SwaggerEntitySchema = { | ||
$ref?: string; | ||
type?: string; | ||
format?: string; | ||
}; | ||
|
||
export type SwaggerRequestContent = Record< | ||
string, | ||
{ schema: SwaggerEntitySchema } | ||
>; | ||
|
||
export type SwaggerRequestBody = { | ||
description?: string; | ||
content?: SwaggerRequestContent; | ||
required?: boolean; | ||
}; | ||
|
||
export type SwaggerDefinition = swaggerJsdoc.SwaggerDefinition & { | ||
tags: SwaggerTag[]; | ||
paths: SwaggerPaths; | ||
}; |
Oops, something went wrong.