Skip to content

Commit

Permalink
Merge pull request #5 from samchon/features/recursiveRef
Browse files Browse the repository at this point in the history
Support `$recursiveRef` of OpenAPI v3.1.
  • Loading branch information
samchon authored Apr 12, 2024
2 parents e8a0059 + 10f6d2c commit 7b94f5a
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 4 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@samchon/openapi",
"version": "0.1.2",
"version": "0.1.4",
"description": "",
"main": "./lib/index.js",
"typings": "./lib/index.d.ts",
Expand Down
29 changes: 28 additions & 1 deletion src/OpenApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,33 @@ import { SwaggerV2Converter } from "./internal/SwaggerV2Converter";
/**
* Emended OpenAPI v3.1 definition used by `typia` and `nestia`.
*
* `OpenApi` is a namespace containing functions and interfaces for emended
* OpenAPI v3.1 specification. The keyword "emended" means that `OpenApi` is
* not a direct OpenAPI v3.1 specification ({@link OpenApiV3_1}), 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`) to be simple and easy to implement.
*
* 1. `type: ["string", "null"]`
* 2. `type: "string", nullable: true`
* 3. `oneOf: [{ type: "string" }, { type: "null" }]`
*
* Here is the entire list of differences between OpenAPI v3.1 and emended `OpenApi`.
*
* - Operation
* - Merged {@link OpenApiV3_1.IPathItem.parameters} to {@link OpenApi.IOperation.parameters}
* - Resolved {@link OpenApi.IJsonSchema.IReference references} of {@link OpenApiV3_1.IOperation} mebers
* - JSON Schema
* - Decomposed mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed}
* - Resolved nullable property: {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable}
* - Array type utilizes only single {@link OpenAPI.IJsonSchema.IArray.items}
* - Tuple type utilizes only {@link OpenApi.IJsonSchema.ITuple.prefixItems}
* - Merged {@link OpenApiV3_1.IJsonSchema.IAnyOf} to {@link OpenApi.IJsonSchema.IOneOf}
* - Merged {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to {@link OpenApi.IJsonSchema.IReference}
*
* @author Jeongho Nam - https://github.com/samchon
*/
export namespace OpenApi {
Expand Down Expand Up @@ -152,6 +179,7 @@ export namespace OpenApi {
| IJsonSchema.INumber
| IJsonSchema.IString
| IJsonSchema.IArray
| IJsonSchema.ITuple
| IJsonSchema.IObject
| IJsonSchema.IReference
| IJsonSchema.IOneOf
Expand Down Expand Up @@ -220,7 +248,6 @@ export namespace OpenApi {
/** @type uint */ maxItems?: number;
}
export interface ITuple extends __ISignificant<"array"> {
items: never;
prefixItems: IJsonSchema[];
additionalItems: boolean | IJsonSchema;
/** @type uint */ minItems?: number;
Expand Down
4 changes: 4 additions & 0 deletions src/OpenApiV3_1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,9 +278,13 @@ export namespace OpenApiV3_1 {
export interface IReference<Key = string> extends __IAttribute {
$ref: Key;
}
export interface IRecursiveReference extends __IAttribute {
$recursiveRef: string;
}

export interface __ISignificant<Type extends string> extends __IAttribute {
type: Type;
nullable?: boolean;
}
export interface __IAttribute {
title?: string;
Expand Down
1 change: 1 addition & 0 deletions src/internal/OpenApiV3Converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ export namespace OpenApiV3Converter {
),
};
const visit = (schema: OpenApiV3.IJsonSchema): void => {
// NULLABLE PROPERTY
if (
(schema as OpenApiV3.IJsonSchema.__ISignificant<any>).nullable === true
)
Expand Down
29 changes: 27 additions & 2 deletions src/internal/OpenApiV3_1Converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,15 @@ export namespace OpenApiV3_1Converter {
),
),
};
const nullable: { value: boolean } = { value: false };

const visit = (schema: OpenApiV3_1.IJsonSchema): void => {
// NULLABLE PROPERTY
if (
(schema as OpenApiV3_1.IJsonSchema.__ISignificant<any>).nullable ===
true
)
nullable.value ||= true;
// MIXED TYPE CASE
if (TypeChecker.isMixed(schema)) {
if (schema.const !== undefined)
Expand Down Expand Up @@ -407,7 +415,7 @@ export namespace OpenApiV3_1Converter {
});
}
// OBJECT TYPE CASE
else if (TypeChecker.isObject(schema)) {
else if (TypeChecker.isObject(schema))
union.push({
...schema,
...{
Expand All @@ -428,12 +436,24 @@ export namespace OpenApiV3_1Converter {
: undefined,
},
});
}
else if (TypeChecker.isRecursiveReference(schema))
union.push({
...schema,
...{
$ref: schema.$recursiveRef,
$recursiveRef: undefined,
},
});
// THE OTHERS
else union.push(schema);
};

visit(input);
if (
nullable.value === true &&
!union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
)
union.push({ type: "null" });
return {
...(union.length === 0
? { type: undefined }
Expand Down Expand Up @@ -477,6 +497,11 @@ export namespace OpenApiV3_1Converter {
schema: OpenApiV3_1.IJsonSchema,
): schema is OpenApiV3_1.IJsonSchema.IReference =>
(schema as OpenApiV3_1.IJsonSchema.IReference).$ref !== undefined;
export const isRecursiveReference = (
schema: OpenApiV3_1.IJsonSchema,
): schema is OpenApiV3_1.IJsonSchema.IRecursiveReference =>
(schema as OpenApiV3_1.IJsonSchema.IRecursiveReference).$recursiveRef !==
undefined;
export const isOneOf = (
schema: OpenApiV3_1.IJsonSchema,
): schema is OpenApiV3_1.IJsonSchema.IOneOf =>
Expand Down

0 comments on commit 7b94f5a

Please sign in to comment.