diff --git a/src/readers/ComponentsReader.ts b/src/readers/ComponentsReader.ts index b9585df..c110ece 100644 --- a/src/readers/ComponentsReader.ts +++ b/src/readers/ComponentsReader.ts @@ -1,7 +1,7 @@ import { OpenAPIV3 } from 'openapi-types'; +import { isBoolean } from '../utils/type-is'; import { BaseReader } from './BaseReader'; -import { Named } from './Named'; -import { TypeAlias, TypeList, TypeOrigin, TypeUnit } from './types'; +import { TypeAlias, TypeItem, TypeList, TypeOrigin, TypeUnit } from './types'; export class ComponentsReader extends BaseReader { readComponents(): TypeList { @@ -25,10 +25,10 @@ export class ComponentsReader extends BaseReader { return t; } - protected readReference(name: string, reference: OpenAPIV3.ReferenceObject, root = false): TypeAlias { + protected readReference(name: string, reference: OpenAPIV3.ReferenceObject, refAble = false): TypeAlias { return this.named.addAlias({ kind: 'alias', - root, + refAble, name, ref: reference.$ref, target: '', @@ -77,35 +77,69 @@ export class ComponentsReader extends BaseReader { protected readSchemaObject(name: string, required: boolean, schema: OpenAPIV3.SchemaObject): TypeOrigin { const properties = Object.entries(schema.properties || {}).sort((a, b) => a[0].localeCompare(b[0])); + const children = properties.map(([propName, propSchema]) => { + return this.isReference(propSchema) + ? this.readReference(propName, propSchema) + : this.readSchema(propName, schema.required?.includes(propName) || false, propSchema); + }); + + const additional = this.readObjectAdditionalProperties(schema.additionalProperties); + if (additional) children.push(additional); + return { ...this.inheritProps(schema), name, required, kind: 'origin', type: 'object', - children: properties.map(([propName, propSchema]) => { - return this.isReference(propSchema) - ? this.readReference(propName, propSchema) - : this.readSchema(propName, schema.required?.includes(propName) || false, propSchema); - }), + children, }; } protected readSchemaArray(name: string, required: boolean, schema: OpenAPIV3.ArraySchemaObject): TypeOrigin { + const children = [schema.items].map((schema) => { + return this.isReference(schema) + ? this.readReference(`${name}[]`, schema) + : this.readSchema(`${name}[]`, schema.nullable === false, schema); + }); + + const additional = this.readObjectAdditionalProperties(schema.additionalProperties); + if (additional) children.push(additional); + return { ...this.inheritProps(schema), name, required, kind: 'origin', type: 'array', - children: [schema.items].map((schema) => { - return this.isReference(schema) - ? this.readReference(`${name}[]`, schema) - : this.readSchema(`${name}[]`, schema.nullable === false, schema); - }), + children: children, }; } + protected readObjectAdditionalProperties( + additionalProperties: OpenAPIV3.SchemaObject['additionalProperties'] + ): TypeItem | undefined { + if (!additionalProperties) return; + + const name = '[key: string]'; + + if (isBoolean(additionalProperties)) { + if (additionalProperties) { + return { + kind: 'origin', + type: 'any', + name, + required: true, + }; + } + return; + } + + return this.isReference(additionalProperties) + ? this.readReference(name, additionalProperties) + : this.readSchema(name, true, additionalProperties); + } + protected readSchemaNever(name: string, required: boolean, schema: OpenAPIV3.SchemaObject): TypeOrigin { return { ...this.inheritProps(schema), diff --git a/src/readers/Named.ts b/src/readers/Named.ts index 8decad7..43a36eb 100644 --- a/src/readers/Named.ts +++ b/src/readers/Named.ts @@ -10,19 +10,21 @@ export class Named { } resolveAlias() { this.unresolvedAliasList.forEach((a) => { + if (!a.ref) return; + const info = refToType(a.ref); a.target = this.getName(info.base); a.props = info.props; // 指向另外一个地址 - const { root, name, target } = a; - if (root) { + const { refAble, name, target } = a; + if (refAble) { this.aliasRelationMap.set(name, target); } }); this.unresolvedAliasList.forEach((a) => { - a.origin = findOrigin(a.root ? a.name : a.target, this.aliasRelationMap); + a.origin = findOrigin(a.refAble ? a.name : a.target, this.aliasRelationMap); }); this.unresolvedAliasList.length = 0; diff --git a/src/readers/PathsReader.ts b/src/readers/PathsReader.ts index 10395e5..178c4f4 100644 --- a/src/readers/PathsReader.ts +++ b/src/readers/PathsReader.ts @@ -131,12 +131,10 @@ export class PathsReader extends ComponentsReader { return { kind: 'alias', name, - root: false, + refAble: false, target: 'Blob', origin: 'Blob', props: [], - required: true, - ref: '', }; } diff --git a/src/readers/types.ts b/src/readers/types.ts index bcd6678..eb9d36b 100644 --- a/src/readers/types.ts +++ b/src/readers/types.ts @@ -1,7 +1,4 @@ -import { OpenAPIV3 } from 'openapi-types'; - -export type TypeKind = 'origin' | 'alias'; -export type TypeUnit = 'number' | 'string' | 'boolean' | 'never' | 'object' | 'array'; +export type TypeUnit = 'number' | 'string' | 'boolean' | 'never' | 'object' | 'array' | 'any'; export interface TypeComments { title?: string; @@ -13,7 +10,7 @@ export interface TypeComments { } export interface TypeOrigin extends TypeComments { - kind: TypeKind; + kind: 'origin'; name: string; type: TypeUnit; required: boolean; @@ -22,13 +19,13 @@ export interface TypeOrigin extends TypeComments { } export interface TypeAlias extends TypeComments { - kind: TypeKind; - root: boolean; + kind: 'alias'; + refAble: boolean; name: string; target: string; origin: string; props: string[]; - ref: string; + ref?: string; } export type TypeItem = TypeOrigin | TypeAlias; diff --git a/test/readers/ComponentsReader.test.ts b/test/readers/ComponentsReader.test.ts index 5bd3afd..2b1188f 100644 --- a/test/readers/ComponentsReader.test.ts +++ b/test/readers/ComponentsReader.test.ts @@ -82,7 +82,7 @@ test('ref once', () => { }, { kind: 'alias', - root: true, + refAble: true, name: 'T', target: 'P', origin: 'P', @@ -118,8 +118,8 @@ test('ref twice', () => { const t = reader.readComponents(); expect(t).toEqual([ { kind: 'origin', name: 'K', type: 'string', required: false }, - { kind: 'alias', root: true, name: 'P', target: 'K', origin: 'K', props: [], ref: '#/components/schemas/K' }, - { kind: 'alias', root: true, name: 'T', target: 'P', origin: 'K', props: [], ref: '#/components/schemas/P' }, + { kind: 'alias', refAble: true, name: 'P', target: 'K', origin: 'K', props: [], ref: '#/components/schemas/K' }, + { kind: 'alias', refAble: true, name: 'T', target: 'P', origin: 'K', props: [], ref: '#/components/schemas/P' }, ]); }); @@ -189,6 +189,7 @@ test('object', () => { }, }, required: ['B', 'S', 'N', 'I'], + additionalProperties: true, }, R: { type: 'string', @@ -209,8 +210,17 @@ test('object', () => { { name: 'B', type: 'boolean', required: true, kind: 'origin' }, { name: 'I', type: 'number', required: true, kind: 'origin' }, { name: 'N', type: 'number', required: true, kind: 'origin' }, - { kind: 'alias', root: false, name: 'R', target: 'R', origin: 'R', props: [], ref: '#/components/schemas/R' }, + { + kind: 'alias', + refAble: false, + name: 'R', + target: 'R', + origin: 'R', + props: [], + ref: '#/components/schemas/R', + }, { name: 'S', type: 'string', required: true, kind: 'origin' }, + { name: '[key: string]', type: 'any', required: true, kind: 'origin' }, ], }, { @@ -238,6 +248,12 @@ test('array', () => { items: { type: 'string', }, + additionalProperties: { + $ref: '#/components/schema/T', + }, + }, + T: { + type: 'string', }, }, }, @@ -250,7 +266,24 @@ test('array', () => { name: 'A', type: 'array', required: true, - children: [{ name: 'A[]', type: 'string', required: false, kind: 'origin' }], + children: [ + { kind: 'origin', name: 'A[]', type: 'string', required: false }, + { + kind: 'alias', + name: '[key: string]', + target: 'T', + origin: 'T', + props: [], + refAble: false, + ref: '#/components/schema/T', + }, + ], + }, + { + kind: 'origin', + name: 'T', + type: 'string', + required: false, }, ]); }); diff --git a/test/readers/DocumentReader.test.ts b/test/readers/DocumentReader.test.ts index d74eac5..b49e745 100644 --- a/test/readers/DocumentReader.test.ts +++ b/test/readers/DocumentReader.test.ts @@ -184,7 +184,7 @@ test('DocumentReader', () => { "origin": "Address", "props": [], "ref": "#/components/schemas/Address", - "root": false, + "refAble": false, "target": "Address", }, ], @@ -344,7 +344,7 @@ test('DocumentReader', () => { "origin": "Category", "props": [], "ref": "#/components/schemas/Category", - "root": false, + "refAble": false, "target": "Category", }, { @@ -426,7 +426,7 @@ test('DocumentReader', () => { "origin": "Tag", "props": [], "ref": "#/components/schemas/Tag", - "root": false, + "refAble": false, "target": "Tag", }, ], @@ -642,7 +642,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, "path": undefined, @@ -655,7 +655,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, }, @@ -674,7 +674,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, "path": undefined, @@ -687,7 +687,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, }, @@ -767,7 +767,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, }, @@ -854,9 +854,7 @@ test('DocumentReader', () => { "name": "UploadFileReqData", "origin": "Blob", "props": [], - "ref": "", - "required": true, - "root": false, + "refAble": false, "target": "Blob", }, "path": { @@ -909,7 +907,7 @@ test('DocumentReader', () => { "origin": "ApiResponse", "props": [], "ref": "#/components/schemas/ApiResponse", - "root": false, + "refAble": false, "target": "ApiResponse", }, }, @@ -959,7 +957,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, ], @@ -1033,7 +1031,7 @@ test('DocumentReader', () => { "origin": "Pet", "props": [], "ref": "#/components/schemas/Pet", - "root": false, + "refAble": false, "target": "Pet", }, ], @@ -1065,7 +1063,21 @@ test('DocumentReader', () => { }, "response": { "body": { - "children": [], + "children": [ + { + "default": undefined, + "deprecated": undefined, + "description": undefined, + "enum": undefined, + "example": undefined, + "format": "int32", + "kind": "origin", + "name": "[key: string]", + "required": true, + "title": undefined, + "type": "number", + }, + ], "default": undefined, "deprecated": undefined, "description": undefined, @@ -1094,7 +1106,7 @@ test('DocumentReader', () => { "origin": "Order", "props": [], "ref": "#/components/schemas/Order", - "root": false, + "refAble": false, "target": "Order", }, "path": undefined, @@ -1107,7 +1119,7 @@ test('DocumentReader', () => { "origin": "Order", "props": [], "ref": "#/components/schemas/Order", - "root": false, + "refAble": false, "target": "Order", }, }, @@ -1187,7 +1199,7 @@ test('DocumentReader', () => { "origin": "Order", "props": [], "ref": "#/components/schemas/Order", - "root": false, + "refAble": false, "target": "Order", }, }, @@ -1206,7 +1218,7 @@ test('DocumentReader', () => { "origin": "User", "props": [], "ref": "#/components/schemas/User", - "root": false, + "refAble": false, "target": "User", }, "path": undefined, @@ -1291,7 +1303,7 @@ test('DocumentReader', () => { "origin": "User", "props": [], "ref": "#/components/schemas/User", - "root": false, + "refAble": false, "target": "User", }, }, @@ -1310,7 +1322,7 @@ test('DocumentReader', () => { "origin": "User", "props": [], "ref": "#/components/schemas/User", - "root": false, + "refAble": false, "target": "User", }, "path": { @@ -1356,7 +1368,7 @@ test('DocumentReader', () => { "origin": "User", "props": [], "ref": "#/components/schemas/User", - "root": false, + "refAble": false, "target": "User", }, ], @@ -1382,7 +1394,7 @@ test('DocumentReader', () => { "origin": "User", "props": [], "ref": "#/components/schemas/User", - "root": false, + "refAble": false, "target": "User", }, }, diff --git a/test/readers/Named.test.ts b/test/readers/Named.test.ts index 37c223a..048df78 100644 --- a/test/readers/Named.test.ts +++ b/test/readers/Named.test.ts @@ -25,7 +25,7 @@ test('named', () => { // A -> A!(A2) -> aa!!/1/2 == A -> aa!!/1/2(Aa3) const a1 = named.addAlias({ kind: 'alias', - root: true, + refAble: true, name: 'A', ref: '#/components/schemas/A!', target: '', @@ -35,7 +35,7 @@ test('named', () => { // A!(A2) -> aa!!/1/2(Aa3) const a2 = named.addAlias({ kind: 'alias', - root: true, + refAble: true, name: 'A2', ref: '#/components/schemas/aa!!/1/2', target: '', diff --git a/test/readers/PathsReader.test.ts b/test/readers/PathsReader.test.ts index a748a84..3c97f22 100644 --- a/test/readers/PathsReader.test.ts +++ b/test/readers/PathsReader.test.ts @@ -136,7 +136,7 @@ test('resp ref', () => { response: { body: { kind: 'alias', - root: false, + refAble: false, name: 'FindPetResData', target: 'T', origin: 'T', @@ -292,9 +292,7 @@ test('req file', () => { name: 'FindPetReqData', origin: 'Blob', props: [], - ref: '', - required: true, - root: false, + refAble: false, target: 'Blob', }, }, @@ -370,7 +368,7 @@ test('req query + path', () => { children: [ { kind: 'alias', - root: false, + refAble: false, name: 'name', ref: '#/components/schemas/O/p', target: 'O',