Skip to content

Commit

Permalink
Merge pull request #21 from samchon/features/migrate
Browse files Browse the repository at this point in the history
New feature, `OpenApi.migrate()` function.
  • Loading branch information
samchon authored Jun 27, 2024
2 parents 4d5103f + 8b88680 commit 4598a53
Show file tree
Hide file tree
Showing 14 changed files with 830 additions and 15 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,13 @@ npm install @samchon/openapi
```

```typescript
import { OpenApi, SwaggerV2, OpenApiV3, OpenApiV3_1 } from "@samchon/openapi";
import {
OpenApi,
SwaggerV2,
OpenApiV3,
OpenApiV3_1,
IMigrateDocument,
} from "@samchon/openapi";

// original Swagger/OpenAPI document
const input:
Expand All @@ -64,6 +70,11 @@ const v3: OpenApiV3 = OpenApi.downgrade(output, "3.0");
// you can utilize it like below
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),
);
```


Expand Down
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@samchon/openapi",
"version": "0.1.22",
"version": "0.2.1",
"description": "OpenAPI definitions and converters for 'typia' and 'nestia'.",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down Expand Up @@ -29,14 +29,14 @@
},
"homepage": "https://github.com/samchon/openapi",
"devDependencies": {
"@nestia/e2e": "^0.4.3",
"@nestia/e2e": "^0.6.0",
"@types/node": "^20.12.7",
"prettier": "^3.2.5",
"rimraf": "^5.0.5",
"ts-patch": "^3.1.2",
"typescript": "^5.4.3",
"typescript": "5.4.5",
"typescript-transform-paths": "^3.4.7",
"typia": "^6.0.3"
"typia": "^6.2.2"
},
"files": [
"lib",
Expand Down
15 changes: 15 additions & 0 deletions src/IMigrateDocument.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IMigrateRoute } from "./IMigrateRoute";
import { OpenApi } from "./OpenApi";

export interface IMigrateDocument {
routes: IMigrateRoute[];
errors: IMigrateDocument.IError[];
}
export namespace IMigrateDocument {
export interface IError {
operation: () => OpenApi.IOperation;
method: "head" | "get" | "post" | "put" | "patch" | "delete";
path: string;
messages: string[];
}
}
49 changes: 49 additions & 0 deletions src/IMigrateRoute.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { OpenApi } from "./OpenApi";

export interface IMigrateRoute {
method: "head" | "get" | "post" | "put" | "patch" | "delete";
path: string;
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<string, IMigrateRoute.IException>; // other status cases
comment: () => string;
operation: () => OpenApi.IOperation;
}
export namespace IMigrateRoute {
export interface IParameter {
name: string;
key: string;
schema: OpenApi.IJsonSchema;
description?: string;
}
export interface IHeaders {
name: string; // headers
key: string; // headers
schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference;
}
export interface IQuery {
name: string;
key: string;
schema: OpenApi.IJsonSchema.IObject | OpenApi.IJsonSchema.IReference;
}
export interface IBody {
name: string;
key: string;
type:
| "text/plain"
| "application/json"
| "application/x-www-form-urlencoded"
| "multipart/form-data";
schema: OpenApi.IJsonSchema;
"x-nestia-encrypted"?: boolean;
}
export interface IException {
description?: string;
schema: OpenApi.IJsonSchema;
}
}
17 changes: 10 additions & 7 deletions src/OpenApi.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { IMigrateDocument } from "./IMigrateDocument";
import { OpenApiV3 } from "./OpenApiV3";
import { OpenApiV3_1 } from "./OpenApiV3_1";
import { SwaggerV2 } from "./SwaggerV2";
import { MigrateConverter } from "./internal/MigrateConverter";
import { OpenApiV3Converter } from "./internal/OpenApiV3Converter";
import { OpenApiV3Downgrader } from "./internal/OpenApiV3Downgrader";
import { OpenApiV3_1Converter } from "./internal/OpenApiV3_1Converter";
Expand Down Expand Up @@ -57,18 +59,18 @@ export namespace OpenApi {
* @param input Swagger or OpenAPI document to convert
* @returns Emended OpenAPI v3.1 document
*/
export const convert = (
export function convert(
input:
| SwaggerV2.IDocument
| OpenApiV3.IDocument
| OpenApiV3_1.IDocument
| OpenApi.IDocument,
): IDocument => {
): IDocument {
if (OpenApiV3_1.is(input)) return OpenApiV3_1Converter.convert(input);
else if (OpenApiV3.is(input)) return OpenApiV3Converter.convert(input);
else if (SwaggerV2.is(input)) return SwaggerV2Converter.convert(input);
throw new TypeError("Unrecognized Swagger/OpenAPI version.");
};
}

/**
* Downgrade to Swagger v2.0 document.
Expand Down Expand Up @@ -106,6 +108,10 @@ export namespace OpenApi {
throw new TypeError("Unrecognized Swagger/OpenAPI version.");
}

export function migrate(document: IDocument): IMigrateDocument {
return MigrateConverter.convert(document);
}

/* -----------------------------------------------------------
PATH ITEMS
----------------------------------------------------------- */
Expand All @@ -115,10 +121,7 @@ export namespace OpenApi {
info?: IDocument.IInfo;
components: IComponents;
paths?: Record<string, IPath>;
webhooks?: Record<
string,
IJsonSchema.IReference<`#/components/pathItems/${string}`> | IPath
>;
webhooks?: Record<string, IPath>;
security?: Record<string, string[]>[];
tags?: IDocument.ITag[];
"x-samchon-emended": true;
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export * from "./IMigrateRoute";
export * from "./IMigrateDocument";
export * from "./OpenApi";

export * from "./SwaggerV2";
Expand Down
50 changes: 50 additions & 0 deletions src/internal/MigrateConverter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { IMigrateRoute } from "../IMigrateRoute";
import { IMigrateDocument } from "../IMigrateDocument";
import { OpenApi } from "../OpenApi";
import { StringUtil } from "../utils/StringUtil";
import { MigrateOperationConverter } from "./MigrateOperationConverter";
import { MigrateRouteAccessor } from "./MigrateRouteAccessor";

export namespace MigrateConverter {
export const convert = (document: OpenApi.IDocument): IMigrateDocument => {
const errors: IMigrateDocument.IError[] = [];
const entire: Array<IMigrateRoute | null> = Object.entries({
...(document.paths ?? {}),
...(document.webhooks ?? {}),
})
.map(([path, collection]) =>
(["head", "get", "post", "put", "patch", "delete"] as const)
.filter((method) => collection[method] !== undefined)
.map((method) => {
const operation: OpenApi.IOperation = collection[method]!;
const migrated: IMigrateRoute | string[] =
MigrateOperationConverter.convert({
document,
method,
path,
emendedPath: StringUtil.reJoinWithDecimalParameters(path),
operation,
});
if (Array.isArray(migrated)) {
errors.push({
method,
path,
operation: () => operation,
messages: migrated,
});
return null;
}
return migrated;
}),
)
.flat();
const operations: IMigrateRoute[] = entire.filter(
(o): o is IMigrateRoute => !!o,
);
MigrateRouteAccessor.overwrite(operations);
return {
routes: operations,
errors,
};
};
}
Loading

0 comments on commit 4598a53

Please sign in to comment.