Skip to content

Commit 7b94f5a

Browse files
authored
Merge pull request #5 from samchon/features/recursiveRef
Support `$recursiveRef` of OpenAPI v3.1.
2 parents e8a0059 + 10f6d2c commit 7b94f5a

File tree

5 files changed

+61
-4
lines changed

5 files changed

+61
-4
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@samchon/openapi",
3-
"version": "0.1.2",
3+
"version": "0.1.4",
44
"description": "",
55
"main": "./lib/index.js",
66
"typings": "./lib/index.d.ts",

Diff for: src/OpenApi.ts

+28-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,33 @@ import { SwaggerV2Converter } from "./internal/SwaggerV2Converter";
88
/**
99
* Emended OpenAPI v3.1 definition used by `typia` and `nestia`.
1010
*
11+
* `OpenApi` is a namespace containing functions and interfaces for emended
12+
* OpenAPI v3.1 specification. The keyword "emended" means that `OpenApi` is
13+
* not a direct OpenAPI v3.1 specification ({@link OpenApiV3_1}), but a little
14+
* bit shrinked to remove ambiguous and duplicated expressions of OpenAPI v3.1
15+
* for the convenience of `typia` and `nestia`.
16+
*
17+
* For example, when representing nullable type, OpenAPI v3.1 supports three ways.
18+
* In that case, `OpenApi` remains only the third way, so that makes `typia` and
19+
* `nestia` (especially `@nestia/editor`) to be simple and easy to implement.
20+
*
21+
* 1. `type: ["string", "null"]`
22+
* 2. `type: "string", nullable: true`
23+
* 3. `oneOf: [{ type: "string" }, { type: "null" }]`
24+
*
25+
* Here is the entire list of differences between OpenAPI v3.1 and emended `OpenApi`.
26+
*
27+
* - Operation
28+
* - Merged {@link OpenApiV3_1.IPathItem.parameters} to {@link OpenApi.IOperation.parameters}
29+
* - Resolved {@link OpenApi.IJsonSchema.IReference references} of {@link OpenApiV3_1.IOperation} mebers
30+
* - JSON Schema
31+
* - Decomposed mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed}
32+
* - Resolved nullable property: {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable}
33+
* - Array type utilizes only single {@link OpenAPI.IJsonSchema.IArray.items}
34+
* - Tuple type utilizes only {@link OpenApi.IJsonSchema.ITuple.prefixItems}
35+
* - Merged {@link OpenApiV3_1.IJsonSchema.IAnyOf} to {@link OpenApi.IJsonSchema.IOneOf}
36+
* - Merged {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to {@link OpenApi.IJsonSchema.IReference}
37+
*
1138
* @author Jeongho Nam - https://github.com/samchon
1239
*/
1340
export namespace OpenApi {
@@ -152,6 +179,7 @@ export namespace OpenApi {
152179
| IJsonSchema.INumber
153180
| IJsonSchema.IString
154181
| IJsonSchema.IArray
182+
| IJsonSchema.ITuple
155183
| IJsonSchema.IObject
156184
| IJsonSchema.IReference
157185
| IJsonSchema.IOneOf
@@ -220,7 +248,6 @@ export namespace OpenApi {
220248
/** @type uint */ maxItems?: number;
221249
}
222250
export interface ITuple extends __ISignificant<"array"> {
223-
items: never;
224251
prefixItems: IJsonSchema[];
225252
additionalItems: boolean | IJsonSchema;
226253
/** @type uint */ minItems?: number;

Diff for: src/OpenApiV3_1.ts

+4
Original file line numberDiff line numberDiff line change
@@ -278,9 +278,13 @@ export namespace OpenApiV3_1 {
278278
export interface IReference<Key = string> extends __IAttribute {
279279
$ref: Key;
280280
}
281+
export interface IRecursiveReference extends __IAttribute {
282+
$recursiveRef: string;
283+
}
281284

282285
export interface __ISignificant<Type extends string> extends __IAttribute {
283286
type: Type;
287+
nullable?: boolean;
284288
}
285289
export interface __IAttribute {
286290
title?: string;

Diff for: src/internal/OpenApiV3Converter.ts

+1
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ export namespace OpenApiV3Converter {
204204
),
205205
};
206206
const visit = (schema: OpenApiV3.IJsonSchema): void => {
207+
// NULLABLE PROPERTY
207208
if (
208209
(schema as OpenApiV3.IJsonSchema.__ISignificant<any>).nullable === true
209210
)

Diff for: src/internal/OpenApiV3_1Converter.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,15 @@ export namespace OpenApiV3_1Converter {
224224
),
225225
),
226226
};
227+
const nullable: { value: boolean } = { value: false };
228+
227229
const visit = (schema: OpenApiV3_1.IJsonSchema): void => {
230+
// NULLABLE PROPERTY
231+
if (
232+
(schema as OpenApiV3_1.IJsonSchema.__ISignificant<any>).nullable ===
233+
true
234+
)
235+
nullable.value ||= true;
228236
// MIXED TYPE CASE
229237
if (TypeChecker.isMixed(schema)) {
230238
if (schema.const !== undefined)
@@ -407,7 +415,7 @@ export namespace OpenApiV3_1Converter {
407415
});
408416
}
409417
// OBJECT TYPE CASE
410-
else if (TypeChecker.isObject(schema)) {
418+
else if (TypeChecker.isObject(schema))
411419
union.push({
412420
...schema,
413421
...{
@@ -428,12 +436,24 @@ export namespace OpenApiV3_1Converter {
428436
: undefined,
429437
},
430438
});
431-
}
439+
else if (TypeChecker.isRecursiveReference(schema))
440+
union.push({
441+
...schema,
442+
...{
443+
$ref: schema.$recursiveRef,
444+
$recursiveRef: undefined,
445+
},
446+
});
432447
// THE OTHERS
433448
else union.push(schema);
434449
};
435450

436451
visit(input);
452+
if (
453+
nullable.value === true &&
454+
!union.some((e) => (e as OpenApi.IJsonSchema.INull).type === "null")
455+
)
456+
union.push({ type: "null" });
437457
return {
438458
...(union.length === 0
439459
? { type: undefined }
@@ -477,6 +497,11 @@ export namespace OpenApiV3_1Converter {
477497
schema: OpenApiV3_1.IJsonSchema,
478498
): schema is OpenApiV3_1.IJsonSchema.IReference =>
479499
(schema as OpenApiV3_1.IJsonSchema.IReference).$ref !== undefined;
500+
export const isRecursiveReference = (
501+
schema: OpenApiV3_1.IJsonSchema,
502+
): schema is OpenApiV3_1.IJsonSchema.IRecursiveReference =>
503+
(schema as OpenApiV3_1.IJsonSchema.IRecursiveReference).$recursiveRef !==
504+
undefined;
480505
export const isOneOf = (
481506
schema: OpenApiV3_1.IJsonSchema,
482507
): schema is OpenApiV3_1.IJsonSchema.IOneOf =>

0 commit comments

Comments
 (0)