org.ballerinalang.openapi.model.BallerinaOpenApi.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.openapi.model.BallerinaOpenApi.java

Source

/*
 * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.ballerinalang.openapi.model;

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.servers.Server;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.ballerinalang.openapi.exception.BallerinaOpenApiException;
import org.ballerinalang.openapi.utils.CodegenUtils;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Wrapper for {@link OpenAPI}.
 * <p>This class can be used to push additional context variables for handlebars</p>
 */
public class BallerinaOpenApi implements BallerinaOpenApiObject<BallerinaOpenApi, OpenAPI> {
    private String srcPackage;
    private String modelPackage;
    private String openapi = "3.0.0";
    private Info info = null;
    private ExternalDocumentation externalDocs = null;
    private List<BallerinaServer> servers = null;
    private Set<Map.Entry<String, String>> security = null;
    private List<Tag> tags = null;
    private Set<Map.Entry<String, BallerinaPath>> paths = null;
    private Set<Map.Entry<String, BallerinaSchema>> schemas = null;
    private Components components = null;
    private Map<String, Object> extensions = null;

    /**
     * Build a {@link BallerinaOpenApi} object from a {@link OpenAPI} object.
     * All non iterable objects using handlebars library is converted into
     * supported iterable object types.
     *
     * @param openAPI {@link OpenAPI} type object to be converted
     * @return Converted {@link BallerinaOpenApi} object
     * @throws BallerinaOpenApiException when OpenAPI to BallerinaOpenApi parsing failed
     */
    @Override
    public BallerinaOpenApi buildContext(OpenAPI openAPI) throws BallerinaOpenApiException {
        this.openapi = openAPI.getOpenapi();
        this.info = openAPI.getInfo();
        this.externalDocs = openAPI.getExternalDocs();
        this.tags = openAPI.getTags();
        this.components = openAPI.getComponents();
        this.extensions = openAPI.getExtensions();

        setPaths(openAPI);
        setSecurityRequirements(openAPI);
        setServers(openAPI);
        setSchemas(openAPI);
        return this;
    }

    @Override
    public BallerinaOpenApi buildContext(OpenAPI definition, OpenAPI openAPI) throws BallerinaOpenApiException {
        return buildContext(definition);
    }

    @Override
    public BallerinaOpenApi getDefaultValue() {
        return null;
    }

    /**
     * Populate path models into iterable structure.
     * This method will also add an operationId to each operation,
     * if operationId not provided in openApi definition
     *
     * @param openAPI {@code OpenAPI} definition object with schema definition
     * @throws BallerinaOpenApiException when context building fails
     */
    private void setPaths(OpenAPI openAPI) throws BallerinaOpenApiException {
        if (openAPI.getPaths() == null) {
            return;
        }

        this.paths = new LinkedHashSet<>();
        Paths pathList = openAPI.getPaths();
        for (Map.Entry<String, PathItem> path : pathList.entrySet()) {
            BallerinaPath balPath = new BallerinaPath().buildContext(path.getValue(), openAPI);
            if (balPath.isNoOperationsForPath()) {
                balPath.setResourceName(path.getKey());
            } else {
                balPath.getOperations().forEach(operation -> {
                    if (operation.getValue().getOperationId() == null) {
                        String pathName = path.getKey().substring(1); //need to drop '/' prefix from the key, ex:'/path'
                        String operationId = operation.getKey() + StringUtils.capitalize(pathName);
                        operation.getValue().setOperationId(CodegenUtils.normalizeForBIdentifier(operationId));
                    }
                });
            }
            paths.add(new AbstractMap.SimpleEntry<>(path.getKey(), balPath));
        }
    }

    /**
     * Populate schemas into a "Set".
     *
     * @param openAPI <code>OpenAPI</code> definition object with schema definition
     */
    private void setSchemas(OpenAPI openAPI) {
        this.schemas = new LinkedHashSet<>();
        Map<String, Schema> schemaMap;
        if (openAPI.getComponents() == null || openAPI.getComponents().getSchemas() == null) {
            return;
        }

        schemaMap = openAPI.getComponents().getSchemas();
        for (Map.Entry<String, Schema> entry : schemaMap.entrySet()) {
            try {
                BallerinaSchema schema = new BallerinaSchema().buildContext(entry.getValue(), openAPI);

                // If schema type has not been set, set the type with Schema name
                if (StringUtils.isEmpty(schema.getType())) {
                    schema.setType(entry.getKey());
                }

                schemas.add(new AbstractMap.SimpleEntry<>(entry.getKey(), schema));
            } catch (BallerinaOpenApiException e) {
                // Ignore exception and try to build next schema. No need to break the flow for a failure of one schema.
            }
        }
    }

    /**
     * Extract endpoint information from OpenAPI server list.
     * If no servers were found, default {@link BallerinaServer} will be set as the server
     *
     * @param openAPI <code>OpenAPI</code> definition object with server details
     * @throws BallerinaOpenApiException on failure to parse {@code Server} list
     */
    private void setServers(OpenAPI openAPI) throws BallerinaOpenApiException {
        this.servers = new ArrayList<>();
        List<Server> serverList = openAPI.getServers();
        if (serverList == null) {
            BallerinaServer server = new BallerinaServer().getDefaultValue();
            servers.add(server);
            return;
        }

        serverList.forEach(server -> {
            try {
                // Note that only one base path is allowed. Though we extract base path per each server
                // defined in the Open Api definition, only the base path of first server will be used
                // in ballerina code generation. Ballerina all endpoints to be in a single base path
                BallerinaServer balServer = new BallerinaServer().buildContext(server);
                servers.add(balServer);
            } catch (BallerinaOpenApiException e) {
                // Ignore the exception, set default value for this server and move forward
                servers.add(new BallerinaServer().getDefaultValue());
            }
        });
    }

    /**
     * Extract security requirements as a set.
     *
     * @param openAPI <code>OpenAPI</code> definition object with security definition
     */
    private void setSecurityRequirements(OpenAPI openAPI) {
        this.security = new LinkedHashSet<>();
        List<SecurityRequirement> requirements = openAPI.getSecurity();
        if (requirements == null || requirements.isEmpty()) {
            return;
        }

        requirements.forEach(r -> r.forEach((key, value) -> {
            Map.Entry entry = new AbstractMap.SimpleEntry<>(key, value);
            security.add(entry);
        }));
    }

    public BallerinaOpenApi srcPackage(String srcPackage) {
        if (srcPackage != null) {
            this.srcPackage = srcPackage.replaceFirst("\\.", "/");
        }
        return this;
    }

    public BallerinaOpenApi modelPackage(String modelPackage) {
        if (modelPackage != null) {
            this.modelPackage = modelPackage.replaceFirst("\\.", "/");
        }
        return this;
    }

    public String getSrcPackage() {
        return srcPackage;
    }

    public String getModelPackage() {
        return modelPackage;
    }

    public String getOpenapi() {
        return openapi;
    }

    public Info getInfo() {
        return info;
    }

    public ExternalDocumentation getExternalDocs() {
        return externalDocs;
    }

    public List<BallerinaServer> getServers() {
        return servers;
    }

    public Set<Map.Entry<String, String>> getSecurity() {
        return security;
    }

    public List<Tag> getTags() {
        return tags;
    }

    public Set<Map.Entry<String, BallerinaPath>> getPaths() {
        return paths;
    }

    public Components getComponents() {
        return components;
    }

    public Map<String, Object> getExtensions() {
        return extensions;
    }

    public Set<Map.Entry<String, BallerinaSchema>> getSchemas() {
        return schemas;
    }
}