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

io.swagger.v3.parser.OpenAPIV3Parser Maven / Gradle / Ivy

The newest version!
package io.swagger.v3.parser;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.core.extensions.SwaggerParserExtension;
import io.swagger.v3.parser.core.models.AuthorizationValue;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
import io.swagger.v3.parser.exception.EncodingNotSupportedException;
import io.swagger.v3.parser.exception.ReadContentException;
import io.swagger.v3.parser.reference.DereferencerContext;
import io.swagger.v3.parser.reference.DereferencersFactory;
import io.swagger.v3.parser.reference.OpenAPIDereferencer;
import io.swagger.v3.parser.util.ClasspathHelper;
import io.swagger.v3.parser.util.DeserializationUtils;
import io.swagger.v3.parser.util.InlineModelResolver;
import io.swagger.v3.parser.util.OpenAPIDeserializer;
import io.swagger.v3.parser.util.RemoteUrl;
import io.swagger.v3.parser.util.ResolverFully;
import java.io.InputStream;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.ServiceLoader;
import javax.net.ssl.SSLHandshakeException;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class OpenAPIV3Parser implements SwaggerParserExtension {

    public static final String DISABLE_OAS31_RESOLVE = "disableOas31Resolve";
    private static final Logger LOGGER = LoggerFactory.getLogger(OpenAPIV3Parser.class);
    private static ObjectMapper JSON_MAPPER, YAML_MAPPER;
    /**
     * Encoding of the resource content with OpenAPI spec to parse.
     */
    private static String encoding = StandardCharsets.UTF_8.displayName();

    static {
        JSON_MAPPER = ObjectMapperFactory.createJson();
        YAML_MAPPER = ObjectMapperFactory.createYaml();
    }

    /**
     * Locates extensions on the current thread class loader and then, if it differs from this class classloader (as in
     * OSGi), locates extensions from this class classloader as well.
     * @return a list of extensions
     */
    public static List getExtensions() {
        final ClassLoader tccl = Thread.currentThread().getContextClassLoader();
        final List extensions = getExtensions(tccl);
        final ClassLoader cl = SwaggerParserExtension.class.getClassLoader();
        if (cl != tccl) {
            extensions.addAll(getExtensions(cl));
        }
        extensions.add(0, new OpenAPIV3Parser());
        return extensions;
    }

    protected static List getExtensions(ClassLoader cl) {
        final List extensions = new ArrayList<>();

        final ServiceLoader loader = ServiceLoader.load(SwaggerParserExtension.class, cl);
        for (SwaggerParserExtension extension : loader) {
            extensions.add(extension);
        }
        return extensions;
    }

    public static String getEncoding() {
        return encoding;
    }

    public static void setEncoding(String encoding) {
        if (!Charset.isSupported(encoding)) {
            throw new EncodingNotSupportedException(encoding);
        }
        OpenAPIV3Parser.encoding = encoding;
    }

    @Override
    public SwaggerParseResult readLocation(String url, List auth, ParseOptions options) {
        try {
            final String content = readContentFromLocation(url, emptyListIfNull(auth));
            LOGGER.debug("Loaded raw data: {}", content);
            return readContents(content, auth, options, url);
        } catch (ReadContentException e) {
            LOGGER.warn("Exception while reading:", e);
            return SwaggerParseResult.ofError(e.getMessage());
        }
    }

    @Override
    public SwaggerParseResult readContents(String swaggerAsString, List auth,
                                           ParseOptions options) {
        return readContents(swaggerAsString, auth, options, null);
    }

    public OpenAPI read(String location) {
        final ParseOptions options = new ParseOptions();
        options.setResolve(true);
        return read(location, null, options);
    }

    public OpenAPI read(String location, List auths, ParseOptions resolve) {
        if (location == null) {
            return null;
        }

        final List parserExtensions = getExtensions();
        SwaggerParseResult parsed;
        for (SwaggerParserExtension extension : parserExtensions) {
            parsed = extension.readLocation(location, auths, resolve);
            if (parsed.getMessages() != null) {
                for (String message : parsed.getMessages()) {
                    LOGGER.info("{}: {}", extension, message);
                }
            }
            final OpenAPI result = parsed.getOpenAPI();
            if (result != null) {
                return result;
            }
        }
        return null;
    }

    @Deprecated
    public SwaggerParseResult readWithInfo(String path, JsonNode node) {
        return parseJsonNode(path, node);
    }

    public SwaggerParseResult parseJsonNode(String path, JsonNode node) {
        return new OpenAPIDeserializer().deserialize(node, path,new ParseOptions());
    }
    public SwaggerParseResult parseJsonNode(String path, JsonNode node, ParseOptions options) {
        return new OpenAPIDeserializer().deserialize(node, path, options, options.isOaiAuthor());
    }

    public SwaggerParseResult readContents(String yaml) {
        final ParseOptions options = new ParseOptions();
        options.setResolve(true);
        return readContents(yaml, null, options);
    }

    public SwaggerParseResult readContents(String swaggerAsString, List auth, ParseOptions options,
                                            String location) {
        if (swaggerAsString == null || swaggerAsString.trim().isEmpty()) {
            return SwaggerParseResult.ofError("Null or empty definition");
        }

        try {
            final ObjectMapper mapper = getRightMapper(swaggerAsString);
            JsonNode rootNode;
            final SwaggerParseResult deserializationUtilsResult = new SwaggerParseResult();
            if (options != null && options.isLegacyYamlDeserialization()) {
                rootNode = mapper.readTree(swaggerAsString);
            } else {
                try {
                    rootNode = DeserializationUtils.deserializeIntoTree(swaggerAsString, location, options, deserializationUtilsResult);
                } catch (Exception e) {
                    rootNode = mapper.readTree(swaggerAsString);
                }
            }

            SwaggerParseResult result;
            if (options != null) {
                result = parseJsonNode(location, rootNode, options);
            }else {
                result = parseJsonNode(location, rootNode);
            }
            if (result.getOpenAPI() != null) {
                result = resolve(result, auth, options, location);
            }
            if (deserializationUtilsResult.getMessages() != null) {
                for (String s: deserializationUtilsResult.getMessages()) {
                    result.message(getParseErrorMessage(s, location));
                }
            }
            return result;

        } catch (JsonProcessingException e) {
            LOGGER.warn("Exception while parsing:", e);
            final String message = getParseErrorMessage(e.getOriginalMessage(), location);
            return SwaggerParseResult.ofError(message);
        } catch (Exception e) {
            LOGGER.warn("Exception while parsing:", e);
            final String message = getParseErrorMessage(e.getMessage(), location);
            return SwaggerParseResult.ofError(message);
        }
    }

    @Deprecated
    public SwaggerParseResult readWithInfo(String location, List auths) {
        return readContents(readContentFromLocation(location, auths), auths, null);
    }

    private SwaggerParseResult resolve(SwaggerParseResult result, List auth, ParseOptions options,
            String location) {
        try {
            if (options != null) {
                if (options.isResolve() || options.isResolveFully()) {
                    if (result.getOpenAPI().getOpenapi() != null && result.getOpenAPI().getOpenapi().startsWith("3.1")) {
                        if (StringUtils.isBlank(System.getenv(DISABLE_OAS31_RESOLVE))) {
                            DereferencerContext dereferencerContext = new DereferencerContext(
                                    result,
                                    auth,
                                    location,
                                    options,
                                    null,
                                    null,
                                    true
                            );
                            List dereferencers = DereferencersFactory.getInstance().getDereferencers();
                            if (dereferencers.iterator().hasNext()) {
                                OpenAPIDereferencer dereferencer = dereferencers.iterator().next();
                                dereferencer.dereference(dereferencerContext, dereferencers.iterator());
                            }
                            if (options.isResolveFully()) {
                                new ResolverFully(options.isResolveCombinators()).resolveFully(result.getOpenAPI());
                            }
                        } else {
                            String msg = "Resolution of OAS 3.1 spec disabled by 'disableOas31Resolve' env variable";
                            LOGGER.warn(msg);
                            result.getMessages().add(msg);
                        }
                    } else {
                        OpenAPIResolver resolver = new OpenAPIResolver(result.getOpenAPI(), emptyListIfNull(auth),
                                location, null, options);
                        resolver.resolve(result);
                        if (options.isResolveFully()) {
                            new ResolverFully(options.isResolveCombinators()).resolveFully(result.getOpenAPI());
                        }
                    }

                }
                if (options.isFlatten()) {
                    final InlineModelResolver inlineModelResolver =
                            new InlineModelResolver(options.isFlattenComposedSchemas(),
                                    options.isCamelCaseFlattenNaming(), options.isSkipMatches());
                    if (result.getOpenAPI()!= null) {
                        inlineModelResolver.flatten(result.getOpenAPI());
                    }
                }
            }
        } catch (Exception e) {
            LOGGER.warn("Exception while resolving:", e);
            // TODO verify if this change makes sense (adding resolve messages instead of replacing)
            result.getMessages().add(e.getMessage());
            // result.setMessages(Collections.singletonList(e.getMessage()));
        }
        return result;
    }

    private String getParseErrorMessage(String originalMessage, String location) {
        if (Objects.isNull(originalMessage)) {
            return String.format("Unable to parse `%s`", location);
        }
        if (originalMessage.startsWith("Duplicate field")) {
            return String.format("%s in `%s`", originalMessage, location);
        }
        return originalMessage;
    }

    private  List emptyListIfNull(List list) {
        return Objects.isNull(list) ? new ArrayList<>() : list;
    }

    private ObjectMapper getRightMapper(String data) {
        if (data.trim().startsWith("{")) {
            return JSON_MAPPER;
        }
        return YAML_MAPPER;
    }

    private String readContentFromLocation(String location, List auth) {
        final String adjustedLocation = location.replaceAll("\\\\", "/");
        try {
            if (adjustedLocation.toLowerCase().startsWith("http")) {
                return RemoteUrl.urlToString(adjustedLocation, auth);
            } else if (adjustedLocation.toLowerCase().startsWith("jar:")) {
                final InputStream in = new URI(adjustedLocation).toURL().openStream();
                return IOUtils.toString(in, encoding);
            } else {
                final String fileScheme = "file:";
                final Path path = adjustedLocation.toLowerCase().startsWith(fileScheme) ?
                        Paths.get(URI.create(adjustedLocation)) : Paths.get(adjustedLocation);
                if (Files.exists(path)) {
                    return FileUtils.readFileToString(path.toFile(), encoding);
                } else {
                    return ClasspathHelper.loadFileFromClasspath(adjustedLocation);
                }
            }
        } catch (SSLHandshakeException e) {
            final String message = String.format(
                    "Unable to read location `%s` due to a SSL configuration error. It is possible that the server SSL certificate is invalid, self-signed, or has an untrusted Certificate Authority.",
                    adjustedLocation);
            throw new ReadContentException(message, e);
        } catch (Exception e) {
            throw new ReadContentException(String.format("Unable to read location `%s`", adjustedLocation), e);
        }
    }

    /**
     * Transform the swagger-model version of AuthorizationValue into a parser-specific one, to avoid
     * dependencies across extensions
     *
     * @param input
     * @return
     */
    @Deprecated
    protected List transform(List input) {
        if(input == null) {
            return null;
        }

        List output = new ArrayList<>();

        for(AuthorizationValue value : input) {
            AuthorizationValue v = new AuthorizationValue();

            v.setKeyName(value.getKeyName());
            v.setValue(value.getValue());
            v.setType(value.getType());
            v.setUrlMatcher(value.getUrlMatcher());

            output.add(v);
        }

        return output;
    }


}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy