fastman
Version:
快速api测试及文档生成
531 lines (458 loc) • 19.7 kB
text/typescript
///<reference path="../../node_modules/@types/node/index.d.ts"/>
import Spec from "./base";
import {OasLibraryUtils} from "../../lib/openapi-parser/library.utils";
import {Oas30Document} from "../../lib/openapi-parser/models/3.0/document.model";
import {OasDocumentFactory} from "../../lib/openapi-parser/factories/document.factory";
import * as yaml from "js-yaml";
import {OasPathItem} from "../../lib/openapi-parser/models/common/path-item.model";
import {Oas30Response} from "../../lib/openapi-parser/models/3.0/response.model";
import {Oas30Operation} from "../../lib/openapi-parser/models/3.0/operation.model";
export namespace OpenApi3 {
export interface ISpecificationExtension {
// Cannot constraint to "^x-" but can filter them later to access to them
[extensionName: string]: any;
}
export interface OpenAPIObject extends ISpecificationExtension {
openapi: string;
info: InfoObject;
servers?: ServerObject[];
paths: PathsObject;
components?: ComponentsObject;
security?: SecurityRequirementObject[];
tags?: TagObject[];
externalDocs?: ExternalDocumentationObject;
}
export interface InfoObject extends ISpecificationExtension {
title: string;
description?: string;
termsOfService?: string;
contact?: ContactObject;
license?: LicenseObject;
version: string;
}
export interface ContactObject extends ISpecificationExtension {
name: string;
url: string;
email: string;
}
export interface LicenseObject extends ISpecificationExtension {
name: string;
url?: string;
}
export interface ServerObject extends ISpecificationExtension {
url: string;
description?: string;
variables?: { [v: string]: ServerVariableObject };
}
export interface ServerVariableObject extends ISpecificationExtension {
enum?: string[] | boolean[] | number[];
default: string | boolean | number;
description?: string;
}
export interface ComponentsObject extends ISpecificationExtension {
schemas?: { [schema: string]: SchemaObject };
responses?: { [response: string]: ResponseObject };
parameters?: { [parameter: string]: ParameterObject };
examples?: { [example: string]: ExampleObject };
requestBodies?: { [request: string]: RequestBodyObject };
headers?: { [heaer: string]: HeaderObject };
securitySchemes?: { [securityScheme: string]: SecuritySchemeObject };
links?: { [link: string]: LinkObject };
callbacks?: { [callback: string]: CallbackObject };
}
export type Component = SchemaObject | ResponseObject | ParameterObject
| ExampleObject | RequestBodyObject | HeaderObject | SecuritySchemeObject
| LinkObject | CallbackObject;
/**
* Rename it to Paths Object to be consistent with the spec
* See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#pathsObject
*/
export interface PathsObject extends ISpecificationExtension {
// [path: string]: PathItemObject;
[path: string]: PathItemObject | any; // Hack for allowing ISpecificationExtension
}
export interface PathItemObject extends ISpecificationExtension {
$ref?: string;
summary?: string;
description?: string;
get?: OperationObject;
put?: OperationObject;
post?: OperationObject;
delete?: OperationObject;
options?: OperationObject;
head?: OperationObject;
patch?: OperationObject;
trace?: OperationObject;
servers?: ServerObject;
parameters?: (ParameterObject | ReferenceObject)[];
}
export interface OperationObject extends ISpecificationExtension {
tags?: string[];
summary?: string;
description?: string;
externalDocs?: ExternalDocumentationObject;
operationId?: string;
parameters?: (ParameterObject | ReferenceObject)[];
requestBody?: RequestBodyObject | ReferenceObject;
responses?: ResponsesObject;
callbacks?: CallbacksObject;
deprecated?: boolean;
security?: SecurityRequirementObject[];
servers?: ServerObject;
}
export interface ExternalDocumentationObject extends ISpecificationExtension {
description?: string;
url: string;
}
/**
* The location of a parameter.
* Possible values are "query", "header", "path" or "cookie".
* Specification:
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#parameter-locations
*/
export type ParameterLocation = "query" | "header" | "path" | "cookie";
/**
* The style of a parameter.
* Describes how the parameter value will be serialized.
* (serialization is not implemented yet)
* Specification:
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#style-values
*/
export type ParameterStyle =
"matrix"
| "label"
| "form"
| "simple"
| "spaceDelimited"
| "pipeDelimited"
| "deepObject";
export interface ParameterObject extends ISpecificationExtension {
name: string;
in: ParameterLocation; // "query" | "header" | "path" | "cookie";
description?: string;
required?: boolean;
deprecated?: boolean;
allowEmptyValue?: boolean;
style?: ParameterStyle; // "matrix" | "label" | "form" | "simple" | "spaceDelimited" | "pipeDelimited" | "deepObject";
explode?: boolean;
allowReserved?: boolean;
schema?: SchemaObject | ReferenceObject;
examples?: { [param: string]: ExampleObject | ReferenceObject };
example?: any;
content?: ContentObject;
}
export interface RequestBodyObject extends ISpecificationExtension {
description?: string;
content: ContentObject;
required?: boolean;
}
export interface ContentObject {
[mediatype: string]: MediaTypeObject;
}
export interface MediaTypeObject extends ISpecificationExtension {
schema?: SchemaObject | ReferenceObject;
examples?: ExampleObject;
example?: ExampleObject | ReferenceObject;
encoding?: EncodingObject;
}
export interface EncodingObject extends ISpecificationExtension {
// [property: string]: EncodingPropertyObject;
[property: string]: EncodingPropertyObject | any; // Hack for allowing ISpecificationExtension
}
export interface EncodingPropertyObject {
contentType?: string;
headers?: { [key: string]: HeaderObject | ReferenceObject };
style?: string;
explode?: boolean;
allowReserved?: boolean;
[key: string]: any; // (any) = Hack for allowing ISpecificationExtension
}
export interface ResponsesObject extends ISpecificationExtension {
default?: ResponseObject | ReferenceObject;
// [statuscode: string]: ResponseObject | ReferenceObject;
[statuscode: string]: ResponseObject | ReferenceObject | any; // (any) = Hack for allowing ISpecificationExtension
}
export interface ResponseObject extends ISpecificationExtension {
description: string;
headers?: HeadersObject;
content?: ContentObject;
links?: LinksObject;
}
export interface CallbacksObject extends ISpecificationExtension {
// [name: string]: CallbackObject | ReferenceObject;
[name: string]: CallbackObject | ReferenceObject | any; // Hack for allowing ISpecificationExtension
}
export interface CallbackObject extends ISpecificationExtension {
// [name: string]: PathItemObject;
[name: string]: PathItemObject | any; // Hack for allowing ISpecificationExtension
}
export interface HeadersObject {
[name: string]: HeaderObject | ReferenceObject;
}
export interface ExampleObject {
summary?: string;
description?: string;
value?: any;
externalValue?: string;
[property: string]: any; // Hack for allowing ISpecificationExtension
}
export interface LinksObject {
[name: string]: LinkObject | ReferenceObject;
}
export interface LinkObject extends ISpecificationExtension {
operationRef?: string;
operationId?: string;
parameters?: LinkParametersObject;
requestBody?: any | string;
description?: string;
server?: ServerObject;
[property: string]: any; // Hack for allowing ISpecificationExtension
}
export interface LinkParametersObject {
[name: string]: any | string;
}
export interface HeaderObject extends ParameterObject {
}
export interface TagObject extends ISpecificationExtension {
name: string;
description?: string;
externalDocs?: ExternalDocumentationObject;
[extension: string]: any; // Hack for allowing ISpecificationExtension
}
export interface ExamplesObject {
[name: string]: any;
}
export interface ReferenceObject {
$ref: string;
}
export interface SchemaObject extends ISpecificationExtension {
nullable?: boolean;
discriminator?: DiscriminatorObject;
readOnly?: boolean;
writeOnly?: boolean;
xml?: XmlObject;
externalDocs?: ExternalDocumentationObject;
example?: any;
examples?: any[];
deprecated?: boolean;
type?: string;
allOf?: (SchemaObject | ReferenceObject)[];
oneOf?: (SchemaObject | ReferenceObject)[];
anyOf?: (SchemaObject | ReferenceObject)[];
not?: SchemaObject | ReferenceObject;
items?: SchemaObject | ReferenceObject;
properties?: { [propertyName: string]: (SchemaObject | ReferenceObject) };
additionalProperties?: (SchemaObject | ReferenceObject);
description?: string;
format?: string;
default?: any;
title?: string;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: boolean;
minimum?: number;
exclusiveMinimum?: boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
maxProperties?: number;
minProperties?: number;
required?: string[];
enum?: any[];
}
export interface SchemasObject {
[schema: string]: SchemaObject;
}
export interface DiscriminatorObject {
propertyName: string;
mapping?: { [key: string]: string };
}
export interface XmlObject extends ISpecificationExtension {
name?: string;
namespace?: string;
prefix?: string;
attribute?: boolean;
wrapped?: boolean;
}
export type SecuritySchemeType =
"apiKey"
| "http"
| "oauth2"
| "openIdConnect";
export interface SecuritySchemeObject extends ISpecificationExtension {
type: SecuritySchemeType;
description?: string;
name?: string; // required only for apiKey
in?: string; // required only for apiKey
scheme?: string; // required only for http
bearerFormat?: string;
flow?: OAuthFlowObject; // required only for oauth2
openIdConnectUrl?: string; // required only for oauth2
}
export interface OAuthFlowsObject extends ISpecificationExtension {
implicit?: OAuthFlowObject;
password?: OAuthFlowObject;
clientCredentials?: OAuthFlowObject;
authorizationCode?: OAuthFlowObject;
}
export interface OAuthFlowObject extends ISpecificationExtension {
authorizationUrl: string;
tokenUrl: string;
refreshUrl?: string;
scopes: ScopesObject;
}
export interface ScopesObject extends ISpecificationExtension {
[scope: string]: any; // Hack for allowing ISpecificationExtension
}
export interface SecurityRequirementObject {
[name: string]: string[];
}
}
export default class OpenApi3Spec extends Spec {
private oasUtils: OasLibraryUtils = new OasLibraryUtils();
private openapiDoc: Oas30Document;
tag(tag: OpenApi3.TagObject) {
const tagNode = this.openapiDoc.createTag();
tagNode.name = tag.name;
tagNode.description = tag.description;
const tags = this.openapiDoc.tags || [];
tags.push(tagNode);
this.openapiDoc.tags = tags;
}
info(info: OpenApi3.InfoObject) {
if (!this.openapiDoc) {
this.openapiDoc = <Oas30Document> new OasDocumentFactory().createEmpty("3.0");
}
const oasInfo = this.openapiDoc.createInfo();
this.oasUtils.readNode(info, oasInfo);
this.openapiDoc.info = oasInfo;
}
load(spec: string | object) {
const factory = new OasDocumentFactory();
if (typeof spec === "string") {
this.openapiDoc = <Oas30Document> factory.createFromJson(spec);
} else {
this.openapiDoc = <Oas30Document> factory.createFromObject(spec);
}
return this;
}
write(format: string = "yaml"): string {
let json = this.oasUtils.writeNode(this.openapiDoc);
return format === "yaml" ? yaml.safeDump(json) : JSON.stringify(json, null, 2);
}
server(server?: OpenApi3.ServerObject): OpenApi3.ServerObject[] {
if (server) {
this.openapiDoc.addServer(server.url, server.description);
}
return this.openapiDoc.servers.map(s => {
return this.oasUtils.writeNode(s);
});
}
/**
* 添加或查找一个资源
* @param {string} path
* @returns {OasPathItem}
*/
path(path: string): OpenApi3.PathItemObject {
let paths = this.openapiDoc.paths || this.openapiDoc.createPaths();
let pathItem = paths.getItem(path) || paths.createPathItem(path);
paths.addItem(path, pathItem);
this.openapiDoc.paths = paths;
return this.oasUtils.writeNode(pathItem);
}
operation(path: string, method: string, operation?: OpenApi3.OperationObject): OpenApi3.OperationObject {
const paths = this.openapiDoc.paths;
let pathNode = paths.getItem(path);
let operationNode = pathNode[method] || pathNode.createOperation(method);
if (operation) {
this.oasUtils.readNode(operation, operationNode);
}
pathNode[method] = operationNode;
return this.oasUtils.writeNode(operationNode);
}
response(status: string, path: string, method: string, response?: OpenApi3.ResponseObject): OpenApi3.ResponseObject {
const paths = this.openapiDoc.paths;
let pathNode = paths.getItem(path);
let operationNode = pathNode[method] || pathNode.createOperation(method);
let responsesNode = operationNode.responses || operationNode.createResponses();
const responseNode = <Oas30Response>(responsesNode.getItem(status) || responsesNode.createResponse(status));
if (response) {
this.oasUtils.readNode(response, responseNode);
}
responsesNode.addItem(status, responseNode);
operationNode.responses = responsesNode;
pathNode[method] = operationNode;
return this.oasUtils.writeNode(responseNode);
}
component(name: string,
value: OpenApi3.Component,
type?: string): any {
const componentType = type || "schemas";
const components = this.openapiDoc.components || this.openapiDoc.createComponents();
let component;
// TODO component的类型需要补充完整
switch (componentType) {
case "schemas": {
component = components.getSchemaDefinition(name) ||
components.createSchemaDefinition(name);
this.oasUtils.readNode(value, component);
components.addSchemaDefinition(name, component);
break;
}
case "parameters": {
component = components.getParameterDefinition(name) ||
components.createParameterDefinition(name);
this.oasUtils.readNode(value, component);
components.addParameterDefinition(name, component);
break;
}
case "responses": {
component = components.getResponseDefinition(name) ||
components.createResponseDefinition(name);
this.oasUtils.readNode(value, component);
components.addResponseDefinition(name, component);
break;
}
}
this.openapiDoc.components = components;
return value;
}
// TODO set version
version(version?: string): number {
return 3;
}
example(name: string, example: OpenApi3.ExampleObject, path: string, method: string, status: string, mediaType: string = "application/json") {
const paths = this.openapiDoc.paths;
let pathNode = paths.getItem(path);
let operationNode = pathNode[method] || pathNode.createOperation(method);
let responsesNode = operationNode.responses || operationNode.createResponses();
const responseNode = <Oas30Response>(responsesNode.getItem(status) || responsesNode.createResponse(status));
const mediaTypeNode = responseNode.getMediaType(mediaType) || responseNode.createMediaType(mediaType);
const exampleNode = mediaTypeNode.getExample(name) || mediaTypeNode.createExample(name);
this.oasUtils.readNode(example, exampleNode);
mediaTypeNode.addExample(exampleNode);
responseNode.addMediaType(mediaType, mediaTypeNode);
responsesNode.addResponse(status, responseNode);
operationNode.responses = responsesNode;
pathNode[method] = operationNode;
return this.oasUtils.writeNode(exampleNode);
}
requestExample(name: string, example: OpenApi3.ExampleObject, path: string, method: string, mediaType: string = "application/json") {
const paths = this.openapiDoc.paths;
let pathNode = paths.getItem(path);
let operationNode = <Oas30Operation> (pathNode[method] || pathNode.createOperation(method));
pathNode[method] = operationNode;
if (operationNode.requestBody) {
let mediaTypeNode = operationNode.requestBody.getMediaType(mediaType)
|| operationNode.requestBody.createMediaType(mediaType);
const exampleNode = mediaTypeNode.createExample(name);
this.oasUtils.readNode(example, exampleNode);
mediaTypeNode.addExample(exampleNode);
operationNode.requestBody.addMediaType(mediaType, mediaTypeNode);
return this.oasUtils.writeNode(exampleNode);
}
}
}