From 543f95ddd112014d9c9a068ab66d6d444f5f9781 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Fri, 28 Jun 2024 02:13:25 +0900 Subject: [PATCH] Documentation of migration structures --- README.md | 11 +- package.json | 8 +- src/IMigrateDocument.ts | 42 +++++ src/IMigrateRoute.ts | 168 +++++++++++++++++- src/internal/MigrateConverter.ts | 4 +- ...nConverter.ts => MigrateRouteConverter.ts} | 2 +- 6 files changed, 216 insertions(+), 19 deletions(-) rename src/internal/{MigrateOperationConverter.ts => MigrateRouteConverter.ts} (99%) diff --git a/README.md b/README.md index 169531e..b76101a 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ OpenAPI definitions and converters (for [typia](https://github.com/samchon/typia 2. [OpenAPI v3.0](https://github.com/samchon/openapi/blob/master/src/OpenApiV3.ts) 3. [OpenAPI v3.1](https://github.com/samchon/openapi/blob/master/src/OpenApiV3_1.ts) -Also, `@samchon/openapi` provides emended OpenAPI v3.1 definition and its converter/inverter from above versions for convenient development. The keyword "emended" means that [`OpenApi`](https://github.com/samchon/openapi/blob/master/src/OpenApi.ts) is not a direct OpenAPI v3.1 specification (OpenApiV3_1), but a little bit shrinked to remove ambiguous and duplicated expressions of OpenAPI v3.1 for the convenience of typia and nestia +Also, `@samchon/openapi` provides emended OpenAPI v3.1 definition and its converter/inverter from above versions for convenient development. The keyword "emended" means that [`OpenApi`](https://github.com/samchon/openapi/blob/master/src/OpenApi.ts) is not a direct OpenAPI v3.1 specification ([`OpenApiV3_1`](https://github.com/samchon/openapi/blob/master/src/OpenApiV3_1.ts)), but a little bit shrinked to remove ambiguous and duplicated expressions of OpenAPI v3.1 for the convenience of `typia` and `nestia`. For example, when representing nullable type, OpenAPI v3.1 supports three ways. In that case, OpenApi remains only the third way, so that makes `typia` and `nestia` (especially [`@nestia/editor`](https://nestia.io/docs/editor/)) to be simple and easy to implement. @@ -37,6 +37,9 @@ Here is the entire list of differences between OpenAPI v3.1 and emended OpenApi. - Merge `OpenApiV3_1.IJsonSchema.IRecursiveReference` to `OpenApi.IJsonSchema.IReference` - Merge `OpenApiV3_1.IJsonSchema.IAllOf` to `OpenApi.IJsonSchema.IObject` +Additionally, `@samchon/openapi` provides [`IMigrateDocument`](https://github.com/samchon/openapi/blob/master/src/IMigrateDocument.ts) for OpenAPI generators. + + ## How to use @@ -71,10 +74,8 @@ const v3: OpenApiV3 = OpenApi.downgrade(output, "3.0"); OpenApi.downgrade(OpenApi.convert(v2), "3.0"); OpenApi.downgrade(OpenApi.convert(v3), "2.0"); -// also supports openapi generator libraries -const migrate: IMigrateDocument = OpenApi.migrate( - OpenApi.convert(input), -); +// also helps openapi generator libraries +const migrate: IMigrateDocument = OpenApi.migrate(output); ``` diff --git a/package.json b/package.json index 959da18..9b4f669 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@samchon/openapi", - "version": "0.2.1", + "version": "0.2.2", "description": "OpenAPI definitions and converters for 'typia' and 'nestia'.", "main": "./lib/index.js", "typings": "./lib/index.d.ts", @@ -33,10 +33,10 @@ "@types/node": "^20.12.7", "prettier": "^3.2.5", "rimraf": "^5.0.5", - "ts-patch": "^3.1.2", - "typescript": "5.4.5", + "ts-patch": "^3.2.1", + "typescript": "^5.5.2", "typescript-transform-paths": "^3.4.7", - "typia": "^6.2.2" + "typia": "^6.3.1" }, "files": [ "lib", diff --git a/src/IMigrateDocument.ts b/src/IMigrateDocument.ts index c322437..63a2874 100644 --- a/src/IMigrateDocument.ts +++ b/src/IMigrateDocument.ts @@ -1,15 +1,57 @@ import { IMigrateRoute } from "./IMigrateRoute"; import { OpenApi } from "./OpenApi"; +/** + * Document of migration. + * + * The `IMigrateDocument` interface is a document of migration from + * {@link OpenAPI.IDocument OpenAPI document} to RPC (Remote Procedure Call) + * functions; {@link IMigrateRoute}. + * + * As the `IMigrateDocument` and {@link IMigrateRoute} have a lot of special + * stories, when you're developing OpenAPI generator library, please read + * their descriptions carefully including the description of properties. + * + * @author Jeongho Nam - https://github.com/samchon + */ export interface IMigrateDocument { + /** + * List of routes for migration. + */ routes: IMigrateRoute[]; + + /** + * List of errors occurred during the migration. + */ errors: IMigrateDocument.IError[]; } export namespace IMigrateDocument { + /** + * Error of migration in the operation level. + */ export interface IError { + /** + * Target operation causing the error. + */ operation: () => OpenApi.IOperation; + + /** + * Method of the operation. + * + * If the {@link OpenApi.IOperation.method} is not one of below type + * values, the operation would be ignored in the migration process for + * the RPC (Remote Procedure Call) function. + */ method: "head" | "get" | "post" | "put" | "patch" | "delete"; + + /** + * Original path from the OpenAPI document. + */ path: string; + + /** + * List of error messages (reasons). + */ messages: string[]; } } diff --git a/src/IMigrateRoute.ts b/src/IMigrateRoute.ts index 47e0fbc..1918828 100644 --- a/src/IMigrateRoute.ts +++ b/src/IMigrateRoute.ts @@ -1,17 +1,171 @@ import { OpenApi } from "./OpenApi"; +/** + * Route information for migration. + * + * The `IMigrateRoute` is a structure representing a route information for + * OpenAPI generated RPC (Remote Procedure Call) function composed from the + * {@link OpenApi.IOperation OpenAPI operation}. + * + * As the `IMigrateRoute` has a lot of speical stories, when you're developing + * OpenAPI generator library, please read its description carefully including + * the description of its properties. + * + * @author Jeongho Nam - https://github.com/samchon + */ export interface IMigrateRoute { + /** + * Method of the route. + * + * If the {@link OpenApi.IOperation.method} is not one of below type + * values, the operation would be ignored in the migration process for + * the RPC (Remote Procedure Call) function. + */ method: "head" | "get" | "post" | "put" | "patch" | "delete"; + + /** + * Original path from the OpenAPI document. + */ path: string; + + /** + * Emended path for OpenAPI generator libraries. + * + * The difference between {@link path} is: + * + * 1. Path parameters are replaced with `:param` format. + * 2. Empty sub-paths are removed. + * 3. Do not starts with `/`. + */ emendedPath: string; - accessor: string[]; // function accessor - parameters: IMigrateRoute.IParameter[]; // path parameters - headers: IMigrateRoute.IHeaders | null; // as an object - query: IMigrateRoute.IQuery | null; // as an object - body: IMigrateRoute.IBody | null; // request body - success: IMigrateRoute.IBody | null; // 200 or 201 status case - exceptions: Record; // other status cases + + /** + * Accessor for the route. + * + * The `accessor` is a list of string values that are representing how to + * access to the OpenAPI generated RPC (Remote Procedure Call) function + * through namespace(s). + * + * The `accessor` is composed with the following rules. At first, namespaces + * are composed by static directory names in the {@link path}. Parametric + * symbols represented by `:param` or `{param}` cannot be a part of the + * namespace. + * + * Instead, they would be a part of the function name. The function + * name is composed with the {@link method HTTP method} and parametric symbols + * like `getByParam` or `postByParam`. If there are multiple path parameters, + * they would be concatenated by `And` like `getByParam1AndParam2`. + * + * For refefence, if the {@link operation}'s {@link method} is `delete`, the + * function name would be replaced to `erase` instead of `delete`. It is + * the reason why the `delete` is a reserved keyword in many programming + * languages. + * + * - Exxample 1 + * - path: `POST /shopping/sellers/sales` + * - accessor: `shopping.sellers.sales.post` + * - Example 2 + * - endpoint: `GET /shoppings/sellers/sales/:saleId/reviews/:reviewId/comments/:id + * - accessor: `shoppings.sellers.sales.reviews.getBySaleIdAndReviewIdAndCommentId` + */ + accessor: string[]; + + /** + * List of path parameters. + */ + parameters: IMigrateRoute.IParameter[]; + + /** + * Metadata of headers. + * + * The `headers` property is a metadata of HTTP request headers for RPC function, + * including the parameter variable name and schema. + * + * Also, its {@link IMigrateRoute.IHeaders.schema} is always object or reference + * to object. Even though the original {@link OpenApi.IOperation OpenAPI operation}'s + * headers are separated to atomic typed properties, the `headers` property forcibly + * combines them into a single object type. + * + * For reference, if the `headers` property has been converted to an object type + * forcibly, its property {@link IMigrateRoute.IHeaders.name name} and + * {@link IMigrateRoute.IHeaders.key key} are always "headers". + */ + headers: IMigrateRoute.IHeaders | null; + + /** + * Metadata of query values. + * + * The `query` property is a metadata of HTTP request query values for RPC function, + * including the parameter variable name and schema. + * + * Also, its {@link IMigrateRoute.IQuery.schema} is always object or reference + * to object. Even though the original {@link OpenApi.IOperation OpenAPI operation}'s + * query parameters are separated to atomic typed properties, the `query` property + * forcibly combines them into a single object type. + * + * For reference, if the `query` property has been converted to an object type + * forcibly, its property {@link IMigrateRoute.IQuery.name name} and + * {@link IMigrateRoute.IQuery.key key} are always "headers". + */ + query: IMigrateRoute.IQuery | null; + + /** + * Metadata of request body. + * + * The `body` property is a metadata of HTTP request body for RPC function, + * including the parameter variable name, content type, and schema. + * + * If the `body` property is `null`, it means the operation does not require + * the request body data. + */ + body: IMigrateRoute.IBody | null; + + /** + * Metadata of response body for success case. + * + * The `success` property is a metadata of HTTP response body for RPC function, + * including content type, and schema when status code is `200` or `201`. + * + * If the `success` property is `null`, it means the operation does not have + * the response body data. In other words, the RPC function would return `void`. + */ + success: IMigrateRoute.IBody | null; + + /** + * Metadata of response body for exceptional status cases. + * + * The `exceptions` property is a metadata of HTTP response body for RPC + * function, including content type, and schema when status code is not `200` + * or `201`. + * + * The key of the `exceptions` property is the status code. It may be a + * stringified number, but sometimes it could be a string like "default", + * because the OpenAPI document allows the status code to be a string. + */ + exceptions: Record; + + /** + * Description comment for the route function. + * + * The `comment` is a function returning description comment for the + * RPC function of OpenAPI generated. The comment is composed with the + * following rules: + * + * 1. Starts from the {@link OpenApi.IOperation.summary} paragraph. + * 2. The next paragraphs are filled with {@link OpenApi.IOperation.description}. + * 3. Parameter descriptions are added with `@param` tag. + * 4. Security requirements are added with `@security` tag. + * 5. Tag names are added with `@tag` tag. + * 6. If {@link OpenApi.IOperation.deprecated}, `@deprecated` tag is added. + */ comment: () => string; + + /** + * Original operation from the OpenAPI document. + * + * The `operation` is a function returning the original + * {@link OpenApi.IOperation} from the {@link OpenAPI} document. + */ operation: () => OpenApi.IOperation; } export namespace IMigrateRoute { diff --git a/src/internal/MigrateConverter.ts b/src/internal/MigrateConverter.ts index c2a6786..37d62f0 100644 --- a/src/internal/MigrateConverter.ts +++ b/src/internal/MigrateConverter.ts @@ -2,7 +2,7 @@ import { IMigrateRoute } from "../IMigrateRoute"; import { IMigrateDocument } from "../IMigrateDocument"; import { OpenApi } from "../OpenApi"; import { StringUtil } from "../utils/StringUtil"; -import { MigrateOperationConverter } from "./MigrateOperationConverter"; +import { MigrateRouteConverter } from "./MigrateRouteConverter"; import { MigrateRouteAccessor } from "./MigrateRouteAccessor"; export namespace MigrateConverter { @@ -18,7 +18,7 @@ export namespace MigrateConverter { .map((method) => { const operation: OpenApi.IOperation = collection[method]!; const migrated: IMigrateRoute | string[] = - MigrateOperationConverter.convert({ + MigrateRouteConverter.convert({ document, method, path, diff --git a/src/internal/MigrateOperationConverter.ts b/src/internal/MigrateRouteConverter.ts similarity index 99% rename from src/internal/MigrateOperationConverter.ts rename to src/internal/MigrateRouteConverter.ts index 6f80d11..9fddabd 100644 --- a/src/internal/MigrateOperationConverter.ts +++ b/src/internal/MigrateRouteConverter.ts @@ -4,7 +4,7 @@ import { Escaper } from "../utils/Escaper"; import { StringUtil } from "../utils/StringUtil"; import { OpenApiTypeChecker } from "./OpenApiTypeChecker"; -export namespace MigrateOperationConverter { +export namespace MigrateRouteConverter { export interface IProps { document: OpenApi.IDocument; method: "head" | "get" | "post" | "put" | "patch" | "delete";