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

org.pipservices3.rpc.services.CommandableSwaggerDocument Maven / Gradle / Ivy

The newest version!
package org.pipservices3.rpc.services;

import jakarta.ws.rs.HttpMethod;
import org.pipservices3.commons.commands.Command;
import org.pipservices3.commons.commands.ICommand;
import org.pipservices3.commons.config.ConfigParams;
import org.pipservices3.commons.convert.TypeCode;
import org.pipservices3.commons.convert.TypeConverter;
import org.pipservices3.commons.reflect.PropertyReflector;
import org.pipservices3.commons.validate.ArraySchema;
import org.pipservices3.commons.validate.ObjectSchema;

import java.util.*;

public class CommandableSwaggerDocument {

    private String content = "";

    public List commands;

    public String version = "3.0.2";
    public String baseRoute;

    public String infoTitle;
    public String infoDescription;
    public String infoVersion = "1";
    public String infoTermsOfService;

    public String infoContactName;
    public String infoContactUrl;
    public String infoContactEmail;

    public String infoLicenseName;
    public String infoLicenseUrl;
    protected final Map objectType = Map.of("type", "object");


    public CommandableSwaggerDocument(String baseRoute, ConfigParams config, List commands) {
        this.baseRoute = baseRoute;
        this.commands = commands != null ? commands : new ArrayList<>();

        config = config != null ? config : new ConfigParams();

        this.infoTitle = config.getAsStringWithDefault("name", "CommandableHttpService");
        this.infoDescription = config.getAsStringWithDefault("description", "Commandable microservice");
    }

    public String toString() {
        this.content = "";
        Map data = new LinkedHashMap<>();

        data.put("openapi", version);

        data.put("info", Map.of(
                "title", infoTitle != null ? infoTitle : "null",
                "description", infoDescription != null ? infoDescription : "null",
                "version", infoVersion != null ? infoVersion : "null",
                "termsOfService", infoTermsOfService != null ? infoTermsOfService : "null",
                "contact", Map.of(
                        "name", infoContactName != null ? infoContactName : "null",
                        "url", infoContactUrl != null ? infoContactUrl : "null",
                        "email", infoContactEmail != null ? infoContactEmail : "null"
                ),
                "license", Map.of(
                        "name", infoLicenseName != null ? infoLicenseName : "null",
                        "url", infoLicenseUrl != null ? infoLicenseUrl : "null"
                )));

        data.put("paths", this.createPathsData());

        this.writeData(0, data);

        return this.content;
    }

    private Map createPathsData() {
        Map data = new HashMap<>();

        for (var command : this.commands) {
            var path = this.baseRoute + "/" + command.getName();
            if (!path.startsWith("/")) path = "/" + path;

            var bodyData = this.createRequestBodyData(command);
            var responseData = this.createResponsesData();
            data.put(path, Map.of(
                    HttpMethod.POST.toLowerCase(), Map.of(
                            "tags", List.of(this.baseRoute),
                            "operationId", command.getName() != null ? command.getName() : "null",
                            "requestBody", bodyData != null ? bodyData : "null",
                            "responses", responseData
                    )
            ));
        }

        return data;
    }

    private Map createRequestBodyData(ICommand command) {
        var schemaData = this.createSchemaData(command);
        return schemaData == null ? null : Map.of(
                "content", Map.of(
                        "application/json", Map.of(
                                "schema", schemaData
                        )
                )
        );
    }

    private Map createSchemaData(ICommand command) {
        ObjectSchema schema = null;

        try {
            // Hack to get private field
            var privateField = Command.class.getDeclaredField("_schema");
            privateField.setAccessible(true);
            schema = (ObjectSchema) privateField.get(command);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            // ignore
        }

        if (schema == null || schema.getProperties() == null)
            return null;

        return this.createPropertyData(schema, true);
    }

