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

io.micronaut.openapi.visitor.OpenApiApplicationVisitor Maven / Gradle / Ivy

/*
 * Copyright 2017-2018 original authors
 *
 * 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 io.micronaut.openapi.visitor;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import io.micronaut.context.env.Environment;
import io.micronaut.core.annotation.AnnotationValue;
import io.micronaut.core.annotation.Experimental;
import io.micronaut.core.util.CollectionUtils;
import io.micronaut.core.util.StringUtils;
import io.micronaut.inject.ast.ClassElement;
import io.micronaut.inject.visitor.TypeElementVisitor;
import io.micronaut.inject.visitor.VisitorContext;
import io.micronaut.inject.writer.GeneratedFile;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.servers.Server;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;

import java.io.File;
import java.io.Writer;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * Visits the application class.
 *
 * @author graemerocher
 * @since 1.0
 */
@Experimental
public class OpenApiApplicationVisitor extends AbstractOpenApiVisitor implements TypeElementVisitor {
    /**
     * System property that enables setting the target file to write to.
     */
    public static final String MICRONAUT_OPENAPI_TARGET_FILE = "micronaut.openapi.target.file";

    private ClassElement classElement;

    @Override
    public void visitClass(ClassElement element, VisitorContext context) {
        context.info("Generating OpenAPI Documentation");
        Optional attr = context.get(ATTR_OPENAPI, OpenAPI.class);
        OpenAPI openAPI = readOpenAPI(element, context);
        if (attr.isPresent()) {
            OpenAPI existing = attr.get();
            existing.setInfo(openAPI.getInfo());
            existing.setTags(openAPI.getTags());
            existing.setServers(openAPI.getServers());
            existing.setSecurity(openAPI.getSecurity());
            existing.setExternalDocs(openAPI.getExternalDocs());
            existing.setExtensions(openAPI.getExtensions());

        } else {
            context.put(ATTR_OPENAPI, openAPI);
        }

        // handle type level tags
        List tagList = processOpenApiAnnotation(
                element,
                context,
                Tag.class,
                io.swagger.v3.oas.models.tags.Tag.class,
                openAPI.getTags()
        );
        openAPI.setTags(tagList);

        // handle type level security requirements
        List securityRequirements = processOpenApiAnnotation(
                element,
                context,
                SecurityRequirement.class,
                io.swagger.v3.oas.models.security.SecurityRequirement.class,
                openAPI.getSecurity()
        );
        openAPI.setSecurity(securityRequirements);

        // handle type level servers
        List servers = processOpenApiAnnotation(
                element,
                context,
                Server.class,
                io.swagger.v3.oas.models.servers.Server.class,
                openAPI.getServers()
        );
        openAPI.setServers(servers);

        // Handle Application security schemes
        processSecuritySchemes(element, context);

        if (Boolean.getBoolean(ATTR_TEST_MODE)) {
            testReference = openAPI;
        }

        this.classElement = element;
    }

    private  List processOpenApiAnnotation(ClassElement element, VisitorContext context, Class annotationType, Class modelType, List tagList) {
        List> annotations = element.getAnnotationValuesByType(annotationType);
        if (CollectionUtils.isNotEmpty(annotations)) {
            if (CollectionUtils.isEmpty(tagList)) {
                tagList = new ArrayList<>();

            }
            for (AnnotationValue tag : annotations) {
                JsonNode jsonNode = toJson(tag.getValues(), context);
                try {
                    T t = jsonMapper.treeToValue(jsonNode, modelType);
                    if (t != null) {
                        tagList.add(t);
                    }
                } catch (JsonProcessingException e) {
                    context.warn("Error reading OpenAPI" + annotationType + " annotation", element);
                }
            }
        }
        return tagList;
    }

    private OpenAPI readOpenAPI(ClassElement element, VisitorContext context) {
        return element.findAnnotation(OpenAPIDefinition.class).flatMap(o -> {
                    JsonNode jsonNode = toJson(o.getValues(), context);

                    try {
                        return Optional.of(jsonMapper.treeToValue(jsonNode, OpenAPI.class));
                    } catch (JsonProcessingException e) {
                        context.warn("Error reading Swagger OpenAPI for element [" + element + "]: " + e.getMessage(), element);
                        return Optional.empty();
                    }
                }).orElse(new OpenAPI());
    }

    @Override
    public void finish(VisitorContext visitorContext) {

        if (classElement != null) {

            Optional attr = visitorContext.get(ATTR_OPENAPI, OpenAPI.class);

            attr.ifPresent(openAPI -> {
                String property = System.getProperty(MICRONAUT_OPENAPI_TARGET_FILE);
                if (StringUtils.isNotEmpty(property)) {
                    File f = new File(property);
                    visitorContext.info("Writing OpenAPI YAML to destination: " + f);
                    try {
                        f.getParentFile().mkdirs();
                        yamlMapper.writeValue(f, openAPI);
                    } catch (Exception e) {
                        visitorContext.warn("Unable to generate swagger.yml: " + e.getMessage() , classElement);
                    }
                } else {

                    String fileName = "swagger.yml";
                    Info info = openAPI.getInfo();
                    if (info != null) {
                        String title = Optional.ofNullable(info.getTitle()).orElse(Environment.DEFAULT_NAME);
                        title = title.toLowerCase().replace(' ', '-');
                        String version = info.getVersion();
                        if (version != null) {
                            fileName = title + "-" + version + ".yml";
                        }
                    }
                    Optional generatedFile = visitorContext.visitMetaInfFile("swagger/" + fileName);
                    if (generatedFile.isPresent()) {
                        GeneratedFile f = generatedFile.get();
                        try {
                            visitorContext.info("Writing OpenAPI YAML to destination: " + f.toURI());
                            Writer writer = f.openWriter();
                            yamlMapper.writeValue(writer, openAPI);
                        } catch (Exception e) {
                            visitorContext.warn("Unable to generate swagger.yml: " + e.getMessage() , classElement);
                        }
                    }
                }
            });
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy