All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.willingxyz.restdoc.swagger3.Swagger3RestDocGenerator Maven / Gradle / Ivy

There is a newer version: 0.2.1.4
Show newest version
package cn.willingxyz.restdoc.swagger3;

import cn.willingxyz.restdoc.core.parse.utils.FormatUtils;
import cn.willingxyz.restdoc.core.parse.utils.ReflectUtils;
import cn.willingxyz.restdoc.core.parse.utils.TextUtils;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.therapi.runtimejavadoc.RuntimeJavadoc;
import cn.willingxyz.restdoc.core.models.*;
import cn.willingxyz.restdoc.core.parse.IRestDocGenerator;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.tags.Tag;
import lombok.var;

import java.lang.reflect.Type;
import java.util.*;
import java.util.stream.Collectors;

public class Swagger3RestDocGenerator implements IRestDocGenerator {

    public Swagger3GeneratorConfig _configuration;

    public Swagger3RestDocGenerator(Swagger3GeneratorConfig configuration)
    {
        _configuration = configuration;
    }

    @Override
    public String generate(RootModel rootModel) {
        var openApi = generateOpenApi(rootModel);

        var objectMapper = new ObjectMapper();
        objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        try {
            var swaggerJson = objectMapper.writeValueAsString(openApi);
            return swaggerJson;
        } catch (JsonProcessingException e) {
            throw new RuntimeException("序列化错误");
        }
    }

    private OpenAPI generateOpenApi(RootModel rootModel) {
        var openApi = new OpenAPI();

        convertServers(rootModel, openApi);
        convertInfo(rootModel, openApi);
        convertTag(rootModel, openApi);
        convertPath(rootModel, openApi);

        return openApi;
    }

    private void convertServers(RootModel rootModel, OpenAPI openApi) {
        var servers = new ArrayList();
        for (var server : _configuration.getServers())
        {
            var serverInfo = new Server();
            serverInfo.setDescription(server.getDescription());
            serverInfo.setUrl(server.getUrl());

            servers.add(serverInfo);
        }
        openApi.setServers(servers);
    }

    private void convertInfo(RootModel rootModel, OpenAPI openApi) {
        var info = new Info();
        info.setDescription(_configuration.getDescription());
        info.setVersion(_configuration.getVersion());
        info.setTitle(_configuration.getTitle());
        openApi.setInfo(info);
    }

    // tag 用于对path进行分组,类似于springmvc的controller
    private void convertTag(RootModel rootModel, OpenAPI openApi) {
        for (var controller : rootModel.getControllers())
        {
            var tag = new Tag();
            tag.setName(_configuration.getTypeNameParser().parse(controller.getControllerClass()));
            tag.setDescription(controller.getDescription());
            openApi.addTagsItem(tag);
        }
    }

    private void convertPath(RootModel rootModel, OpenAPI openApi) {
        for (var controller : rootModel.getControllers())
        {
            for (var method : controller.getControllerMethods())
            {
                for (var mapping : method.getMappings())
                {
                    convertSinglePath(openApi, controller, method, mapping);
                }
            }
        }
    }

    private void convertSinglePath(OpenAPI openApi, ControllerModel controller, PathModel method, MappingModel mapping) {
        var pathItem = new PathItem();
        pathItem.setDescription(method.getDescription());
        // summary 属于简短的描述
        pathItem.setSummary(TextUtils.getFirstLine(method.getDescription()));

        var operation = new Operation();
        operation.addTagsItem(_configuration.getTypeNameParser().parse(controller.getControllerClass()));
        operation.setSummary(TextUtils.getFirstLine(method.getDescription()));
        operation.setDescription(method.getDescription());
        operation.setDeprecated(method.getDeprecated());

        // 参数解析
        for (var param : method.getParameters()) {
            if (param.getLocation() == ParameterModel.ParameterLocation.QUERY) {
                convertQueryString(param, openApi).forEach(o -> operation.addParametersItem(o));
            }
            else if (param.getLocation() == ParameterModel.ParameterLocation.BODY)
            {
                operation.setRequestBody(convertRequestBody(param, openApi));
            }
            else if (param.getLocation() == ParameterModel.ParameterLocation.PATH)
            {
                operation.addParametersItem(convertPathParameter(param));
            }
            else if (param.getLocation() == ParameterModel.ParameterLocation.HEADER)
            {
                operation.addParametersItem(convertHeaderParameter(param));
            }
            else if (param.getLocation() == ParameterModel.ParameterLocation.FILE)
            {
                operation.setRequestBody(convertFileParameter(param));
            }
        }
        // 响应解析
        operation.setResponses(convertResponses(method, openApi));

        setHttpMethod(mapping, pathItem, operation);

        for (var path : mapping.getPaths()) {
            openApi.path(path, pathItem);
        }
    }