    private Map createPropertyData(ObjectSchema schema, boolean includeRequired) {
        Map properties = new HashMap<>();
        List required = new ArrayList<>();
        Map data = new HashMap<>();

        schema.getProperties().forEach(property -> {

            if (property.getType() == null) {
                properties.put(property.getName(), this.objectType);
            } else {
                var propertyName = property.getName();
                var propertyType = property.getType();

                if (propertyType instanceof ArraySchema) {
                    properties.put(propertyName, Map.of(
                            "type", "array",
                            "items", this.createPropertyTypeData(((ArraySchema) propertyType).getValueType())
                    ));
                } else {
                    properties.put(propertyName, this.createPropertyTypeData(propertyType));
                }

                if (includeRequired && property.isRequired()) required.add(propertyName);
            }
        });

        data.put("properties", properties);

        if (required.size() > 0) {
            data.put("required", required);
        }

        return data;
    }

    private Map createPropertyTypeData(Object propertyType) {
        if (propertyType instanceof ObjectSchema) {
            var objectMap = this.createPropertyData((ObjectSchema) propertyType, false);
            var newMap = new HashMap();

            newMap.putAll(objectType);
            newMap.putAll(objectMap);

            return newMap;
        } else {
            TypeCode typeCode;

            if (Arrays.stream(TypeCode.values()).toList().contains(propertyType)) {
                typeCode = (TypeCode) propertyType;
            } else {
                typeCode = TypeConverter.toTypeCode(propertyType);
            }

            if (typeCode == TypeCode.Unknown || typeCode == TypeCode.Map) {
                typeCode = TypeCode.Object;
            }

            switch (typeCode) {
                case Integer:
                    return Map.of(
                            "type", "integer",
                            "format", "int32"
                    );
                case Long:
                    return Map.of(
                            "type", "number",
                            "format", "int64"
                    );
                case Float:
                    return Map.of(
                            "type", "number",
                            "format", "float"
                    );
                case Double:
                    return Map.of(
                            "type", "number",
                            "format", "double"
                    );
                case DateTime:
                    return Map.of(
                            "type", "string",
                            "format", "date-time"
                    );
                case Boolean:
                    return Map.of(
                            "type", "boolean"
                    );
                default:
                    return Map.of("type", TypeConverter.toString(typeCode));
            }
        }
    }

    private Map createResponsesData() {
        return Map.of(
                "200", Map.of(
                        "description", "Successful response",
                        "content", Map.of(
                                "application/json", Map.of(
                                        "schema", Map.of(
                                                "type", "object"
                                        )
                                )
                        )
                )
        );
    }

    protected void writeData(int indent, Map data) {
        for (var entry : data.entrySet()) {
            var key = entry.getKey();
            var value = entry.getValue();

            if (value == null
                    || (value instanceof String && value.equals("null"))
                    || value instanceof Map && ((Map) value).values().stream().allMatch(v -> v.equals("null"))) {
                // Skip...
            } else if (value instanceof String) {
                this.writeAsString(indent, key, (String) value);
            } else if (value instanceof List) {
                if (((List) value).size() > 0) {
                    this.writeName(indent, key);
                    for (var index = 0; index < ((List) value).size(); index++) {
                        final var item = ((List) value).get(index);
                        this.writeArrayItem(indent + 1, (String) item, null);
                    }
                }
            } else if (value instanceof Map) {
                var props = PropertyReflector.getProperties(value).entrySet().stream().filter(Objects::nonNull);
                if (props.toArray().length >= 0) {
                    this.writeName(indent, key);
                    this.writeData(indent + 1, (Map) value);
                }
            } else {
                this.writeAsObject(indent, key, value);
            }
        }
    }

    protected void writeName(int indent, String name) {
        var spaces = this.getSpaces(indent);
        this.content += spaces + name + ":\n";
    }

    protected void writeArrayItem(int indent, String name, Boolean isObjectItem) {
        isObjectItem = isObjectItem != null ? isObjectItem : false;
        var spaces = this.getSpaces(indent);

        this.content += spaces + "- ";

        if (isObjectItem)
            this.content += name + ":\n";
        else
            this.content += name + "\n";
    }

    protected void writeAsObject(int indent, String name, Object value) {
        if (value == null) return;

        var spaces = this.getSpaces(indent);
        this.content += spaces + name + ": " + value + "\n";
    }

    protected void writeAsString(int indent, String name, String value) {
        if (value == null) return;

        var spaces = this.getSpaces(indent);

        this.content += spaces + name + ": '" + value + "'\n";
    }

    protected String getSpaces(int length) {
        return " ".repeat(Math.max(0, length * 2));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy