From 886d35c66b55892408de78cba3cf3f13c12138a4 Mon Sep 17 00:00:00 2001 From: Himenon Date: Mon, 26 Apr 2021 20:19:32 +0900 Subject: [PATCH 01/39] refactor: remove typescript dependency from walker --- .dependency-cruiser.js | 10 ++++++++++ src/internal/OpenApiTools/Parser.ts | 6 +++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js index b40abf12..ce9931f1 100644 --- a/.dependency-cruiser.js +++ b/.dependency-cruiser.js @@ -20,6 +20,16 @@ module.exports = { path: "^src/internal", }, }, + { + name: "Don't rely on typescript from Walker", + severity: "error", + from: { + path: "^src/internal/OpenApiTools/Walker", + }, + to: { + path: "typescript", + }, + }, { name: "not-to-test", comment: diff --git a/src/internal/OpenApiTools/Parser.ts b/src/internal/OpenApiTools/Parser.ts index aebd3d39..dbbd038d 100644 --- a/src/internal/OpenApiTools/Parser.ts +++ b/src/internal/OpenApiTools/Parser.ts @@ -11,18 +11,18 @@ import * as ConvertContext from "./ConverterContext"; import * as Extractor from "./Extractor"; import * as Paths from "./paths"; import * as TypeNodeContext from "./TypeNodeContext"; -import { Store } from "./Walker"; +import * as Walker from "./Walker"; export class Parser { private currentPoint: string; private convertContext: ConvertContext.Types; - private store: Store; + private store: Walker.Store; private factory: TypeScriptCodeGenerator.Factory.Type; constructor(private entryPoint: string, private rootSchema: OpenApi.Document, noReferenceOpenApiSchema: OpenApi.Document) { this.currentPoint = entryPoint; this.convertContext = ConvertContext.create(); this.factory = TypeScriptCodeGenerator.Factory.create(); - this.store = new Store(this.factory, noReferenceOpenApiSchema); + this.store = new Walker.Store(this.factory, noReferenceOpenApiSchema); this.initialize(); } From 68a6fd30ed846ac2b4a652c184e62e2f794d92f3 Mon Sep 17 00:00:00 2001 From: Himenon Date: Mon, 26 Apr 2021 20:52:11 +0900 Subject: [PATCH 02/39] feat: Define pure schema --- src/internal/IntermediateSchema/Schemas.ts | 77 ++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 src/internal/IntermediateSchema/Schemas.ts diff --git a/src/internal/IntermediateSchema/Schemas.ts b/src/internal/IntermediateSchema/Schemas.ts new file mode 100644 index 00000000..e38f4f61 --- /dev/null +++ b/src/internal/IntermediateSchema/Schemas.ts @@ -0,0 +1,77 @@ +export type Kind = + | "string" + | "integer" + | "number" + | "boolean" + | "undefined" + | "null" + | "never" + | "any" + | "void" + | "union" + | "intersection" + | "array" + | "object"; + +export interface BaseSchema { + kind: Kind; +} + +export interface StringSchema extends BaseSchema { + kind: "string"; + enum?: string[]; +} + +export interface IntegerSchema extends BaseSchema { + kind: "integer"; + enum?: number[]; +} + +export interface NumberSchema extends BaseSchema { + kind: "number"; + enum?: number[]; +} + +export interface BooleanSchema extends BaseSchema { + kind: "boolean"; +} + +export interface UndefinedSchema extends BaseSchema { + kind: "undefined"; +} + +export interface NullSchema extends BaseSchema { + kind: "null"; +} + +export interface NeverSchema extends BaseSchema { + kind: "never"; +} + +export interface AnySchema extends BaseSchema { + kind: "any"; +} + +export interface VoidSchema extends BaseSchema { + kind: "void"; +} + +export interface UnionSchema extends BaseSchema { + kind: "union"; + value: BaseSchema[]; +} + +export interface IntersectionSchema extends BaseSchema { + kind: "intersection"; + value: BaseSchema[]; +} + +export interface ArrayParams { + type: "array"; + value: BaseSchema; +} + +export interface ObjectParams { + type: "object"; + value: BaseSchema[]; +} From bc619b2a3cedcf926752756299c0c3268c4ae918 Mon Sep 17 00:00:00 2001 From: Himenon Date: Mon, 26 Apr 2021 22:44:04 +0900 Subject: [PATCH 03/39] feat: create to intermediate schema --- .dependency-cruiser.js | 4 +- src/internal/IntermediateSchema/Schemas.ts | 78 +++-- .../OpenApiTools/toIntermediateSchemaType.ts | 279 ++++++++++++++++++ 3 files changed, 339 insertions(+), 22 deletions(-) create mode 100644 src/internal/OpenApiTools/toIntermediateSchemaType.ts diff --git a/.dependency-cruiser.js b/.dependency-cruiser.js index ce9931f1..4ac156df 100644 --- a/.dependency-cruiser.js +++ b/.dependency-cruiser.js @@ -21,10 +21,10 @@ module.exports = { }, }, { - name: "Don't rely on typescript from Walker", + name: "Don't dependent on typescript from Walker", severity: "error", from: { - path: "^src/internal/OpenApiTools/Walker", + path: "^src/internal/OpenApiTools", }, to: { path: "typescript", diff --git a/src/internal/IntermediateSchema/Schemas.ts b/src/internal/IntermediateSchema/Schemas.ts index e38f4f61..04fb9a6b 100644 --- a/src/internal/IntermediateSchema/Schemas.ts +++ b/src/internal/IntermediateSchema/Schemas.ts @@ -10,68 +10,106 @@ export type Kind = | "void" | "union" | "intersection" + | "reference" | "array" + | "PropertySignature" + | "IndexSignature" | "object"; -export interface BaseSchema { +export interface BaseSchemaTypes { kind: Kind; } -export interface StringSchema extends BaseSchema { +export interface StringSchema extends BaseSchemaTypes { kind: "string"; enum?: string[]; } -export interface IntegerSchema extends BaseSchema { +export interface IntegerSchema extends BaseSchemaTypes { kind: "integer"; enum?: number[]; } -export interface NumberSchema extends BaseSchema { +export interface NumberSchema extends BaseSchemaTypes { kind: "number"; enum?: number[]; } -export interface BooleanSchema extends BaseSchema { +export interface BooleanSchema extends BaseSchemaTypes { kind: "boolean"; } -export interface UndefinedSchema extends BaseSchema { +export interface UndefinedSchema extends BaseSchemaTypes { kind: "undefined"; } -export interface NullSchema extends BaseSchema { +export interface NullSchema extends BaseSchemaTypes { kind: "null"; } -export interface NeverSchema extends BaseSchema { +export interface NeverSchema extends BaseSchemaTypes { kind: "never"; } -export interface AnySchema extends BaseSchema { +export interface AnySchema extends BaseSchemaTypes { kind: "any"; } -export interface VoidSchema extends BaseSchema { +export interface VoidSchema extends BaseSchemaTypes { kind: "void"; } -export interface UnionSchema extends BaseSchema { +export interface UnionSchema extends BaseSchemaTypes { kind: "union"; - value: BaseSchema[]; + schemaTypes: BaseSchemaTypes[]; } -export interface IntersectionSchema extends BaseSchema { +export interface IntersectionSchema extends BaseSchemaTypes { kind: "intersection"; - value: BaseSchema[]; + schemaTypes: BaseSchemaTypes[]; } -export interface ArrayParams { - type: "array"; - value: BaseSchema; +export interface IndexSignatureSchema extends BaseSchemaTypes { + kind: "IndexSignature"; + name: string; + schemaType: BaseSchemaTypes; } -export interface ObjectParams { - type: "object"; - value: BaseSchema[]; +export interface ReferenceSchema extends BaseSchemaTypes { + kind: "reference"; + name: string; } + +export interface ArrayParams extends BaseSchemaTypes { + kind: "array"; + schemaType: BaseSchemaTypes; +} + +export interface PropertySignatureParams extends BaseSchemaTypes { + kind: "PropertySignature"; + name: string; + optional: boolean; + comment?: string; + schemaType: BaseSchemaTypes; +} + +export interface ObjectParams extends BaseSchemaTypes { + kind: "object"; + properties: (PropertySignatureParams | IndexSignatureSchema)[]; +} + +export type SchemaType = + | StringSchema + | IntegerSchema + | NumberSchema + | BooleanSchema + | UndefinedSchema + | NullSchema + | NeverSchema + | AnySchema + | VoidSchema + | UnionSchema + | IntersectionSchema + | ReferenceSchema + | ArrayParams + | ObjectParams; diff --git a/src/internal/OpenApiTools/toIntermediateSchemaType.ts b/src/internal/OpenApiTools/toIntermediateSchemaType.ts new file mode 100644 index 00000000..2b74e1dd --- /dev/null +++ b/src/internal/OpenApiTools/toIntermediateSchemaType.ts @@ -0,0 +1,279 @@ +import type { OpenApi } from "../../types"; +import { UnsetTypeError } from "../Exception"; +import { UnSupportError } from "../Exception"; +import type * as Intermediate from "../IntermediateSchema/Schemas"; +import * as Logger from "../Logger"; +import { Factory } from "../TsGenerator"; +import * as Reference from "./components/Reference"; +import * as ConverterContext from "./ConverterContext"; +import * as Guard from "./Guard"; +import * as InferredType from "./InferredType"; +import { ObjectSchemaWithAdditionalProperties } from "./types"; + +export interface ResolveReferencePath { + name: string; + maybeResolvedName: string; + unresolvedPaths: string[]; +} + +export interface Context { + setReferenceHandler: (currentPoint: string, reference: Reference.Type) => void; + resolveReferencePath: (currentPoint: string, referencePath: string) => ResolveReferencePath; +} + +export type Convert = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, + setReference: Context, + convertContext: ConverterContext.Types, + option?: Option, +) => Intermediate.SchemaType; + +export interface Option { + parent?: any; +} + +export const generateMultiTypeNode = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + schemas: OpenApi.JSONSchema[], + setReference: Context, + convert: Convert, + convertContext: ConverterContext.Types, + multiType: "oneOf" | "allOf" | "anyOf", +): Intermediate.SchemaType => { + const value = schemas.map(schema => convert(entryPoint, currentPoint, factory, schema, setReference, convertContext)); + if (multiType === "oneOf") { + return { + kind: "union", + schemaTypes: value, + }; + } + if (multiType === "allOf") { + return { + kind: "intersection", + schemaTypes: value, + }; + } + // TODO Feature Development: Calculate intersection types + return { + kind: "never", + }; +}; + +const nullable = (factory: Factory.Type, schemaType: Intermediate.SchemaType, nullable: boolean): Intermediate.SchemaType => { + if (nullable) { + return { + kind: "union", + schemaTypes: [ + schemaType, + { + kind: "null", + }, + ], + }; + } + return schemaType; +}; + +export const convert: Convert = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, + context: Context, + converterContext: ConverterContext.Types, + option?: Option, +): Intermediate.SchemaType => { + if (typeof schema === "boolean") { + // https://swagger.io/docs/specification/data-models/dictionaries/#free-form + return { + kind: "object", + properties: [], + }; + } + if (Guard.isReference(schema)) { + const reference = Reference.generate(entryPoint, currentPoint, schema); + if (reference.type === "local") { + // Type Aliasを作成 (or すでにある場合は作成しない) + context.setReferenceHandler(currentPoint, reference); + const { maybeResolvedName } = context.resolveReferencePath(currentPoint, reference.path); + return { + kind: "reference", + name: converterContext.escapeDeclarationText(maybeResolvedName), + }; + } + // サポートしているディレクトリに対して存在する場合 + if (reference.componentName) { + // Type AliasもしくはInterfaceを作成 + context.setReferenceHandler(currentPoint, reference); + // Aliasを貼る + return { + kind: "reference", + name: context.resolveReferencePath(currentPoint, reference.path).name, + }; + } + // サポートしていないディレクトリに存在する場合、直接Interface、もしくはTypeAliasを作成 + return convert(entryPoint, reference.referencePoint, factory, reference.data, context, converterContext, { parent: schema }); + } + + if (Guard.isOneOfSchema(schema)) { + return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.oneOf, context, convert, converterContext, "oneOf"); + } + if (Guard.isAllOfSchema(schema)) { + return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.allOf, context, convert, converterContext, "allOf"); + } + if (Guard.isAnyOfSchema(schema)) { + return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.anyOf, context, convert, converterContext, "anyOf"); + } + + if (Guard.isHasNoMembersObject(schema)) { + return { + kind: "object", + properties: [], + }; + } + + // schema.type + if (!schema.type) { + const inferredSchema = InferredType.getInferredType(schema); + if (inferredSchema) { + return convert(entryPoint, currentPoint, factory, inferredSchema, context, converterContext, { parent: schema }); + } + // typeを指定せずに、nullableのみを指定している場合に type object変換する + if (typeof schema.nullable === "boolean") { + return nullable(factory, { kind: "any" }, schema.nullable); + } + if (option && option.parent) { + Logger.info("Parent Schema:"); + Logger.info(JSON.stringify(option.parent)); + } + Logger.showFilePosition(entryPoint, currentPoint); + throw new UnsetTypeError("Please set 'type' or '$ref' property \n" + JSON.stringify(schema)); + } + switch (schema.type) { + case "boolean": { + return nullable(factory, { kind: "boolean" }, !!schema.nullable); + } + case "null": { + return { + kind: "null", + }; + } + case "integer": + case "number": { + const items = schema.enum; + let typeNode: Intermediate.SchemaType; + if (items && Guard.isNumberArray(items)) { + typeNode = { + kind: "number", + enum: items, + }; + } else { + typeNode = { + kind: "number", + }; + } + return nullable(factory, typeNode, !!schema.nullable); + } + case "string": { + const items = schema.enum; + let typeNode: Intermediate.SchemaType; + if (items && Guard.isStringArray(items)) { + typeNode = { + kind: "string", + enum: items, + }; + } else { + typeNode = { + kind: "string", + }; + } + return nullable(factory, typeNode, !!schema.nullable); + } + case "array": { + if (Array.isArray(schema.items) || typeof schema.items === "boolean") { + throw new UnSupportError(`schema.items = ${JSON.stringify(schema.items)}`); + } + const typeNode: Intermediate.SchemaType = { + kind: "array", + schemaType: schema.items + ? convert(entryPoint, currentPoint, factory, schema.items, context, converterContext, { parent: schema }) + : { + kind: "undefined", + }, + }; + return nullable(factory, typeNode, !!schema.nullable); + } + case "object": { + const required: string[] = schema.required || []; + // // https://swagger.io/docs/specification/data-models/dictionaries/#free-form + if (schema.additionalProperties === true) { + return { + kind: "object", + properties: [], + }; + } + const value: Intermediate.PropertySignatureParams[] = Object.entries(schema.properties || {}).map(([name, jsonSchema]) => { + return { + kind: "PropertySignature", + name: converterContext.escapePropertySignatureName(name), + schemaType: convert(entryPoint, currentPoint, factory, jsonSchema, context, converterContext, { parent: schema.properties }), + optional: !required.includes(name), + comment: typeof jsonSchema !== "boolean" ? jsonSchema.description : undefined, + }; + }); + if (schema.additionalProperties) { + const additionalProperties: Intermediate.IndexSignatureSchema = { + kind: "IndexSignature", + name: "key", + schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, context, converterContext, { + parent: schema.properties, + }), + }; + return { + kind: "object", + properties: [...value, additionalProperties], + }; + } + const typeNode: Intermediate.SchemaType = { + kind: "object", + properties: value, + }; + return nullable(factory, typeNode, !!schema.nullable); + } + default: + return { + kind: "any", + }; + // throw new UnknownError("what is this? \n" + JSON.stringify(schema, null, 2)); + } +}; + +export const convertAdditionalProperties = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + schema: ObjectSchemaWithAdditionalProperties, + setReference: Context, + convertContext: ConverterContext.Types, +): Intermediate.IndexSignatureSchema => { + // // https://swagger.io/docs/specification/data-models/dictionaries/#free-form + if (schema.additionalProperties === true) { + factory.TypeNode.create({ + type: schema.type, + value: [], + }); + } + const additionalProperties: Intermediate.IndexSignatureSchema = { + kind: "IndexSignature", + name: "key", + schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, setReference, convertContext, { + parent: schema.properties, + }), + }; + return additionalProperties; +}; From 71d4b1a677561c766b68a600852c90f13c3fa1f1 Mon Sep 17 00:00:00 2001 From: Himenon Date: Tue, 27 Apr 2021 16:26:27 +0900 Subject: [PATCH 04/39] feat: add types --- .../{Schemas.ts => index.ts} | 2 +- .../components2/ExternalDocumentation.ts | 10 + .../OpenApiTools/components2/Header.ts | 89 +++++++ .../OpenApiTools/components2/Headers.ts | 54 +++++ .../OpenApiTools/components2/MediaType.ts | 55 +++++ .../OpenApiTools/components2/Operation.ts | 224 ++++++++++++++++++ .../OpenApiTools/components2/Parameter.ts | 135 +++++++++++ .../OpenApiTools/components2/Parameters.ts | 113 +++++++++ .../OpenApiTools/components2/PathItem.ts | 126 ++++++++++ .../OpenApiTools/components2/PathItems.ts | 56 +++++ .../OpenApiTools/components2/Reference.ts | 199 ++++++++++++++++ .../OpenApiTools/components2/RequestBodies.ts | 50 ++++ .../OpenApiTools/components2/RequestBody.ts | 56 +++++ .../OpenApiTools/components2/Response.ts | 117 +++++++++ .../OpenApiTools/components2/Responses.ts | 200 ++++++++++++++++ .../OpenApiTools/components2/Schema.ts | 211 +++++++++++++++++ .../OpenApiTools/components2/Schemas.ts | 149 ++++++++++++ .../components2/SecuritySchema.ts | 51 ++++ .../components2/SecuritySchemas.ts | 44 ++++ .../OpenApiTools/components2/Server.ts | 10 + .../OpenApiTools/components2/Servers.ts | 11 + .../OpenApiTools/toIntermediateSchemaType.ts | 2 +- 22 files changed, 1962 insertions(+), 2 deletions(-) rename src/internal/IntermediateSchema/{Schemas.ts => index.ts} (98%) create mode 100644 src/internal/OpenApiTools/components2/ExternalDocumentation.ts create mode 100644 src/internal/OpenApiTools/components2/Header.ts create mode 100644 src/internal/OpenApiTools/components2/Headers.ts create mode 100644 src/internal/OpenApiTools/components2/MediaType.ts create mode 100644 src/internal/OpenApiTools/components2/Operation.ts create mode 100644 src/internal/OpenApiTools/components2/Parameter.ts create mode 100644 src/internal/OpenApiTools/components2/Parameters.ts create mode 100644 src/internal/OpenApiTools/components2/PathItem.ts create mode 100644 src/internal/OpenApiTools/components2/PathItems.ts create mode 100644 src/internal/OpenApiTools/components2/Reference.ts create mode 100644 src/internal/OpenApiTools/components2/RequestBodies.ts create mode 100644 src/internal/OpenApiTools/components2/RequestBody.ts create mode 100644 src/internal/OpenApiTools/components2/Response.ts create mode 100644 src/internal/OpenApiTools/components2/Responses.ts create mode 100644 src/internal/OpenApiTools/components2/Schema.ts create mode 100644 src/internal/OpenApiTools/components2/Schemas.ts create mode 100644 src/internal/OpenApiTools/components2/SecuritySchema.ts create mode 100644 src/internal/OpenApiTools/components2/SecuritySchemas.ts create mode 100644 src/internal/OpenApiTools/components2/Server.ts create mode 100644 src/internal/OpenApiTools/components2/Servers.ts diff --git a/src/internal/IntermediateSchema/Schemas.ts b/src/internal/IntermediateSchema/index.ts similarity index 98% rename from src/internal/IntermediateSchema/Schemas.ts rename to src/internal/IntermediateSchema/index.ts index 04fb9a6b..186e0cd9 100644 --- a/src/internal/IntermediateSchema/Schemas.ts +++ b/src/internal/IntermediateSchema/index.ts @@ -90,7 +90,7 @@ export interface PropertySignatureParams extends BaseSchemaTypes { name: string; optional: boolean; comment?: string; - schemaType: BaseSchemaTypes; + schemaType: SchemaType; } export interface ObjectParams extends BaseSchemaTypes { diff --git a/src/internal/OpenApiTools/components2/ExternalDocumentation.ts b/src/internal/OpenApiTools/components2/ExternalDocumentation.ts new file mode 100644 index 00000000..d3327b61 --- /dev/null +++ b/src/internal/OpenApiTools/components2/ExternalDocumentation.ts @@ -0,0 +1,10 @@ +import { EOL } from "os"; + +import type { OpenApi } from "../../../types"; + +export const addComment = (comment?: string, externalDocs?: OpenApi.ExternalDocumentation): string | undefined => { + if (!externalDocs) { + return comment; + } + return [comment, "", `@see ${externalDocs.url}`, externalDocs.description].filter(Boolean).join(EOL); +}; diff --git a/src/internal/OpenApiTools/components2/Header.ts b/src/internal/OpenApiTools/components2/Header.ts new file mode 100644 index 00000000..0c988f3b --- /dev/null +++ b/src/internal/OpenApiTools/components2/Header.ts @@ -0,0 +1,89 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as ToTypeNode from "../toTypeNode"; +import * as Reference from "./Reference"; + +export const generateTypeNode = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + header: OpenApi.Header, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.TypeAliasDeclaration => { + return factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(name), + type: ToTypeNode.convert(entryPoint, currentPoint, factory, header.schema || { type: "null" }, context, converterContext), + }); +}; + +export const generatePropertySignature = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + header: OpenApi.Header | OpenApi.Reference, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature => { + if (Guard.isReference(header)) { + const reference = Reference.generate(entryPoint, currentPoint, header); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(name), + optional: false, + type: factory.TypeReferenceNode.create({ + name: context.resolveReferencePath(currentPoint, reference.path).name, + }), + }); + } + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(name), + optional: false, + type: factory.TypeReferenceNode.create({ + name: context.resolveReferencePath(currentPoint, reference.path).name, + }), + }); + } + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(name), + optional: false, + type: ToTypeNode.convert(entryPoint, currentPoint, factory, header.schema || { type: "null" }, context, converterContext), + }); +}; + +export const generatePropertySignatures = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + headers: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature[] => { + return Object.entries(headers).map(([headerName, header]) => { + return generatePropertySignature(entryPoint, currentPoint, factory, headerName, header, context, converterContext); + }); +}; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + headers: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.InterfaceDeclaration => { + return factory.InterfaceDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(name), + members: generatePropertySignatures(entryPoint, currentPoint, factory, headers, context, converterContext), + }); +}; diff --git a/src/internal/OpenApiTools/components2/Headers.ts b/src/internal/OpenApiTools/components2/Headers.ts new file mode 100644 index 00000000..74aba1b8 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Headers.ts @@ -0,0 +1,54 @@ +import type { OpenApi } from "../../../types"; +import { UndefinedComponent } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Header from "./Header"; +import * as Reference from "./Reference"; +import * as Schema from "./Schema"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + headers: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + store.addComponent("headers", { + kind: "namespace", + name: Name.Components.Headers, + }); + Object.entries(headers).forEach(([name, header]) => { + if (Guard.isReference(header)) { + const reference = Reference.generate(entryPoint, currentPoint, header); + if (reference.type === "local") { + if (!store.hasStatement(reference.path, ["interface"])) { + throw new UndefinedComponent(`Reference "${header.$ref}" did not found in ${reference.path} by ${reference.name}`); + } + } else if (reference.type === "remote") { + Schema.addSchema( + entryPoint, + currentPoint, + store, + factory, + reference.path, + reference.name, + reference.data.schema, + context, + converterContext, + ); + } + } else { + store.addStatement(`components/headers/${name}`, { + kind: "typeAlias", + name: converterContext.escapeDeclarationText(name), + value: Header.generateTypeNode(entryPoint, currentPoint, factory, name, header, context, converterContext), + }); + } + }); +}; diff --git a/src/internal/OpenApiTools/components2/MediaType.ts b/src/internal/OpenApiTools/components2/MediaType.ts new file mode 100644 index 00000000..dc3bf4e5 --- /dev/null +++ b/src/internal/OpenApiTools/components2/MediaType.ts @@ -0,0 +1,55 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as ToTypeNode from "../toTypeNode"; + +export const generatePropertySignature = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + protocol: string, + schema: OpenApi.Schema, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature => { + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(protocol), + optional: false, + type: ToTypeNode.convert(entryPoint, currentPoint, factory, schema, context, converterContext), + comment: schema.description, + }); +}; + +export const generatePropertySignatures = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + content: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature[] => { + return Object.entries(content).reduce((previous, [protocol, mediaType]) => { + if (!mediaType.schema) { + return previous; + } + return previous.concat(generatePropertySignature(entryPoint, currentPoint, factory, protocol, mediaType.schema, context, converterContext)); + }, []); +}; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + content: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.InterfaceDeclaration => { + return factory.InterfaceDeclaration.create({ + export: true, + name, + members: generatePropertySignatures(entryPoint, currentPoint, factory, content, context, converterContext), + }); +}; diff --git a/src/internal/OpenApiTools/components2/Operation.ts b/src/internal/OpenApiTools/components2/Operation.ts new file mode 100644 index 00000000..4b9f9dce --- /dev/null +++ b/src/internal/OpenApiTools/components2/Operation.ts @@ -0,0 +1,224 @@ +import { EOL } from "os"; +import * as path from "path"; + +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as ExternalDocumentation from "./ExternalDocumentation"; +import * as Parameter from "./Parameter"; +import * as Reference from "./Reference"; +import * as RequestBody from "./RequestBody"; +import * as Responses from "./Responses"; +import * as Servers from "./Servers"; + +const generateComment = (operation: OpenApi.Operation): string => { + const comments: string[] = []; + if (operation.summary) { + comments.push(operation.summary); + } + if (operation.description) { + comments.push(operation.description); + } + if (operation.tags) { + comments.push(`tags: ${operation.tags.join(", ")}`); + } + return comments.join(EOL); +}; + +// 使わない可能性あり +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentPath: string, + name: string, + operation: OpenApi.Operation, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = `${parentPath}/${name}`; + const operationId = operation.operationId; + if (!operationId) { + throw new Error("not setting operationId\n" + JSON.stringify(operation)); + } + store.addStatement(basePath, { + kind: "namespace", + name, + comment: ExternalDocumentation.addComment(Servers.addComment([generateComment(operation)], operation.servers), operation.externalDocs), + deprecated: operation.deprecated, + }); + + if (operation.parameters) { + const parameterName = "Parameter"; + store.addStatement(`${basePath}/Parameter`, { + kind: "interface", + name: parameterName, + value: Parameter.generateInterface( + entryPoint, + currentPoint, + store, + factory, + parameterName, + operation.parameters, + context, + converterContext, + ), + }); + } + + if (operation.requestBody) { + if (Guard.isReference(operation.requestBody)) { + const reference = Reference.generate(entryPoint, currentPoint, operation.requestBody); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + // TODO (not-use) 追加する必要がある(このメソッドを使わない可能性あり) + factory.TypeReferenceNode.create({ name: context.resolveReferencePath(currentPoint, reference.path).name }); + } else if (reference.type === "remote" && reference.componentName) { + const contentPath = path.join(reference.path, "Content"); // requestBodyはNamespaceを形成するため + const name = "Content"; + store.addStatement(contentPath, { + kind: "interface", + name: name, + value: RequestBody.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data, context, converterContext), + }); + const typeAliasName = context.resolveReferencePath(currentPoint, contentPath).name; + store.addStatement(`${basePath}/RequestBody`, { + kind: "typeAlias", + name: typeAliasName, + value: factory.TypeAliasDeclaration.create({ + export: true, + name: "RequestBody", + type: factory.TypeReferenceNode.create({ name: typeAliasName }), + }), + }); + } + } else { + RequestBody.generateNamespace( + entryPoint, + currentPoint, + store, + factory, + basePath, + "RequestBody", + operation.requestBody, + context, + converterContext, + ); + } + } + + if (operation.responses) { + Responses.generateNamespaceWithStatusCode( + entryPoint, + currentPoint, + store, + factory, + basePath, + operation.responses, + context, + converterContext, + ); + } +}; + +export const generateStatements = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + requestUri: string, + httpMethod: string, // PUT POST PATCH + operation: OpenApi.Operation, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.Statement[] => { + let statements: ts.Statement[] = []; + const operationId = operation.operationId; + if (!operationId) { + throw new Error("not setting operationId\n" + JSON.stringify(operation)); + } + store.updateOperationState(httpMethod, requestUri, operationId, {}); + if (operation.parameters) { + const parameterName = converterContext.generateParameterName(operationId); + statements.push( + Parameter.generateAliasInterface( + entryPoint, + currentPoint, + store, + factory, + parameterName, + operation.parameters, + context, + converterContext, + ), + ); + } + if (operation.requestBody) { + const requestBodyName = converterContext.generateRequestBodyName(operationId); + if (Guard.isReference(operation.requestBody)) { + const reference = Reference.generate(entryPoint, currentPoint, operation.requestBody); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + statements.push( + factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.generateRequestBodyName(operationId), + type: factory.TypeReferenceNode.create({ + name: context.resolveReferencePath(currentPoint, `${reference.path}`) + "." + Name.ComponentChild.Content, // TODO Contextから作成? + }), + }), + ); + } else if (reference.type === "remote" && reference.componentName) { + const contentPath = path.join(reference.path, "Content"); // requestBodyはNamespaceを形成するため + const name = "Content"; + store.addStatement(contentPath, { + kind: "interface", + name: name, + value: RequestBody.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data, context, converterContext), + }); + statements.push( + factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(requestBodyName), + type: factory.TypeReferenceNode.create({ name: context.resolveReferencePath(currentPoint, contentPath).name }), + }), + ); + + store.updateOperationState(httpMethod, requestUri, operationId, { + requestBodyName: requestBodyName, + }); + } + } else { + statements.push( + RequestBody.generateInterface(entryPoint, currentPoint, factory, requestBodyName, operation.requestBody, context, converterContext), + ); + store.updateOperationState(httpMethod, requestUri, operationId, { + requestBodyName: requestBodyName, + }); + } + } + + if (operation.responses) { + statements = statements.concat( + Responses.generateInterfacesWithStatusCode( + entryPoint, + currentPoint, + store, + factory, + operationId, + operation.responses, + context, + converterContext, + ).flat(), + ); + } + + return statements; +}; diff --git a/src/internal/OpenApiTools/components2/Parameter.ts b/src/internal/OpenApiTools/components2/Parameter.ts new file mode 100644 index 00000000..b3d19bb6 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Parameter.ts @@ -0,0 +1,135 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Reference from "./Reference"; + +export const generateTypeNode = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + parameter: OpenApi.Parameter, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.TypeNode => { + return ToTypeNode.convert(entryPoint, currentPoint, factory, parameter.schema || { type: "null" }, context, converterContext); +}; + +export const generateTypeAlias = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + parameter: OpenApi.Parameter, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.TypeAliasDeclaration => { + return factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(name), + comment: parameter.description, + type: generateTypeNode(entryPoint, currentPoint, factory, parameter, context, converterContext), + }); +}; + +export const generatePropertySignature = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parameter: OpenApi.Parameter | OpenApi.Reference, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature => { + if (Guard.isReference(parameter)) { + const reference = Reference.generate(entryPoint, currentPoint, parameter); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + const localRef = store.getParameter(reference.path); + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(localRef.name), + optional: false, + comment: localRef.description, + type: factory.TypeReferenceNode.create({ + name: context.resolveReferencePath(currentPoint, reference.path).name, + }), + }); + } + const isPathProperty = reference.data.in === "path"; + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(reference.data.name), + optional: isPathProperty ? false : !reference.data.required, + comment: reference.data.description, + type: ToTypeNode.convert( + entryPoint, + reference.referencePoint, + factory, + reference.data.schema || { type: "null" }, + context, + converterContext, + ), + }); + } + const isPathProperty = parameter.in === "path"; + return factory.PropertySignature.create({ + name: converterContext.escapePropertySignatureName(parameter.name), + optional: isPathProperty ? false : !parameter.required, + type: ToTypeNode.convert(entryPoint, currentPoint, factory, parameter.schema || { type: "null" }, context, converterContext), + comment: parameter.description, + }); +}; + +export const generatePropertySignatures = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parameters: (OpenApi.Parameter | OpenApi.Reference)[], + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.PropertySignature[] => { + return parameters.map(parameter => { + return generatePropertySignature(entryPoint, currentPoint, store, factory, parameter, context, converterContext); + }); +}; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + name: string, + parameters: [OpenApi.Parameter | OpenApi.Reference], + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.InterfaceDeclaration => { + return factory.InterfaceDeclaration.create({ + export: true, + name, + members: generatePropertySignatures(entryPoint, currentPoint, store, factory, parameters, context, converterContext), + }); +}; + +/** + * Alias作成用 + */ +export const generateAliasInterface = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + name: string, + parameters: (OpenApi.Parameter | OpenApi.Reference)[], + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.InterfaceDeclaration => { + return factory.InterfaceDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(name), + members: generatePropertySignatures(entryPoint, currentPoint, store, factory, parameters, context, converterContext), + }); +}; diff --git a/src/internal/OpenApiTools/components2/Parameters.ts b/src/internal/OpenApiTools/components2/Parameters.ts new file mode 100644 index 00000000..90444bc2 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Parameters.ts @@ -0,0 +1,113 @@ +import type { OpenApi } from "../../../types"; +import { UnSupportError } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Paramter from "./Parameter"; +import * as Reference from "./Reference"; +import * as Schema from "./Schema"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parameters: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = "components/parameters"; + store.addComponent("parameters", { + kind: "namespace", + name: Name.Components.Parameters, + }); + + Object.entries(parameters).forEach(([name, parameter]) => { + if (Guard.isReference(parameter)) { + const reference = Reference.generate(entryPoint, currentPoint, parameter); + if (reference.type === "local") { + throw new UnSupportError("What is components.parameters local reference?"); + } else { + if (!reference.data.schema) { + return; + } + Schema.addSchema( + entryPoint, + currentPoint, + store, + factory, + reference.path, + reference.name, + reference.data.schema, + context, + converterContext, + ); + store.addStatement(`${basePath}/${name}`, { + kind: "typeAlias", + name: converterContext.escapeDeclarationText(name), + value: factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(name), + type: factory.TypeReferenceNode.create({ + name: context.resolveReferencePath(currentPoint, reference.path).name, + }), + }), + }); + } + } else { + const path = `${basePath}/${name}`; + store.addStatement(path, { + kind: "typeAlias", + name: converterContext.escapeDeclarationText(name), + value: Paramter.generateTypeAlias(entryPoint, currentPoint, factory, name, parameter, context, converterContext), + }); + } + }); +}; + +export const generateNamespaceWithList = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parameters: (OpenApi.Parameter | OpenApi.Reference)[], + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + store.addComponent("parameters", { + kind: "namespace", + name: Name.Components.Parameters, + }); + + parameters.forEach(parameter => { + if (Guard.isReference(parameter)) { + const reference = Reference.generate(entryPoint, currentPoint, parameter); + if (reference.type === "local") { + throw new UnSupportError("What is components.parameters local reference?"); + } + const path = `components/parameters/${reference.name}`; + return store.addStatement(path, { + kind: "typeAlias", + name: reference.name, + value: Paramter.generateTypeAlias( + entryPoint, + reference.referencePoint, + factory, + reference.name, + reference.data, + context, + converterContext, + ), + }); + } + const path = `components/parameters/${parameter.name}`; + return store.addStatement(path, { + kind: "typeAlias", + name: parameter.name, + value: Paramter.generateTypeAlias(entryPoint, currentPoint, factory, parameter.name, parameter, context, converterContext), + }); + }); +}; diff --git a/src/internal/OpenApiTools/components2/PathItem.ts b/src/internal/OpenApiTools/components2/PathItem.ts new file mode 100644 index 00000000..7c7db96c --- /dev/null +++ b/src/internal/OpenApiTools/components2/PathItem.ts @@ -0,0 +1,126 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Operation from "./Operation"; +import * as Parameters from "./Parameters"; +import * as Servers from "./Servers"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentPath: string, + name: string, + pathItem: OpenApi.PathItem, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, + options?: { topComment?: string }, +): void => { + const basePath = `${parentPath}/${name}`; + const topComment = options && options.topComment && options.topComment; + store.addStatement(basePath, { + kind: "namespace", + name, + comment: Servers.addComment([topComment, pathItem.description], pathItem.servers), + }); + if (pathItem.get) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "GET", pathItem.get, context, converterContext); + } + if (pathItem.put) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "PUT", pathItem.put, context, converterContext); + } + if (pathItem.post) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "POST", pathItem.post, context, converterContext); + } + if (pathItem.delete) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "DELETE", pathItem.delete, context, converterContext); + } + if (pathItem.options) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "OPTIONS", pathItem.options, context, converterContext); + } + if (pathItem.head) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "HEAD", pathItem.head, context, converterContext); + } + if (pathItem.patch) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "PATCH", pathItem.patch, context, converterContext); + } + if (pathItem.trace) { + Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "TRACE", pathItem.trace, context, converterContext); + } + if (pathItem.parameters) { + Parameters.generateNamespaceWithList(entryPoint, currentPoint, store, factory, pathItem.parameters, context, converterContext); + } +}; + +export const generateStatements = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + requestUri: string, + pathItem: OpenApi.PathItem, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.Statement[] => { + const statements: ts.Statement[][] = []; + if (pathItem.get) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "GET", pathItem.get, context, converterContext), + ); + } + if (pathItem.put) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "PUT", pathItem.put, context, converterContext), + ); + } + if (pathItem.post) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "POST", pathItem.post, context, converterContext), + ); + } + if (pathItem.delete) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "DELETE", pathItem.delete, context, converterContext), + ); + } + if (pathItem.options) { + statements.push( + Operation.generateStatements( + entryPoint, + currentPoint, + store, + factory, + requestUri, + "OPTIONS", + pathItem.options, + context, + converterContext, + ), + ); + } + if (pathItem.head) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "HEAD", pathItem.head, context, converterContext), + ); + } + if (pathItem.patch) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "PATCH", pathItem.patch, context, converterContext), + ); + } + if (pathItem.trace) { + statements.push( + Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "TRACE", pathItem.trace, context, converterContext), + ); + } + // if (pathItem.parameters) { + // Parameters.generateNamespaceWithList(entryPoint, currentPoint, store, factory, pathItem.parameters, context); + // } + + return statements.flat(); +}; diff --git a/src/internal/OpenApiTools/components2/PathItems.ts b/src/internal/OpenApiTools/components2/PathItems.ts new file mode 100644 index 00000000..76785672 --- /dev/null +++ b/src/internal/OpenApiTools/components2/PathItems.ts @@ -0,0 +1,56 @@ +import type { OpenApi } from "../../../types"; +import { FeatureDevelopmentError, UnSupportError } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as PathItem from "./PathItem"; +import * as Reference from "./Reference"; + +// 使わない可能性あり +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + pathItems: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = "components/pathItems"; + + store.addComponent("pathItems", { + kind: "namespace", + name: Name.Components.PathItems, + }); + + Object.entries(pathItems).forEach(([key, pathItem]) => { + if (Guard.isReference(pathItem)) { + const reference = Reference.generate(entryPoint, currentPoint, pathItem); + if (reference.type === "local") { + throw new UnSupportError("can't use components.pathItems local reference"); + } else if (reference.componentName) { + if (key !== reference.name) { + throw new UnSupportError(`can't use difference pathItem key name. "${key}" !== "${reference.name}"`); + } + PathItem.generateNamespace( + entryPoint, + reference.referencePoint, + store, + factory, + basePath, + reference.name, + reference.data, + context, + converterContext, + ); + } else { + throw new FeatureDevelopmentError("存在しないReferenceを参照する場合は全部生成する"); + } + } else { + PathItem.generateNamespace(entryPoint, currentPoint, store, factory, basePath, key, pathItem, context, converterContext); + } + }); +}; diff --git a/src/internal/OpenApiTools/components2/Reference.ts b/src/internal/OpenApiTools/components2/Reference.ts new file mode 100644 index 00000000..ff685064 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Reference.ts @@ -0,0 +1,199 @@ +import * as path from "path"; + +import type { OpenApi } from "../../../types"; +import { DevelopmentError, FeatureDevelopmentError, NotFoundFileError } from "../../Exception"; +import { FileSystem } from "../../FileSystem"; +import * as Logger from "../../Logger"; +import * as Guard from "../Guard"; +import { Def } from "../Walker"; + +export type LocalReferencePattern = + | "#/components/schemas/" + | "#/components/responses/" + | "#/components/parameters/" + | "#/components/examples/" + | "#/components/requestBodies/" + | "#/components/headers/" + | "#/components/securitySchemes/" + | "#/components/links/" + | "#/components/callbacks/" + | "#/components/pathItems/"; + +export interface LocalReference { + type: "local"; + /** + * @example #/components/schemas/Hoge -> Hoge + */ + name: string; + /** + * startsWith `components` + * components/headers/hoge/fuga + */ + path: string; +} + +export interface RemoteReference { + type: "remote"; + /** + * file path + */ + referencePoint: string; + /** + * startsWith `components` + * components/headers/hoge/fuga + */ + path: string; + /** + * From filename - extension + * @example a/b/c/Hoge.yml -> Hoge + */ + name: string; + /** + * If "componentName" exists, you can create an alias for the type. + * If it does not exist, you need to define the type directly. + */ + componentName?: Def.ComponentName; + data: T; +} + +export type Type = LocalReference | RemoteReference; + +const localReferencePatterns: readonly LocalReferencePattern[] = [ + "#/components/schemas/", + "#/components/responses/", + "#/components/parameters/", + "#/components/examples/", + "#/components/requestBodies/", + "#/components/headers/", + "#/components/securitySchemes/", + "#/components/links/", + "#/components/callbacks/", + "#/components/pathItems/", +]; + +export const localReferenceComponents = { + "#/components/schemas/": "components/schemas", + "#/components/responses/": "components/responses", + "#/components/parameters/": "components/parameters", + "#/components/examples/": "components/examples", + "#/components/requestBodies/": "components/requestBodies", + "#/components/headers/": "components/headers", + "#/components/securitySchemes/": "components/securitySchemes", + "#/components/links/": "components/links", + "#/components/callbacks/": "components/callbacks", + "#/components/pathItems/": "components/pathItems", +} as const; + +export const getLocalReferencePattern = (reference: OpenApi.Reference) => { + let localReferencePattern: LocalReferencePattern | undefined; + localReferencePatterns.forEach(referencePattern => { + if (new RegExp("^" + referencePattern).test(reference.$ref)) { + localReferencePattern = referencePattern; + } + }); + return localReferencePattern; +}; + +export const generateLocalReference = (reference: OpenApi.Reference): LocalReference | undefined => { + const localReferencePattern = getLocalReferencePattern(reference); + if (!localReferencePattern) { + return; + } + const name = reference.$ref.split(localReferencePattern)[1]; + const localPath = path.posix.join(localReferenceComponents[localReferencePattern], name); + if (!localPath.startsWith("components")) { + throw new DevelopmentError(`localPath is not start "components":\n${localPath}`); + } + return { + type: "local", + name, + path: localPath, + }; +}; + +export const generateReferencePoint = (currentPoint: string, reference: OpenApi.Reference): string => { + const basedir = path.dirname(currentPoint); + const ref = reference.$ref; + const referencePoint = path.join(basedir, ref); + return referencePoint; +}; + +export const generate = (entryPoint: string, currentPoint: string, reference: OpenApi.Reference): Type => { + const localReference = generateLocalReference(reference); + if (localReference) { + return localReference; + } + + if (reference.$ref.startsWith("http")) { + throw new FeatureDevelopmentError("Please Pull Request ! Welcome !"); + } + + const referencePoint = generateReferencePoint(currentPoint, reference); + + if (!FileSystem.existSync(referencePoint)) { + Logger.showFilePosition(entryPoint, currentPoint, referencePoint); + Logger.error(JSON.stringify(reference, null, 2)); + throw new NotFoundFileError(`Not found reference point from current point. \n Path: ${referencePoint}`); + } + + const relativePathFromEntryPoint = path.relative(path.dirname(entryPoint), referencePoint); // components/hoge/fuga.yml + const ext = path.extname(relativePathFromEntryPoint); // .yml + const pathArray: string[] = relativePathFromEntryPoint.replace(ext, "").split(path.sep); // ["components", "hoge", "fuga"] + const targetPath: string = pathArray.join("/"); // components/hoge/fuga + const schemaName = pathArray[pathArray.length - 1]; // fuga + const componentName = pathArray[0] === "components" ? pathArray[1] : ""; + const data = FileSystem.loadJsonOrYaml(referencePoint); + + if (Guard.isReference(data)) { + return generate(entryPoint, referencePoint, data); + } + + if (!targetPath.startsWith("components")) { + throw new DevelopmentError(`targetPath is not start "components":\n${targetPath}`); + } + + return { + type: "remote", + referencePoint, + path: targetPath, + name: schemaName, + componentName: Guard.isComponentName(componentName) ? componentName : undefined, + data, + }; +}; + +export const resolveRemoteReference = ( + entryPoint: string, + currentPoint: string, + reference: OpenApi.Reference, +): { referencePoint: string; data: any } => { + if (reference.$ref.startsWith("#") || reference.$ref.startsWith("http")) { + return { referencePoint: currentPoint, data: reference }; + } + const referencePoint = generateReferencePoint(currentPoint, reference); + if (!FileSystem.existSync(referencePoint)) { + Logger.showFilePosition(entryPoint, currentPoint, referencePoint); + Logger.error(JSON.stringify(reference, null, 2)); + throw new NotFoundFileError(`Not found reference point from current point. \n Path: ${referencePoint}`); + } + const data = FileSystem.loadJsonOrYaml(referencePoint); + if (Guard.isReference(data)) { + return resolveRemoteReference(entryPoint, referencePoint, data); + } + return { + referencePoint, + data, + }; +}; + +export const resolveLocalReference = (entryPoint: string, currentPoint: string, reference: OpenApi.Reference): any => { + if (!reference.$ref.startsWith("#")) { + return reference; + } + const referencePoint = generateReferencePoint(currentPoint, reference); + const data = FileSystem.loadJsonOrYaml(referencePoint); + if (Guard.isReference(data)) { + return resolveRemoteReference(entryPoint, referencePoint, data); + } + return data; +}; diff --git a/src/internal/OpenApiTools/components2/RequestBodies.ts b/src/internal/OpenApiTools/components2/RequestBodies.ts new file mode 100644 index 00000000..600487a2 --- /dev/null +++ b/src/internal/OpenApiTools/components2/RequestBodies.ts @@ -0,0 +1,50 @@ +import * as path from "path"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Reference from "./Reference"; +import * as RequestBody from "./RequestBody"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + requestBodies: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = "components/requestBodies"; + store.addComponent("requestBodies", { + kind: "namespace", + name: Name.Components.RequestBodies, + }); + + Object.entries(requestBodies).forEach(([name, requestBody]) => { + if (Guard.isReference(requestBody)) { + const reference = Reference.generate(entryPoint, currentPoint, requestBody); + if (reference.type === "local") { + throw new Error("not support"); + } else if (reference.type === "remote") { + RequestBody.generateNamespace( + entryPoint, + reference.referencePoint, + store, + factory, + path.dirname(reference.path), + reference.name, + reference.data, + context, + converterContext, + ); + } + } else { + RequestBody.generateNamespace(entryPoint, currentPoint, store, factory, basePath, name, requestBody, context, converterContext); + } + }); +}; diff --git a/src/internal/OpenApiTools/components2/RequestBody.ts b/src/internal/OpenApiTools/components2/RequestBody.ts new file mode 100644 index 00000000..bb286b8c --- /dev/null +++ b/src/internal/OpenApiTools/components2/RequestBody.ts @@ -0,0 +1,56 @@ +import * as ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as MediaType from "./MediaType"; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + requestBody: OpenApi.RequestBody, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.InterfaceDeclaration => { + const contentSignatures = MediaType.generatePropertySignatures( + entryPoint, + currentPoint, + factory, + requestBody.content || {}, + context, + converterContext, + ); + return factory.InterfaceDeclaration.create({ + export: true, + name, + members: contentSignatures, + }); +}; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentName: string, + name: string, + requestBody: OpenApi.RequestBody, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = `${parentName}/${name}`; + store.addStatement(basePath, { + kind: "namespace", + name, + comment: requestBody.description, + }); + store.addStatement(`${basePath}/Content`, { + kind: "interface", + name: "Content", + value: generateInterface(entryPoint, currentPoint, factory, "Content", requestBody, context, converterContext), + }); +}; diff --git a/src/internal/OpenApiTools/components2/Response.ts b/src/internal/OpenApiTools/components2/Response.ts new file mode 100644 index 00000000..7a2c834e --- /dev/null +++ b/src/internal/OpenApiTools/components2/Response.ts @@ -0,0 +1,117 @@ +import * as path from "path"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Header from "./Header"; +import * as MediaType from "./MediaType"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentPath: string, + name: string, + response: OpenApi.Response, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = `${parentPath}/${name}`; + store.addStatement(basePath, { + kind: "namespace", + name, + comment: response.description, + }); + + if (response.headers) { + store.addStatement(`${basePath}/Header`, { + kind: "interface", + name: Name.ComponentChild.Header, + value: Header.generateInterface( + entryPoint, + currentPoint, + factory, + Name.ComponentChild.Header, + response.headers, + context, + converterContext, + ), + }); + } + + if (response.content) { + store.addStatement(`${basePath}/Content`, { + kind: "interface", + name: Name.ComponentChild.Content, + value: MediaType.generateInterface( + entryPoint, + currentPoint, + factory, + Name.ComponentChild.Content, + response.content, + context, + converterContext, + ), + }); + } +}; + +export const generateReferenceNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentPath: string, + nameWithStatusCode: string, + responseReference: { name: string; path: string }, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = `${parentPath}/${nameWithStatusCode}`; + const referenceNamespaceName = context.resolveReferencePath(currentPoint, responseReference.path).name; + store.addStatement(basePath, { + kind: "namespace", + name: nameWithStatusCode, + }); + const headerNamespace = store.getStatement(path.posix.join(responseReference.path, "Header"), "namespace"); + if (headerNamespace) { + store.addStatement(`${basePath}/Header`, { + kind: "namespace", + name: Name.ComponentChild.Header, + }); + Object.values(headerNamespace.getChildren()).forEach(statement => { + if (!statement) { + return; + } + if (statement.kind === "interface" || statement.kind === "typeAlias") { + const aliasName = [referenceNamespaceName, Name.ComponentChild.Header, statement.name].join("."); + store.addStatement(`${basePath}/Header/${statement.name}`, { + kind: "typeAlias", + name: aliasName, + value: factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(statement.name), + type: factory.TypeReferenceNode.create({ + name: aliasName, + }), + }), + }); + } + }); + } + store.addStatement(`${basePath}/Content`, { + kind: "typeAlias", + name: Name.ComponentChild.Content, + value: factory.TypeAliasDeclaration.create({ + export: true, + name: Name.ComponentChild.Content, + type: factory.TypeReferenceNode.create({ + name: referenceNamespaceName + "." + Name.ComponentChild.Content, // TODO Contextから作成? + }), + }), + }); +}; diff --git a/src/internal/OpenApiTools/components2/Responses.ts b/src/internal/OpenApiTools/components2/Responses.ts new file mode 100644 index 00000000..b27a179f --- /dev/null +++ b/src/internal/OpenApiTools/components2/Responses.ts @@ -0,0 +1,200 @@ +import * as path from "path"; + +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { UndefinedComponent } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as MediaType from "./MediaType"; +import * as Reference from "./Reference"; +import * as Response from "./Response"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + responses: Record, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = "components/responses"; + store.addComponent("responses", { + kind: "namespace", + name: Name.Components.Responses, + }); + Object.entries(responses).forEach(([name, response]) => { + if (Guard.isReference(response)) { + const reference = Reference.generate(entryPoint, currentPoint, response); + if (reference.type === "local") { + if (!store.hasStatement(reference.path, ["interface"])) { + throw new UndefinedComponent(`Reference "${response.$ref}" did not found in ${reference.path} by ${reference.name}`); + } + } else if (reference.type === "remote") { + Response.generateNamespace( + entryPoint, + reference.referencePoint, + store, + factory, + path.posix.dirname(reference.path), // referencePoint basename === namespace name + reference.name, + reference.data, + context, + converterContext, + ); + } + } else { + Response.generateNamespace(entryPoint, currentPoint, store, factory, basePath, name, response, context, converterContext); + } + }); +}; + +export const generateNamespaceWithStatusCode = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + parentPath: string, + responses: OpenApi.Responses, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): void => { + const basePath = `${parentPath}/responses`; + store.addStatement(basePath, { + kind: "namespace", + name: Name.ComponentChild.Response, + }); + + Object.entries(responses).forEach(([statusCode, response]) => { + const nameWithStatusCode = `Status$${statusCode}`; + if (Guard.isReference(response)) { + const reference = Reference.generate(entryPoint, currentPoint, response); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + Response.generateReferenceNamespace( + entryPoint, + currentPoint, + store, + factory, + basePath, + nameWithStatusCode, + reference, + context, + converterContext, + ); + } else if (reference.componentName) { + // reference先に定義を作成 + Response.generateNamespace( + entryPoint, + reference.referencePoint, + store, + factory, + path.posix.dirname(reference.path), // referencePoint basename === namespace name + reference.name, + reference.data, + context, + converterContext, + ); + // referenceのTypeAliasの作成 + Response.generateReferenceNamespace( + entryPoint, + currentPoint, + store, + factory, + basePath, + nameWithStatusCode, + reference, + context, + converterContext, + ); + } + } else { + Response.generateNamespace(entryPoint, currentPoint, store, factory, basePath, nameWithStatusCode, response, context, converterContext); + } + }); +}; + +export const generateInterfacesWithStatusCode = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + operationId: string, + responses: OpenApi.Responses, + context: ToTypeNode.Context, + converterContext: ConverterContext.Types, +): ts.Statement[] => { + const statements: ts.Statement[] = []; + Object.entries(responses).forEach(([statusCode, response]) => { + if (!response) { + return; + } + if (Guard.isReference(response)) { + const reference = Reference.generate(entryPoint, currentPoint, response); + if (reference.type === "local") { + context.setReferenceHandler(currentPoint, reference); + const { maybeResolvedName, unresolvedPaths } = context.resolveReferencePath(currentPoint, `${reference.path}/Content`); + if (unresolvedPaths.length === 0) { + statements.push( + factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.generateResponseName(operationId, statusCode), + type: factory.TypeReferenceNode.create({ + name: maybeResolvedName, + }), + }), + ); + } + } else if (reference.componentName) { + // reference先に定義を作成 + Response.generateNamespace( + entryPoint, + reference.referencePoint, + store, + factory, + path.posix.dirname(reference.path), // referencePoint basename === namespace name + reference.name, + reference.data, + context, + converterContext, + ); + // referenceのTypeAliasの作成 + const content = reference.data.content; + if (content) { + statements.push( + MediaType.generateInterface( + entryPoint, + reference.referencePoint, + factory, + converterContext.generateResponseName(operationId, statusCode), + content, + context, + converterContext, + ), + ); + } + } + } else { + if (response.content) { + statements.push( + MediaType.generateInterface( + entryPoint, + currentPoint, + factory, + converterContext.generateResponseName(operationId, statusCode), + response.content, + context, + converterContext, + ), + ); + } + } + }); + + return statements; +}; diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts new file mode 100644 index 00000000..d3f97874 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -0,0 +1,211 @@ +import * as Intermediate from "../../IntermediateSchema"; +import type { OpenApi } from "../../../types"; +import { FeatureDevelopmentError } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConvertContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as ToTypeNode from "../toIntermediateSchemaType"; +import type { ArraySchema, ObjectSchema, PrimitiveSchema } from "../types"; +import type * as Walker from "../Walker"; +import * as ExternalDocumentation from "./ExternalDocumentation"; + +export const generatePropertySignatures = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + schema: ObjectSchema, + context: ToTypeNode.Context, + convertContext: ConvertContext.Types, +): Intermediate.PropertySignatureParams[] => { + if (!schema.properties) { + return []; + } + const required: string[] = schema.required || []; + return Object.entries(schema.properties).map(([propertyName, property]) => { + if (!property) { + return { + kind: "PropertySignature", + name: convertContext.escapePropertySignatureName(propertyName), + optional: !required.includes(propertyName), + comment: schema.description, + schemaType: { + kind: "any", + }, + }; + } + return { + kind: "PropertySignature", + name: convertContext.escapePropertySignatureName(propertyName), + optional: !required.includes(propertyName), + comment: typeof property !== "boolean" ? property.description : undefined, + schemaType: ToTypeNode.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), + } + }); +}; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + schema: ObjectSchema, + context: ToTypeNode.Context, + convertContext: ConvertContext.Types, +): ts.InterfaceDeclaration => { + if (schema.type !== "object") { + throw new FeatureDevelopmentError("Please use generateTypeAlias"); + } + let members: ts.TypeElement[] = []; + const propertySignatures = generatePropertySignatures(entryPoint, currentPoint, factory, schema, context, convertContext); + if (Guard.isObjectSchemaWithAdditionalProperties(schema)) { + const additionalProperties = ToTypeNode.convertAdditionalProperties(entryPoint, currentPoint, factory, schema, context, convertContext); + if (schema.additionalProperties === true) { + members = members.concat(additionalProperties); + } else { + members = [...propertySignatures, additionalProperties]; + } + } else { + members = propertySignatures; + } + return factory.InterfaceDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + members, + comment: ExternalDocumentation.addComment(schema.description, schema.externalDocs), + }); +}; + +export const generateArrayTypeAlias = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + schema: ArraySchema, + context: ToTypeNode.Context, + convertContext: ConvertContext.Types, +): ts.TypeAliasDeclaration => { + return factory.TypeAliasDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + comment: schema.description, + type: ToTypeNode.convert(entryPoint, currentPoint, factory, schema, context, convertContext), + }); +}; + +export const generateTypeAlias = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + schema: PrimitiveSchema, + convertContext: ConvertContext.Types, +): ts.TypeAliasDeclaration => { + let type: ts.TypeNode; + if (schema.enum) { + if (Guard.isNumberArray(schema.enum) && (schema.type === "number" || schema.type === "integer")) { + type = factory.TypeNode.create({ + type: schema.type, + enum: schema.enum, + }); + } else if (Guard.isStringArray(schema.enum) && schema.type === "string") { + type = factory.TypeNode.create({ + type: schema.type, + enum: schema.enum, + }); + } else { + type = factory.TypeNode.create({ + type: schema.type, + }); + } + } else { + type = factory.TypeNode.create({ + type: schema.type, + }); + } + return factory.TypeAliasDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + type, + comment: schema.description, + }); +}; + +export const generateMultiTypeAlias = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + schemas: OpenApi.Schema[], + context: ToTypeNode.Context, + multiType: "oneOf" | "allOf" | "anyOf", + convertContext: ConvertContext.Types, +): ts.TypeAliasDeclaration => { + const type = ToTypeNode.generateMultiTypeNode( + entryPoint, + currentPoint, + factory, + schemas, + context, + ToTypeNode.convert, + convertContext, + multiType, + ); + return factory.TypeAliasDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + type, + }); +}; + +export const addSchema = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + targetPoint: string, + declarationName: string, + schema: OpenApi.Schema | undefined, + context: ToTypeNode.Context, + convertContext: ConvertContext.Types, +): void => { + if (!schema) { + return; + } + if (Guard.isAllOfSchema(schema)) { + store.addStatement(targetPoint, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(declarationName), + value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), + }); + } else if (Guard.isOneOfSchema(schema)) { + store.addStatement(targetPoint, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(declarationName), + value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), + }); + } else if (Guard.isAnyOfSchema(schema)) { + store.addStatement(targetPoint, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(declarationName), + value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), + }); + } else if (Guard.isArraySchema(schema)) { + store.addStatement(targetPoint, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(declarationName), + value: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + }); + } else if (Guard.isObjectSchema(schema)) { + store.addStatement(targetPoint, { + kind: "interface", + name: convertContext.escapeDeclarationText(declarationName), + value: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + }); + } else if (Guard.isPrimitiveSchema(schema)) { + store.addStatement(targetPoint, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(declarationName), + value: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), + }); + } +}; diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts new file mode 100644 index 00000000..861056f0 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -0,0 +1,149 @@ +import type { OpenApi } from "../../../types"; +import { UnSupportError } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as ConverterContext from "../ConverterContext"; +import * as Guard from "../Guard"; +import * as InferredType from "../InferredType"; +import * as Name from "../Name"; +import * as ToTypeNode from "../toTypeNode"; +import type * as Walker from "../Walker"; +import * as Reference from "./Reference"; +import * as Schema from "./Schema"; + +const createNullableTypeNode = (factory: Factory.Type, schema: OpenApi.Schema) => { + if (!schema.type && typeof schema.nullable === "boolean") { + const typeNode = factory.TypeNode.create({ + type: "any", + }); + return factory.UnionTypeNode.create({ + typeNodes: [ + typeNode, + factory.TypeNode.create({ + type: "null", + }), + ], + }); + } +}; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + schemas: Record, + context: ToTypeNode.Context, + convertContext: ConverterContext.Types, +): void => { + const basePath = "components/schemas"; + store.addComponent("schemas", { + kind: "namespace", + name: Name.Components.Schemas, + }); + Object.entries(schemas).forEach(([name, targetSchema]) => { + if (Guard.isReference(targetSchema)) { + const schema = targetSchema; + const reference = Reference.generate(entryPoint, currentPoint, schema); + if (reference.type === "local") { + const { maybeResolvedName } = context.resolveReferencePath(currentPoint, reference.path); + store.addStatement(`${basePath}/${name}`, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: factory.TypeAliasDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + type: factory.TypeReferenceNode.create({ + name: convertContext.escapeDeclarationText(maybeResolvedName), + }), + }), + }); + return; + } + Schema.addSchema( + entryPoint, + reference.referencePoint, + store, + factory, + reference.path, + reference.name, + reference.data, + context, + convertContext, + ); + if (store.hasStatement(`${basePath}/${name}`, ["interface", "typeAlias"])) { + return; + } + return store.addStatement(`${basePath}/${name}`, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: factory.TypeAliasDeclaration.create({ + export: true, + name: convertContext.escapeDeclarationText(name), + comment: reference.data.description, + type: factory.TypeReferenceNode.create({ + name: convertContext.escapeDeclarationText(context.resolveReferencePath(currentPoint, reference.path).name), + }), + }), + }); + } + const schema = InferredType.getInferredType(targetSchema); + if (!schema) { + const typeNode = createNullableTypeNode(factory, targetSchema); + if (!typeNode) { + throw new UnSupportError("schema.type not specified \n" + JSON.stringify(targetSchema)); + } + return typeNode; + } + const path = `${basePath}/${name}`; + if (Guard.isAllOfSchema(schema)) { + return store.addStatement(path, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), + }); + } + if (Guard.isOneOfSchema(schema)) { + return store.addStatement(path, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), + }); + } + if (Guard.isAnyOfSchema(schema)) { + return store.addStatement(path, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), + }); + } + if (Guard.isArraySchema(schema)) { + return store.addStatement(path, { + kind: "typeAlias", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), + }); + } + if (Guard.isObjectSchema(schema)) { + return store.addStatement(path, { + kind: "interface", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + }); + } + if (Guard.isObjectSchema(schema)) { + return store.addStatement(path, { + kind: "interface", + name: convertContext.escapeDeclarationText(name), + value: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + }); + } + if (Guard.isPrimitiveSchema(schema)) { + return store.addStatement(path, { + kind: "typeAlias", + name, + value: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), + }); + } + throw new UnSupportError("schema.type = Array[] not supported. " + JSON.stringify(schema)); + }); +}; diff --git a/src/internal/OpenApiTools/components2/SecuritySchema.ts b/src/internal/OpenApiTools/components2/SecuritySchema.ts new file mode 100644 index 00000000..9885b072 --- /dev/null +++ b/src/internal/OpenApiTools/components2/SecuritySchema.ts @@ -0,0 +1,51 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; +import { Factory } from "../../TsGenerator"; + +export const generatePropertySignatures = ( + _entryPoint: string, + _currentPoint: string, + factory: Factory.Type, + securitySchema: OpenApi.SecuritySchema, +): ts.PropertySignature[] => { + const signatures: ts.PropertySignature[] = [ + factory.PropertySignature.create({ + name: "type", + optional: false, + type: factory.LiteralTypeNode.create({ value: securitySchema.type }), + }), + factory.PropertySignature.create({ + name: "name", + optional: false, + type: factory.LiteralTypeNode.create({ value: securitySchema.name }), + }), + factory.PropertySignature.create({ + name: "in", + optional: false, + type: factory.LiteralTypeNode.create({ value: securitySchema.in }), + }), + factory.PropertySignature.create({ + name: "openIdConnectUrl", + optional: false, + type: factory.LiteralTypeNode.create({ value: securitySchema.openIdConnectUrl }), + }), + ]; + + return signatures; +}; + +export const generateInterface = ( + entryPoint: string, + currentPoint: string, + factory: Factory.Type, + name: string, + securitySchema: OpenApi.SecuritySchema, +): ts.InterfaceDeclaration => { + return factory.InterfaceDeclaration.create({ + export: true, + name, + comment: securitySchema.description, + members: generatePropertySignatures(entryPoint, currentPoint, factory, securitySchema), + }); +}; diff --git a/src/internal/OpenApiTools/components2/SecuritySchemas.ts b/src/internal/OpenApiTools/components2/SecuritySchemas.ts new file mode 100644 index 00000000..5acc9072 --- /dev/null +++ b/src/internal/OpenApiTools/components2/SecuritySchemas.ts @@ -0,0 +1,44 @@ +import type { OpenApi } from "../../../types"; +import { UndefinedComponent } from "../../Exception"; +import { Factory } from "../../TsGenerator"; +import * as Guard from "../Guard"; +import * as Name from "../Name"; +import type * as Walker from "../Walker"; +import * as Reference from "./Reference"; +import * as SecuritySchema from "./SecuritySchema"; + +export const generateNamespace = ( + entryPoint: string, + currentPoint: string, + store: Walker.Store, + factory: Factory.Type, + requestBodies: Record, +): void => { + store.addComponent("securitySchemes", { + kind: "namespace", + name: Name.Components.SecuritySchemas, + }); + Object.entries(requestBodies).forEach(([name, requestBody]) => { + if (Guard.isReference(requestBody)) { + const reference = Reference.generate(entryPoint, currentPoint, requestBody); + if (reference.type === "local") { + if (!store.hasStatement(reference.path, ["interface"])) { + throw new UndefinedComponent(`Reference "${requestBody.$ref}" did not found in ${reference.path} by ${reference.name}`); + } + return; + } + const path = `components/securitySchemes/${reference.name}`; + return store.addStatement(path, { + kind: "interface", + name, + value: SecuritySchema.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data), + }); + } + const path = `components/securitySchemes/${name}`; + return store.addStatement(path, { + kind: "interface", + name: name, + value: SecuritySchema.generateInterface(entryPoint, currentPoint, factory, name, requestBody), + }); + }); +}; diff --git a/src/internal/OpenApiTools/components2/Server.ts b/src/internal/OpenApiTools/components2/Server.ts new file mode 100644 index 00000000..93a2f43a --- /dev/null +++ b/src/internal/OpenApiTools/components2/Server.ts @@ -0,0 +1,10 @@ +import { EOL } from "os"; + +import type { OpenApi } from "../../../types"; + +export const addComment = (comment?: string, server?: OpenApi.Server): string | undefined => { + if (!server) { + return comment; + } + return [comment, server.url, server.description].filter(Boolean).join(EOL); +}; diff --git a/src/internal/OpenApiTools/components2/Servers.ts b/src/internal/OpenApiTools/components2/Servers.ts new file mode 100644 index 00000000..2deabf21 --- /dev/null +++ b/src/internal/OpenApiTools/components2/Servers.ts @@ -0,0 +1,11 @@ +import { EOL } from "os"; + +import type { OpenApi } from "../../../types"; +import * as Server from "./Server"; + +export const addComment = (comments: (string | undefined)[], servers: OpenApi.Server[] = []): string | undefined => { + const comment = comments.filter(Boolean).join(EOL) as string | undefined; + return servers.reduce((newComment, server) => { + return Server.addComment(newComment, server); + }, comment); +}; diff --git a/src/internal/OpenApiTools/toIntermediateSchemaType.ts b/src/internal/OpenApiTools/toIntermediateSchemaType.ts index 2b74e1dd..6817587a 100644 --- a/src/internal/OpenApiTools/toIntermediateSchemaType.ts +++ b/src/internal/OpenApiTools/toIntermediateSchemaType.ts @@ -1,7 +1,7 @@ import type { OpenApi } from "../../types"; import { UnsetTypeError } from "../Exception"; import { UnSupportError } from "../Exception"; -import type * as Intermediate from "../IntermediateSchema/Schemas"; +import type * as Intermediate from "../IntermediateSchema"; import * as Logger from "../Logger"; import { Factory } from "../TsGenerator"; import * as Reference from "./components/Reference"; From c8a7bb16ad957bd878bcb389d7ab2121aae4c880 Mon Sep 17 00:00:00 2001 From: Himenon Date: Thu, 29 Apr 2021 22:59:22 +0900 Subject: [PATCH 05/39] chore: rename --- src/internal/AbstractDataStructure/index.ts | 132 ++++++++++++++++++ src/internal/IntermediateSchema/index.ts | 115 --------------- src/internal/OpenApiTools/Walker/Store.ts | 4 + .../OpenApiTools/components2/Schema.ts | 98 ++++++------- ...hemaType.ts => toAbstractDataStructure.ts} | 26 ++-- 5 files changed, 198 insertions(+), 177 deletions(-) create mode 100644 src/internal/AbstractDataStructure/index.ts delete mode 100644 src/internal/IntermediateSchema/index.ts rename src/internal/OpenApiTools/{toIntermediateSchemaType.ts => toAbstractDataStructure.ts} (93%) diff --git a/src/internal/AbstractDataStructure/index.ts b/src/internal/AbstractDataStructure/index.ts new file mode 100644 index 00000000..7c5ec80f --- /dev/null +++ b/src/internal/AbstractDataStructure/index.ts @@ -0,0 +1,132 @@ +export type Kind = + | "string" + | "integer" + | "number" + | "boolean" + | "undefined" + | "null" + | "never" + | "any" + | "void" + | "union" + | "intersection" + | "reference" + | "array" + | "PropertySignature" + | "IndexSignature" + | "object" + | "interface" + | "alias"; + +export interface BaseStruct { + kind: Kind; +} + +export interface StringStruct extends BaseStruct { + kind: "string"; + enum?: string[]; +} + +export interface IntegerStruct extends BaseStruct { + kind: "integer"; + enum?: number[]; +} + +export interface NumberStruct extends BaseStruct { + kind: "number"; + enum?: number[]; +} + +export interface BooleanStruct extends BaseStruct { + kind: "boolean"; +} + +export interface UndefinedStruct extends BaseStruct { + kind: "undefined"; +} + +export interface NullStruct extends BaseStruct { + kind: "null"; +} + +export interface NeverStruct extends BaseStruct { + kind: "never"; +} + +export interface AnyStruct extends BaseStruct { + kind: "any"; +} + +export interface VoidStruct extends BaseStruct { + kind: "void"; +} + +export interface UnionStruct extends BaseStruct { + kind: "union"; + schemaTypes: BaseStruct[]; +} + +export interface IntersectionStruct extends BaseStruct { + kind: "intersection"; + schemaTypes: BaseStruct[]; +} + +export interface IndexSignatureStruct extends BaseStruct { + kind: "IndexSignature"; + name: string; + schemaType: BaseStruct; +} + +export interface ReferenceStruct extends BaseStruct { + kind: "reference"; + name: string; +} + +export interface ArrayStruct extends BaseStruct { + kind: "array"; + schemaType: BaseStruct; +} + +export interface AliasStruct extends BaseStruct { + kind: "alias"; + name: string; + comment?: string; + schema: Type; +} + +export interface PropertySignatureStruct extends BaseStruct { + kind: "PropertySignature"; + name: string; + optional: boolean; + comment?: string; + schemaType: Type; +} + +export interface InterfaceDeclarationStruct extends BaseStruct { + kind: "interface"; + name: string; + members: (IndexSignatureStruct | PropertySignatureStruct)[]; + comment?: string; +} + +export interface ObjectStruct extends BaseStruct { + kind: "object"; + properties: (PropertySignatureStruct | IndexSignatureStruct)[]; +} + +export type Type = + | StringStruct + | IntegerStruct + | NumberStruct + | BooleanStruct + | UndefinedStruct + | NullStruct + | NeverStruct + | AnyStruct + | VoidStruct + | UnionStruct + | IntersectionStruct + | ReferenceStruct + | ArrayStruct + | ObjectStruct + | InterfaceDeclarationStruct; diff --git a/src/internal/IntermediateSchema/index.ts b/src/internal/IntermediateSchema/index.ts deleted file mode 100644 index 186e0cd9..00000000 --- a/src/internal/IntermediateSchema/index.ts +++ /dev/null @@ -1,115 +0,0 @@ -export type Kind = - | "string" - | "integer" - | "number" - | "boolean" - | "undefined" - | "null" - | "never" - | "any" - | "void" - | "union" - | "intersection" - | "reference" - | "array" - | "PropertySignature" - | "IndexSignature" - | "object"; - -export interface BaseSchemaTypes { - kind: Kind; -} - -export interface StringSchema extends BaseSchemaTypes { - kind: "string"; - enum?: string[]; -} - -export interface IntegerSchema extends BaseSchemaTypes { - kind: "integer"; - enum?: number[]; -} - -export interface NumberSchema extends BaseSchemaTypes { - kind: "number"; - enum?: number[]; -} - -export interface BooleanSchema extends BaseSchemaTypes { - kind: "boolean"; -} - -export interface UndefinedSchema extends BaseSchemaTypes { - kind: "undefined"; -} - -export interface NullSchema extends BaseSchemaTypes { - kind: "null"; -} - -export interface NeverSchema extends BaseSchemaTypes { - kind: "never"; -} - -export interface AnySchema extends BaseSchemaTypes { - kind: "any"; -} - -export interface VoidSchema extends BaseSchemaTypes { - kind: "void"; -} - -export interface UnionSchema extends BaseSchemaTypes { - kind: "union"; - schemaTypes: BaseSchemaTypes[]; -} - -export interface IntersectionSchema extends BaseSchemaTypes { - kind: "intersection"; - schemaTypes: BaseSchemaTypes[]; -} - -export interface IndexSignatureSchema extends BaseSchemaTypes { - kind: "IndexSignature"; - name: string; - schemaType: BaseSchemaTypes; -} - -export interface ReferenceSchema extends BaseSchemaTypes { - kind: "reference"; - name: string; -} - -export interface ArrayParams extends BaseSchemaTypes { - kind: "array"; - schemaType: BaseSchemaTypes; -} - -export interface PropertySignatureParams extends BaseSchemaTypes { - kind: "PropertySignature"; - name: string; - optional: boolean; - comment?: string; - schemaType: SchemaType; -} - -export interface ObjectParams extends BaseSchemaTypes { - kind: "object"; - properties: (PropertySignatureParams | IndexSignatureSchema)[]; -} - -export type SchemaType = - | StringSchema - | IntegerSchema - | NumberSchema - | BooleanSchema - | UndefinedSchema - | NullSchema - | NeverSchema - | AnySchema - | VoidSchema - | UnionSchema - | IntersectionSchema - | ReferenceSchema - | ArrayParams - | ObjectParams; diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index 4c141706..e255383a 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -11,6 +11,7 @@ import * as Def from "./Definition"; import * as Operation from "./Operation"; import * as State from "./State"; import * as Structure from "./structure"; +import * as ADS from "../../AbstractDataStructure" class Store { private state: State.Type; @@ -69,6 +70,9 @@ class Store { public hasStatement(path: string, types: Structure.DataStructure.Kind[]): boolean { const alreadyRegistered = types.some(type => !!this.operator.getChildByPaths(path, type)); return alreadyRegistered; + } + public addAbstractDataStruct(abstractDataStruct: ADS.Type): void { + } /** * @params path: "components/headers/hoge" diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index d3f97874..06e13f3b 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -1,10 +1,10 @@ -import * as Intermediate from "../../IntermediateSchema"; import type { OpenApi } from "../../../types"; import { FeatureDevelopmentError } from "../../Exception"; +import * as ADS from "../../AbstractDataStructure"; import { Factory } from "../../TsGenerator"; import * as ConvertContext from "../ConverterContext"; import * as Guard from "../Guard"; -import * as ToTypeNode from "../toIntermediateSchemaType"; +import * as ToAbstractDataStructure from "../toAbstractDataStructure"; import type { ArraySchema, ObjectSchema, PrimitiveSchema } from "../types"; import type * as Walker from "../Walker"; import * as ExternalDocumentation from "./ExternalDocumentation"; @@ -14,14 +14,14 @@ export const generatePropertySignatures = ( currentPoint: string, factory: Factory.Type, schema: ObjectSchema, - context: ToTypeNode.Context, + context: ToAbstractDataStructure.Context, convertContext: ConvertContext.Types, -): Intermediate.PropertySignatureParams[] => { +): ADS.PropertySignatureStruct[] => { if (!schema.properties) { return []; } const required: string[] = schema.required || []; - return Object.entries(schema.properties).map(([propertyName, property]) => { + return Object.entries(schema.properties).map(([propertyName, property]) => { if (!property) { return { kind: "PropertySignature", @@ -38,8 +38,8 @@ export const generatePropertySignatures = ( name: convertContext.escapePropertySignatureName(propertyName), optional: !required.includes(propertyName), comment: typeof property !== "boolean" ? property.description : undefined, - schemaType: ToTypeNode.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), - } + schemaType: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), + }; }); }; @@ -49,16 +49,16 @@ export const generateInterface = ( factory: Factory.Type, name: string, schema: ObjectSchema, - context: ToTypeNode.Context, + context: ToAbstractDataStructure.Context, convertContext: ConvertContext.Types, -): ts.InterfaceDeclaration => { +): ADS.InterfaceDeclarationStruct => { if (schema.type !== "object") { throw new FeatureDevelopmentError("Please use generateTypeAlias"); } - let members: ts.TypeElement[] = []; + let members: (ADS.IndexSignatureStruct | ADS.PropertySignatureStruct)[] = []; const propertySignatures = generatePropertySignatures(entryPoint, currentPoint, factory, schema, context, convertContext); if (Guard.isObjectSchemaWithAdditionalProperties(schema)) { - const additionalProperties = ToTypeNode.convertAdditionalProperties(entryPoint, currentPoint, factory, schema, context, convertContext); + const additionalProperties = ToAbstractDataStructure.convertAdditionalProperties(entryPoint, currentPoint, factory, schema, context, convertContext); if (schema.additionalProperties === true) { members = members.concat(additionalProperties); } else { @@ -67,12 +67,12 @@ export const generateInterface = ( } else { members = propertySignatures; } - return factory.InterfaceDeclaration.create({ - export: true, + return { + kind: "interface", name: convertContext.escapeDeclarationText(name), - members, comment: ExternalDocumentation.addComment(schema.description, schema.externalDocs), - }); + members, + }; }; export const generateArrayTypeAlias = ( @@ -81,15 +81,15 @@ export const generateArrayTypeAlias = ( factory: Factory.Type, name: string, schema: ArraySchema, - context: ToTypeNode.Context, + context: ToAbstractDataStructure.Context, convertContext: ConvertContext.Types, -): ts.TypeAliasDeclaration => { - return factory.TypeAliasDeclaration.create({ - export: true, +): ADS.AliasStruct => { + return { + kind: "alias", name: convertContext.escapeDeclarationText(name), comment: schema.description, - type: ToTypeNode.convert(entryPoint, currentPoint, factory, schema, context, convertContext), - }); + schema: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, schema, context, convertContext), + }; }; export const generateTypeAlias = ( @@ -99,35 +99,35 @@ export const generateTypeAlias = ( name: string, schema: PrimitiveSchema, convertContext: ConvertContext.Types, -): ts.TypeAliasDeclaration => { - let type: ts.TypeNode; +): ADS.AliasStruct => { + let type: ADS.Type; if (schema.enum) { if (Guard.isNumberArray(schema.enum) && (schema.type === "number" || schema.type === "integer")) { - type = factory.TypeNode.create({ - type: schema.type, + type = { + kind: "number", enum: schema.enum, - }); + }; } else if (Guard.isStringArray(schema.enum) && schema.type === "string") { - type = factory.TypeNode.create({ - type: schema.type, + type = { + kind: "string", enum: schema.enum, - }); + }; } else { - type = factory.TypeNode.create({ - type: schema.type, - }); + type = { + kind: schema.type, + }; } } else { - type = factory.TypeNode.create({ - type: schema.type, - }); + type = { + kind: schema.type, + }; } - return factory.TypeAliasDeclaration.create({ - export: true, + return { + kind: "alias", name: convertContext.escapeDeclarationText(name), - type, comment: schema.description, - }); + schema: type, + } }; export const generateMultiTypeAlias = ( @@ -136,17 +136,17 @@ export const generateMultiTypeAlias = ( factory: Factory.Type, name: string, schemas: OpenApi.Schema[], - context: ToTypeNode.Context, + context: ToAbstractDataStructure.Context, multiType: "oneOf" | "allOf" | "anyOf", convertContext: ConvertContext.Types, ): ts.TypeAliasDeclaration => { - const type = ToTypeNode.generateMultiTypeNode( + const type = ToAbstractDataStructure.generateMultiTypeNode( entryPoint, currentPoint, factory, schemas, context, - ToTypeNode.convert, + ToAbstractDataStructure.convert, convertContext, multiType, ); @@ -165,44 +165,44 @@ export const addSchema = ( targetPoint: string, declarationName: string, schema: OpenApi.Schema | undefined, - context: ToTypeNode.Context, + context: ToAbstractDataStructure.Context, convertContext: ConvertContext.Types, ): void => { if (!schema) { return; } if (Guard.isAllOfSchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "typeAlias", name: convertContext.escapeDeclarationText(declarationName), value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), }); } else if (Guard.isOneOfSchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "typeAlias", name: convertContext.escapeDeclarationText(declarationName), value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), }); } else if (Guard.isAnyOfSchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "typeAlias", name: convertContext.escapeDeclarationText(declarationName), value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), }); } else if (Guard.isArraySchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "typeAlias", name: convertContext.escapeDeclarationText(declarationName), value: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isObjectSchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "interface", name: convertContext.escapeDeclarationText(declarationName), value: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isPrimitiveSchema(schema)) { - store.addStatement(targetPoint, { + store.addAbstractDataStruct(targetPoint, { kind: "typeAlias", name: convertContext.escapeDeclarationText(declarationName), value: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), diff --git a/src/internal/OpenApiTools/toIntermediateSchemaType.ts b/src/internal/OpenApiTools/toAbstractDataStructure.ts similarity index 93% rename from src/internal/OpenApiTools/toIntermediateSchemaType.ts rename to src/internal/OpenApiTools/toAbstractDataStructure.ts index 6817587a..0ec54f69 100644 --- a/src/internal/OpenApiTools/toIntermediateSchemaType.ts +++ b/src/internal/OpenApiTools/toAbstractDataStructure.ts @@ -1,7 +1,7 @@ import type { OpenApi } from "../../types"; import { UnsetTypeError } from "../Exception"; import { UnSupportError } from "../Exception"; -import type * as Intermediate from "../IntermediateSchema"; +import type * as Intermediate from "../AbstractDataStructure"; import * as Logger from "../Logger"; import { Factory } from "../TsGenerator"; import * as Reference from "./components/Reference"; @@ -29,7 +29,7 @@ export type Convert = ( setReference: Context, convertContext: ConverterContext.Types, option?: Option, -) => Intermediate.SchemaType; +) => Intermediate.Type; export interface Option { parent?: any; @@ -44,7 +44,7 @@ export const generateMultiTypeNode = ( convert: Convert, convertContext: ConverterContext.Types, multiType: "oneOf" | "allOf" | "anyOf", -): Intermediate.SchemaType => { +): Intermediate.Type => { const value = schemas.map(schema => convert(entryPoint, currentPoint, factory, schema, setReference, convertContext)); if (multiType === "oneOf") { return { @@ -64,7 +64,7 @@ export const generateMultiTypeNode = ( }; }; -const nullable = (factory: Factory.Type, schemaType: Intermediate.SchemaType, nullable: boolean): Intermediate.SchemaType => { +const nullable = (factory: Factory.Type, schemaType: Intermediate.Type, nullable: boolean): Intermediate.Type => { if (nullable) { return { kind: "union", @@ -87,7 +87,7 @@ export const convert: Convert = ( context: Context, converterContext: ConverterContext.Types, option?: Option, -): Intermediate.SchemaType => { +): Intermediate.Type => { if (typeof schema === "boolean") { // https://swagger.io/docs/specification/data-models/dictionaries/#free-form return { @@ -166,7 +166,7 @@ export const convert: Convert = ( case "integer": case "number": { const items = schema.enum; - let typeNode: Intermediate.SchemaType; + let typeNode: Intermediate.Type; if (items && Guard.isNumberArray(items)) { typeNode = { kind: "number", @@ -181,7 +181,7 @@ export const convert: Convert = ( } case "string": { const items = schema.enum; - let typeNode: Intermediate.SchemaType; + let typeNode: Intermediate.Type; if (items && Guard.isStringArray(items)) { typeNode = { kind: "string", @@ -198,7 +198,7 @@ export const convert: Convert = ( if (Array.isArray(schema.items) || typeof schema.items === "boolean") { throw new UnSupportError(`schema.items = ${JSON.stringify(schema.items)}`); } - const typeNode: Intermediate.SchemaType = { + const typeNode: Intermediate.Type = { kind: "array", schemaType: schema.items ? convert(entryPoint, currentPoint, factory, schema.items, context, converterContext, { parent: schema }) @@ -217,7 +217,7 @@ export const convert: Convert = ( properties: [], }; } - const value: Intermediate.PropertySignatureParams[] = Object.entries(schema.properties || {}).map(([name, jsonSchema]) => { + const value: Intermediate.PropertySignatureStruct[] = Object.entries(schema.properties || {}).map(([name, jsonSchema]) => { return { kind: "PropertySignature", name: converterContext.escapePropertySignatureName(name), @@ -227,7 +227,7 @@ export const convert: Convert = ( }; }); if (schema.additionalProperties) { - const additionalProperties: Intermediate.IndexSignatureSchema = { + const additionalProperties: Intermediate.IndexSignatureStruct = { kind: "IndexSignature", name: "key", schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, context, converterContext, { @@ -239,7 +239,7 @@ export const convert: Convert = ( properties: [...value, additionalProperties], }; } - const typeNode: Intermediate.SchemaType = { + const typeNode: Intermediate.Type = { kind: "object", properties: value, }; @@ -260,7 +260,7 @@ export const convertAdditionalProperties = ( schema: ObjectSchemaWithAdditionalProperties, setReference: Context, convertContext: ConverterContext.Types, -): Intermediate.IndexSignatureSchema => { +): Intermediate.IndexSignatureStruct => { // // https://swagger.io/docs/specification/data-models/dictionaries/#free-form if (schema.additionalProperties === true) { factory.TypeNode.create({ @@ -268,7 +268,7 @@ export const convertAdditionalProperties = ( value: [], }); } - const additionalProperties: Intermediate.IndexSignatureSchema = { + const additionalProperties: Intermediate.IndexSignatureStruct = { kind: "IndexSignature", name: "key", schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, setReference, convertContext, { From 9bab4cb7eab5c55bdf9df8ca3224f0cdceac9092 Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 09:17:50 +0900 Subject: [PATCH 06/39] chore: update --- src/internal/AbstractDataStructure/index.ts | 11 +++++- src/internal/OpenApiTools/Walker/Store.ts | 2 +- .../OpenApiTools/components2/Schema.ts | 36 +++++++++---------- 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/internal/AbstractDataStructure/index.ts b/src/internal/AbstractDataStructure/index.ts index 7c5ec80f..f35c3d53 100644 --- a/src/internal/AbstractDataStructure/index.ts +++ b/src/internal/AbstractDataStructure/index.ts @@ -16,6 +16,7 @@ export type Kind = | "IndexSignature" | "object" | "interface" + | "typedef" | "alias"; export interface BaseStruct { @@ -109,6 +110,12 @@ export interface InterfaceDeclarationStruct extends BaseStruct { comment?: string; } +export interface TypeDefinitionStruct extends BaseStruct { + kind: "typedef"; + name: string; + schemaType: Type; +} + export interface ObjectStruct extends BaseStruct { kind: "object"; properties: (PropertySignatureStruct | IndexSignatureStruct)[]; @@ -129,4 +136,6 @@ export type Type = | ReferenceStruct | ArrayStruct | ObjectStruct - | InterfaceDeclarationStruct; + | InterfaceDeclarationStruct + | AliasStruct + | TypeDefinitionStruct; diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index e255383a..b0e4ddf6 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -71,7 +71,7 @@ class Store { const alreadyRegistered = types.some(type => !!this.operator.getChildByPaths(path, type)); return alreadyRegistered; } - public addAbstractDataStruct(abstractDataStruct: ADS.Type): void { + public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Type): void { } /** diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index 06e13f3b..7548fbf6 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -139,8 +139,8 @@ export const generateMultiTypeAlias = ( context: ToAbstractDataStructure.Context, multiType: "oneOf" | "allOf" | "anyOf", convertContext: ConvertContext.Types, -): ts.TypeAliasDeclaration => { - const type = ToAbstractDataStructure.generateMultiTypeNode( +): ADS.AliasStruct => { + const schema = ToAbstractDataStructure.generateMultiTypeNode( entryPoint, currentPoint, factory, @@ -150,11 +150,11 @@ export const generateMultiTypeAlias = ( convertContext, multiType, ); - return factory.TypeAliasDeclaration.create({ - export: true, + return { + kind: "alias", name: convertContext.escapeDeclarationText(name), - type, - }); + schema: schema, + } }; export const addSchema = ( @@ -173,39 +173,39 @@ export const addSchema = ( } if (Guard.isAllOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "typeAlias", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), + schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), }); } else if (Guard.isOneOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "typeAlias", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), + schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), }); } else if (Guard.isAnyOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "typeAlias", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), + schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), }); } else if (Guard.isArraySchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "typeAlias", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + schemaType: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isObjectSchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "interface", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + schemaType: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isPrimitiveSchema(schema)) { store.addAbstractDataStruct(targetPoint, { - kind: "typeAlias", + kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - value: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), + schemaType: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), }); } }; From 4c21ba8c7b459d2735b15451338168dc7299584d Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 09:19:09 +0900 Subject: [PATCH 07/39] chore: update --- .../components2/ExternalDocumentation.ts | 10 - .../OpenApiTools/components2/Header.ts | 89 ------- .../OpenApiTools/components2/Headers.ts | 54 ----- .../OpenApiTools/components2/MediaType.ts | 55 ----- .../OpenApiTools/components2/Operation.ts | 224 ------------------ .../OpenApiTools/components2/Parameter.ts | 135 ----------- .../OpenApiTools/components2/Parameters.ts | 113 --------- .../OpenApiTools/components2/PathItem.ts | 126 ---------- .../OpenApiTools/components2/PathItems.ts | 56 ----- .../OpenApiTools/components2/RequestBodies.ts | 50 ---- .../OpenApiTools/components2/RequestBody.ts | 56 ----- .../OpenApiTools/components2/Response.ts | 117 --------- .../OpenApiTools/components2/Responses.ts | 200 ---------------- .../components2/SecuritySchema.ts | 51 ---- .../components2/SecuritySchemas.ts | 44 ---- .../OpenApiTools/components2/Server.ts | 10 - .../OpenApiTools/components2/Servers.ts | 11 - 17 files changed, 1401 deletions(-) delete mode 100644 src/internal/OpenApiTools/components2/ExternalDocumentation.ts delete mode 100644 src/internal/OpenApiTools/components2/Header.ts delete mode 100644 src/internal/OpenApiTools/components2/Headers.ts delete mode 100644 src/internal/OpenApiTools/components2/MediaType.ts delete mode 100644 src/internal/OpenApiTools/components2/Operation.ts delete mode 100644 src/internal/OpenApiTools/components2/Parameter.ts delete mode 100644 src/internal/OpenApiTools/components2/Parameters.ts delete mode 100644 src/internal/OpenApiTools/components2/PathItem.ts delete mode 100644 src/internal/OpenApiTools/components2/PathItems.ts delete mode 100644 src/internal/OpenApiTools/components2/RequestBodies.ts delete mode 100644 src/internal/OpenApiTools/components2/RequestBody.ts delete mode 100644 src/internal/OpenApiTools/components2/Response.ts delete mode 100644 src/internal/OpenApiTools/components2/Responses.ts delete mode 100644 src/internal/OpenApiTools/components2/SecuritySchema.ts delete mode 100644 src/internal/OpenApiTools/components2/SecuritySchemas.ts delete mode 100644 src/internal/OpenApiTools/components2/Server.ts delete mode 100644 src/internal/OpenApiTools/components2/Servers.ts diff --git a/src/internal/OpenApiTools/components2/ExternalDocumentation.ts b/src/internal/OpenApiTools/components2/ExternalDocumentation.ts deleted file mode 100644 index d3327b61..00000000 --- a/src/internal/OpenApiTools/components2/ExternalDocumentation.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { EOL } from "os"; - -import type { OpenApi } from "../../../types"; - -export const addComment = (comment?: string, externalDocs?: OpenApi.ExternalDocumentation): string | undefined => { - if (!externalDocs) { - return comment; - } - return [comment, "", `@see ${externalDocs.url}`, externalDocs.description].filter(Boolean).join(EOL); -}; diff --git a/src/internal/OpenApiTools/components2/Header.ts b/src/internal/OpenApiTools/components2/Header.ts deleted file mode 100644 index 0c988f3b..00000000 --- a/src/internal/OpenApiTools/components2/Header.ts +++ /dev/null @@ -1,89 +0,0 @@ -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as ToTypeNode from "../toTypeNode"; -import * as Reference from "./Reference"; - -export const generateTypeNode = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - header: OpenApi.Header, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.TypeAliasDeclaration => { - return factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(name), - type: ToTypeNode.convert(entryPoint, currentPoint, factory, header.schema || { type: "null" }, context, converterContext), - }); -}; - -export const generatePropertySignature = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - header: OpenApi.Header | OpenApi.Reference, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature => { - if (Guard.isReference(header)) { - const reference = Reference.generate(entryPoint, currentPoint, header); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(name), - optional: false, - type: factory.TypeReferenceNode.create({ - name: context.resolveReferencePath(currentPoint, reference.path).name, - }), - }); - } - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(name), - optional: false, - type: factory.TypeReferenceNode.create({ - name: context.resolveReferencePath(currentPoint, reference.path).name, - }), - }); - } - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(name), - optional: false, - type: ToTypeNode.convert(entryPoint, currentPoint, factory, header.schema || { type: "null" }, context, converterContext), - }); -}; - -export const generatePropertySignatures = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - headers: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature[] => { - return Object.entries(headers).map(([headerName, header]) => { - return generatePropertySignature(entryPoint, currentPoint, factory, headerName, header, context, converterContext); - }); -}; - -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - headers: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.InterfaceDeclaration => { - return factory.InterfaceDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(name), - members: generatePropertySignatures(entryPoint, currentPoint, factory, headers, context, converterContext), - }); -}; diff --git a/src/internal/OpenApiTools/components2/Headers.ts b/src/internal/OpenApiTools/components2/Headers.ts deleted file mode 100644 index 74aba1b8..00000000 --- a/src/internal/OpenApiTools/components2/Headers.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { OpenApi } from "../../../types"; -import { UndefinedComponent } from "../../Exception"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Header from "./Header"; -import * as Reference from "./Reference"; -import * as Schema from "./Schema"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - headers: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - store.addComponent("headers", { - kind: "namespace", - name: Name.Components.Headers, - }); - Object.entries(headers).forEach(([name, header]) => { - if (Guard.isReference(header)) { - const reference = Reference.generate(entryPoint, currentPoint, header); - if (reference.type === "local") { - if (!store.hasStatement(reference.path, ["interface"])) { - throw new UndefinedComponent(`Reference "${header.$ref}" did not found in ${reference.path} by ${reference.name}`); - } - } else if (reference.type === "remote") { - Schema.addSchema( - entryPoint, - currentPoint, - store, - factory, - reference.path, - reference.name, - reference.data.schema, - context, - converterContext, - ); - } - } else { - store.addStatement(`components/headers/${name}`, { - kind: "typeAlias", - name: converterContext.escapeDeclarationText(name), - value: Header.generateTypeNode(entryPoint, currentPoint, factory, name, header, context, converterContext), - }); - } - }); -}; diff --git a/src/internal/OpenApiTools/components2/MediaType.ts b/src/internal/OpenApiTools/components2/MediaType.ts deleted file mode 100644 index dc3bf4e5..00000000 --- a/src/internal/OpenApiTools/components2/MediaType.ts +++ /dev/null @@ -1,55 +0,0 @@ -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as ToTypeNode from "../toTypeNode"; - -export const generatePropertySignature = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - protocol: string, - schema: OpenApi.Schema, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature => { - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(protocol), - optional: false, - type: ToTypeNode.convert(entryPoint, currentPoint, factory, schema, context, converterContext), - comment: schema.description, - }); -}; - -export const generatePropertySignatures = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - content: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature[] => { - return Object.entries(content).reduce((previous, [protocol, mediaType]) => { - if (!mediaType.schema) { - return previous; - } - return previous.concat(generatePropertySignature(entryPoint, currentPoint, factory, protocol, mediaType.schema, context, converterContext)); - }, []); -}; - -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - content: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.InterfaceDeclaration => { - return factory.InterfaceDeclaration.create({ - export: true, - name, - members: generatePropertySignatures(entryPoint, currentPoint, factory, content, context, converterContext), - }); -}; diff --git a/src/internal/OpenApiTools/components2/Operation.ts b/src/internal/OpenApiTools/components2/Operation.ts deleted file mode 100644 index 4b9f9dce..00000000 --- a/src/internal/OpenApiTools/components2/Operation.ts +++ /dev/null @@ -1,224 +0,0 @@ -import { EOL } from "os"; -import * as path from "path"; - -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as ExternalDocumentation from "./ExternalDocumentation"; -import * as Parameter from "./Parameter"; -import * as Reference from "./Reference"; -import * as RequestBody from "./RequestBody"; -import * as Responses from "./Responses"; -import * as Servers from "./Servers"; - -const generateComment = (operation: OpenApi.Operation): string => { - const comments: string[] = []; - if (operation.summary) { - comments.push(operation.summary); - } - if (operation.description) { - comments.push(operation.description); - } - if (operation.tags) { - comments.push(`tags: ${operation.tags.join(", ")}`); - } - return comments.join(EOL); -}; - -// 使わない可能性あり -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentPath: string, - name: string, - operation: OpenApi.Operation, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = `${parentPath}/${name}`; - const operationId = operation.operationId; - if (!operationId) { - throw new Error("not setting operationId\n" + JSON.stringify(operation)); - } - store.addStatement(basePath, { - kind: "namespace", - name, - comment: ExternalDocumentation.addComment(Servers.addComment([generateComment(operation)], operation.servers), operation.externalDocs), - deprecated: operation.deprecated, - }); - - if (operation.parameters) { - const parameterName = "Parameter"; - store.addStatement(`${basePath}/Parameter`, { - kind: "interface", - name: parameterName, - value: Parameter.generateInterface( - entryPoint, - currentPoint, - store, - factory, - parameterName, - operation.parameters, - context, - converterContext, - ), - }); - } - - if (operation.requestBody) { - if (Guard.isReference(operation.requestBody)) { - const reference = Reference.generate(entryPoint, currentPoint, operation.requestBody); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - // TODO (not-use) 追加する必要がある(このメソッドを使わない可能性あり) - factory.TypeReferenceNode.create({ name: context.resolveReferencePath(currentPoint, reference.path).name }); - } else if (reference.type === "remote" && reference.componentName) { - const contentPath = path.join(reference.path, "Content"); // requestBodyはNamespaceを形成するため - const name = "Content"; - store.addStatement(contentPath, { - kind: "interface", - name: name, - value: RequestBody.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data, context, converterContext), - }); - const typeAliasName = context.resolveReferencePath(currentPoint, contentPath).name; - store.addStatement(`${basePath}/RequestBody`, { - kind: "typeAlias", - name: typeAliasName, - value: factory.TypeAliasDeclaration.create({ - export: true, - name: "RequestBody", - type: factory.TypeReferenceNode.create({ name: typeAliasName }), - }), - }); - } - } else { - RequestBody.generateNamespace( - entryPoint, - currentPoint, - store, - factory, - basePath, - "RequestBody", - operation.requestBody, - context, - converterContext, - ); - } - } - - if (operation.responses) { - Responses.generateNamespaceWithStatusCode( - entryPoint, - currentPoint, - store, - factory, - basePath, - operation.responses, - context, - converterContext, - ); - } -}; - -export const generateStatements = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - requestUri: string, - httpMethod: string, // PUT POST PATCH - operation: OpenApi.Operation, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.Statement[] => { - let statements: ts.Statement[] = []; - const operationId = operation.operationId; - if (!operationId) { - throw new Error("not setting operationId\n" + JSON.stringify(operation)); - } - store.updateOperationState(httpMethod, requestUri, operationId, {}); - if (operation.parameters) { - const parameterName = converterContext.generateParameterName(operationId); - statements.push( - Parameter.generateAliasInterface( - entryPoint, - currentPoint, - store, - factory, - parameterName, - operation.parameters, - context, - converterContext, - ), - ); - } - if (operation.requestBody) { - const requestBodyName = converterContext.generateRequestBodyName(operationId); - if (Guard.isReference(operation.requestBody)) { - const reference = Reference.generate(entryPoint, currentPoint, operation.requestBody); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - statements.push( - factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.generateRequestBodyName(operationId), - type: factory.TypeReferenceNode.create({ - name: context.resolveReferencePath(currentPoint, `${reference.path}`) + "." + Name.ComponentChild.Content, // TODO Contextから作成? - }), - }), - ); - } else if (reference.type === "remote" && reference.componentName) { - const contentPath = path.join(reference.path, "Content"); // requestBodyはNamespaceを形成するため - const name = "Content"; - store.addStatement(contentPath, { - kind: "interface", - name: name, - value: RequestBody.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data, context, converterContext), - }); - statements.push( - factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(requestBodyName), - type: factory.TypeReferenceNode.create({ name: context.resolveReferencePath(currentPoint, contentPath).name }), - }), - ); - - store.updateOperationState(httpMethod, requestUri, operationId, { - requestBodyName: requestBodyName, - }); - } - } else { - statements.push( - RequestBody.generateInterface(entryPoint, currentPoint, factory, requestBodyName, operation.requestBody, context, converterContext), - ); - store.updateOperationState(httpMethod, requestUri, operationId, { - requestBodyName: requestBodyName, - }); - } - } - - if (operation.responses) { - statements = statements.concat( - Responses.generateInterfacesWithStatusCode( - entryPoint, - currentPoint, - store, - factory, - operationId, - operation.responses, - context, - converterContext, - ).flat(), - ); - } - - return statements; -}; diff --git a/src/internal/OpenApiTools/components2/Parameter.ts b/src/internal/OpenApiTools/components2/Parameter.ts deleted file mode 100644 index b3d19bb6..00000000 --- a/src/internal/OpenApiTools/components2/Parameter.ts +++ /dev/null @@ -1,135 +0,0 @@ -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Reference from "./Reference"; - -export const generateTypeNode = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - parameter: OpenApi.Parameter, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.TypeNode => { - return ToTypeNode.convert(entryPoint, currentPoint, factory, parameter.schema || { type: "null" }, context, converterContext); -}; - -export const generateTypeAlias = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - parameter: OpenApi.Parameter, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.TypeAliasDeclaration => { - return factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(name), - comment: parameter.description, - type: generateTypeNode(entryPoint, currentPoint, factory, parameter, context, converterContext), - }); -}; - -export const generatePropertySignature = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parameter: OpenApi.Parameter | OpenApi.Reference, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature => { - if (Guard.isReference(parameter)) { - const reference = Reference.generate(entryPoint, currentPoint, parameter); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - const localRef = store.getParameter(reference.path); - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(localRef.name), - optional: false, - comment: localRef.description, - type: factory.TypeReferenceNode.create({ - name: context.resolveReferencePath(currentPoint, reference.path).name, - }), - }); - } - const isPathProperty = reference.data.in === "path"; - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(reference.data.name), - optional: isPathProperty ? false : !reference.data.required, - comment: reference.data.description, - type: ToTypeNode.convert( - entryPoint, - reference.referencePoint, - factory, - reference.data.schema || { type: "null" }, - context, - converterContext, - ), - }); - } - const isPathProperty = parameter.in === "path"; - return factory.PropertySignature.create({ - name: converterContext.escapePropertySignatureName(parameter.name), - optional: isPathProperty ? false : !parameter.required, - type: ToTypeNode.convert(entryPoint, currentPoint, factory, parameter.schema || { type: "null" }, context, converterContext), - comment: parameter.description, - }); -}; - -export const generatePropertySignatures = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parameters: (OpenApi.Parameter | OpenApi.Reference)[], - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.PropertySignature[] => { - return parameters.map(parameter => { - return generatePropertySignature(entryPoint, currentPoint, store, factory, parameter, context, converterContext); - }); -}; - -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - name: string, - parameters: [OpenApi.Parameter | OpenApi.Reference], - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.InterfaceDeclaration => { - return factory.InterfaceDeclaration.create({ - export: true, - name, - members: generatePropertySignatures(entryPoint, currentPoint, store, factory, parameters, context, converterContext), - }); -}; - -/** - * Alias作成用 - */ -export const generateAliasInterface = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - name: string, - parameters: (OpenApi.Parameter | OpenApi.Reference)[], - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.InterfaceDeclaration => { - return factory.InterfaceDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(name), - members: generatePropertySignatures(entryPoint, currentPoint, store, factory, parameters, context, converterContext), - }); -}; diff --git a/src/internal/OpenApiTools/components2/Parameters.ts b/src/internal/OpenApiTools/components2/Parameters.ts deleted file mode 100644 index 90444bc2..00000000 --- a/src/internal/OpenApiTools/components2/Parameters.ts +++ /dev/null @@ -1,113 +0,0 @@ -import type { OpenApi } from "../../../types"; -import { UnSupportError } from "../../Exception"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Paramter from "./Parameter"; -import * as Reference from "./Reference"; -import * as Schema from "./Schema"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parameters: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = "components/parameters"; - store.addComponent("parameters", { - kind: "namespace", - name: Name.Components.Parameters, - }); - - Object.entries(parameters).forEach(([name, parameter]) => { - if (Guard.isReference(parameter)) { - const reference = Reference.generate(entryPoint, currentPoint, parameter); - if (reference.type === "local") { - throw new UnSupportError("What is components.parameters local reference?"); - } else { - if (!reference.data.schema) { - return; - } - Schema.addSchema( - entryPoint, - currentPoint, - store, - factory, - reference.path, - reference.name, - reference.data.schema, - context, - converterContext, - ); - store.addStatement(`${basePath}/${name}`, { - kind: "typeAlias", - name: converterContext.escapeDeclarationText(name), - value: factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(name), - type: factory.TypeReferenceNode.create({ - name: context.resolveReferencePath(currentPoint, reference.path).name, - }), - }), - }); - } - } else { - const path = `${basePath}/${name}`; - store.addStatement(path, { - kind: "typeAlias", - name: converterContext.escapeDeclarationText(name), - value: Paramter.generateTypeAlias(entryPoint, currentPoint, factory, name, parameter, context, converterContext), - }); - } - }); -}; - -export const generateNamespaceWithList = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parameters: (OpenApi.Parameter | OpenApi.Reference)[], - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - store.addComponent("parameters", { - kind: "namespace", - name: Name.Components.Parameters, - }); - - parameters.forEach(parameter => { - if (Guard.isReference(parameter)) { - const reference = Reference.generate(entryPoint, currentPoint, parameter); - if (reference.type === "local") { - throw new UnSupportError("What is components.parameters local reference?"); - } - const path = `components/parameters/${reference.name}`; - return store.addStatement(path, { - kind: "typeAlias", - name: reference.name, - value: Paramter.generateTypeAlias( - entryPoint, - reference.referencePoint, - factory, - reference.name, - reference.data, - context, - converterContext, - ), - }); - } - const path = `components/parameters/${parameter.name}`; - return store.addStatement(path, { - kind: "typeAlias", - name: parameter.name, - value: Paramter.generateTypeAlias(entryPoint, currentPoint, factory, parameter.name, parameter, context, converterContext), - }); - }); -}; diff --git a/src/internal/OpenApiTools/components2/PathItem.ts b/src/internal/OpenApiTools/components2/PathItem.ts deleted file mode 100644 index 7c7db96c..00000000 --- a/src/internal/OpenApiTools/components2/PathItem.ts +++ /dev/null @@ -1,126 +0,0 @@ -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Operation from "./Operation"; -import * as Parameters from "./Parameters"; -import * as Servers from "./Servers"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentPath: string, - name: string, - pathItem: OpenApi.PathItem, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, - options?: { topComment?: string }, -): void => { - const basePath = `${parentPath}/${name}`; - const topComment = options && options.topComment && options.topComment; - store.addStatement(basePath, { - kind: "namespace", - name, - comment: Servers.addComment([topComment, pathItem.description], pathItem.servers), - }); - if (pathItem.get) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "GET", pathItem.get, context, converterContext); - } - if (pathItem.put) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "PUT", pathItem.put, context, converterContext); - } - if (pathItem.post) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "POST", pathItem.post, context, converterContext); - } - if (pathItem.delete) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "DELETE", pathItem.delete, context, converterContext); - } - if (pathItem.options) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "OPTIONS", pathItem.options, context, converterContext); - } - if (pathItem.head) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "HEAD", pathItem.head, context, converterContext); - } - if (pathItem.patch) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "PATCH", pathItem.patch, context, converterContext); - } - if (pathItem.trace) { - Operation.generateNamespace(entryPoint, currentPoint, store, factory, basePath, "TRACE", pathItem.trace, context, converterContext); - } - if (pathItem.parameters) { - Parameters.generateNamespaceWithList(entryPoint, currentPoint, store, factory, pathItem.parameters, context, converterContext); - } -}; - -export const generateStatements = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - requestUri: string, - pathItem: OpenApi.PathItem, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.Statement[] => { - const statements: ts.Statement[][] = []; - if (pathItem.get) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "GET", pathItem.get, context, converterContext), - ); - } - if (pathItem.put) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "PUT", pathItem.put, context, converterContext), - ); - } - if (pathItem.post) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "POST", pathItem.post, context, converterContext), - ); - } - if (pathItem.delete) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "DELETE", pathItem.delete, context, converterContext), - ); - } - if (pathItem.options) { - statements.push( - Operation.generateStatements( - entryPoint, - currentPoint, - store, - factory, - requestUri, - "OPTIONS", - pathItem.options, - context, - converterContext, - ), - ); - } - if (pathItem.head) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "HEAD", pathItem.head, context, converterContext), - ); - } - if (pathItem.patch) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "PATCH", pathItem.patch, context, converterContext), - ); - } - if (pathItem.trace) { - statements.push( - Operation.generateStatements(entryPoint, currentPoint, store, factory, requestUri, "TRACE", pathItem.trace, context, converterContext), - ); - } - // if (pathItem.parameters) { - // Parameters.generateNamespaceWithList(entryPoint, currentPoint, store, factory, pathItem.parameters, context); - // } - - return statements.flat(); -}; diff --git a/src/internal/OpenApiTools/components2/PathItems.ts b/src/internal/OpenApiTools/components2/PathItems.ts deleted file mode 100644 index 76785672..00000000 --- a/src/internal/OpenApiTools/components2/PathItems.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { OpenApi } from "../../../types"; -import { FeatureDevelopmentError, UnSupportError } from "../../Exception"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as PathItem from "./PathItem"; -import * as Reference from "./Reference"; - -// 使わない可能性あり -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - pathItems: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = "components/pathItems"; - - store.addComponent("pathItems", { - kind: "namespace", - name: Name.Components.PathItems, - }); - - Object.entries(pathItems).forEach(([key, pathItem]) => { - if (Guard.isReference(pathItem)) { - const reference = Reference.generate(entryPoint, currentPoint, pathItem); - if (reference.type === "local") { - throw new UnSupportError("can't use components.pathItems local reference"); - } else if (reference.componentName) { - if (key !== reference.name) { - throw new UnSupportError(`can't use difference pathItem key name. "${key}" !== "${reference.name}"`); - } - PathItem.generateNamespace( - entryPoint, - reference.referencePoint, - store, - factory, - basePath, - reference.name, - reference.data, - context, - converterContext, - ); - } else { - throw new FeatureDevelopmentError("存在しないReferenceを参照する場合は全部生成する"); - } - } else { - PathItem.generateNamespace(entryPoint, currentPoint, store, factory, basePath, key, pathItem, context, converterContext); - } - }); -}; diff --git a/src/internal/OpenApiTools/components2/RequestBodies.ts b/src/internal/OpenApiTools/components2/RequestBodies.ts deleted file mode 100644 index 600487a2..00000000 --- a/src/internal/OpenApiTools/components2/RequestBodies.ts +++ /dev/null @@ -1,50 +0,0 @@ -import * as path from "path"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Reference from "./Reference"; -import * as RequestBody from "./RequestBody"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - requestBodies: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = "components/requestBodies"; - store.addComponent("requestBodies", { - kind: "namespace", - name: Name.Components.RequestBodies, - }); - - Object.entries(requestBodies).forEach(([name, requestBody]) => { - if (Guard.isReference(requestBody)) { - const reference = Reference.generate(entryPoint, currentPoint, requestBody); - if (reference.type === "local") { - throw new Error("not support"); - } else if (reference.type === "remote") { - RequestBody.generateNamespace( - entryPoint, - reference.referencePoint, - store, - factory, - path.dirname(reference.path), - reference.name, - reference.data, - context, - converterContext, - ); - } - } else { - RequestBody.generateNamespace(entryPoint, currentPoint, store, factory, basePath, name, requestBody, context, converterContext); - } - }); -}; diff --git a/src/internal/OpenApiTools/components2/RequestBody.ts b/src/internal/OpenApiTools/components2/RequestBody.ts deleted file mode 100644 index bb286b8c..00000000 --- a/src/internal/OpenApiTools/components2/RequestBody.ts +++ /dev/null @@ -1,56 +0,0 @@ -import * as ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as MediaType from "./MediaType"; - -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - requestBody: OpenApi.RequestBody, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.InterfaceDeclaration => { - const contentSignatures = MediaType.generatePropertySignatures( - entryPoint, - currentPoint, - factory, - requestBody.content || {}, - context, - converterContext, - ); - return factory.InterfaceDeclaration.create({ - export: true, - name, - members: contentSignatures, - }); -}; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentName: string, - name: string, - requestBody: OpenApi.RequestBody, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = `${parentName}/${name}`; - store.addStatement(basePath, { - kind: "namespace", - name, - comment: requestBody.description, - }); - store.addStatement(`${basePath}/Content`, { - kind: "interface", - name: "Content", - value: generateInterface(entryPoint, currentPoint, factory, "Content", requestBody, context, converterContext), - }); -}; diff --git a/src/internal/OpenApiTools/components2/Response.ts b/src/internal/OpenApiTools/components2/Response.ts deleted file mode 100644 index 7a2c834e..00000000 --- a/src/internal/OpenApiTools/components2/Response.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as path from "path"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as Header from "./Header"; -import * as MediaType from "./MediaType"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentPath: string, - name: string, - response: OpenApi.Response, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = `${parentPath}/${name}`; - store.addStatement(basePath, { - kind: "namespace", - name, - comment: response.description, - }); - - if (response.headers) { - store.addStatement(`${basePath}/Header`, { - kind: "interface", - name: Name.ComponentChild.Header, - value: Header.generateInterface( - entryPoint, - currentPoint, - factory, - Name.ComponentChild.Header, - response.headers, - context, - converterContext, - ), - }); - } - - if (response.content) { - store.addStatement(`${basePath}/Content`, { - kind: "interface", - name: Name.ComponentChild.Content, - value: MediaType.generateInterface( - entryPoint, - currentPoint, - factory, - Name.ComponentChild.Content, - response.content, - context, - converterContext, - ), - }); - } -}; - -export const generateReferenceNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentPath: string, - nameWithStatusCode: string, - responseReference: { name: string; path: string }, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = `${parentPath}/${nameWithStatusCode}`; - const referenceNamespaceName = context.resolveReferencePath(currentPoint, responseReference.path).name; - store.addStatement(basePath, { - kind: "namespace", - name: nameWithStatusCode, - }); - const headerNamespace = store.getStatement(path.posix.join(responseReference.path, "Header"), "namespace"); - if (headerNamespace) { - store.addStatement(`${basePath}/Header`, { - kind: "namespace", - name: Name.ComponentChild.Header, - }); - Object.values(headerNamespace.getChildren()).forEach(statement => { - if (!statement) { - return; - } - if (statement.kind === "interface" || statement.kind === "typeAlias") { - const aliasName = [referenceNamespaceName, Name.ComponentChild.Header, statement.name].join("."); - store.addStatement(`${basePath}/Header/${statement.name}`, { - kind: "typeAlias", - name: aliasName, - value: factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.escapeDeclarationText(statement.name), - type: factory.TypeReferenceNode.create({ - name: aliasName, - }), - }), - }); - } - }); - } - store.addStatement(`${basePath}/Content`, { - kind: "typeAlias", - name: Name.ComponentChild.Content, - value: factory.TypeAliasDeclaration.create({ - export: true, - name: Name.ComponentChild.Content, - type: factory.TypeReferenceNode.create({ - name: referenceNamespaceName + "." + Name.ComponentChild.Content, // TODO Contextから作成? - }), - }), - }); -}; diff --git a/src/internal/OpenApiTools/components2/Responses.ts b/src/internal/OpenApiTools/components2/Responses.ts deleted file mode 100644 index b27a179f..00000000 --- a/src/internal/OpenApiTools/components2/Responses.ts +++ /dev/null @@ -1,200 +0,0 @@ -import * as path from "path"; - -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { UndefinedComponent } from "../../Exception"; -import { Factory } from "../../TsGenerator"; -import * as ConverterContext from "../ConverterContext"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; -import type * as Walker from "../Walker"; -import * as MediaType from "./MediaType"; -import * as Reference from "./Reference"; -import * as Response from "./Response"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - responses: Record, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = "components/responses"; - store.addComponent("responses", { - kind: "namespace", - name: Name.Components.Responses, - }); - Object.entries(responses).forEach(([name, response]) => { - if (Guard.isReference(response)) { - const reference = Reference.generate(entryPoint, currentPoint, response); - if (reference.type === "local") { - if (!store.hasStatement(reference.path, ["interface"])) { - throw new UndefinedComponent(`Reference "${response.$ref}" did not found in ${reference.path} by ${reference.name}`); - } - } else if (reference.type === "remote") { - Response.generateNamespace( - entryPoint, - reference.referencePoint, - store, - factory, - path.posix.dirname(reference.path), // referencePoint basename === namespace name - reference.name, - reference.data, - context, - converterContext, - ); - } - } else { - Response.generateNamespace(entryPoint, currentPoint, store, factory, basePath, name, response, context, converterContext); - } - }); -}; - -export const generateNamespaceWithStatusCode = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - parentPath: string, - responses: OpenApi.Responses, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): void => { - const basePath = `${parentPath}/responses`; - store.addStatement(basePath, { - kind: "namespace", - name: Name.ComponentChild.Response, - }); - - Object.entries(responses).forEach(([statusCode, response]) => { - const nameWithStatusCode = `Status$${statusCode}`; - if (Guard.isReference(response)) { - const reference = Reference.generate(entryPoint, currentPoint, response); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - Response.generateReferenceNamespace( - entryPoint, - currentPoint, - store, - factory, - basePath, - nameWithStatusCode, - reference, - context, - converterContext, - ); - } else if (reference.componentName) { - // reference先に定義を作成 - Response.generateNamespace( - entryPoint, - reference.referencePoint, - store, - factory, - path.posix.dirname(reference.path), // referencePoint basename === namespace name - reference.name, - reference.data, - context, - converterContext, - ); - // referenceのTypeAliasの作成 - Response.generateReferenceNamespace( - entryPoint, - currentPoint, - store, - factory, - basePath, - nameWithStatusCode, - reference, - context, - converterContext, - ); - } - } else { - Response.generateNamespace(entryPoint, currentPoint, store, factory, basePath, nameWithStatusCode, response, context, converterContext); - } - }); -}; - -export const generateInterfacesWithStatusCode = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - operationId: string, - responses: OpenApi.Responses, - context: ToTypeNode.Context, - converterContext: ConverterContext.Types, -): ts.Statement[] => { - const statements: ts.Statement[] = []; - Object.entries(responses).forEach(([statusCode, response]) => { - if (!response) { - return; - } - if (Guard.isReference(response)) { - const reference = Reference.generate(entryPoint, currentPoint, response); - if (reference.type === "local") { - context.setReferenceHandler(currentPoint, reference); - const { maybeResolvedName, unresolvedPaths } = context.resolveReferencePath(currentPoint, `${reference.path}/Content`); - if (unresolvedPaths.length === 0) { - statements.push( - factory.TypeAliasDeclaration.create({ - export: true, - name: converterContext.generateResponseName(operationId, statusCode), - type: factory.TypeReferenceNode.create({ - name: maybeResolvedName, - }), - }), - ); - } - } else if (reference.componentName) { - // reference先に定義を作成 - Response.generateNamespace( - entryPoint, - reference.referencePoint, - store, - factory, - path.posix.dirname(reference.path), // referencePoint basename === namespace name - reference.name, - reference.data, - context, - converterContext, - ); - // referenceのTypeAliasの作成 - const content = reference.data.content; - if (content) { - statements.push( - MediaType.generateInterface( - entryPoint, - reference.referencePoint, - factory, - converterContext.generateResponseName(operationId, statusCode), - content, - context, - converterContext, - ), - ); - } - } - } else { - if (response.content) { - statements.push( - MediaType.generateInterface( - entryPoint, - currentPoint, - factory, - converterContext.generateResponseName(operationId, statusCode), - response.content, - context, - converterContext, - ), - ); - } - } - }); - - return statements; -}; diff --git a/src/internal/OpenApiTools/components2/SecuritySchema.ts b/src/internal/OpenApiTools/components2/SecuritySchema.ts deleted file mode 100644 index 9885b072..00000000 --- a/src/internal/OpenApiTools/components2/SecuritySchema.ts +++ /dev/null @@ -1,51 +0,0 @@ -import ts from "typescript"; - -import type { OpenApi } from "../../../types"; -import { Factory } from "../../TsGenerator"; - -export const generatePropertySignatures = ( - _entryPoint: string, - _currentPoint: string, - factory: Factory.Type, - securitySchema: OpenApi.SecuritySchema, -): ts.PropertySignature[] => { - const signatures: ts.PropertySignature[] = [ - factory.PropertySignature.create({ - name: "type", - optional: false, - type: factory.LiteralTypeNode.create({ value: securitySchema.type }), - }), - factory.PropertySignature.create({ - name: "name", - optional: false, - type: factory.LiteralTypeNode.create({ value: securitySchema.name }), - }), - factory.PropertySignature.create({ - name: "in", - optional: false, - type: factory.LiteralTypeNode.create({ value: securitySchema.in }), - }), - factory.PropertySignature.create({ - name: "openIdConnectUrl", - optional: false, - type: factory.LiteralTypeNode.create({ value: securitySchema.openIdConnectUrl }), - }), - ]; - - return signatures; -}; - -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - securitySchema: OpenApi.SecuritySchema, -): ts.InterfaceDeclaration => { - return factory.InterfaceDeclaration.create({ - export: true, - name, - comment: securitySchema.description, - members: generatePropertySignatures(entryPoint, currentPoint, factory, securitySchema), - }); -}; diff --git a/src/internal/OpenApiTools/components2/SecuritySchemas.ts b/src/internal/OpenApiTools/components2/SecuritySchemas.ts deleted file mode 100644 index 5acc9072..00000000 --- a/src/internal/OpenApiTools/components2/SecuritySchemas.ts +++ /dev/null @@ -1,44 +0,0 @@ -import type { OpenApi } from "../../../types"; -import { UndefinedComponent } from "../../Exception"; -import { Factory } from "../../TsGenerator"; -import * as Guard from "../Guard"; -import * as Name from "../Name"; -import type * as Walker from "../Walker"; -import * as Reference from "./Reference"; -import * as SecuritySchema from "./SecuritySchema"; - -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - requestBodies: Record, -): void => { - store.addComponent("securitySchemes", { - kind: "namespace", - name: Name.Components.SecuritySchemas, - }); - Object.entries(requestBodies).forEach(([name, requestBody]) => { - if (Guard.isReference(requestBody)) { - const reference = Reference.generate(entryPoint, currentPoint, requestBody); - if (reference.type === "local") { - if (!store.hasStatement(reference.path, ["interface"])) { - throw new UndefinedComponent(`Reference "${requestBody.$ref}" did not found in ${reference.path} by ${reference.name}`); - } - return; - } - const path = `components/securitySchemes/${reference.name}`; - return store.addStatement(path, { - kind: "interface", - name, - value: SecuritySchema.generateInterface(entryPoint, reference.referencePoint, factory, name, reference.data), - }); - } - const path = `components/securitySchemes/${name}`; - return store.addStatement(path, { - kind: "interface", - name: name, - value: SecuritySchema.generateInterface(entryPoint, currentPoint, factory, name, requestBody), - }); - }); -}; diff --git a/src/internal/OpenApiTools/components2/Server.ts b/src/internal/OpenApiTools/components2/Server.ts deleted file mode 100644 index 93a2f43a..00000000 --- a/src/internal/OpenApiTools/components2/Server.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { EOL } from "os"; - -import type { OpenApi } from "../../../types"; - -export const addComment = (comment?: string, server?: OpenApi.Server): string | undefined => { - if (!server) { - return comment; - } - return [comment, server.url, server.description].filter(Boolean).join(EOL); -}; diff --git a/src/internal/OpenApiTools/components2/Servers.ts b/src/internal/OpenApiTools/components2/Servers.ts deleted file mode 100644 index 2deabf21..00000000 --- a/src/internal/OpenApiTools/components2/Servers.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { EOL } from "os"; - -import type { OpenApi } from "../../../types"; -import * as Server from "./Server"; - -export const addComment = (comments: (string | undefined)[], servers: OpenApi.Server[] = []): string | undefined => { - const comment = comments.filter(Boolean).join(EOL) as string | undefined; - return servers.reduce((newComment, server) => { - return Server.addComment(newComment, server); - }, comment); -}; From cb9d359f791aeb39655542cc48914689370fa50d Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 09:22:07 +0900 Subject: [PATCH 08/39] chore: update --- .../OpenApiTools/components2/Schema.ts | 3 +- .../OpenApiTools/components2/Schemas.ts | 72 ++++++++++--------- 2 files changed, 38 insertions(+), 37 deletions(-) diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index 7548fbf6..256c8e0e 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -7,7 +7,6 @@ import * as Guard from "../Guard"; import * as ToAbstractDataStructure from "../toAbstractDataStructure"; import type { ArraySchema, ObjectSchema, PrimitiveSchema } from "../types"; import type * as Walker from "../Walker"; -import * as ExternalDocumentation from "./ExternalDocumentation"; export const generatePropertySignatures = ( entryPoint: string, @@ -70,7 +69,7 @@ export const generateInterface = ( return { kind: "interface", name: convertContext.escapeDeclarationText(name), - comment: ExternalDocumentation.addComment(schema.description, schema.externalDocs), + // comment: ExternalDocumentation.addComment(schema.description, schema.externalDocs), members, }; }; diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index 861056f0..255d4f9d 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -46,16 +46,17 @@ export const generateNamespace = ( const reference = Reference.generate(entryPoint, currentPoint, schema); if (reference.type === "local") { const { maybeResolvedName } = context.resolveReferencePath(currentPoint, reference.path); - store.addStatement(`${basePath}/${name}`, { - kind: "typeAlias", + store.addAbstractDataStruct(`${basePath}/${name}`, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: factory.TypeAliasDeclaration.create({ - export: true, + schemaType: { + kind: "alias", name: convertContext.escapeDeclarationText(name), - type: factory.TypeReferenceNode.create({ + schema: { + kind: "reference", name: convertContext.escapeDeclarationText(maybeResolvedName), - }), - }), + }, + }, }); return; } @@ -73,17 +74,18 @@ export const generateNamespace = ( if (store.hasStatement(`${basePath}/${name}`, ["interface", "typeAlias"])) { return; } - return store.addStatement(`${basePath}/${name}`, { - kind: "typeAlias", + return store.addAbstractDataStruct(`${basePath}/${name}`, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: factory.TypeAliasDeclaration.create({ - export: true, + schemaType: { + kind: "alias", name: convertContext.escapeDeclarationText(name), comment: reference.data.description, - type: factory.TypeReferenceNode.create({ + schema: { + kind: "reference", name: convertContext.escapeDeclarationText(context.resolveReferencePath(currentPoint, reference.path).name), - }), - }), + }, + }, }); } const schema = InferredType.getInferredType(targetSchema); @@ -96,52 +98,52 @@ export const generateNamespace = ( } const path = `${basePath}/${name}`; if (Guard.isAllOfSchema(schema)) { - return store.addStatement(path, { - kind: "typeAlias", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), + schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), }); } if (Guard.isOneOfSchema(schema)) { - return store.addStatement(path, { - kind: "typeAlias", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), + schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), }); } if (Guard.isAnyOfSchema(schema)) { - return store.addStatement(path, { - kind: "typeAlias", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), + schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), }); } if (Guard.isArraySchema(schema)) { - return store.addStatement(path, { - kind: "typeAlias", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), + schemaType: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isObjectSchema(schema)) { - return store.addStatement(path, { - kind: "interface", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + schemaType: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isObjectSchema(schema)) { - return store.addStatement(path, { - kind: "interface", + return store.addAbstractDataStruct(path, { + kind: "typedef", name: convertContext.escapeDeclarationText(name), - value: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + schemaType: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isPrimitiveSchema(schema)) { - return store.addStatement(path, { - kind: "typeAlias", + return store.addAbstractDataStruct(path, { + kind: "typedef", name, - value: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), + schemaType: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), }); } throw new UnSupportError("schema.type = Array[] not supported. " + JSON.stringify(schema)); From 549778b947456316a0a24e8cd6c489377b664455 Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 09:28:12 +0900 Subject: [PATCH 09/39] chore: update --- src/internal/AbstractDataStructure/index.ts | 58 +++++++++---------- src/internal/OpenApiTools/Walker/Store.ts | 2 +- .../OpenApiTools/components2/Schema.ts | 18 +++--- .../OpenApiTools/components2/Schemas.ts | 18 +++--- .../OpenApiTools/toAbstractDataStructure.ts | 30 +++++----- 5 files changed, 61 insertions(+), 65 deletions(-) diff --git a/src/internal/AbstractDataStructure/index.ts b/src/internal/AbstractDataStructure/index.ts index f35c3d53..931bcc2d 100644 --- a/src/internal/AbstractDataStructure/index.ts +++ b/src/internal/AbstractDataStructure/index.ts @@ -19,109 +19,105 @@ export type Kind = | "typedef" | "alias"; -export interface BaseStruct { - kind: Kind; -} - -export interface StringStruct extends BaseStruct { +export interface StringStruct { kind: "string"; enum?: string[]; } -export interface IntegerStruct extends BaseStruct { +export interface IntegerStruct { kind: "integer"; enum?: number[]; } -export interface NumberStruct extends BaseStruct { +export interface NumberStruct { kind: "number"; enum?: number[]; } -export interface BooleanStruct extends BaseStruct { +export interface BooleanStruct { kind: "boolean"; } -export interface UndefinedStruct extends BaseStruct { +export interface UndefinedStruct { kind: "undefined"; } -export interface NullStruct extends BaseStruct { +export interface NullStruct { kind: "null"; } -export interface NeverStruct extends BaseStruct { +export interface NeverStruct { kind: "never"; } -export interface AnyStruct extends BaseStruct { +export interface AnyStruct { kind: "any"; } -export interface VoidStruct extends BaseStruct { +export interface VoidStruct { kind: "void"; } -export interface UnionStruct extends BaseStruct { +export interface UnionStruct { kind: "union"; - schemaTypes: BaseStruct[]; + structs: Struct[]; } -export interface IntersectionStruct extends BaseStruct { +export interface IntersectionStruct { kind: "intersection"; - schemaTypes: BaseStruct[]; + structs: Struct[]; } -export interface IndexSignatureStruct extends BaseStruct { +export interface IndexSignatureStruct { kind: "IndexSignature"; name: string; - schemaType: BaseStruct; + struct: Struct; } -export interface ReferenceStruct extends BaseStruct { +export interface ReferenceStruct { kind: "reference"; name: string; } -export interface ArrayStruct extends BaseStruct { +export interface ArrayStruct { kind: "array"; - schemaType: BaseStruct; + struct: Struct; } -export interface AliasStruct extends BaseStruct { +export interface AliasStruct { kind: "alias"; name: string; comment?: string; - schema: Type; + schema: Struct; } -export interface PropertySignatureStruct extends BaseStruct { +export interface PropertySignatureStruct { kind: "PropertySignature"; name: string; optional: boolean; comment?: string; - schemaType: Type; + struct: Struct; } -export interface InterfaceDeclarationStruct extends BaseStruct { +export interface InterfaceDeclarationStruct { kind: "interface"; name: string; members: (IndexSignatureStruct | PropertySignatureStruct)[]; comment?: string; } -export interface TypeDefinitionStruct extends BaseStruct { +export interface TypeDefinitionStruct { kind: "typedef"; name: string; - schemaType: Type; + struct: Struct; } -export interface ObjectStruct extends BaseStruct { +export interface ObjectStruct { kind: "object"; properties: (PropertySignatureStruct | IndexSignatureStruct)[]; } -export type Type = +export type Struct = | StringStruct | IntegerStruct | NumberStruct diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index b0e4ddf6..5e38ea71 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -71,7 +71,7 @@ class Store { const alreadyRegistered = types.some(type => !!this.operator.getChildByPaths(path, type)); return alreadyRegistered; } - public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Type): void { + public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Struct): void { } /** diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index 256c8e0e..da938219 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -27,7 +27,7 @@ export const generatePropertySignatures = ( name: convertContext.escapePropertySignatureName(propertyName), optional: !required.includes(propertyName), comment: schema.description, - schemaType: { + struct: { kind: "any", }, }; @@ -37,7 +37,7 @@ export const generatePropertySignatures = ( name: convertContext.escapePropertySignatureName(propertyName), optional: !required.includes(propertyName), comment: typeof property !== "boolean" ? property.description : undefined, - schemaType: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), + struct: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), }; }); }; @@ -99,7 +99,7 @@ export const generateTypeAlias = ( schema: PrimitiveSchema, convertContext: ConvertContext.Types, ): ADS.AliasStruct => { - let type: ADS.Type; + let type: ADS.Struct; if (schema.enum) { if (Guard.isNumberArray(schema.enum) && (schema.type === "number" || schema.type === "integer")) { type = { @@ -174,37 +174,37 @@ export const addSchema = ( store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), + struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), }); } else if (Guard.isOneOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), + struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), }); } else if (Guard.isAnyOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), + struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), }); } else if (Guard.isArraySchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + struct: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isObjectSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + struct: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), }); } else if (Guard.isPrimitiveSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", name: convertContext.escapeDeclarationText(declarationName), - schemaType: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), + struct: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), }); } }; diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index 255d4f9d..99400bf7 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -49,7 +49,7 @@ export const generateNamespace = ( store.addAbstractDataStruct(`${basePath}/${name}`, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: { + struct: { kind: "alias", name: convertContext.escapeDeclarationText(name), schema: { @@ -77,7 +77,7 @@ export const generateNamespace = ( return store.addAbstractDataStruct(`${basePath}/${name}`, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: { + struct: { kind: "alias", name: convertContext.escapeDeclarationText(name), comment: reference.data.description, @@ -101,49 +101,49 @@ export const generateNamespace = ( return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), + struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), }); } if (Guard.isOneOfSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), + struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), }); } if (Guard.isAnyOfSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), + struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), }); } if (Guard.isArraySchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), + struct: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isObjectSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + struct: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isObjectSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name: convertContext.escapeDeclarationText(name), - schemaType: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + struct: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), }); } if (Guard.isPrimitiveSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name, - schemaType: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), + struct: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), }); } throw new UnSupportError("schema.type = Array[] not supported. " + JSON.stringify(schema)); diff --git a/src/internal/OpenApiTools/toAbstractDataStructure.ts b/src/internal/OpenApiTools/toAbstractDataStructure.ts index 0ec54f69..6e2e38a1 100644 --- a/src/internal/OpenApiTools/toAbstractDataStructure.ts +++ b/src/internal/OpenApiTools/toAbstractDataStructure.ts @@ -29,7 +29,7 @@ export type Convert = ( setReference: Context, convertContext: ConverterContext.Types, option?: Option, -) => Intermediate.Type; +) => Intermediate.Struct; export interface Option { parent?: any; @@ -44,18 +44,18 @@ export const generateMultiTypeNode = ( convert: Convert, convertContext: ConverterContext.Types, multiType: "oneOf" | "allOf" | "anyOf", -): Intermediate.Type => { +): Intermediate.Struct => { const value = schemas.map(schema => convert(entryPoint, currentPoint, factory, schema, setReference, convertContext)); if (multiType === "oneOf") { return { kind: "union", - schemaTypes: value, + structs: value, }; } if (multiType === "allOf") { return { kind: "intersection", - schemaTypes: value, + structs: value, }; } // TODO Feature Development: Calculate intersection types @@ -64,11 +64,11 @@ export const generateMultiTypeNode = ( }; }; -const nullable = (factory: Factory.Type, schemaType: Intermediate.Type, nullable: boolean): Intermediate.Type => { +const nullable = (factory: Factory.Type, schemaType: Intermediate.Struct, nullable: boolean): Intermediate.Struct => { if (nullable) { return { kind: "union", - schemaTypes: [ + structs: [ schemaType, { kind: "null", @@ -87,7 +87,7 @@ export const convert: Convert = ( context: Context, converterContext: ConverterContext.Types, option?: Option, -): Intermediate.Type => { +): Intermediate.Struct => { if (typeof schema === "boolean") { // https://swagger.io/docs/specification/data-models/dictionaries/#free-form return { @@ -166,7 +166,7 @@ export const convert: Convert = ( case "integer": case "number": { const items = schema.enum; - let typeNode: Intermediate.Type; + let typeNode: Intermediate.Struct; if (items && Guard.isNumberArray(items)) { typeNode = { kind: "number", @@ -181,7 +181,7 @@ export const convert: Convert = ( } case "string": { const items = schema.enum; - let typeNode: Intermediate.Type; + let typeNode: Intermediate.Struct; if (items && Guard.isStringArray(items)) { typeNode = { kind: "string", @@ -198,9 +198,9 @@ export const convert: Convert = ( if (Array.isArray(schema.items) || typeof schema.items === "boolean") { throw new UnSupportError(`schema.items = ${JSON.stringify(schema.items)}`); } - const typeNode: Intermediate.Type = { + const typeNode: Intermediate.Struct = { kind: "array", - schemaType: schema.items + struct: schema.items ? convert(entryPoint, currentPoint, factory, schema.items, context, converterContext, { parent: schema }) : { kind: "undefined", @@ -221,7 +221,7 @@ export const convert: Convert = ( return { kind: "PropertySignature", name: converterContext.escapePropertySignatureName(name), - schemaType: convert(entryPoint, currentPoint, factory, jsonSchema, context, converterContext, { parent: schema.properties }), + struct: convert(entryPoint, currentPoint, factory, jsonSchema, context, converterContext, { parent: schema.properties }), optional: !required.includes(name), comment: typeof jsonSchema !== "boolean" ? jsonSchema.description : undefined, }; @@ -230,7 +230,7 @@ export const convert: Convert = ( const additionalProperties: Intermediate.IndexSignatureStruct = { kind: "IndexSignature", name: "key", - schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, context, converterContext, { + struct: convert(entryPoint, currentPoint, factory, schema.additionalProperties, context, converterContext, { parent: schema.properties, }), }; @@ -239,7 +239,7 @@ export const convert: Convert = ( properties: [...value, additionalProperties], }; } - const typeNode: Intermediate.Type = { + const typeNode: Intermediate.Struct = { kind: "object", properties: value, }; @@ -271,7 +271,7 @@ export const convertAdditionalProperties = ( const additionalProperties: Intermediate.IndexSignatureStruct = { kind: "IndexSignature", name: "key", - schemaType: convert(entryPoint, currentPoint, factory, schema.additionalProperties, setReference, convertContext, { + struct: convert(entryPoint, currentPoint, factory, schema.additionalProperties, setReference, convertContext, { parent: schema.properties, }), }; From 6d13b019adb35c3c84112d30b3ca3c88bf109bc0 Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 17:04:25 +0900 Subject: [PATCH 10/39] chore: update --- src/internal/OpenApiTools/ConverterContext.ts | 36 +----- .../OpenApiTools/components2/Schema.ts | 116 +++++------------ .../OpenApiTools/components2/Schemas.ts | 95 ++++++-------- .../OpenApiTools/toAbstractDataStructure.ts | 122 ++++++------------ src/internal/OpenApiTools/types/context.ts | 26 ++++ src/internal/OpenApiTools/types/tmp.ts | 32 +++++ 6 files changed, 177 insertions(+), 250 deletions(-) create mode 100644 src/internal/OpenApiTools/types/context.ts create mode 100644 src/internal/OpenApiTools/types/tmp.ts diff --git a/src/internal/OpenApiTools/ConverterContext.ts b/src/internal/OpenApiTools/ConverterContext.ts index 819b7f4c..193ffc4d 100644 --- a/src/internal/OpenApiTools/ConverterContext.ts +++ b/src/internal/OpenApiTools/ConverterContext.ts @@ -1,42 +1,10 @@ import * as Utils from "../../utils"; -/** - * ユーザーが利用できる各種変換オプション - */ -// export interface Options { - -// } - -export interface Types { - /** - * operationIdに対するescape - */ - escapeOperationIdText: (operationId: string) => string; - /** - * interface/namespace/typeAliasのnameをescapeする - * import/exportなどの予約語も裁く - */ - escapeDeclarationText: (text: string) => string; - /** - * 非破壊: PropertySignatureのname用のescape - */ - escapePropertySignatureName: (text: string) => string; - /** - * 破壊: TypeReferenceのname用のescape - */ - escapeTypeReferenceNodeName: (text: string) => string; - generateResponseName: (operationId: string, statusCode: string) => string; - generateArgumentParamsTypeDeclaration: (operationId: string) => string; - generateRequestContentTypeName: (operationId: string) => string; - generateResponseContentTypeName: (operationId: string) => string; - generateParameterName: (operationId: string) => string; - generateRequestBodyName: (operationId: string) => string; - generateFunctionName: (operationId: string) => string; -} +import type { ConverterContext } from "./types/context"; /** * ユーザーが利用できる各種変換オプション */ -export const create = (): Types => { +export const create = (): ConverterContext => { const convertReservedWord = (word: string): string => { if (["import", "export"].includes(word)) { return word + "_"; diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index da938219..47ce4ae5 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -1,21 +1,14 @@ import type { OpenApi } from "../../../types"; -import { FeatureDevelopmentError } from "../../Exception"; import * as ADS from "../../AbstractDataStructure"; -import { Factory } from "../../TsGenerator"; -import * as ConvertContext from "../ConverterContext"; +import { FeatureDevelopmentError } from "../../Exception"; import * as Guard from "../Guard"; import * as ToAbstractDataStructure from "../toAbstractDataStructure"; import type { ArraySchema, ObjectSchema, PrimitiveSchema } from "../types"; +import type { Payload } from "../types/tmp"; import type * as Walker from "../Walker"; -export const generatePropertySignatures = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - schema: ObjectSchema, - context: ToAbstractDataStructure.Context, - convertContext: ConvertContext.Types, -): ADS.PropertySignatureStruct[] => { +export const generatePropertySignatures = (payload: Payload, schema: ObjectSchema): ADS.PropertySignatureStruct[] => { + const { converterContext } = payload; if (!schema.properties) { return []; } @@ -24,7 +17,7 @@ export const generatePropertySignatures = ( if (!property) { return { kind: "PropertySignature", - name: convertContext.escapePropertySignatureName(propertyName), + name: converterContext.escapePropertySignatureName(propertyName), optional: !required.includes(propertyName), comment: schema.description, struct: { @@ -34,30 +27,23 @@ export const generatePropertySignatures = ( } return { kind: "PropertySignature", - name: convertContext.escapePropertySignatureName(propertyName), + name: converterContext.escapePropertySignatureName(propertyName), optional: !required.includes(propertyName), comment: typeof property !== "boolean" ? property.description : undefined, - struct: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, property, context, convertContext, { parent: schema }), + struct: ToAbstractDataStructure.convert(payload, property, { parent: schema }), }; }); }; -export const generateInterface = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - schema: ObjectSchema, - context: ToAbstractDataStructure.Context, - convertContext: ConvertContext.Types, -): ADS.InterfaceDeclarationStruct => { +export const generateInterface = (payload: Payload, name: string, schema: ObjectSchema): ADS.InterfaceDeclarationStruct => { + const { converterContext } = payload; if (schema.type !== "object") { throw new FeatureDevelopmentError("Please use generateTypeAlias"); } let members: (ADS.IndexSignatureStruct | ADS.PropertySignatureStruct)[] = []; - const propertySignatures = generatePropertySignatures(entryPoint, currentPoint, factory, schema, context, convertContext); + const propertySignatures = generatePropertySignatures(payload, schema); if (Guard.isObjectSchemaWithAdditionalProperties(schema)) { - const additionalProperties = ToAbstractDataStructure.convertAdditionalProperties(entryPoint, currentPoint, factory, schema, context, convertContext); + const additionalProperties = ToAbstractDataStructure.convertAdditionalProperties(payload, schema); if (schema.additionalProperties === true) { members = members.concat(additionalProperties); } else { @@ -68,37 +54,22 @@ export const generateInterface = ( } return { kind: "interface", - name: convertContext.escapeDeclarationText(name), + name: converterContext.escapeDeclarationText(name), // comment: ExternalDocumentation.addComment(schema.description, schema.externalDocs), members, }; }; -export const generateArrayTypeAlias = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - schema: ArraySchema, - context: ToAbstractDataStructure.Context, - convertContext: ConvertContext.Types, -): ADS.AliasStruct => { +export const generateArrayTypeAlias = (payload: Payload, name: string, schema: ArraySchema): ADS.AliasStruct => { return { kind: "alias", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), comment: schema.description, - schema: ToAbstractDataStructure.convert(entryPoint, currentPoint, factory, schema, context, convertContext), + schema: ToAbstractDataStructure.convert(payload, schema), }; }; -export const generateTypeAlias = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - name: string, - schema: PrimitiveSchema, - convertContext: ConvertContext.Types, -): ADS.AliasStruct => { +export const generateTypeAlias = (payload: Payload, name: string, schema: PrimitiveSchema): ADS.AliasStruct => { let type: ADS.Struct; if (schema.enum) { if (Guard.isNumberArray(schema.enum) && (schema.type === "number" || schema.type === "integer")) { @@ -123,49 +94,32 @@ export const generateTypeAlias = ( } return { kind: "alias", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), comment: schema.description, schema: type, - } + }; }; export const generateMultiTypeAlias = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, + payload: Payload, name: string, schemas: OpenApi.Schema[], - context: ToAbstractDataStructure.Context, multiType: "oneOf" | "allOf" | "anyOf", - convertContext: ConvertContext.Types, ): ADS.AliasStruct => { - const schema = ToAbstractDataStructure.generateMultiTypeNode( - entryPoint, - currentPoint, - factory, - schemas, - context, - ToAbstractDataStructure.convert, - convertContext, - multiType, - ); + const schema = ToAbstractDataStructure.generateMultiTypeNode(payload, schemas, ToAbstractDataStructure.convert, multiType); return { kind: "alias", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), schema: schema, - } + }; }; export const addSchema = ( - entryPoint: string, - currentPoint: string, + payload: Payload, store: Walker.Store, - factory: Factory.Type, targetPoint: string, declarationName: string, schema: OpenApi.Schema | undefined, - context: ToAbstractDataStructure.Context, - convertContext: ConvertContext.Types, ): void => { if (!schema) { return; @@ -173,38 +127,38 @@ export const addSchema = ( if (Guard.isAllOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.allOf, context, "allOf", convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateMultiTypeAlias(payload, declarationName, schema.allOf, "allOf"), }); } else if (Guard.isOneOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.oneOf, context, "oneOf", convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateMultiTypeAlias(payload, declarationName, schema.oneOf, "oneOf"), }); } else if (Guard.isAnyOfSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateMultiTypeAlias(entryPoint, currentPoint, factory, declarationName, schema.anyOf, context, "allOf", convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateMultiTypeAlias(payload, declarationName, schema.anyOf, "allOf"), }); } else if (Guard.isArraySchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateArrayTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateArrayTypeAlias(payload, declarationName, schema), }); } else if (Guard.isObjectSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateInterface(entryPoint, currentPoint, factory, declarationName, schema, context, convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateInterface(payload, declarationName, schema), }); } else if (Guard.isPrimitiveSchema(schema)) { store.addAbstractDataStruct(targetPoint, { kind: "typedef", - name: convertContext.escapeDeclarationText(declarationName), - struct: generateTypeAlias(entryPoint, currentPoint, factory, declarationName, schema, convertContext), + name: payload.converterContext.escapeDeclarationText(declarationName), + struct: generateTypeAlias(payload, declarationName, schema), }); } }; diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index 99400bf7..dde79695 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -1,40 +1,33 @@ import type { OpenApi } from "../../../types"; +import * as ADS from "../../AbstractDataStructure"; import { UnSupportError } from "../../Exception"; -import { Factory } from "../../TsGenerator"; import * as ConverterContext from "../ConverterContext"; import * as Guard from "../Guard"; import * as InferredType from "../InferredType"; import * as Name from "../Name"; -import * as ToTypeNode from "../toTypeNode"; +import * as ToAbstractDataStructure from "../toAbstractDataStructure"; +import type { Payload } from "../types/tmp"; import type * as Walker from "../Walker"; import * as Reference from "./Reference"; import * as Schema from "./Schema"; -const createNullableTypeNode = (factory: Factory.Type, schema: OpenApi.Schema) => { +const createNullableTypeNode = (schema: OpenApi.Schema): ADS.UnionStruct | undefined => { if (!schema.type && typeof schema.nullable === "boolean") { - const typeNode = factory.TypeNode.create({ - type: "any", - }); - return factory.UnionTypeNode.create({ - typeNodes: [ - typeNode, - factory.TypeNode.create({ - type: "null", - }), + return { + kind: "union", + structs: [ + { + kind: "any", + }, + { + kind: "null", + }, ], - }); + }; } }; -export const generateNamespace = ( - entryPoint: string, - currentPoint: string, - store: Walker.Store, - factory: Factory.Type, - schemas: Record, - context: ToTypeNode.Context, - convertContext: ConverterContext.Types, -): void => { +export const generateNamespace = (payload: Payload, store: Walker.Store, schemas: Record): void => { const basePath = "components/schemas"; store.addComponent("schemas", { kind: "namespace", @@ -43,54 +36,46 @@ export const generateNamespace = ( Object.entries(schemas).forEach(([name, targetSchema]) => { if (Guard.isReference(targetSchema)) { const schema = targetSchema; - const reference = Reference.generate(entryPoint, currentPoint, schema); + const reference = Reference.generate(payload.entryPoint, payload.currentPoint, schema); if (reference.type === "local") { - const { maybeResolvedName } = context.resolveReferencePath(currentPoint, reference.path); + const { maybeResolvedName } = payload.context.resolveReferencePath(payload.currentPoint, reference.path); store.addAbstractDataStruct(`${basePath}/${name}`, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), struct: { kind: "alias", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), schema: { kind: "reference", - name: convertContext.escapeDeclarationText(maybeResolvedName), + name: payload.converterContext.escapeDeclarationText(maybeResolvedName), }, }, }); return; } - Schema.addSchema( - entryPoint, - reference.referencePoint, - store, - factory, - reference.path, - reference.name, - reference.data, - context, - convertContext, - ); + Schema.addSchema({ ...payload, currentPoint: reference.referencePoint }, store, reference.path, reference.name, reference.data); if (store.hasStatement(`${basePath}/${name}`, ["interface", "typeAlias"])) { return; } return store.addAbstractDataStruct(`${basePath}/${name}`, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), struct: { kind: "alias", - name: convertContext.escapeDeclarationText(name), + name: payload.converterContext.escapeDeclarationText(name), comment: reference.data.description, schema: { kind: "reference", - name: convertContext.escapeDeclarationText(context.resolveReferencePath(currentPoint, reference.path).name), + name: payload.converterContext.escapeDeclarationText( + payload.context.resolveReferencePath(payload.currentPoint, reference.path).name, + ), }, }, }); } const schema = InferredType.getInferredType(targetSchema); if (!schema) { - const typeNode = createNullableTypeNode(factory, targetSchema); + const typeNode = createNullableTypeNode(targetSchema); if (!typeNode) { throw new UnSupportError("schema.type not specified \n" + JSON.stringify(targetSchema)); } @@ -100,50 +85,50 @@ export const generateNamespace = ( if (Guard.isAllOfSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.allOf, context, "allOf", convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateMultiTypeAlias(payload, name, schema.allOf, "allOf"), }); } if (Guard.isOneOfSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.oneOf, context, "oneOf", convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateMultiTypeAlias(payload, name, schema.oneOf, "oneOf"), }); } if (Guard.isAnyOfSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateMultiTypeAlias(entryPoint, currentPoint, factory, name, schema.anyOf, context, "anyOf", convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateMultiTypeAlias(payload, name, schema.anyOf, "anyOf"), }); } if (Guard.isArraySchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateArrayTypeAlias(entryPoint, currentPoint, factory, name, schema, context, convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateArrayTypeAlias(payload, name, schema), }); } if (Guard.isObjectSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateInterface(payload, name, schema), }); } if (Guard.isObjectSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", - name: convertContext.escapeDeclarationText(name), - struct: Schema.generateInterface(entryPoint, currentPoint, factory, name, schema, context, convertContext), + name: payload.converterContext.escapeDeclarationText(name), + struct: Schema.generateInterface(payload, name, schema), }); } if (Guard.isPrimitiveSchema(schema)) { return store.addAbstractDataStruct(path, { kind: "typedef", name, - struct: Schema.generateTypeAlias(entryPoint, currentPoint, factory, name, schema, convertContext), + struct: Schema.generateTypeAlias(payload, name, schema), }); } throw new UnSupportError("schema.type = Array[] not supported. " + JSON.stringify(schema)); diff --git a/src/internal/OpenApiTools/toAbstractDataStructure.ts b/src/internal/OpenApiTools/toAbstractDataStructure.ts index 6e2e38a1..86ae0fc6 100644 --- a/src/internal/OpenApiTools/toAbstractDataStructure.ts +++ b/src/internal/OpenApiTools/toAbstractDataStructure.ts @@ -1,51 +1,22 @@ import type { OpenApi } from "../../types"; -import { UnsetTypeError } from "../Exception"; -import { UnSupportError } from "../Exception"; -import type * as Intermediate from "../AbstractDataStructure"; +import type * as ADS from "../AbstractDataStructure"; +import { UnSupportError, UnsetTypeError } from "../Exception"; import * as Logger from "../Logger"; -import { Factory } from "../TsGenerator"; -import * as Reference from "./components/Reference"; -import * as ConverterContext from "./ConverterContext"; +import * as Reference from "./components2/Reference"; + import * as Guard from "./Guard"; import * as InferredType from "./InferredType"; import { ObjectSchemaWithAdditionalProperties } from "./types"; +import { Payload, Convert, Option } from "./types/tmp"; -export interface ResolveReferencePath { - name: string; - maybeResolvedName: string; - unresolvedPaths: string[]; -} - -export interface Context { - setReferenceHandler: (currentPoint: string, reference: Reference.Type) => void; - resolveReferencePath: (currentPoint: string, referencePath: string) => ResolveReferencePath; -} - -export type Convert = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, - setReference: Context, - convertContext: ConverterContext.Types, - option?: Option, -) => Intermediate.Struct; - -export interface Option { - parent?: any; -} export const generateMultiTypeNode = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, + payload: Payload, schemas: OpenApi.JSONSchema[], - setReference: Context, convert: Convert, - convertContext: ConverterContext.Types, multiType: "oneOf" | "allOf" | "anyOf", -): Intermediate.Struct => { - const value = schemas.map(schema => convert(entryPoint, currentPoint, factory, schema, setReference, convertContext)); +): ADS.Struct => { + const value = schemas.map(schema => convert(payload, schema)); if (multiType === "oneOf") { return { kind: "union", @@ -64,7 +35,7 @@ export const generateMultiTypeNode = ( }; }; -const nullable = (factory: Factory.Type, schemaType: Intermediate.Struct, nullable: boolean): Intermediate.Struct => { +const nullable = (schemaType: ADS.Struct, nullable: boolean): ADS.Struct => { if (nullable) { return { kind: "union", @@ -80,14 +51,11 @@ const nullable = (factory: Factory.Type, schemaType: Intermediate.Struct, nullab }; export const convert: Convert = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, + payload: Payload, schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, - context: Context, - converterContext: ConverterContext.Types, option?: Option, -): Intermediate.Struct => { +): ADS.Struct => { + const { context, currentPoint, converterContext } = payload; if (typeof schema === "boolean") { // https://swagger.io/docs/specification/data-models/dictionaries/#free-form return { @@ -96,7 +64,7 @@ export const convert: Convert = ( }; } if (Guard.isReference(schema)) { - const reference = Reference.generate(entryPoint, currentPoint, schema); + const reference = Reference.generate(payload, schema); if (reference.type === "local") { // Type Aliasを作成 (or すでにある場合は作成しない) context.setReferenceHandler(currentPoint, reference); @@ -117,17 +85,17 @@ export const convert: Convert = ( }; } // サポートしていないディレクトリに存在する場合、直接Interface、もしくはTypeAliasを作成 - return convert(entryPoint, reference.referencePoint, factory, reference.data, context, converterContext, { parent: schema }); + return convert({ ...payload, currentPoint: reference.referencePoint }, reference.data, { parent: schema }); } if (Guard.isOneOfSchema(schema)) { - return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.oneOf, context, convert, converterContext, "oneOf"); + return generateMultiTypeNode(payload, schema.oneOf, convert, "oneOf"); } if (Guard.isAllOfSchema(schema)) { - return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.allOf, context, convert, converterContext, "allOf"); + return generateMultiTypeNode(payload, schema.allOf, convert, "allOf"); } if (Guard.isAnyOfSchema(schema)) { - return generateMultiTypeNode(entryPoint, currentPoint, factory, schema.anyOf, context, convert, converterContext, "anyOf"); + return generateMultiTypeNode(payload, schema.anyOf, convert, "anyOf"); } if (Guard.isHasNoMembersObject(schema)) { @@ -141,22 +109,22 @@ export const convert: Convert = ( if (!schema.type) { const inferredSchema = InferredType.getInferredType(schema); if (inferredSchema) { - return convert(entryPoint, currentPoint, factory, inferredSchema, context, converterContext, { parent: schema }); + return convert(payload, inferredSchema, { parent: schema }); } // typeを指定せずに、nullableのみを指定している場合に type object変換する if (typeof schema.nullable === "boolean") { - return nullable(factory, { kind: "any" }, schema.nullable); + return nullable({ kind: "any" }, schema.nullable); } if (option && option.parent) { Logger.info("Parent Schema:"); Logger.info(JSON.stringify(option.parent)); } - Logger.showFilePosition(entryPoint, currentPoint); + // Logger.showFilePosition(entryPoint, currentPoint); throw new UnsetTypeError("Please set 'type' or '$ref' property \n" + JSON.stringify(schema)); } switch (schema.type) { case "boolean": { - return nullable(factory, { kind: "boolean" }, !!schema.nullable); + return nullable({ kind: "boolean" }, !!schema.nullable); } case "null": { return { @@ -166,7 +134,7 @@ export const convert: Convert = ( case "integer": case "number": { const items = schema.enum; - let typeNode: Intermediate.Struct; + let typeNode: ADS.Struct; if (items && Guard.isNumberArray(items)) { typeNode = { kind: "number", @@ -177,11 +145,11 @@ export const convert: Convert = ( kind: "number", }; } - return nullable(factory, typeNode, !!schema.nullable); + return nullable(typeNode, !!schema.nullable); } case "string": { const items = schema.enum; - let typeNode: Intermediate.Struct; + let typeNode: ADS.Struct; if (items && Guard.isStringArray(items)) { typeNode = { kind: "string", @@ -192,21 +160,21 @@ export const convert: Convert = ( kind: "string", }; } - return nullable(factory, typeNode, !!schema.nullable); + return nullable(typeNode, !!schema.nullable); } case "array": { if (Array.isArray(schema.items) || typeof schema.items === "boolean") { throw new UnSupportError(`schema.items = ${JSON.stringify(schema.items)}`); } - const typeNode: Intermediate.Struct = { + const typeNode: ADS.Struct = { kind: "array", struct: schema.items - ? convert(entryPoint, currentPoint, factory, schema.items, context, converterContext, { parent: schema }) + ? convert(payload, schema.items, { parent: schema }) : { kind: "undefined", }, }; - return nullable(factory, typeNode, !!schema.nullable); + return nullable(typeNode, !!schema.nullable); } case "object": { const required: string[] = schema.required || []; @@ -217,20 +185,20 @@ export const convert: Convert = ( properties: [], }; } - const value: Intermediate.PropertySignatureStruct[] = Object.entries(schema.properties || {}).map(([name, jsonSchema]) => { + const value: ADS.PropertySignatureStruct[] = Object.entries(schema.properties || {}).map(([name, jsonSchema]) => { return { kind: "PropertySignature", name: converterContext.escapePropertySignatureName(name), - struct: convert(entryPoint, currentPoint, factory, jsonSchema, context, converterContext, { parent: schema.properties }), + struct: convert(payload, jsonSchema, { parent: schema.properties }), optional: !required.includes(name), comment: typeof jsonSchema !== "boolean" ? jsonSchema.description : undefined, }; }); if (schema.additionalProperties) { - const additionalProperties: Intermediate.IndexSignatureStruct = { + const additionalProperties: ADS.IndexSignatureStruct = { kind: "IndexSignature", name: "key", - struct: convert(entryPoint, currentPoint, factory, schema.additionalProperties, context, converterContext, { + struct: convert(payload, schema.additionalProperties, { parent: schema.properties, }), }; @@ -239,11 +207,11 @@ export const convert: Convert = ( properties: [...value, additionalProperties], }; } - const typeNode: Intermediate.Struct = { + const typeNode: ADS.Struct = { kind: "object", properties: value, }; - return nullable(factory, typeNode, !!schema.nullable); + return nullable(typeNode, !!schema.nullable); } default: return { @@ -253,25 +221,19 @@ export const convert: Convert = ( } }; -export const convertAdditionalProperties = ( - entryPoint: string, - currentPoint: string, - factory: Factory.Type, - schema: ObjectSchemaWithAdditionalProperties, - setReference: Context, - convertContext: ConverterContext.Types, -): Intermediate.IndexSignatureStruct => { +export const convertAdditionalProperties = (payload: Payload, schema: ObjectSchemaWithAdditionalProperties): ADS.IndexSignatureStruct => { // // https://swagger.io/docs/specification/data-models/dictionaries/#free-form if (schema.additionalProperties === true) { - factory.TypeNode.create({ - type: schema.type, - value: [], - }); + // TODO バグってそう + // factory.TypeNode.create({ + // type: schema.type, + // value: [], + // }); } - const additionalProperties: Intermediate.IndexSignatureStruct = { + const additionalProperties: ADS.IndexSignatureStruct = { kind: "IndexSignature", name: "key", - struct: convert(entryPoint, currentPoint, factory, schema.additionalProperties, setReference, convertContext, { + struct: convert(payload, schema.additionalProperties, { parent: schema.properties, }), }; diff --git a/src/internal/OpenApiTools/types/context.ts b/src/internal/OpenApiTools/types/context.ts new file mode 100644 index 00000000..39677a37 --- /dev/null +++ b/src/internal/OpenApiTools/types/context.ts @@ -0,0 +1,26 @@ +export interface ConverterContext { + /** + * operationIdに対するescape + */ + escapeOperationIdText: (operationId: string) => string; + /** + * interface/namespace/typeAliasのnameをescapeする + * import/exportなどの予約語も裁く + */ + escapeDeclarationText: (text: string) => string; + /** + * 非破壊: PropertySignatureのname用のescape + */ + escapePropertySignatureName: (text: string) => string; + /** + * 破壊: TypeReferenceのname用のescape + */ + escapeTypeReferenceNodeName: (text: string) => string; + generateResponseName: (operationId: string, statusCode: string) => string; + generateArgumentParamsTypeDeclaration: (operationId: string) => string; + generateRequestContentTypeName: (operationId: string) => string; + generateResponseContentTypeName: (operationId: string) => string; + generateParameterName: (operationId: string) => string; + generateRequestBodyName: (operationId: string) => string; + generateFunctionName: (operationId: string) => string; +} diff --git a/src/internal/OpenApiTools/types/tmp.ts b/src/internal/OpenApiTools/types/tmp.ts new file mode 100644 index 00000000..0bafbea7 --- /dev/null +++ b/src/internal/OpenApiTools/types/tmp.ts @@ -0,0 +1,32 @@ +import type { OpenApi } from "../../../types"; +import type * as ADS from "../../AbstractDataStructure"; +import * as Reference from "../components2/Reference"; +import { ConverterContext } from "./context"; + +export interface Option { + parent?: any; +} + +export interface ResolveReferencePath { + name: string; + maybeResolvedName: string; + unresolvedPaths: string[]; +} + +export type Convert = ( + payload: Payload, + schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, + option?: Option, +) => ADS.Struct; + +export interface Context { + setReferenceHandler: (currentPoint: string, reference: Reference.Type) => void; + resolveReferencePath: (currentPoint: string, referencePath: string) => ResolveReferencePath; +} + +export interface Payload { + entryPoint: string; + currentPoint: string; + context: Context; + converterContext: ConverterContext; +} From 581744bc473d786805a1a7323bda02d7453f4ded Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 20:05:40 +0900 Subject: [PATCH 11/39] chore: update --- src/internal/OpenApiTools/Parser.ts | 32 ++-- src/internal/OpenApiTools/StructContext.ts | 153 ++++++++++++++++++ src/internal/OpenApiTools/TypeNodeContext.ts | 4 +- .../OpenApiTools/components2/Schemas.ts | 2 - .../OpenApiTools/toAbstractDataStructure.ts | 2 +- src/internal/OpenApiTools/toTypeNode.ts | 10 +- 6 files changed, 183 insertions(+), 20 deletions(-) create mode 100644 src/internal/OpenApiTools/StructContext.ts diff --git a/src/internal/OpenApiTools/Parser.ts b/src/internal/OpenApiTools/Parser.ts index dbbd038d..1f4851d9 100644 --- a/src/internal/OpenApiTools/Parser.ts +++ b/src/internal/OpenApiTools/Parser.ts @@ -7,27 +7,29 @@ import * as Parameters from "./components/Parameters"; import * as RequestBodies from "./components/RequestBodies"; import * as Responses from "./components/Responses"; import * as Schemas from "./components/Schemas"; +import * as Schemas2 from "./components2/Schemas"; import * as ConvertContext from "./ConverterContext"; import * as Extractor from "./Extractor"; import * as Paths from "./paths"; +import * as StructContext from "./StructContext"; import * as TypeNodeContext from "./TypeNodeContext"; import * as Walker from "./Walker"; export class Parser { private currentPoint: string; - private convertContext: ConvertContext.Types; + private converterContext = ConvertContext.create(); private store: Walker.Store; private factory: TypeScriptCodeGenerator.Factory.Type; constructor(private entryPoint: string, private rootSchema: OpenApi.Document, noReferenceOpenApiSchema: OpenApi.Document) { this.currentPoint = entryPoint; - this.convertContext = ConvertContext.create(); this.factory = TypeScriptCodeGenerator.Factory.create(); this.store = new Walker.Store(this.factory, noReferenceOpenApiSchema); this.initialize(); } private initialize(): void { - const toTypeNodeContext = TypeNodeContext.create(this.entryPoint, this.store, this.factory, this.convertContext); + const toTypeNodeContext = TypeNodeContext.create(this.entryPoint, this.store, this.factory, this.converterContext); + const structContext = StructContext.create(this.entryPoint, this.store, this.factory, this.converterContext); const rootSchema = this.rootSchema; if (rootSchema.components) { if (rootSchema.components.schemas) { @@ -38,7 +40,17 @@ export class Parser { this.factory, rootSchema.components.schemas, toTypeNodeContext, - this.convertContext, + this.converterContext, + ); + Schemas2.generateNamespace( + { + entryPoint: this.entryPoint, + currentPoint: this.currentPoint, + context: structContext, + converterContext: this.converterContext, + }, + this.store, + rootSchema.components.schemas, ); } if (rootSchema.components.headers) { @@ -49,7 +61,7 @@ export class Parser { this.factory, rootSchema.components.headers, toTypeNodeContext, - this.convertContext, + this.converterContext, ); } if (rootSchema.components.responses) { @@ -60,7 +72,7 @@ export class Parser { this.factory, rootSchema.components.responses, toTypeNodeContext, - this.convertContext, + this.converterContext, ); } if (rootSchema.components.parameters) { @@ -71,7 +83,7 @@ export class Parser { this.factory, rootSchema.components.parameters, toTypeNodeContext, - this.convertContext, + this.converterContext, ); } if (rootSchema.components.requestBodies) { @@ -82,7 +94,7 @@ export class Parser { this.factory, rootSchema.components.requestBodies, toTypeNodeContext, - this.convertContext, + this.converterContext, ); } // if (rootSchema.components.securitySchemes) { @@ -109,13 +121,13 @@ export class Parser { this.factory, rootSchema.paths, toTypeNodeContext, - this.convertContext, + this.converterContext, ); } } public getCodeGeneratorParamsArray(allowOperationIds?: string[]): CodeGenerator.Params[] { - return Extractor.generateCodeGeneratorParamsArray(this.store, this.convertContext, allowOperationIds); + return Extractor.generateCodeGeneratorParamsArray(this.store, this.converterContext, allowOperationIds); } public getOpenApiTypeDefinitionStatements(): ts.Statement[] { diff --git a/src/internal/OpenApiTools/StructContext.ts b/src/internal/OpenApiTools/StructContext.ts new file mode 100644 index 00000000..5e2437fe --- /dev/null +++ b/src/internal/OpenApiTools/StructContext.ts @@ -0,0 +1,153 @@ +import * as Path from "path"; + +import ts from "typescript"; + +import { DevelopmentError } from "../Exception"; +import * as TypeScriptCodeGenerator from "../TsGenerator"; +import type { ConverterContext } from "./types/context"; +import * as ToTypeNode from "./toTypeNode"; +import type * as Walker from "./Walker"; + +export interface ReferencePathSet { + pathArray: string[]; + base: string; +} + +const generatePath = (entryPoint: string, currentPoint: string, referencePath: string): ReferencePathSet => { + const ext = Path.extname(currentPoint); // .yml + const from = Path.relative(Path.dirname(entryPoint), currentPoint).replace(ext, ""); // components/schemas/A/B + const base = Path.dirname(from).replace(Path.sep, "/"); + const result = Path.posix.relative(base, referencePath); // remoteの場合? localの場合 referencePath.split("/") + const pathArray = result.split("/"); + return { + pathArray, + base, + }; +}; + +const calculateReferencePath = (store: Walker.Store, base: string, pathArray: string[]): ToTypeNode.ResolveReferencePath => { + let names: string[] = []; + let unresolvedPaths: string[] = []; + pathArray.reduce((previous, lastPath, index) => { + const current = Path.posix.join(previous, lastPath); + // ディレクトリが深い場合は相対パスが`..`を繰り返す可能性があり、 + // その場合はすでに登録されたnamesを削除する + if (lastPath === ".." && names.length > 0) { + names = names.slice(0, names.length - 1); + } + const isFinalPath = index === pathArray.length - 1; + if (isFinalPath) { + const statement = store.getStatement(current, "interface"); + const statement2 = store.getStatement(current, "typeAlias"); + const statement3 = store.getStatement(current, "namespace"); + if (statement) { + names.push(statement.name); + return current; + } else if (statement2) { + names.push(statement2.name); + return current; + } else if (statement3) { + names.push(statement3.name); + return current; + } else { + unresolvedPaths.push(lastPath); + } + } else { + const statement = store.getStatement(current, "namespace"); + if (statement) { + unresolvedPaths = unresolvedPaths.slice(0, unresolvedPaths.length - 1); + names.push(statement.name); + } else { + unresolvedPaths.push(lastPath); + } + } + return current; + }, base); + if (names.length === 0) { + throw new DevelopmentError("Local Reference Error \n" + JSON.stringify({ pathArray, names, base }, null, 2)); + } + return { + name: names.join("."), + maybeResolvedName: names.concat(unresolvedPaths).join("."), + unresolvedPaths, + }; +}; + +export const create = ( + entryPoint: string, + store: Walker.Store, + factory: TypeScriptCodeGenerator.Factory.Type, + converterContext: ConverterContext, +): ToTypeNode.Context => { + const resolveReferencePath: ToTypeNode.Context["resolveReferencePath"] = (currentPoint, referencePath) => { + const { pathArray, base } = generatePath(entryPoint, currentPoint, referencePath); + return calculateReferencePath(store, base, pathArray); + }; + const setReferenceHandler: ToTypeNode.Context["setReferenceHandler"] = (currentPoint, reference) => { + if (store.hasStatement(reference.path, ["interface", "typeAlias"])) { + return; + } + if (reference.type === "remote") { + const typeNode = ToTypeNode.convert( + entryPoint, + reference.referencePoint, + factory, + reference.data, + { + setReferenceHandler, + resolveReferencePath, + }, + converterContext, + ); + if (ts.isTypeLiteralNode(typeNode)) { + store.addStatement(reference.path, { + kind: "interface", + name: reference.name, + value: factory.InterfaceDeclaration.create({ + export: true, + name: reference.name, + members: typeNode.members, + }), + }); + } else { + const value = factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(reference.name), + type: ToTypeNode.convert( + entryPoint, + reference.referencePoint, + factory, + reference.data, + { + setReferenceHandler, + resolveReferencePath, + }, + converterContext, + ), + }); + store.addStatement(reference.path, { + name: reference.name, + kind: "typeAlias", + value, + }); + } + } else if (reference.type === "local") { + if (!store.isAfterDefined(reference.path)) { + const { maybeResolvedName } = resolveReferencePath(currentPoint, reference.path); + const value = factory.TypeAliasDeclaration.create({ + export: true, + name: converterContext.escapeDeclarationText(reference.name), + type: factory.TypeReferenceNode.create({ + name: converterContext.escapeTypeReferenceNodeName(maybeResolvedName), + }), + }); + store.addStatement(reference.path, { + name: reference.name, + kind: "typeAlias", + value, + }); + } + } + }; + return { setReferenceHandler: setReferenceHandler, resolveReferencePath: resolveReferencePath }; +}; diff --git a/src/internal/OpenApiTools/TypeNodeContext.ts b/src/internal/OpenApiTools/TypeNodeContext.ts index 0270a813..5e2437fe 100644 --- a/src/internal/OpenApiTools/TypeNodeContext.ts +++ b/src/internal/OpenApiTools/TypeNodeContext.ts @@ -4,7 +4,7 @@ import ts from "typescript"; import { DevelopmentError } from "../Exception"; import * as TypeScriptCodeGenerator from "../TsGenerator"; -import * as ConverterContext from "./ConverterContext"; +import type { ConverterContext } from "./types/context"; import * as ToTypeNode from "./toTypeNode"; import type * as Walker from "./Walker"; @@ -77,7 +77,7 @@ export const create = ( entryPoint: string, store: Walker.Store, factory: TypeScriptCodeGenerator.Factory.Type, - converterContext: ConverterContext.Types, + converterContext: ConverterContext, ): ToTypeNode.Context => { const resolveReferencePath: ToTypeNode.Context["resolveReferencePath"] = (currentPoint, referencePath) => { const { pathArray, base } = generatePath(entryPoint, currentPoint, referencePath); diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index dde79695..ac21db92 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -1,11 +1,9 @@ import type { OpenApi } from "../../../types"; import * as ADS from "../../AbstractDataStructure"; import { UnSupportError } from "../../Exception"; -import * as ConverterContext from "../ConverterContext"; import * as Guard from "../Guard"; import * as InferredType from "../InferredType"; import * as Name from "../Name"; -import * as ToAbstractDataStructure from "../toAbstractDataStructure"; import type { Payload } from "../types/tmp"; import type * as Walker from "../Walker"; import * as Reference from "./Reference"; diff --git a/src/internal/OpenApiTools/toAbstractDataStructure.ts b/src/internal/OpenApiTools/toAbstractDataStructure.ts index 86ae0fc6..5b81f153 100644 --- a/src/internal/OpenApiTools/toAbstractDataStructure.ts +++ b/src/internal/OpenApiTools/toAbstractDataStructure.ts @@ -64,7 +64,7 @@ export const convert: Convert = ( }; } if (Guard.isReference(schema)) { - const reference = Reference.generate(payload, schema); + const reference = Reference.generate(payload.entryPoint, payload.currentPoint, schema); if (reference.type === "local") { // Type Aliasを作成 (or すでにある場合は作成しない) context.setReferenceHandler(currentPoint, reference); diff --git a/src/internal/OpenApiTools/toTypeNode.ts b/src/internal/OpenApiTools/toTypeNode.ts index 76014ca1..a70ff4f3 100644 --- a/src/internal/OpenApiTools/toTypeNode.ts +++ b/src/internal/OpenApiTools/toTypeNode.ts @@ -6,7 +6,7 @@ import { UnSupportError } from "../Exception"; import * as Logger from "../Logger"; import { Factory } from "../TsGenerator"; import * as Reference from "./components/Reference"; -import * as ConverterContext from "./ConverterContext"; +import { ConverterContext } from "./types/context"; import * as Guard from "./Guard"; import * as InferredType from "./InferredType"; import { ObjectSchemaWithAdditionalProperties } from "./types"; @@ -28,7 +28,7 @@ export type Convert = ( factory: Factory.Type, schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, setReference: Context, - convertContext: ConverterContext.Types, + convertContext: ConverterContext, option?: Option, ) => ts.TypeNode; @@ -43,7 +43,7 @@ export const generateMultiTypeNode = ( schemas: OpenApi.JSONSchema[], setReference: Context, convert: Convert, - convertContext: ConverterContext.Types, + convertContext: ConverterContext, multiType: "oneOf" | "allOf" | "anyOf", ): ts.TypeNode => { const typeNodes = schemas.map(schema => convert(entryPoint, currentPoint, factory, schema, setReference, convertContext)); @@ -81,7 +81,7 @@ export const convert: Convert = ( factory: Factory.Type, schema: OpenApi.Schema | OpenApi.Reference | OpenApi.JSONSchemaDefinition, context: Context, - converterContext: ConverterContext.Types, + converterContext: ConverterContext, option?: Option, ): ts.TypeNode => { if (typeof schema === "boolean") { @@ -253,7 +253,7 @@ export const convertAdditionalProperties = ( factory: Factory.Type, schema: ObjectSchemaWithAdditionalProperties, setReference: Context, - convertContext: ConverterContext.Types, + convertContext: ConverterContext, ): ts.IndexSignatureDeclaration => { // // https://swagger.io/docs/specification/data-models/dictionaries/#free-form if (schema.additionalProperties === true) { From 34489decf0e16aaf688e309cfe691d588d35ec84 Mon Sep 17 00:00:00 2001 From: Himenon Date: Fri, 30 Apr 2021 23:16:42 +0900 Subject: [PATCH 12/39] chore: update --- scripts/testCodeGen.ts | 28 ++++++++-------- src/internal/OpenApiTools/ConverterContext.ts | 2 ++ src/internal/OpenApiTools/Parser.ts | 1 + src/internal/OpenApiTools/Walker/Store.ts | 14 ++++++-- .../Walker/structure/AbstractDataStructure.ts | 32 +++++++++++++++++++ 5 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts diff --git a/scripts/testCodeGen.ts b/scripts/testCodeGen.ts index bd6088d3..5f5e4a9d 100644 --- a/scripts/testCodeGen.ts +++ b/scripts/testCodeGen.ts @@ -99,24 +99,24 @@ const generateParameter = (inputFilename: string, outputFilename: string) => { const main = () => { generateTypedefCodeOnly("test/api.test.domain/index.yml", "test/code/typedef-only/api.test.domain.ts", true); - generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false); + // generateTypedefCodeOnly("test/infer.domain/index.yml", "test/code/typedef-only/infer.domain.ts", false); - generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false }); - generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true }); - generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true }); + // generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/api.test.domain.ts", true, { sync: false }); + // generateTemplateCodeOnly("test/api.test.domain/index.yml", "test/code/template-only/sync-api.test.domain.ts", true, { sync: true }); + // generateTemplateCodeOnly("test/infer.domain/index.yml", "test/code/template-only/infer.domain.ts", false, { sync: true }); - generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, { - sync: false, - }); - generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, { - sync: true, - }); - generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false }); + // generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/api.test.domain.ts", true, { + // sync: false, + // }); + // generateTypedefWithTemplateCode("test/api.test.domain/index.yml", "test/code/typedef-with-template/sync-api.test.domain.ts", true, { + // sync: true, + // }); + // generateTypedefWithTemplateCode("test/infer.domain/index.yml", "test/code/typedef-with-template/infer.domain.ts", false, { sync: false }); - generateSplitCode("test/api.test.domain/index.yml", "test/code/split"); + // generateSplitCode("test/api.test.domain/index.yml", "test/code/split"); - generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json"); - generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json"); + // generateParameter("test/api.test.domain/index.yml", "test/code/parameter/api.test.domain.json"); + // generateParameter("test/infer.domain/index.yml", "test/code/parameter/infer.domain.json"); }; main(); diff --git a/src/internal/OpenApiTools/ConverterContext.ts b/src/internal/OpenApiTools/ConverterContext.ts index 193ffc4d..a929dfa8 100644 --- a/src/internal/OpenApiTools/ConverterContext.ts +++ b/src/internal/OpenApiTools/ConverterContext.ts @@ -1,6 +1,8 @@ import * as Utils from "../../utils"; import type { ConverterContext } from "./types/context"; +export type Types = ConverterContext; + /** * ユーザーが利用できる各種変換オプション */ diff --git a/src/internal/OpenApiTools/Parser.ts b/src/internal/OpenApiTools/Parser.ts index 1f4851d9..2d1f058a 100644 --- a/src/internal/OpenApiTools/Parser.ts +++ b/src/internal/OpenApiTools/Parser.ts @@ -124,6 +124,7 @@ export class Parser { this.converterContext, ); } + this.store.debugAbstractDataStruct(); } public getCodeGeneratorParamsArray(allowOperationIds?: string[]): CodeGenerator.Params[] { diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index 5e38ea71..63155f2d 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -1,3 +1,4 @@ +import * as fs from "fs"; import * as Path from "path"; import { Tree } from "@himenon/path-oriented-data-structure"; @@ -5,22 +6,25 @@ import Dot from "dot-prop"; import ts from "typescript"; import type { OpenApi } from "../../../types"; +import * as ADS from "../../AbstractDataStructure"; import { UnSupportError } from "../../Exception"; import { Factory } from "../../TsGenerator"; import * as Def from "./Definition"; import * as Operation from "./Operation"; import * as State from "./State"; import * as Structure from "./structure"; -import * as ADS from "../../AbstractDataStructure" +import * as AdsStructure from "./structure/AbstractDataStructure"; class Store { private state: State.Type; private operator: Structure.OperatorType; + private adsOperator: AdsStructure.OperatorType; private getChildByPaths: Structure.GetChildByPaths; constructor(private factory: Factory.Type, rootDocument: OpenApi.Document) { this.state = State.createDefaultState(rootDocument); const { operator, getChildByPaths } = Structure.create(); this.operator = operator; + this.adsOperator = AdsStructure.create().operator; this.getChildByPaths = getChildByPaths; } @@ -72,7 +76,13 @@ class Store { return alreadyRegistered; } public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Struct): void { - + const targetPath = Path.posix.relative("components", path); + this.adsOperator.set(targetPath, new AdsStructure.TypeDefItem({ name: path, value: abstractDataStruct })); + } + public debugAbstractDataStruct() { + const data = this.adsOperator.getHierarchy(); + console.log("output"); + fs.writeFileSync("debug.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); } /** * @params path: "components/headers/hoge" diff --git a/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts b/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts new file mode 100644 index 00000000..ffd12a4a --- /dev/null +++ b/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts @@ -0,0 +1,32 @@ +import { Operator, Node as BaseNode } from "@himenon/path-oriented-data-structure"; +import * as ADS from "../../../AbstractDataStructure"; + +import * as DataStructure from "./DataStructure"; + +export type Kind = "typedef"; + +export interface Params { + name: string; + value: ADS.Struct; +} + +export class TypeDefItem extends BaseNode { + public value: ADS.Struct; + constructor(params: Params) { + super("typedef", params.name); + this.value = params.value; + } +} + + +export const create = () => { + const operator = new Operator("typedef"); + return { + operator, + getChildByPaths: DataStructure.createGetChildByPaths(operator), + }; +}; + +export type OperatorType = Operator<"typedef">; + +export type GetChildByPaths = DataStructure.GetChildByPaths; From e9de1f169b20cae5a3941e5d4f93125f9e8abd68 Mon Sep 17 00:00:00 2001 From: Himenon Date: Sun, 2 May 2021 13:29:19 +0900 Subject: [PATCH 13/39] chore: update --- package.json | 2 +- src/internal/OpenApiTools/Walker/Store.ts | 3 ++- src/internal/OpenApiTools/components2/Schemas.ts | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 197ffe78..f5174be2 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "node-fetch": "2.6.1" }, "dependencies": { - "@himenon/path-oriented-data-structure": "0.1.3", + "@himenon/path-oriented-data-structure": "0.2.1", "@types/json-schema": "7.0.7", "ajv": "8.0.5", "dot-prop": "6.0.1", diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index 63155f2d..e4d0a18b 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -82,7 +82,8 @@ class Store { public debugAbstractDataStruct() { const data = this.adsOperator.getHierarchy(); console.log("output"); - fs.writeFileSync("debug.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); + fs.writeFileSync("debug/hierarchy.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); + fs.writeFileSync("debug/paths.json", JSON.stringify(this.adsOperator.getNodePaths("typedef"), null, 2), { encoding: "utf-8" }); } /** * @params path: "components/headers/hoge" diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index ac21db92..eff4d8eb 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -27,10 +27,10 @@ const createNullableTypeNode = (schema: OpenApi.Schema): ADS.UnionStruct | undef export const generateNamespace = (payload: Payload, store: Walker.Store, schemas: Record): void => { const basePath = "components/schemas"; - store.addComponent("schemas", { - kind: "namespace", - name: Name.Components.Schemas, - }); + // store.addComponent("schemas", { + // kind: "namespace", + // name: Name.Components.Schemas, + // }); Object.entries(schemas).forEach(([name, targetSchema]) => { if (Guard.isReference(targetSchema)) { const schema = targetSchema; diff --git a/yarn.lock b/yarn.lock index 36a15d78..a1a177ea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -553,10 +553,10 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@himenon/path-oriented-data-structure@0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@himenon/path-oriented-data-structure/-/path-oriented-data-structure-0.1.3.tgz#a9298afd0097e2f5e466a2f86318c887e82570ed" - integrity sha512-1QaIv/cFdpg5GkpG6pQgrYgDouRO7dzz3djiSKLgd2+GjqMG0TsVvUaAxtIo7Y6Hqdk/pL7u7U+cYf80ww9tcw== +"@himenon/path-oriented-data-structure@0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@himenon/path-oriented-data-structure/-/path-oriented-data-structure-0.2.1.tgz#abf7df54dd1436942c7851881dd55e2a78d87747" + integrity sha512-TbrbcmJW/E60xA5GPZAf9pBDDUYGbOFL83D/ovDvGxt1s3UkUPbeTced6FbwanCtWM/ZpEHQuX32Z7a7Q25rRg== "@istanbuljs/load-nyc-config@^1.0.0": version "1.0.0" From 8154a093ac9ac68d0e55cf90f3cf2cbd59b55ae6 Mon Sep 17 00:00:00 2001 From: Himenon Date: Sun, 2 May 2021 15:44:23 +0900 Subject: [PATCH 14/39] chore: update --- src/code-templates/typedef/index.ts | 1 + src/internal/OpenApiTools/Parser.ts | 9 +++- src/internal/OpenApiTools/Walker/Store.ts | 15 ------ .../Walker/structure/AbstractDataStructure.ts | 32 ------------- .../OpenApiTools/Walker2/Definition.ts | 11 +++++ .../OpenApiTools/Walker2/Operation.ts | 46 +++++++++++++++++++ src/internal/OpenApiTools/Walker2/State.ts | 33 +++++++++++++ src/internal/OpenApiTools/Walker2/Store.ts | 38 +++++++++++++++ src/internal/OpenApiTools/Walker2/index.ts | 3 ++ .../structure/AbstractDataStructure.ts | 19 ++++++++ .../Walker2/structure/DataStructure.ts | 21 +++++++++ .../Walker2/structure/Directory.ts | 16 +++++++ .../OpenApiTools/Walker2/structure/index.ts | 42 +++++++++++++++++ .../OpenApiTools/components2/Schema.ts | 2 +- .../OpenApiTools/components2/Schemas.ts | 12 ++--- src/typedef/ParsedSchema.ts | 13 ++++++ 16 files changed, 258 insertions(+), 55 deletions(-) create mode 100644 src/code-templates/typedef/index.ts delete mode 100644 src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts create mode 100644 src/internal/OpenApiTools/Walker2/Definition.ts create mode 100644 src/internal/OpenApiTools/Walker2/Operation.ts create mode 100644 src/internal/OpenApiTools/Walker2/State.ts create mode 100644 src/internal/OpenApiTools/Walker2/Store.ts create mode 100644 src/internal/OpenApiTools/Walker2/index.ts create mode 100644 src/internal/OpenApiTools/Walker2/structure/AbstractDataStructure.ts create mode 100644 src/internal/OpenApiTools/Walker2/structure/DataStructure.ts create mode 100644 src/internal/OpenApiTools/Walker2/structure/Directory.ts create mode 100644 src/internal/OpenApiTools/Walker2/structure/index.ts create mode 100644 src/typedef/ParsedSchema.ts diff --git a/src/code-templates/typedef/index.ts b/src/code-templates/typedef/index.ts new file mode 100644 index 00000000..dcc24ebf --- /dev/null +++ b/src/code-templates/typedef/index.ts @@ -0,0 +1 @@ +import {} from "../" \ No newline at end of file diff --git a/src/internal/OpenApiTools/Parser.ts b/src/internal/OpenApiTools/Parser.ts index 2d1f058a..1d0e072b 100644 --- a/src/internal/OpenApiTools/Parser.ts +++ b/src/internal/OpenApiTools/Parser.ts @@ -14,16 +14,19 @@ import * as Paths from "./paths"; import * as StructContext from "./StructContext"; import * as TypeNodeContext from "./TypeNodeContext"; import * as Walker from "./Walker"; +import * as Walker2 from "./Walker2"; export class Parser { private currentPoint: string; private converterContext = ConvertContext.create(); private store: Walker.Store; + private store2: Walker2.Store; private factory: TypeScriptCodeGenerator.Factory.Type; constructor(private entryPoint: string, private rootSchema: OpenApi.Document, noReferenceOpenApiSchema: OpenApi.Document) { this.currentPoint = entryPoint; this.factory = TypeScriptCodeGenerator.Factory.create(); this.store = new Walker.Store(this.factory, noReferenceOpenApiSchema); + this.store2 = new Walker2.Store(noReferenceOpenApiSchema); this.initialize(); } @@ -49,7 +52,7 @@ export class Parser { context: structContext, converterContext: this.converterContext, }, - this.store, + this.store2, rootSchema.components.schemas, ); } @@ -131,6 +134,10 @@ export class Parser { return Extractor.generateCodeGeneratorParamsArray(this.store, this.converterContext, allowOperationIds); } + /** + * + * @returns 依存を排除する + */ public getOpenApiTypeDefinitionStatements(): ts.Statement[] { return this.store.getRootStatements(); } diff --git a/src/internal/OpenApiTools/Walker/Store.ts b/src/internal/OpenApiTools/Walker/Store.ts index e4d0a18b..4c141706 100644 --- a/src/internal/OpenApiTools/Walker/Store.ts +++ b/src/internal/OpenApiTools/Walker/Store.ts @@ -1,4 +1,3 @@ -import * as fs from "fs"; import * as Path from "path"; import { Tree } from "@himenon/path-oriented-data-structure"; @@ -6,25 +5,21 @@ import Dot from "dot-prop"; import ts from "typescript"; import type { OpenApi } from "../../../types"; -import * as ADS from "../../AbstractDataStructure"; import { UnSupportError } from "../../Exception"; import { Factory } from "../../TsGenerator"; import * as Def from "./Definition"; import * as Operation from "./Operation"; import * as State from "./State"; import * as Structure from "./structure"; -import * as AdsStructure from "./structure/AbstractDataStructure"; class Store { private state: State.Type; private operator: Structure.OperatorType; - private adsOperator: AdsStructure.OperatorType; private getChildByPaths: Structure.GetChildByPaths; constructor(private factory: Factory.Type, rootDocument: OpenApi.Document) { this.state = State.createDefaultState(rootDocument); const { operator, getChildByPaths } = Structure.create(); this.operator = operator; - this.adsOperator = AdsStructure.create().operator; this.getChildByPaths = getChildByPaths; } @@ -75,16 +70,6 @@ class Store { const alreadyRegistered = types.some(type => !!this.operator.getChildByPaths(path, type)); return alreadyRegistered; } - public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Struct): void { - const targetPath = Path.posix.relative("components", path); - this.adsOperator.set(targetPath, new AdsStructure.TypeDefItem({ name: path, value: abstractDataStruct })); - } - public debugAbstractDataStruct() { - const data = this.adsOperator.getHierarchy(); - console.log("output"); - fs.writeFileSync("debug/hierarchy.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); - fs.writeFileSync("debug/paths.json", JSON.stringify(this.adsOperator.getNodePaths("typedef"), null, 2), { encoding: "utf-8" }); - } /** * @params path: "components/headers/hoge" */ diff --git a/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts b/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts deleted file mode 100644 index ffd12a4a..00000000 --- a/src/internal/OpenApiTools/Walker/structure/AbstractDataStructure.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { Operator, Node as BaseNode } from "@himenon/path-oriented-data-structure"; -import * as ADS from "../../../AbstractDataStructure"; - -import * as DataStructure from "./DataStructure"; - -export type Kind = "typedef"; - -export interface Params { - name: string; - value: ADS.Struct; -} - -export class TypeDefItem extends BaseNode { - public value: ADS.Struct; - constructor(params: Params) { - super("typedef", params.name); - this.value = params.value; - } -} - - -export const create = () => { - const operator = new Operator("typedef"); - return { - operator, - getChildByPaths: DataStructure.createGetChildByPaths(operator), - }; -}; - -export type OperatorType = Operator<"typedef">; - -export type GetChildByPaths = DataStructure.GetChildByPaths; diff --git a/src/internal/OpenApiTools/Walker2/Definition.ts b/src/internal/OpenApiTools/Walker2/Definition.ts new file mode 100644 index 00000000..c5ce0bbd --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/Definition.ts @@ -0,0 +1,11 @@ +export type ComponentName = "schemas" | "headers" | "responses" | "parameters" | "requestBodies" | "securitySchemes" | "pathItems"; + +export const componentNames: ComponentName[] = [ + "schemas", + "headers", + "responses", + "parameters", + "requestBodies", + "securitySchemes", + "pathItems", +]; diff --git a/src/internal/OpenApiTools/Walker2/Operation.ts b/src/internal/OpenApiTools/Walker2/Operation.ts new file mode 100644 index 00000000..74eb864b --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/Operation.ts @@ -0,0 +1,46 @@ +import { EOL } from "os"; + +import type { CodeGenerator, OpenApi } from "../../../types"; + +const httpMethodList = ["get", "put", "post", "delete", "options", "head", "patch", "trace"] as const; + +export interface State { + [operationId: string]: CodeGenerator.OpenApiOperation; +} + +type UniqueParameterMap = Record; + +const uniqParameters = (rawParameters: OpenApi.Parameter[]): OpenApi.Parameter[] => { + const parameterMap = rawParameters.reduce((all, parameter) => { + return { ...all, [`${parameter.in}:${parameter.name}`]: parameter }; + }, {}); + return Object.values(parameterMap); +}; + +export const create = (rootSchema: OpenApi.Document): State => { + const paths = rootSchema.paths || {}; + const state: State = {}; + Object.entries(paths).forEach(([requestUri, pathItem]) => { + httpMethodList.forEach(httpMethod => { + const operation = pathItem[httpMethod]; + if (!operation) { + return; + } + if (!operation.operationId) { + return; + } + const parameters = [...(pathItem.parameters || []), ...(operation.parameters || [])] as OpenApi.Parameter[]; + + state[operation.operationId] = { + httpMethod, + requestUri, + comment: [operation.summary, operation.description].filter(Boolean).join(EOL), + deprecated: !!operation.deprecated, + requestBody: operation.requestBody as OpenApi.RequestBody, + parameters: uniqParameters(parameters), + responses: operation.responses as CodeGenerator.OpenApiResponses, + }; + }); + }); + return state; +}; diff --git a/src/internal/OpenApiTools/Walker2/State.ts b/src/internal/OpenApiTools/Walker2/State.ts new file mode 100644 index 00000000..f9561b20 --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/State.ts @@ -0,0 +1,33 @@ +import ts from "typescript"; + +import type { OpenApi } from "../../../types"; + +export interface OperationState { + requestUri: string; + httpMethod: string; + parameterName?: string; + requestBodyName?: string; +} + +export interface Type { + document: OpenApi.Document; + additionalStatements: ts.Statement[]; + operations: { + [operationId: string]: OperationState; + }; +} + +export const createDefaultOperationState = (httpMethod: string, requestUri: string, state: Partial): OperationState => { + return { + httpMethod: httpMethod, + requestUri: requestUri, + parameterName: state.parameterName, + requestBodyName: state.parameterName, + }; +}; + +export const createDefaultState = (rootDocument: OpenApi.Document): Type => ({ + document: rootDocument, + additionalStatements: [], + operations: {}, +}); diff --git a/src/internal/OpenApiTools/Walker2/Store.ts b/src/internal/OpenApiTools/Walker2/Store.ts new file mode 100644 index 00000000..33fd292d --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/Store.ts @@ -0,0 +1,38 @@ +import * as fs from "fs"; +import * as Path from "path"; + +import type { OpenApi } from "../../../types"; +import * as ADS from "../../AbstractDataStructure"; +import * as Def from "./Definition"; +import * as State from "./State"; +import * as Structure from "./structure"; + +class Store { + private state: State.Type; + private operator: Structure.OperatorType; + private getChildByPaths: Structure.GetChildByPaths; + constructor(rootDocument: OpenApi.Document) { + this.state = State.createDefaultState(rootDocument); + const { operator, getChildByPaths } = Structure.create(); + this.operator = operator; + this.getChildByPaths = getChildByPaths; + } + public addAbstractDataStruct(path: string, abstractDataStruct: ADS.Struct): void { + const targetPath = Path.posix.relative("components", path); + this.operator.set(targetPath, new Structure.AbstractDataStructure.Item({ name: path, value: abstractDataStruct })); + } + public createDirectory(componentName: Def.ComponentName, componentProperty: Structure.DirectoryTreeProperty): void { + this.operator.set(`${componentName}`, Structure.createInstance(componentProperty)); + } + public existTypeDef(path: string): boolean { + return !!this.operator.getChildByPaths(path, Structure.AbstractDataStructure.Kind); + } + public debugAbstractDataStruct() { + const data = this.operator.getHierarchy(); + console.log("output"); + fs.writeFileSync("debug/hierarchy.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); + fs.writeFileSync("debug/paths.json", JSON.stringify(this.operator.getNodePaths("typedef"), null, 2), { encoding: "utf-8" }); + } +} + +export { Store }; diff --git a/src/internal/OpenApiTools/Walker2/index.ts b/src/internal/OpenApiTools/Walker2/index.ts new file mode 100644 index 00000000..412e4745 --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/index.ts @@ -0,0 +1,3 @@ +export { Store } from "./Store"; +export * as State from "./State"; +export * as Def from "./Definition"; diff --git a/src/internal/OpenApiTools/Walker2/structure/AbstractDataStructure.ts b/src/internal/OpenApiTools/Walker2/structure/AbstractDataStructure.ts new file mode 100644 index 00000000..f88c91eb --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/structure/AbstractDataStructure.ts @@ -0,0 +1,19 @@ +import { Node as BaseNode } from "@himenon/path-oriented-data-structure"; + +import * as ADS from "../../../AbstractDataStructure"; + +export type Kind = "abstract-data"; +export const Kind: Kind = "abstract-data"; + +export interface Property { + name: string; + value: ADS.Struct; +} + +export class Item extends BaseNode { + public value: ADS.Struct; + constructor(property: Property) { + super("abstract-data", property.name); + this.value = property.value; + } +} diff --git a/src/internal/OpenApiTools/Walker2/structure/DataStructure.ts b/src/internal/OpenApiTools/Walker2/structure/DataStructure.ts new file mode 100644 index 00000000..9e640566 --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/structure/DataStructure.ts @@ -0,0 +1,21 @@ +import type { Operator } from "@himenon/path-oriented-data-structure"; + +import * as AbstractDataStructure from "./AbstractDataStructure"; +import * as Directory from "./Directory"; + +export { AbstractDataStructure, Directory }; + +export type Kind = AbstractDataStructure.Kind | Directory.Kind; + +export type GetChild = T extends AbstractDataStructure.Kind + ? AbstractDataStructure.Item + : T extends Directory.Kind + ? Directory.Item + : never; + +// Type Safe method +export const createGetChildByPaths = (operator: Operator) => (path: string, kind: T): GetChild | undefined => { + return operator.getChildByPaths(path, kind) as GetChild | undefined; +}; + +export type GetChildByPaths = (path: string, kind: T) => GetChild | undefined; diff --git a/src/internal/OpenApiTools/Walker2/structure/Directory.ts b/src/internal/OpenApiTools/Walker2/structure/Directory.ts new file mode 100644 index 00000000..d8610290 --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/structure/Directory.ts @@ -0,0 +1,16 @@ +import { Tree as BaseTree } from "@himenon/path-oriented-data-structure"; + +export type Kind = "directory"; +export const Kind: Kind = "directory"; + +export interface Property { + name: string; + comment?: string; + deprecated?: boolean; +} + +export class Item extends BaseTree { + constructor(public property: Property) { + super("directory", property.name); + } +} diff --git a/src/internal/OpenApiTools/Walker2/structure/index.ts b/src/internal/OpenApiTools/Walker2/structure/index.ts new file mode 100644 index 00000000..703bdd49 --- /dev/null +++ b/src/internal/OpenApiTools/Walker2/structure/index.ts @@ -0,0 +1,42 @@ +import { Operator } from "@himenon/path-oriented-data-structure"; + +import * as AbstractDataStructure from "./AbstractDataStructure"; +import * as DataStructure from "./DataStructure"; +import * as Directory from "./Directory"; + +export { DataStructure, AbstractDataStructure, Directory }; + +export interface AbstractDataStructureNodeProperty extends AbstractDataStructure.Property { + kind: AbstractDataStructure.Kind; +} + +export interface DirectoryTreeProperty extends Directory.Property { + kind: Directory.Kind; +} + +export type ComponentProperty = AbstractDataStructureNodeProperty | DirectoryTreeProperty; + +export type Instance = AbstractDataStructure.Item | Directory.Item; + +export const createInstance = (componentProperty: ComponentProperty): Instance => { + if (componentProperty.kind === "abstract-data") { + return new AbstractDataStructure.Item(componentProperty); + } + if (componentProperty.kind === "directory") { + return new Directory.Item(componentProperty); + } + + throw new Error("not registers"); +}; + +export const create = () => { + const operator = new Operator("directory"); + return { + operator, + getChildByPaths: DataStructure.createGetChildByPaths(operator), + }; +}; + +export type OperatorType = Operator; + +export type GetChildByPaths = DataStructure.GetChildByPaths; diff --git a/src/internal/OpenApiTools/components2/Schema.ts b/src/internal/OpenApiTools/components2/Schema.ts index 47ce4ae5..2f94c366 100644 --- a/src/internal/OpenApiTools/components2/Schema.ts +++ b/src/internal/OpenApiTools/components2/Schema.ts @@ -5,7 +5,7 @@ import * as Guard from "../Guard"; import * as ToAbstractDataStructure from "../toAbstractDataStructure"; import type { ArraySchema, ObjectSchema, PrimitiveSchema } from "../types"; import type { Payload } from "../types/tmp"; -import type * as Walker from "../Walker"; +import type * as Walker from "../Walker2"; export const generatePropertySignatures = (payload: Payload, schema: ObjectSchema): ADS.PropertySignatureStruct[] => { const { converterContext } = payload; diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index eff4d8eb..3565747b 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -5,7 +5,7 @@ import * as Guard from "../Guard"; import * as InferredType from "../InferredType"; import * as Name from "../Name"; import type { Payload } from "../types/tmp"; -import type * as Walker from "../Walker"; +import type * as Walker from "../Walker2"; import * as Reference from "./Reference"; import * as Schema from "./Schema"; @@ -27,10 +27,10 @@ const createNullableTypeNode = (schema: OpenApi.Schema): ADS.UnionStruct | undef export const generateNamespace = (payload: Payload, store: Walker.Store, schemas: Record): void => { const basePath = "components/schemas"; - // store.addComponent("schemas", { - // kind: "namespace", - // name: Name.Components.Schemas, - // }); + store.createDirectory("schemas", { + kind: "directory", + name: Name.Components.Schemas, + }); Object.entries(schemas).forEach(([name, targetSchema]) => { if (Guard.isReference(targetSchema)) { const schema = targetSchema; @@ -52,7 +52,7 @@ export const generateNamespace = (payload: Payload, store: Walker.Store, schemas return; } Schema.addSchema({ ...payload, currentPoint: reference.referencePoint }, store, reference.path, reference.name, reference.data); - if (store.hasStatement(`${basePath}/${name}`, ["interface", "typeAlias"])) { + if (store.existTypeDef(`${basePath}/${name}`)) { return; } return store.addAbstractDataStruct(`${basePath}/${name}`, { diff --git a/src/typedef/ParsedSchema.ts b/src/typedef/ParsedSchema.ts new file mode 100644 index 00000000..c6898b15 --- /dev/null +++ b/src/typedef/ParsedSchema.ts @@ -0,0 +1,13 @@ +import type * as PathOrientedDataStructure from "@himenon/path-oriented-data-structure"; + +import * as AbstractDataStructure from "../internal/AbstractDataStructure"; + +export type T = PathOrientedDataStructure.Operator<"typedef">; + +export interface TypeDefNode extends PathOrientedDataStructure.Node<"typedef"> { + value: AbstractDataStructure.Struct; +} + +export type Kind = "typedef"; + +export type GetChild = T extends "typedef" ? TypeDefNode : never; From 32b544b441c3af376008fdefd2925a99e771aaef Mon Sep 17 00:00:00 2001 From: Himenon Date: Sun, 2 May 2021 16:31:58 +0900 Subject: [PATCH 15/39] feat: update code template --- src/code-templates/typedef/index.ts | 6 +++++- src/index.ts | 19 ++++++++++++++++--- src/internal/OpenApiTools/Parser.ts | 15 +++++++++++---- src/internal/OpenApiTools/Walker2/State.ts | 9 +++++++++ src/internal/OpenApiTools/Walker2/Store.ts | 10 +++++++++- src/internal/OpenApiTools/Walker2/index.ts | 2 +- .../OpenApiTools/Walker2/structure/index.ts | 2 +- .../OpenApiTools/components2/Schemas.ts | 2 +- src/typedef/CodeGenerator.ts | 14 +++++++++++++- src/typedef/ParsedSchema.ts | 13 ++++--------- src/types.ts | 1 + 11 files changed, 71 insertions(+), 22 deletions(-) diff --git a/src/code-templates/typedef/index.ts b/src/code-templates/typedef/index.ts index dcc24ebf..9d95ff9d 100644 --- a/src/code-templates/typedef/index.ts +++ b/src/code-templates/typedef/index.ts @@ -1 +1,5 @@ -import {} from "../" \ No newline at end of file +import type { CodeGenerator } from "../../types"; + +export const generator: CodeGenerator.AdvancedGenerateFunction = (accessor): CodeGenerator.IntermediateCode[] => { + return accessor.operator.getNodePaths("typedef"); +}; diff --git a/src/index.ts b/src/index.ts index ca022235..4d585e4b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -42,9 +42,19 @@ export class CodeGenerator { const create = () => { const statements = this.parser.getOpenApiTypeDefinitionStatements(); generatorTemplates?.forEach(generatorTemplate => { - const payload = this.parser.getCodeGeneratorParamsArray(allowOperationIds); - const extraStatements = Api.TsGenerator.Utils.convertIntermediateCodes(generatorTemplate.generator(payload, generatorTemplate.option)); - statements.push(...extraStatements); + if (generatorTemplate.kind === "advanced") { + const extraStatements = Api.TsGenerator.Utils.convertIntermediateCodes( + generatorTemplate.generator(this.parser.accessor, generatorTemplate.option), + ); + statements.push(...extraStatements); + } else { + const payload = this.parser.getCodeGeneratorParamsArray(allowOperationIds); + generatorTemplate.generator; + const extraStatements = Api.TsGenerator.Utils.convertIntermediateCodes( + generatorTemplate.generator(payload, generatorTemplate.option), + ); + statements.push(...extraStatements); + } }); return statements; }; @@ -62,6 +72,9 @@ export class CodeGenerator { const create = () => { return generatorTemplates .map(generatorTemplate => { + if (generatorTemplate.kind === "advanced") { + return Api.TsGenerator.Utils.convertIntermediateCodes(generatorTemplate?.generator(this.parser.accessor, generatorTemplate.option)); + } return Api.TsGenerator.Utils.convertIntermediateCodes(generatorTemplate?.generator(payload, generatorTemplate.option)); }) .flat(); diff --git a/src/internal/OpenApiTools/Parser.ts b/src/internal/OpenApiTools/Parser.ts index 1d0e072b..1392183c 100644 --- a/src/internal/OpenApiTools/Parser.ts +++ b/src/internal/OpenApiTools/Parser.ts @@ -1,6 +1,6 @@ import ts from "typescript"; -import type { CodeGenerator, OpenApi } from "../../types"; +import type { CodeGenerator, OpenApi, ParsedSchema } from "../../types"; import * as TypeScriptCodeGenerator from "../TsGenerator"; import * as Headers from "./components/Headers"; import * as Parameters from "./components/Parameters"; @@ -45,7 +45,7 @@ export class Parser { toTypeNodeContext, this.converterContext, ); - Schemas2.generateNamespace( + Schemas2.createTypeDefSet( { entryPoint: this.entryPoint, currentPoint: this.currentPoint, @@ -127,7 +127,14 @@ export class Parser { this.converterContext, ); } - this.store.debugAbstractDataStruct(); + this.store2.debugAbstractDataStruct(); + } + + /** + * 抽象化されたAccessor + */ + public get accessor(): ParsedSchema.Accessor { + return this.store2.accessor; } public getCodeGeneratorParamsArray(allowOperationIds?: string[]): CodeGenerator.Params[] { @@ -135,7 +142,7 @@ export class Parser { } /** - * + * * @returns 依存を排除する */ public getOpenApiTypeDefinitionStatements(): ts.Statement[] { diff --git a/src/internal/OpenApiTools/Walker2/State.ts b/src/internal/OpenApiTools/Walker2/State.ts index f9561b20..b8969b35 100644 --- a/src/internal/OpenApiTools/Walker2/State.ts +++ b/src/internal/OpenApiTools/Walker2/State.ts @@ -2,6 +2,15 @@ import ts from "typescript"; import type { OpenApi } from "../../../types"; +export type { + OperatorType, + GetChildByPaths, + Instance, + ComponentProperty, + DirectoryTreeProperty, + AbstractDataStructureNodeProperty, +} from "./structure"; + export interface OperationState { requestUri: string; httpMethod: string; diff --git a/src/internal/OpenApiTools/Walker2/Store.ts b/src/internal/OpenApiTools/Walker2/Store.ts index 33fd292d..4e4c9277 100644 --- a/src/internal/OpenApiTools/Walker2/Store.ts +++ b/src/internal/OpenApiTools/Walker2/Store.ts @@ -1,12 +1,14 @@ import * as fs from "fs"; import * as Path from "path"; -import type { OpenApi } from "../../../types"; +import type { OpenApi, ParsedSchema } from "../../../types"; import * as ADS from "../../AbstractDataStructure"; import * as Def from "./Definition"; import * as State from "./State"; import * as Structure from "./structure"; +export { Structure }; + class Store { private state: State.Type; private operator: Structure.OperatorType; @@ -33,6 +35,12 @@ class Store { fs.writeFileSync("debug/hierarchy.json", JSON.stringify(data, null, 2), { encoding: "utf-8" }); fs.writeFileSync("debug/paths.json", JSON.stringify(this.operator.getNodePaths("typedef"), null, 2), { encoding: "utf-8" }); } + public get accessor(): ParsedSchema.Accessor { + return { + operator: this.operator, + getChildByPaths: this.getChildByPaths, + }; + } } export { Store }; diff --git a/src/internal/OpenApiTools/Walker2/index.ts b/src/internal/OpenApiTools/Walker2/index.ts index 412e4745..810effcf 100644 --- a/src/internal/OpenApiTools/Walker2/index.ts +++ b/src/internal/OpenApiTools/Walker2/index.ts @@ -1,3 +1,3 @@ -export { Store } from "./Store"; +export { Store, Structure } from "./Store"; export * as State from "./State"; export * as Def from "./Definition"; diff --git a/src/internal/OpenApiTools/Walker2/structure/index.ts b/src/internal/OpenApiTools/Walker2/structure/index.ts index 703bdd49..0e5db53a 100644 --- a/src/internal/OpenApiTools/Walker2/structure/index.ts +++ b/src/internal/OpenApiTools/Walker2/structure/index.ts @@ -30,7 +30,7 @@ export const createInstance = (componentProperty: ComponentProperty): Instance = }; export const create = () => { - const operator = new Operator("directory"); + const operator = new Operator(Directory.Kind); return { operator, getChildByPaths: DataStructure.createGetChildByPaths(operator), diff --git a/src/internal/OpenApiTools/components2/Schemas.ts b/src/internal/OpenApiTools/components2/Schemas.ts index 3565747b..9ef59f37 100644 --- a/src/internal/OpenApiTools/components2/Schemas.ts +++ b/src/internal/OpenApiTools/components2/Schemas.ts @@ -25,7 +25,7 @@ const createNullableTypeNode = (schema: OpenApi.Schema): ADS.UnionStruct | undef } }; -export const generateNamespace = (payload: Payload, store: Walker.Store, schemas: Record): void => { +export const createTypeDefSet = (payload: Payload, store: Walker.Store, schemas: Record): void => { const basePath = "components/schemas"; store.createDirectory("schemas", { kind: "directory", diff --git a/src/typedef/CodeGenerator.ts b/src/typedef/CodeGenerator.ts index 866182c3..3a8659c8 100644 --- a/src/typedef/CodeGenerator.ts +++ b/src/typedef/CodeGenerator.ts @@ -1,6 +1,7 @@ import type ts from "typescript"; import type * as OpenApi from "./OpenApi"; +import type * as ParsedSchema from "./ParsedSchema"; export type PickedParameter = Pick; @@ -58,6 +59,8 @@ export interface Params { */ export type IntermediateCode = string | ts.Statement; +export type AdvancedGenerateFunction