    private void setHttpMethod(MappingModel mapping, PathItem pathItem, Operation operation) {
        for (var httpMethod : mapping.getHttpMethods())
        {
            switch (httpMethod) {
                case GET:
                    pathItem.get(operation); break;
                case PUT:
                    pathItem.put(operation); break;
                case POST:
                    pathItem.post(operation); break;
                case DELETE:
                    pathItem.delete(operation); break;
                case HEAD:
                    pathItem.head(operation); break;
                case PATCH:
                    pathItem.patch(operation); break;
                case TRACE:
                    pathItem.patch(operation); break;
                case OPTIONS:
                    pathItem.options(operation); break;
            }
        }
    }

    /**
     * 转换RequestBody
     */
    private RequestBody convertRequestBody(ParameterModel parameterModel, OpenAPI openAPI) {
        var requestBody = new RequestBody();

        if (parameterModel.isArray()) {
            // 简单数组类型
            if (parameterModel.getChildren() == null || parameterModel.getChildren().isEmpty())
            {
                var arraySchema = new ArraySchema();
                var itemSchema = new Schema();
                itemSchema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(_configuration.getTypeInspector().getCollectionComponentType(parameterModel.getParameterType())));
                arraySchema.setItems(itemSchema);

                Content content = createContent(arraySchema);
                requestBody.setContent(content);
                requestBody.setRequired(parameterModel.isRequired());
                requestBody.setDescription(parameterModel.getDescription());
            }
            else { // 复杂数组类型
                var itemSchema = new Schema();
                itemSchema.set$ref("#/components/schemas/" + putSchemaComponent(parameterModel.getParameterType(), parameterModel.getChildren(), openAPI));

                var content = createContent(itemSchema);
                requestBody.setContent(content);
                requestBody.setRequired(parameterModel.isRequired());
                requestBody.setDescription(parameterModel.getDescription());
            }
        }
        else if (parameterModel.getChildren() == null || parameterModel.getChildren().size() == 0)
        {
            // 如果是单个参数,直接嵌入
            Schema schema = null;
            if (ReflectUtils.isEnum(parameterModel.getParameterType()))
            {
                var clazz = (Class)parameterModel.getParameterType();
                    var arraySchema = new ArraySchema();
                    var enumDoc = RuntimeJavadoc.getJavadoc(clazz);
                    arraySchema.setDescription(FormatUtils.format(enumDoc.getComment()));
                    var itemSchema = new Schema();
                    itemSchema.setType("string");
                    var enums = Arrays.stream(clazz.getEnumConstants()).map(o -> o.toString()).collect(Collectors.toList());
                    itemSchema.setEnum(enums);
                    arraySchema.setItems(itemSchema);
                    if (enums.size() > 0)
                    {
                        itemSchema.setDefault(enums.get(0));
                    }
                    schema = arraySchema;
                    schema.setDescription(parameterModel.getDescription());
                    if (parameterModel.isRequired()) {
                    schema.setRequired(Arrays.asList(parameterModel.getName()));
                }
            }
            else {
                schema = new Schema();

                schema.setDescription(parameterModel.getDescription());
                schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(parameterModel.getParameterType()));
                schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(parameterModel.getParameterType()));

