Skip to content

Commit

Permalink
feat(reader): 支持 additionalProperties 解读
Browse files Browse the repository at this point in the history
  • Loading branch information
cloudcome committed Apr 15, 2023
1 parent 9aee151 commit fe4eeb6
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 63 deletions.
62 changes: 48 additions & 14 deletions src/readers/ComponentsReader.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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: '',
Expand Down Expand Up @@ -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),
Expand Down
8 changes: 5 additions & 3 deletions src/readers/Named.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 1 addition & 3 deletions src/readers/PathsReader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: '',
};
}

Expand Down
13 changes: 5 additions & 8 deletions src/readers/types.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -13,7 +10,7 @@ export interface TypeComments {
}

export interface TypeOrigin extends TypeComments {
kind: TypeKind;
kind: 'origin';
name: string;
type: TypeUnit;
required: boolean;
Expand All @@ -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;
Expand Down
43 changes: 38 additions & 5 deletions test/readers/ComponentsReader.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ test('ref once', () => {
},
{
kind: 'alias',
root: true,
refAble: true,
name: 'T',
target: 'P',
origin: 'P',
Expand Down Expand Up @@ -118,8 +118,8 @@ test('ref twice', () => {
const t = reader.readComponents();
expect(t).toEqual<TypeList>([
{ 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' },
]);
});

Expand Down Expand Up @@ -189,6 +189,7 @@ test('object', () => {
},
},
required: ['B', 'S', 'N', 'I'],
additionalProperties: true,
},
R: {
type: 'string',
Expand All @@ -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' },
],
},
{
Expand Down Expand Up @@ -238,6 +248,12 @@ test('array', () => {
items: {
type: 'string',
},
additionalProperties: {
$ref: '#/components/schema/T',
},
},
T: {
type: 'string',
},
},
},
Expand All @@ -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,
},
]);
});
Expand Down
Loading

0 comments on commit fe4eeb6

Please sign in to comment.