                if (parameterModel.isRequired()) {
                    schema.setRequired(Arrays.asList(parameterModel.getName()));
                }
            }

            var content = createContent(schema);
            requestBody.setContent(content);
        }
        else {
            // 如果是复杂类型,引用Component
            var schema = new Schema();

            var mediaType = new MediaType();
            mediaType.setSchema(schema);
            var content = new Content();
            content.addMediaType("application/json", mediaType);
            requestBody.setContent(content);
            requestBody.setRequired(parameterModel.isRequired());
            requestBody.setDescription(parameterModel.getDescription());

            schema.set$ref("#/components/schemas/" + putSchemaComponent(parameterModel.getParameterType(), parameterModel.getChildren(), openAPI));
        }
        return requestBody;
    }

    private List convertQueryString(ParameterModel paramModel, OpenAPI openAPI) {
        var parameters = new ArrayList();
        if (paramModel.isArray())
        {
            if (paramModel.getChildren().size() == 0)
            { // 简单类型数据
                addSimpleQueryParameterSchema(paramModel, parameters);
            }
            else
            { // 复杂类型数据
                Parameter parameter = new Parameter();
                parameter.setName(paramModel.getName());
                parameter.setDescription(paramModel.getDescription());
                parameter.setIn("query");

                ArraySchema arraySchema = new ArraySchema();
                arraySchema.setType("array");
                Schema schema = new Schema();
                schema.set$ref(putSchemaComponent(paramModel.getParameterType(), paramModel.getChildren(), openAPI));
                arraySchema.setItems(schema);

                parameter.setSchema(arraySchema);
                parameters.add(parameter);
            }
        }
        else if (paramModel.getChildren() == null || paramModel.getChildren().size() == 0) // 简单类型
        {
            addSimpleQueryParameterSchema(paramModel, parameters);
        }
        else // 复杂对象
        {
            convertParameterChildren(paramModel.getChildren(), null, paramModel.getDescription(), paramModel.isRequired(), false)
                    .stream().forEach(o -> parameters.add(o));
        }
        return parameters;
    }

    private Parameter convertPathParameter(ParameterModel param) {
        var parameter = new Parameter();

        parameter.setIn("path");
        parameter.setName(param.getName());
        parameter.setDescription(param.getDescription());
        parameter.setSchema(generateSimpleParameterSchema(param));

        return parameter;
    }

    private Parameter convertHeaderParameter(ParameterModel paramModel) {
        var parameter = new Parameter();

        parameter.setIn("header");
        parameter.setName(paramModel.getName());
        parameter.setDescription(paramModel.getDescription());

        parameter.setSchema(generateSimpleParameterSchema(paramModel));
        return parameter;
    }

    private RequestBody convertFileParameter(ParameterModel param) {
        var requestBody = new RequestBody();

        var propSchema = new Schema();
        propSchema.setType("string");
        propSchema.format("binary");

        Schema schema = new Schema();
        schema.setType("object");
        schema.addProperties(param.getName(), propSchema);

        Content content = new Content();
        var mediaType = new MediaType();
        mediaType.setSchema(schema);
        content.addMediaType("multipart/form-data", mediaType);

        requestBody.setContent(content);
        requestBody.setDescription(param.getDescription());
        requestBody.setRequired(param.isRequired());

        return requestBody;
    }

    private ApiResponse convertResponse(ResponseModel responseModel, OpenAPI openAPI) {
        var apiResponse = new ApiResponse();
        var returnModel = responseModel.getReturnModel();

        apiResponse.setDescription(returnModel.getDescription());
        if (returnModel.getReturnType() == void.class || returnModel.getReturnType() == Void.class)
            return apiResponse;

        if (returnModel.isArray()) {
            if (returnModel.getChildren() == null || returnModel.getChildren().isEmpty())
            {
                var schema = generateSimpleArraySchema(returnModel);
                apiResponse.setContent(createContent(schema));
            }
            else {
                var itemSchema = new Schema();
                itemSchema.setDescription(returnModel.getDescription());
                itemSchema.set$ref("#/components/schemas/" + putSchemaComponent(returnModel.getReturnType(), returnModel.getChildren(), openAPI));

                Content content = createContent(itemSchema);
                apiResponse.setContent(content);
            }
        }
        else if (returnModel.getChildren() != null && returnModel.getChildren().size() > 0) // 复杂类型
        {
            var schema = new Schema();
            var content = createContent(schema);
            apiResponse.setContent(content);

            schema.set$ref("#/components/schemas/" + putSchemaComponent(returnModel.getReturnType(), returnModel.getChildren(), openAPI));
        }
        else // 简单类型
        {
            var schema = new Schema();
            if (ReflectUtils.isEnum(returnModel.getReturnType()))
            {
                schema = generateEnumSchema((Class)returnModel.getReturnType());
            }
            else {
                schema.setDescription(returnModel.getDescription());
                schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(returnModel.getReturnType()));
                schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(returnModel.getReturnType()));
            }

            var content = createContent(schema);
            apiResponse.setContent(content);
            apiResponse.setDescription(returnModel.getDescription());
        }
        return apiResponse;
    }

    // 生成Schema(复杂对象),并放入openapi中。
    private String putSchemaComponent(Type type, List children, OpenAPI openAPI) {
        var className = getComponentName(type);

        if (openAPI.getComponents() == null)
            openAPI.components(new Components());
        if (openAPI.getComponents().getSchemas() == null)
            openAPI.getComponents().schemas(new HashMap<>());
        if (openAPI.getComponents().getSchemas().containsKey(className))
        {
            return className;
        }
        var schema = generateSchemaComponent(type, children, openAPI);

        openAPI.getComponents().addSchemas(className, schema);

        return className;
    }

    // List -> [String
    // List -> [[String
    // com.willing.List -> com.willing.[String
    private String getComponentName(Type type) {
        // 统计嵌套集合的深度
        int collectionCount = 0;
        while (_configuration.getTypeInspector().isCollection(type)) {
            collectionCount++;
            type = _configuration.getTypeInspector().getCollectionComponentType(type);
        }
        var name = _configuration.getTypeNameParser().parse(type);
        int $index = name.lastIndexOf('$');
        int dotIndex = name.lastIndexOf('.');
        if ($index > dotIndex) {
            return convertCollectionName(collectionCount, name, '$');
        }
        else
        {
            return convertCollectionName(collectionCount, name, ',');
        }
    }

    private String convertCollectionName(int collectionCount, String name, char ch) {
        String prefix = "";
        int index = name.lastIndexOf(ch);
        if (index != -1)
        {
            prefix = name.substring(0, index + 1);
            name = name.substring(index + 1);
        }
        while (collectionCount-- > 0)
        {
            name = "[" + name;
        }
        return prefix + name;
    }

    // 生成复杂对象的schema
    private Schema generateSchemaComponent(Type type, List children, OpenAPI openAPI) {

        if (_configuration.getTypeInspector().isCollection(type))
        {
            var arraySchema = new ArraySchema();
            arraySchema.setItems(generateSchemaComponent(_configuration.getTypeInspector().getCollectionComponentType(type), children, openAPI));
            return arraySchema;
        }
        if (ReflectUtils.isEnum(type))
        {
            return generateEnumSchema((Class) type);
        }
        var schema = new Schema();
        var className = _configuration.getTypeNameParser().parse(type);
        schema.setName(className);
        schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(type));


        var classDoc = RuntimeJavadoc.getJavadoc(type.getTypeName());
        schema.setDescription(FormatUtils.format(classDoc.getComment()));

        schema.setProperties(generateSchemaProperty(children, openAPI));

        return schema;
    }
    private Map generateSchemaProperty(List propertyModels, OpenAPI openAPI) {
        var schemas = new HashMap();

        for (var propertyModel : propertyModels) {
            var schema = new Schema();
            if (_configuration.getTypeInspector().isCollection(propertyModel.getPropertyType()))
            {
                var arraySchema = new ArraySchema();
                arraySchema.setItems(generateSchemaComponent(_configuration.getTypeInspector().getCollectionComponentType(propertyModel.getPropertyType()), propertyModel.getChildren(), openAPI));
                schema = arraySchema;
            }
            else if (ReflectUtils.isEnum(propertyModel.getPropertyType()))
            {
                schema = generateEnumSchema((Class)propertyModel.getPropertyType());
            }
            else if (propertyModel.getChildren() == null || propertyModel.getChildren().size() == 0)
            {
//                schema.setTitle(parameterModel.getPropertyType().getCanonicalName());
                schema.setDescription(propertyModel.getDescription());

                if (ReflectUtils.isEnum(propertyModel.getPropertyType())) {
                    var clazz = (Class)propertyModel.getPropertyType();
                    schema = generateEnumSchema(clazz);
                }
                else {
                    schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(propertyModel.getPropertyType()));
                    schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(propertyModel.getPropertyType()));
                }

                if (propertyModel.getRequired() != null && propertyModel.getRequired()) {
                    schema.setRequired(Arrays.asList(propertyModel.getName()));
                }
            }
            else
            {
                schema.set$ref("#/components/schemas/" + putSchemaComponent(propertyModel.getPropertyType(), propertyModel.getChildren(), openAPI));
            }
            schemas.put(propertyModel.getName(), schema);
        }
        return schemas;
    }
    private ArraySchema generateEnumSchema(Class clazz) {
        var arraySchema = new ArraySchema();
        arraySchema.setType("string");
        var enumDoc = RuntimeJavadoc.getJavadoc(clazz);
        arraySchema.setDescription(FormatUtils.format(enumDoc.getComment()));
        var enums = Arrays.stream(clazz.getEnumConstants()).map(o -> o.toString()).collect(Collectors.toList());
        var itemSchema = new Schema();
        itemSchema.setType("string"); // todo 如何决定是string还是int
        itemSchema.setEnum(enums);
        if (enums.size() > 0) {
            itemSchema.setDefault(enums.get(0));
        }
        arraySchema.setItems(itemSchema);
        return arraySchema;
    }

    private ApiResponses convertResponses(PathModel method, OpenAPI openAPI) {
        var response = new ApiResponses();
        for (var res : method.getResponse())
        {
            ApiResponse apiResponse = convertResponse(res, openAPI);
            response.addApiResponse(res.getStatusCode() + "", apiResponse);
        }
        return response;
    }

    private Schema generateSimpleArraySchema(ReturnModel returnModel) {
        var arraySchema = new ArraySchema();
        arraySchema.setDescription(returnModel.getDescription());
        var itemSchema = new Schema();
        itemSchema.setDescription(returnModel.getDescription());
        if (ReflectUtils.isEnum(_configuration.getTypeInspector().getCollectionComponentType(returnModel.getReturnType())))
        {
            itemSchema = generateEnumSchema((Class) _configuration.getTypeInspector().getCollectionComponentType(returnModel.getReturnType()));
        }
        else {
            itemSchema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(_configuration.getTypeInspector().getCollectionComponentType(returnModel.getReturnType())));
            itemSchema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(returnModel.getReturnType()));
        }

        arraySchema.setItems(itemSchema);

        return arraySchema;
    }

    private Content createContent(Schema schema) {
        var mediaType = new MediaType();
        mediaType.setSchema(schema);
        var content = new Content();
        content.addMediaType("application/json", mediaType);
        return content;
    }

    private void addSimpleQueryParameterSchema(ParameterModel paramModel, ArrayList parameters) {
        var parameter = new Parameter();
        parameter.setName(paramModel.getName());
        parameter.setDescription(paramModel.getDescription());
        parameter.setIn("query");

        parameter.setSchema(generateSimpleParameterSchema(paramModel));
        parameters.add(parameter);
    }

    private Schema generateSimpleParameterSchema(ParameterModel paramModel) {
        if (ReflectUtils.isEnum(paramModel.getParameterType()))
        {
            return generateEnumSchema((Class)paramModel.getParameterType());
        }
        if (_configuration.getTypeInspector().isCollection(paramModel.getParameterType()))
        {
            return generateArraySchema(paramModel);
        }
        var schema = new Schema();
        schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(paramModel.getParameterType()));
        schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(paramModel.getParameterType()));

        return schema;
    }

    private Schema generateArraySchema(ParameterModel paramModel) {
        var arraySchema = new ArraySchema();

        var schema = new Schema();
        schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(_configuration.getTypeInspector().getCollectionComponentType(paramModel.getParameterType())));
        schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(paramModel.getParameterType()));

        arraySchema.setItems(schema);

        return arraySchema;
    }

    private Schema generateSimpleParameterSchema(PropertyModel child) {
        if (ReflectUtils.isEnum(child.getPropertyType()))
        {
            return generateEnumSchema((Class)child.getPropertyType());
        }
        if (_configuration.getTypeInspector().isCollection(child.getPropertyType()))
        {
            return generateArraySchema(child);
        }
        var schema = new Schema();
        schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(child.getPropertyType()));
        schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(child.getPropertyType()));
        return schema;
    }

    private List convertParameterChildren(List propertyModels, String paraName, String desc, Boolean required, boolean isArray)
    {
        var parameters = new ArrayList();
        String name = "";
        if (paraName != null)
            name = paraName + ".";
        for (var child : propertyModels)
        {
            if (child.isArray())
            {
//                convertParameterChildren(child.getChildren(), child.getName(), true).stream().forEach(o -> parameters.add(o));
            }
            else if (child.getChildren() == null || child.getChildren().size() == 0) {
                var parameter = new Parameter();

                parameter.setName(name + child.getName());
                parameter.setDescription(child.getDescription());
                parameter.setIn("query");
                if (isArray)
                {
                    parameter.setSchema(generateArraySchema(child));
                }
                else {
                    parameter.setSchema(generateSimpleParameterSchema(child));
                }
                parameters.add(parameter);
            }
            else
            {
//                parameters.addAll(convertParameterChildren(child.getChildren(), name + child.getName(), false));
            }
            // todo path
        }
        return parameters;
    }

    private Schema generateArraySchema(PropertyModel child) {
        var arraySchema = new ArraySchema();

        var schema = new Schema();
        schema.setType(_configuration.getSwaggerTypeInspector().toSwaggerType(child.getPropertyType()));
        schema.setFormat(_configuration.getSwaggerTypeInspector().toSwaggerFormat(child.getPropertyType()));

        arraySchema.setItems(schema);

        return arraySchema;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy