<file_sep>package io.swagger.v3.jaxrs2.resources;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Map;
import java.util.UUID;

@OpenAPIDefinition
@Path("distances")
public interface Ticket2793Resource {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    @ApiResponses({
            @ApiResponse(responseCode = "200", content = @Content(schema = @Schema(implementation = DistancesResponse.class)))
    })
    Response getDistances();

    abstract class DistancesResponse implements Map<UUID, Map<RouteMetric, Integer>> { }

    public static class RouteMetric {
        public String foo;
    }
}
<file_sep>package io.swagger.v3.jaxrs2.it.resources;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonView;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.tags.Tag;

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import java.util.Arrays;
import java.util.List;

@Path("/cars")
@Tag(name = "cars")
@Produces("application/json")
@Consumes("application/json")
public class CarResource {

    private static class View {

        interface Summary {
        }

        interface Detail extends View.Summary {
        }

        interface Sale {
        }
    }

    private static class Car {

        @JsonView(View.Summary.class)
        @JsonProperty("manufacture")
        private String made = "Honda";

        @JsonView({View.Summary.class, View.Detail.class})
        private String model = "Accord Hybrid";

        @JsonView({View.Detail.class})
        @JsonProperty
        private List<Tire> tires = Arrays.asList(new Tire());

        @JsonView({View.Sale.class})
        @JsonProperty
        private int price = 40000;

        // always in
        private String color = "White";

        public String getColor() {
            return color;
        }
    }

    private static class Tire {
        @JsonView(View.Summary.class)
        @JsonProperty("brand")
        private String made = "Michelin";

        @JsonView(View.Detail.class)
        @JsonProperty
        private String condition = "new";
    }

    @GET
    @Path("/summary")
    @JsonView({View.Summary.class})
    @Operation(description = "Return car summaries",
            responses = @ApiResponse(responseCode = "200", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Car.class)))))
    public Response getSummaries() {
        return Response.ok(Arrays.asList(new Car())).build();
    }

    @GET
    @Path("/detail")
    @JsonView({View.Detail.class})
    @Operation(description = "Return car detail",
            responses = @ApiResponse(responseCode = "200", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Car.class)))))
    public List<Car> getDetails() {
        return Arrays.asList(new Car());
    }

    @GET
    @Path("/sale")
    @JsonView({View.Summary.class, View.Sale.class})
    public List<Car> getSaleSummaries() {
        return Arrays.asList(new Car());
    }

    @GET
    @Path("/all")
    @Operation(description = "Return whole car",
            responses = @ApiResponse(responseCode = "200", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Car.class)))))
    public List<Car> getAll() {
        return Arrays.asList(new Car());
    }

}<file_sep>package io.swagger.v3.jaxrs2.integration.resources;

import io.swagger.v3.core.filter.OpenAPISpecFilter;
import io.swagger.v3.core.filter.SpecFilter;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.jaxrs2.integration.JaxrsOpenApiContextBuilder;
import io.swagger.v3.oas.integration.api.OpenAPIConfiguration;
import io.swagger.v3.oas.integration.api.OpenApiContext;
import io.swagger.v3.oas.models.OpenAPI;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletConfig;
import javax.ws.rs.core.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static io.swagger.v3.jaxrs2.integration.ServletConfigContextUtils.getContextIdFromServletConfig;

public abstract class BaseOpenApiResource {

    private static Logger LOGGER = LoggerFactory.getLogger(BaseOpenApiResource.class);

    protected Response getOpenApi(HttpHeaders headers,
                                  ServletConfig config,
                                  Application app,
                                  UriInfo uriInfo,
                                  String type) throws Exception {

        String ctxId = getContextIdFromServletConfig(config);
        OpenApiContext ctx = new JaxrsOpenApiContextBuilder()
                .servletConfig(config)
                .application(app)
                .resourcePackages(resourcePackages)
                .configLocation(configLocation)
                .openApiConfiguration(openApiConfiguration)
                .ctxId(ctxId)
                .buildContext(true);
        OpenAPI oas = ctx.read();
        boolean pretty = false;
        if (ctx.getOpenApiConfiguration() != null && Boolean.TRUE.equals(ctx.getOpenApiConfiguration().isPrettyPrint())) {
            pretty = true;
        }

        if (oas != null) {
            if (ctx.getOpenApiConfiguration() != null && ctx.getOpenApiConfiguration().getFilterClass() != null) {
                try {
                    OpenAPISpecFilter filterImpl = (OpenAPISpecFilter) Class.forName(ctx.getOpenApiConfiguration().getFilterClass()).newInstance();
                    SpecFilter f = new SpecFilter();
                    oas = f.filter(oas, filterImpl, getQueryParams(uriInfo.getQueryParameters()), getCookies(headers),
                            getHeaders(headers));
                } catch (Exception e) {
                    LOGGER.error("failed to load filter", e);
                }
            }
        }

        if (oas == null) {
            return Response.status(404).build();
        }

        if (StringUtils.isNotBlank(type) && type.trim().equalsIgnoreCase("yaml")) {
            return Response.status(Response.Status.OK)
                    .entity(pretty ? Yaml.pretty(oas) : Yaml.mapper().writeValueAsString(oas))
                    .type("application/yaml")
                    .build();
        } else {
            return Response.status(Response.Status.OK)
                    .entity(pretty ? Json.pretty(oas) : Json.mapper().writeValueAsString(oas))
                    .type(MediaType.APPLICATION_JSON_TYPE)
                    .build();
        }
    }

    private static Map<String, List<String>> getQueryParams(MultivaluedMap<String, String> params) {
        Map<String, List<String>> output = new HashMap<String, List<String>>();
        if (params != null) {
            for (String key : params.keySet()) {
                List<String> values = params.get(key);
                output.put(key, values);
            }
        }
        return output;
    }

    private static Map<String, String> getCookies(HttpHeaders headers) {
        Map<String, String> output = new HashMap<String, String>();
        if (headers != null) {
            for (String key : headers.getCookies().keySet()) {
                Cookie cookie = headers.getCookies().get(key);
                output.put(key, cookie.getValue());
            }
        }
        return output;
    }

    private static Map<String, List<String>> getHeaders(HttpHeaders headers) {
        Map<String, List<String>> output = new HashMap<String, List<String>>();
        if (headers != null) {
            for (String key : headers.getRequestHeaders().keySet()) {
                List<String> values = headers.getRequestHeaders().get(key);
                output.put(key, values);
            }
        }
        return output;
    }

    protected String configLocation;

    public String getConfigLocation() {
        return configLocation;
    }

    public void setConfigLocation(String configLocation) {
        this.configLocation = configLocation;
    }

    public BaseOpenApiResource configLocation(String configLocation) {
        setConfigLocation(configLocation);
        return this;
    }

    protected Set<String> resourcePackages;

    public Set<String> getResourcePackages() {
        return resourcePackages;
    }

    public void setResourcePackages(Set<String> resourcePackages) {
        this.resourcePackages = resourcePackages;
    }

    public BaseOpenApiResource resourcePackages(Set<String> resourcePackages) {
        setResourcePackages(resourcePackages);
        return this;
    }

    protected OpenAPIConfiguration openApiConfiguration;

    public OpenAPIConfiguration getOpenApiConfiguration() {
        return openApiConfiguration;
    }

    public void setOpenApiConfiguration(OpenAPIConfiguration openApiConfiguration) {
        this.openApiConfiguration = openApiConfiguration;
    }

    public BaseOpenApiResource openApiConfiguration(OpenAPIConfiguration openApiConfiguration) {
        setOpenApiConfiguration(openApiConfiguration);
        return this;
    }

}
<file_sep>package io.swagger.v3.jaxrs2.resources;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;

import javax.servlet.ServletConfig;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;

@Path("/resource/{id}")
@Produces({"application/json", "application/xml"})
public class ResourceWithKnownInjections {

    private Integer constructorParam;
    @QueryParam("fieldParam")
    private String fieldParam; // injection into a class field

    @Parameter(hidden = true)
    @QueryParam("hiddenParam")
    private String hiddenParam;

    // injection into a constructor parameter
    public ResourceWithKnownInjections(@PathParam("id") Integer constructorParam,
                                       @QueryParam("hiddenParam") @Parameter(hidden = true) String hiddenParam,
                                       @Context ServletConfig context) {
        this.constructorParam = constructorParam;
    }

    private ResourceWithKnownInjections(@PathParam("id") Integer constructorParam, @QueryParam("fakeParam") String
            fakeParam) {
        this.constructorParam = constructorParam;
    }

    @GET
    @Operation
    public String get(@QueryParam("methodParam") String methodParam) {
        // injection into a resource method parameter
        final StringBuilder sb = new StringBuilder();
        sb.append("Constructor param: ").append(constructorParam).append("\n");
        sb.append("Field param: ").append(fieldParam).append("\n");
        sb.append("Method param: ").append(methodParam).append("\n");
        return sb.toString();
    }
}
<file_sep>## [Since I could not get any feedback, this is no longer supported]

This is an extended version for Swagger Core with the addition of many features for those who would like to directly build the OpenAPI object instead of letting Swagger handle things automatically. Some options should also be valid for those using the automatic way.

By directly manipulating the OpenAPI object and this version, you have lots of customization that the standard versions can only dream of. For example:

1. You can add addition models to your OpenAPI with OpenAPIBuilderToolbox.add(api, SomeClass.class). 

2. If you only care about the documentation, not code generation and sometimes you want the Schema to have the fullname of the class (including package name), you can just set OpenAPIBuilderOptions.USE_FULLNAME to true.

3. For generic class like ClassA<T>, the generated API is sometimes ugly lile ClassAObject, and if you want to ignore that generic part to have only ClassA, you can just set OpenAPIBuilderOptions.OMIT_GENERIC to true.
 
4. For Enum, sometimes the default toString() and name() may differ, so if you want to ensure that name() is always used, you can just set OpenAPIBuilderOptions.USE_ENUMNAME to true.

5. For Enum, sometimes you have only 1 enum, but you have to use it lots of times, isn't it much better if we can declare it as a Schema and just put a reference for it instead of inlining all the time? You can achieve that easily by setting the OpenAPIBuilderOptions.RECYCLE_ENUM to true.

6. You know, you can specify parent with Schema(allOf = {...}), but then in the generated OpenAPI, all the properties from parent will be repeated again, which is a bit annoying. Therefore, how about we exclude all of those which have been declared in parent schema? Just set OpenAPIBuilderOptions.HIDE_PARENTS to true.

7. Last, but not least, sometimes, you do not want to use the default visibility, right? Normally, you want to also see the private fields, ignore setter and getter, but how can we do it in the old version? Use annotation? Oh my god, imagine that I have a bit more than 1000 classes in my project and you seriously expect me to put the damn annotations on all of them? Fat chance :V !!! Here, I expose the default object mapper in ObjectMapperFactory.defaultMapper and you can use it to control a lot more than you think.

Bonus: If you want to build the OpenAPI object directly, you can use Reflections to achieve much better results compared to letting Swagger do everything on its own.

<NAME>

A super handsome programmer who feels frustrated with Swagger Core's lack of functionalities :) 
<file_sep>package io.swagger.v3.core.converting.override;

import com.fasterxml.jackson.databind.JavaType;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.Schema;
import org.testng.annotations.Test;

import java.util.Iterator;

import static org.testng.Assert.*;

public class CustomConverterTest {

    @Test(description = "it should ignore properties with type Bar")
    public void testCustomConverter() {
        // add the custom converter
        final ModelConverters converters = new ModelConverters();
        converters.addConverter(new CustomConverter());

        final Schema model = converters.read(Foo.class).get("Foo");
        assertNotNull(model);
        assertEquals(model.getProperties().size(), 1);

        final Schema barProperty = (Schema) model.getProperties().get("bar");
        assertNull(barProperty);

        final Schema titleProperty = (Schema) model.getProperties().get("title");
        assertNotNull(titleProperty);
    }

    class CustomConverter implements ModelConverter {

        @Override
        public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator<ModelConverter> chain) {
            final JavaType jType = Json.mapper().constructType(type.getType());
            if (jType != null) {
                final Class<?> cls = jType.getRawClass();
                if (cls.equals(Bar.class)) {
                    return null;
                } else {
                    return chain.next().resolve(type, context, chain);
                }
            } else {
                return null;
            }
        }
    }

    class Foo {
        public Bar bar = null;
        public String title = null;
    }

    class Bar {
        public String foo = null;
    }
}
<file_sep>package io.swagger.v3.jaxrs2;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContextImpl;
import io.swagger.v3.core.filter.AbstractSpecFilter;
import io.swagger.v3.core.filter.OpenAPISpecFilter;
import io.swagger.v3.core.filter.SpecFilter;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.core.model.ApiDescription;
import io.swagger.v3.jaxrs2.matchers.SerializationMatchers;
import io.swagger.v3.jaxrs2.resources.BasicFieldsResource;
import io.swagger.v3.jaxrs2.resources.BookStoreTicket2646;
import io.swagger.v3.jaxrs2.resources.ClassPathParentResource;
import io.swagger.v3.jaxrs2.resources.ClassPathSubResource;
import io.swagger.v3.jaxrs2.resources.CompleteFieldsResource;
import io.swagger.v3.jaxrs2.resources.DeprecatedFieldsResource;
import io.swagger.v3.jaxrs2.resources.DuplicatedOperationIdResource;
import io.swagger.v3.jaxrs2.resources.DuplicatedOperationMethodNameResource;
import io.swagger.v3.jaxrs2.resources.DuplicatedSecurityResource;
import io.swagger.v3.jaxrs2.resources.EnhancedResponsesResource;
import io.swagger.v3.jaxrs2.resources.ExternalDocsReference;
import io.swagger.v3.jaxrs2.resources.MyClass;
import io.swagger.v3.jaxrs2.resources.MyOtherClass;
import io.swagger.v3.jaxrs2.resources.RefCallbackResource;
import io.swagger.v3.jaxrs2.resources.RefExamplesResource;
import io.swagger.v3.jaxrs2.resources.RefHeaderResource;
import io.swagger.v3.jaxrs2.resources.RefLinksResource;
import io.swagger.v3.jaxrs2.resources.RefParameterResource;
import io.swagger.v3.jaxrs2.resources.RefRequestBodyResource;
import io.swagger.v3.jaxrs2.resources.RefResponsesResource;
import io.swagger.v3.jaxrs2.resources.RefSecurityResource;
import io.swagger.v3.jaxrs2.resources.ResourceWithSubResource;
import io.swagger.v3.jaxrs2.resources.ResponseContentWithArrayResource;
import io.swagger.v3.jaxrs2.resources.ResponsesResource;
import io.swagger.v3.jaxrs2.resources.SecurityResource;
import io.swagger.v3.jaxrs2.resources.ServersResource;
import io.swagger.v3.jaxrs2.resources.SimpleCallbackResource;
import io.swagger.v3.jaxrs2.resources.SimpleExamplesResource;
import io.swagger.v3.jaxrs2.resources.SimpleMethods;
import io.swagger.v3.jaxrs2.resources.SimpleParameterResource;
import io.swagger.v3.jaxrs2.resources.SimpleRequestBodyResource;
import io.swagger.v3.jaxrs2.resources.SimpleResponsesResource;
import io.swagger.v3.jaxrs2.resources.SubResourceHead;
import io.swagger.v3.jaxrs2.resources.TagsResource;
import io.swagger.v3.jaxrs2.resources.Test2607;
import io.swagger.v3.jaxrs2.resources.TestResource;
import io.swagger.v3.jaxrs2.resources.Ticket2644ConcreteImplementation;
import io.swagger.v3.jaxrs2.resources.Ticket2763Resource;
import io.swagger.v3.jaxrs2.resources.Ticket2793Resource;
import io.swagger.v3.jaxrs2.resources.Ticket2794Resource;
import io.swagger.v3.jaxrs2.resources.Ticket2806Resource;
import io.swagger.v3.jaxrs2.resources.Ticket2818Resource;
import io.swagger.v3.jaxrs2.resources.Ticket2848Resource;
import io.swagger.v3.jaxrs2.resources.UserAnnotationResource;
import io.swagger.v3.jaxrs2.resources.extensions.ExtensionsResource;
import io.swagger.v3.jaxrs2.resources.extensions.OperationExtensionsResource;
import io.swagger.v3.jaxrs2.resources.extensions.ParameterExtensionsResource;
import io.swagger.v3.jaxrs2.resources.extensions.RequestBodyExtensionsResource;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
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.Operation;
import io.swagger.v3.oas.models.PathItem;
import io.swagger.v3.oas.models.Paths;
import io.swagger.v3.oas.models.callbacks.Callback;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.Schema;
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.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme;
import org.testng.annotations.Test;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.OPTIONS;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;

/**
 * Test for the Reader Class
 */
public class ReaderTest {
    private static final String EXAMPLE_TAG = "Example Tag";
    private static final String SECOND_TAG = "Second Tag";
    private static final String OPERATION_SUMMARY = "Operation Summary";
    private static final String OPERATION_DESCRIPTION = "Operation Description";
    private static final String CALLBACK_POST_OPERATION_DESCRIPTION = "payload data will be sent";
    private static final String CALLBACK_GET_OPERATION_DESCRIPTION = "payload data will be received";
    private static final String RESPONSE_CODE_200 = "200";
    private static final String RESPONSE_DESCRIPTION = "voila!";
    private static final String EXTERNAL_DOCS_DESCRIPTION = "External documentation description";
    private static final String EXTERNAL_DOCS_URL = "http://url.com";
    private static final String PARAMETER_IN = "path";
    private static final String PARAMETER_NAME = "subscriptionId";
    private static final String PARAMETER_DESCRIPTION = "parameter description";
    private static final String CALLBACK_SUBSCRIPTION_ID = "subscription";
    private static final String CALLBACK_URL = "http://$request.query.url";
    private static final String SECURITY_KEY = "security_key";
    private static final String SCOPE_VALUE1 = "write:pets";
    private static final String SCOPE_VALUE2 = "read:pets";
    private static final String PATH_REF = "/";
    private static final String PATH_1_REF = "/1";
    private static final String PATH_2_REF = "/path";
    private static final String SCHEMA_TYPE = "string";
    private static final String SCHEMA_FORMAT = "uuid";
    private static final String SCHEMA_DESCRIPTION = "the generated UUID";

    private static final int RESPONSES_NUMBER = 2;
    private static final int TAG_NUMBER = 2;
    private static final int SECURITY_SCHEMAS = 2;
    private static final int PARAMETER_NUMBER = 1;
    private static final int SECURITY_REQUIREMENT_NUMBER = 1;
    private static final int SCOPE_NUMBER = 2;
    private static final int PATHS_NUMBER = 1;

    @Test(description = "test a simple resource class")
    public void testSimpleReadClass() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(BasicFieldsResource.class);
        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 6);
        PathItem pathItem = paths.get(PATH_1_REF);
        assertNotNull(pathItem);
        assertNull(pathItem.getPost());
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertEquals(OPERATION_SUMMARY, operation.getSummary());
        assertEquals(OPERATION_DESCRIPTION, operation.getDescription());
    }

    @Test(description = "scan methods")
    public void testCompleteReadClass() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(CompleteFieldsResource.class);
        Paths paths = openAPI.getPaths();
        assertEquals(PATHS_NUMBER, paths.size());
        PathItem pathItem = paths.get(PATH_REF);
        assertNotNull(pathItem);
        assertNull(pathItem.getPost());
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertEquals(OPERATION_SUMMARY, operation.getSummary());
        assertEquals(OPERATION_DESCRIPTION, operation.getDescription());

        assertEquals(TAG_NUMBER, operation.getTags().size());
        assertEquals(EXAMPLE_TAG, operation.getTags().get(0));
        assertEquals(SECOND_TAG, operation.getTags().get(1));

        ExternalDocumentation externalDocs = operation.getExternalDocs();
        assertEquals(EXTERNAL_DOCS_DESCRIPTION, externalDocs.getDescription());
        assertEquals(EXTERNAL_DOCS_URL, externalDocs.getUrl());
    }

    @Test(description = "scan methods")
    public void testScanMethods() {
        Reader reader = new Reader(new OpenAPI());
        Method[] methods = SimpleMethods.class.getMethods();
        for (final Method method : methods) {
            if (isValidRestPath(method)) {
                Operation operation = reader.parseMethod(method, null, null);
                assertNotNull(operation);
            }
        }
    }

    @Test(description = "Get a Summary and Description")
    public void testGetSummaryAndDescription() {
        Reader reader = new Reader(new OpenAPI());
        Method[] methods = BasicFieldsResource.class.getMethods();
        Operation operation = reader.parseMethod(methods[0], null, null);
        assertNotNull(operation);
        assertEquals(OPERATION_SUMMARY, operation.getSummary());
        assertEquals(OPERATION_DESCRIPTION, operation.getDescription());
    }

    @Test(description = "Get a Duplicated Operation Id")
    public void testResolveDuplicatedOperationId() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(DuplicatedOperationIdResource.class);

        Paths paths = openAPI.getPaths();
        assertNotNull(paths);
        Operation firstOperation = paths.get(PATH_REF).getGet();
        Operation secondOperation = paths.get(PATH_2_REF).getGet();
        Operation thirdOperation = paths.get(PATH_REF).getPost();
        assertNotNull(firstOperation);
        assertNotNull(secondOperation);
        assertNotNull(thirdOperation);
        assertNotEquals(firstOperation.getOperationId(), secondOperation.getOperationId());
        assertNotEquals(firstOperation.getOperationId(), thirdOperation.getOperationId());
        assertNotEquals(secondOperation.getOperationId(), thirdOperation.getOperationId());
    }

    @Test(description = "Get a Duplicated Operation Id with same id as method name")
    public void testResolveDuplicatedOperationIdMethodName() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(DuplicatedOperationMethodNameResource.class);

        Paths paths = openAPI.getPaths();
        assertNotNull(paths);
        Operation firstOperation = paths.get("/1").getGet();
        Operation secondOperation = paths.get("/2").getGet();
        Operation secondPostOperation = paths.get("/2").getPost();
        Operation thirdPostOperation = paths.get("/3").getPost();
        assertNotNull(firstOperation);
        assertNotNull(secondOperation);
        assertNotNull(secondPostOperation);
        assertNotNull(thirdPostOperation);
        assertNotEquals(firstOperation.getOperationId(), secondOperation.getOperationId());
        assertNotEquals(secondOperation.getOperationId(), secondPostOperation.getOperationId());
        assertNotEquals(secondPostOperation.getOperationId(), thirdPostOperation.getOperationId());
        Operation thirdOperation = paths.get("/3").getGet();
        Operation fourthOperation = paths.get("/4").getGet();
        assertNotNull(thirdOperation);
        assertNotNull(fourthOperation);
        assertNotEquals(thirdOperation.getOperationId(), fourthOperation.getOperationId());

    }

    @Test(description = "Test a Set of classes")
    public void testSetOfClasses() {
        Set<Class<?>> classes = new HashSet<>();
        classes.add(SecurityResource.class);
        classes.add(DuplicatedSecurityResource.class);

        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(classes);
        assertNotNull(openAPI);
        assertEquals(openAPI.getPaths().get("/").getGet().getSecurity().size(), 2);
        assertEquals(openAPI.getPaths().get("/2").getGet().getSecurity().size(), 3);
        Components components = openAPI.getComponents();
        assertNotNull(components);
        Map<String, SecurityScheme> securitySchemes = components.getSecuritySchemes();
        assertNotNull(securitySchemes);
        assertEquals(SECURITY_SCHEMAS, securitySchemes.size());
    }

    @Test(description = "Deprecated Method")
    public void testDeprecatedMethod() {
        Reader reader = new Reader(new OpenAPI());
        Method[] methods = DeprecatedFieldsResource.class.getMethods();
        Operation deprecatedOperation = reader.parseMethod(methods[0], null, null);
        assertNotNull(deprecatedOperation);
        assertTrue(deprecatedOperation.getDeprecated());
    }

    @Test(description = "Get tags")
    public void testGetTags() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(TagsResource.class);
        Operation operation = openAPI.getPaths().get("/").getGet();
        assertNotNull(operation);
        assertEquals(6, operation.getTags().size());
        assertEquals(operation.getTags().get(3), EXAMPLE_TAG);
        assertEquals(operation.getTags().get(1), SECOND_TAG);
        assertEquals(openAPI.getTags().get(1).getDescription(), "desc definition");
        assertEquals(openAPI.getTags().get(2).getExternalDocs().getDescription(), "docs desc");
    }

    @Test(description = "Get servers")
    public void testGetServers() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ServersResource.class);
        Operation operation = openAPI.getPaths().get("/").getGet();
        assertNotNull(operation);
        assertEquals(5, operation.getServers().size());
        assertEquals(operation.getServers().get(0).getUrl(), "http://class1");
        assertEquals(operation.getServers().get(1).getUrl(), "http://class2");
        assertEquals(operation.getServers().get(2).getUrl(), "http://method1");
        assertEquals(operation.getServers().get(3).getUrl(), "http://method2");
        assertEquals(operation.getServers().get(4).getUrl(), "http://op1");

        assertEquals(operation.getServers().get(0).getVariables().size(), 2);
        assertEquals(operation.getServers().get(0).getVariables().get("var1").getDescription(), "var 1");
        assertEquals(operation.getServers().get(0).getVariables().get("var1").getEnum().size(), 2);

        assertEquals(openAPI.getServers().get(0).getDescription(), "definition server 1");
    }

    @Test(description = "Responses")
    public void testGetResponses() {
        Reader reader = new Reader(new OpenAPI());

        Method[] methods = ResponsesResource.class.getMethods();

        Operation responseOperation = reader.parseMethod(Arrays.stream(methods).filter(
                (method -> method.getName().equals("getResponses"))).findFirst().get(), null, null);
        assertNotNull(responseOperation);

        ApiResponses responses = responseOperation.getResponses();
        assertEquals(RESPONSES_NUMBER, responses.size());

        ApiResponse apiResponse = responses.get(RESPONSE_CODE_200);
        assertNotNull(apiResponse);
        assertEquals(RESPONSE_DESCRIPTION, apiResponse.getDescription());
    }

    @Test(description = "More Responses")
    public void testMoreResponses() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(EnhancedResponsesResource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "        object\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "        404:\n" +
                "          description: not found!\n" +
                "        400:\n" +
                "          description: boo\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/GenericError'\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  schemas:\n" +
                "    GenericError:\n" +
                "      type: object\n" +
                "    SampleResponseSchema:\n" +
                "      type: object\n";

        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Responses with composition")
    public void testGetResponsesWithComposition() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(ResponsesResource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "        object\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "        default:\n" +
                "          description: boo\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/GenericError'\n" +
                "      deprecated: true\n" +
                "  /allOf:\n" +
                "    get:\n" +
                "      summary: Test inheritance / polymorphism\n" +
                "      operationId: getAllOf\n" +
                "      parameters:\n" +
                "      - name: number\n" +
                "        in: query\n" +
                "        description: Test inheritance / polymorphism\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "        example: 1\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: bean answer\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                type: string\n" +
                "                allOf:\n" +
                "                - $ref: '#/components/schemas/MultipleSub1Bean'\n" +
                "                - $ref: '#/components/schemas/MultipleSub2Bean'\n" +
                "  /anyOf:\n" +
                "    get:\n" +
                "      summary: Test inheritance / polymorphism\n" +
                "      operationId: getAnyOf\n" +
                "      parameters:\n" +
                "      - name: number\n" +
                "        in: query\n" +
                "        description: Test inheritance / polymorphism\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "        example: 1\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: bean answer\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                type: string\n" +
                "                anyOf:\n" +
                "                - $ref: '#/components/schemas/MultipleSub1Bean'\n" +
                "                - $ref: '#/components/schemas/MultipleSub2Bean'\n" +
                "  /oneOf:\n" +
                "    get:\n" +
                "      summary: Test inheritance / polymorphism\n" +
                "      operationId: getOneOf\n" +
                "      parameters:\n" +
                "      - name: number\n" +
                "        in: query\n" +
                "        description: Test inheritance / polymorphism\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "        example: 1\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: bean answer\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                type: string\n" +
                "                oneOf:\n" +
                "                - $ref: '#/components/schemas/MultipleSub1Bean'\n" +
                "                - $ref: '#/components/schemas/MultipleSub2Bean'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    MultipleSub2Bean:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        d:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      description: MultipleSub2Bean\n" +
                "      allOf:\n" +
                "      - $ref: '#/components/schemas/MultipleBaseBean'\n" +
                "    GenericError:\n" +
                "      type: object\n" +
                "    MultipleBaseBean:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        beanType:\n" +
                "          type: string\n" +
                "        a:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "        b:\n" +
                "          type: string\n" +
                "      description: MultipleBaseBean\n" +
                "    MultipleSub1Bean:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        c:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      description: MultipleSub1Bean\n" +
                "      allOf:\n" +
                "      - $ref: '#/components/schemas/MultipleBaseBean'\n" +
                "    SampleResponseSchema:\n" +
                "      type: object";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "External Docs")
    public void testGetExternalDocs() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(ExternalDocsReference.class);
        Operation externalDocsOperation = openAPI.getPaths().get("/").getGet();

        ExternalDocumentation externalDocs = externalDocsOperation.getExternalDocs();
        assertEquals(externalDocs.getDescription(), "External documentation description in method");
        assertEquals(externalDocs.getUrl(), EXTERNAL_DOCS_URL);
        externalDocs = openAPI.getComponents().getSchemas().get("ExternalDocsSchema").getExternalDocs();
        assertEquals("External documentation description in schema", externalDocs.getDescription());
        assertEquals(externalDocs.getUrl(), EXTERNAL_DOCS_URL);
    }

    @Test(description = "OperationExtensions Tests")
    public void testOperationExtensions() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(OperationExtensionsResource.class);
        assertNotNull(openAPI);
        Map<String, Object> extensions = openAPI.getPaths().get("/").getGet().getExtensions();
        assertEquals(extensions.size(), 2);
        assertNotNull(extensions.get("x-operation"));
        assertNotNull(extensions.get("x-operation-extensions"));
    }

    @Test(description = "ParameterExtensions Tests")
    public void testParameterExtensions() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ParameterExtensionsResource.class);
        assertNotNull(openAPI);
        Map<String, Object> extensions = openAPI.getPaths().get("/").getGet().getParameters().get(0).getExtensions();
        assertNotNull(extensions);
        assertEquals(1, extensions.size());
        assertNotNull(extensions.get("x-parameter"));

    }

    @Test(description = "RequestBody Tests")
    public void testRequestBodyExtensions() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(RequestBodyExtensionsResource.class);
        assertNotNull(openAPI);
        Map<String, Object> extensions = openAPI.getPaths().get("/user").getGet().
                getRequestBody().getExtensions();
        assertNotNull(extensions);
        assertEquals(extensions.size(), 2);
        assertNotNull(extensions.get("x-extension"));
        assertNotNull(extensions.get("x-extension2"));
    }

    @Test(description = "Extensions Tests")
    public void testExtensions() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ExtensionsResource.class);
        assertNotNull(openAPI);
        SerializationMatchers.assertEqualsToYaml(openAPI, ExtensionsResource.YAML);
    }

    @Test(description = "Security Requirement")
    public void testSecurityRequirement() {
        Reader reader = new Reader(new OpenAPI());
        Method[] methods = SecurityResource.class.getDeclaredMethods();
        Operation securityOperation = reader.parseMethod(Arrays.stream(methods).filter(
                (method -> method.getName().equals("getSecurity"))).findFirst().get(), null, null);
        assertNotNull(securityOperation);
        List<SecurityRequirement> securityRequirements = securityOperation.getSecurity();
        assertNotNull(securityRequirements);
        assertEquals(SECURITY_REQUIREMENT_NUMBER, securityRequirements.size());
        List<String> scopes = securityRequirements.get(0).get(SECURITY_KEY);
        assertNotNull(scopes);
        assertEquals(SCOPE_NUMBER, scopes.size());
        assertEquals(SCOPE_VALUE1, scopes.get(0));
        assertEquals(SCOPE_VALUE2, scopes.get(1));
    }

    @Test(description = "Callbacks")
    public void testGetCallbacks() {
        Reader reader = new Reader(new OpenAPI());
        Method[] methods = SimpleCallbackResource.class.getMethods();
        Operation callbackOperation = reader.parseMethod(methods[0], null, null);
        assertNotNull(callbackOperation);
        Map<String, Callback> callbacks = callbackOperation.getCallbacks();
        assertNotNull(callbacks);
        Callback callback = callbacks.get(CALLBACK_SUBSCRIPTION_ID);
        assertNotNull(callback);
        PathItem pathItem = callback.get(CALLBACK_URL);
        assertNotNull(pathItem);
        Operation postOperation = pathItem.getPost();
        assertNotNull(postOperation);
        assertEquals(CALLBACK_POST_OPERATION_DESCRIPTION, postOperation.getDescription());

        Operation getOperation = pathItem.getGet();
        assertNotNull(getOperation);
        assertEquals(CALLBACK_GET_OPERATION_DESCRIPTION, getOperation.getDescription());

        Operation putOperation = pathItem.getPut();
        assertNotNull(putOperation);
        assertEquals(CALLBACK_POST_OPERATION_DESCRIPTION, putOperation.getDescription());
    }

    @Test(description = "Get the Param of an operation")
    public void testSubscriptionIdParam() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(BasicFieldsResource.class);
        assertNotNull(openAPI);
        Paths openAPIPaths = openAPI.getPaths();
        assertNotNull(openAPIPaths);
        PathItem pathItem = openAPIPaths.get(PATH_1_REF);
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        List<Parameter> parameters = operation.getParameters();
        assertNotNull(parameters);
        assertEquals(PARAMETER_NUMBER, parameters.size());
        Parameter parameter = parameters.get(0);
        assertNotNull(parameter);
        assertEquals(PARAMETER_IN, parameter.getIn());
        assertEquals(PARAMETER_NAME, parameter.getName());
        assertEquals(PARAMETER_DESCRIPTION, parameter.getDescription());
        assertEquals(Boolean.TRUE, parameter.getRequired());
        assertEquals(Boolean.TRUE, parameter.getAllowEmptyValue());
        assertEquals(Boolean.TRUE, parameter.getAllowReserved());
        Schema schema = parameter.getSchema();
        assertNotNull(schema);
        assertEquals(SCHEMA_TYPE, schema.getType());
        assertEquals(SCHEMA_FORMAT, schema.getFormat());
        assertEquals(SCHEMA_DESCRIPTION, schema.getDescription());
        assertEquals(Boolean.TRUE, schema.getReadOnly());

    }

    private Boolean isValidRestPath(Method method) {
        for (Class<? extends Annotation> item : Arrays.asList(GET.class, PUT.class, POST.class, DELETE.class,
                OPTIONS.class, HEAD.class)) {
            if (method.getAnnotation(item) != null) {
                return true;
            }
        }
        return false;
    }

    @Test
    public void testClassWithGenericType() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ClassWithGenericType.class);
        assertNotNull(openAPI);

        assertNotNull(openAPI.getComponents().getSchemas().get("IssueTemplateRet"));
        assertNotNull(openAPI.getComponents().getSchemas().get("B"));
        assertNotNull(openAPI.getComponents().getSchemas().get("B").getProperties().get("test"));
        assertEquals(((Schema) openAPI.getComponents().getSchemas().get("B").getProperties().get("test")).get$ref(), "#/components/schemas/IssueTemplateRet");

        //Yaml.prettyPrint(openAPI);
    }

    public static class A {
        public B b;
    }

    public static class IssueTemplate<T> {

        public T getTemplateTest() {
            return null;
        }

        public String getTemplateTestString() {
            return null;
        }
    }

    public static class B {
        public IssueTemplate<Ret> getTest() {
            return null;
        }
    }

    public static class Ret {
        public String c;

    }

    static class ClassWithGenericType {
        @Path("/test")
        @Produces("application/json")
        @Consumes("application/json")
        @GET
        @io.swagger.v3.oas.annotations.Operation(tags = "/receiver/rest")
        //public void test1(@QueryParam("aa") String a) {
        public void test1(A a) {
        }
    }

    @Test(description = "test resource with array in response content")
    public void test2497() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ResponseContentWithArrayResource.class);

        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 1);
        PathItem pathItem = paths.get("/user");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        ArraySchema schema = (ArraySchema) operation.getResponses().get("200").getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getItems().get$ref(), "#/components/schemas/User");
    }

    @Test(description = "test resource with subresources")
    public void testResourceWithSubresources() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ResourceWithSubResource.class);

        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 3);
        PathItem pathItem = paths.get("/employees/{id}");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        ArraySchema arraySchema = (ArraySchema) operation.getResponses().get("200").getContent().values().iterator().next().getSchema();
        assertNotNull(arraySchema);
        assertEquals(arraySchema.getItems().get$ref(), "#/components/schemas/Pet");

        pathItem = paths.get("/employees/{id}/{id}");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        Schema schema = operation.getResponses().get("200").getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.get$ref(), "#/components/schemas/Pet");

        pathItem = paths.get("/employees/noPath");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");
    }

    @Test(description = "test another resource with subresources")
    public void testAnotherResourceWithSubresources() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(TestResource.class);

        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 3);
        PathItem pathItem = paths.get("/test/status");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("application/json"));
        Schema schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

        pathItem = paths.get("/test/more/otherStatus");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("application/json"));
        assertFalse(operation.getResponses().getDefault().getContent().keySet().contains("application/xml"));
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

        pathItem = paths.get("/test/evenmore/otherStatus");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("application/json"));
        assertFalse(operation.getResponses().getDefault().getContent().keySet().contains("application/xml"));
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");
        assertEquals(operation.getRequestBody().getContent().get("application/json").getSchema().get$ref(), "#/components/schemas/Pet");
    }

    @Test(description = "test user annotation")
    public void testUserAnnotation() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(UserAnnotationResource.class);
        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 1);
        PathItem pathItem = paths.get("/test/status");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getTags().contains("test"));
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("application/json"));
        Schema schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

    }

    @Test(description = "scan resource with class-based sub-resources")
    public void testResourceWithClassBasedSubresources() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(SubResourceHead.class);

        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 3);
        PathItem pathItem = paths.get("/head/tail/hello");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("*/*"));
        Schema schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

        pathItem = paths.get("/head/tail/{string}");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("*/*"));
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

        pathItem = paths.get("/head/noPath");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("*/*"));
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");
    }

    @Test(description = "test ticket #2607 resource with subresources")
    public void test2607() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(Test2607.class);

        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 2);
        PathItem pathItem = paths.get("/swaggertest/name");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("text/plain"));
        Schema schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");

        pathItem = paths.get("/swaggertest/subresource/version");
        assertNotNull(pathItem);
        operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("text/plain"));
        schema = operation.getResponses().getDefault().getContent().values().iterator().next().getSchema();
        assertNotNull(schema);
        assertEquals(schema.getType(), "string");
    }

    @Test(description = "test ticket #2646 method annotated with @Produce")
    public void test2646() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(BookStoreTicket2646.class);
        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 2);
        PathItem pathItem = paths.get("/bookstore");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("application/json"));

        pathItem = paths.get("/bookstore/{id}");
        assertNotNull(pathItem);
        operation = pathItem.getDelete();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("*/*"));

    }

    @Test(description = "test ticket #2644 annotated interface")
    public void test2644() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(Ticket2644ConcreteImplementation.class);
        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 1);
        PathItem pathItem = paths.get("/resources");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertTrue(operation.getResponses().getDefault().getContent().keySet().contains("*/*"));

    }

    @Test(description = "Scan subresource per #2632")
    public void testSubResourceHasTheRightApiPath() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(ClassPathParentResource.class);
        assertNotNull(openAPI);
        assertNotNull(openAPI.getPaths().get("/v1/parent"));
        assertNotNull(openAPI.getPaths().get("/v1/parent/{id}"));
        assertEquals(openAPI.getPaths().size(), 2);

        OpenAPI subResourceApi = new Reader(new OpenAPI()).read(ClassPathSubResource.class);
        assertNotNull(subResourceApi);
        assertNotNull(subResourceApi.getPaths().get("/subresource"));
        assertNotNull(subResourceApi.getPaths().get("/subresource/{id}"));
        assertEquals(subResourceApi.getPaths().size(), 2);
    }

    @Test(description = "Resolve Model with XML Properties starting with is prefix per #2635")
    public void testModelResolverXMLPropertiesName() {
        final MyClass myClass = new MyClass();
        myClass.populate("isotonicDrink value", "softDrink value",
                "isoDrink value", "isotonicDrinkOnlyXmlElement value");

        Map<String, Schema> schemas = resolveJaxb(MyClass.class);
        assertNull(schemas.get("MyClass").getProperties().get("isotonicDrink"));
        assertNotNull(schemas.get("MyClass").getProperties().get("beerDrink"));
        assertNotNull(schemas.get("MyClass").getProperties().get("saltDrink"));

        // No JsonProperty or ApiModelProperty, keep original name
        assertNull(schemas.get("MyClass").getProperties().get("beerDrinkXmlElement"));
        assertNotNull(schemas.get("MyClass").getProperties().get("isotonicDrinkOnlyXmlElement"));

    }

    @Test(description = "Maintain Property names per #2635")
    public void testMaintainPropertyNames() {
        final MyOtherClass myOtherClass = new MyOtherClass();
        myOtherClass.populate("myPropertyName value");

        Map<String, Schema> schemas = resolveJaxb(MyOtherClass.class);
        assertNotNull(schemas.get("MyOtherClass").getProperties().get("MyPrOperTyName"));

    }

    private Map<String, Schema> resolveJaxb(Type type) {

        List<ModelConverter> converters = new CopyOnWriteArrayList<ModelConverter> ();

        ObjectMapper mapper = JaxbObjectMapperFactory.getMapper();
        converters.add(new ModelResolver(mapper));

        ModelConverterContextImpl context = new ModelConverterContextImpl(
                converters);

        Schema resolve = context.resolve(new AnnotatedType().type(type));
        Map<String, Schema> schemas = new HashMap<String, Schema>();
        for (Map.Entry<String, Schema> entry : context.getDefinedModels()
                .entrySet()) {
            if (entry.getValue().equals(resolve)) {
                schemas.put(entry.getKey(), entry.getValue());
            }
        }
        return schemas;
    }

    @Test(description = "Responses with array schema")
    public void testTicket2763() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(Ticket2763Resource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /array:\n" +
                "    get:\n" +
                "      operationId: getArrayResponses\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                type: array\n" +
                "                items:\n" +
                "                  $ref: https://openebench.bsc.es/monitor/tool/tool.json\n" +
                "  /schema:\n" +
                "    get:\n" +
                "      operationId: getSchemaResponses\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: https://openebench.bsc.es/monitor/tool/tool.json";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "array schema example")
    public void testTicket2806() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(Ticket2806Resource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /test:\n" +
                "    get:\n" +
                "      operationId: getTest\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Test'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    Test:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        stringArray:\n" +
                "          maxItems: 4\n" +
                "          minItems: 2\n" +
                "          uniqueItems: true\n" +
                "          type: array\n" +
                "          description: Array desc\n" +
                "          example:\n" +
                "          - aaa\n" +
                "          - bbb\n" +
                "          items:\n" +
                "            type: string\n" +
                "            description: Hello, World!\n" +
                "            example: Lorem ipsum dolor set\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "NotNull parameters")
    public void testTicket2794() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(Ticket2794Resource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /notnullparameter:\n" +
                "    get:\n" +
                "      operationId: getBooks\n" +
                "      parameters:\n" +
                "      - name: page\n" +
                "        in: query\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            application/json: {}\n" +
                "  /notnullparameter/newnotnull:\n" +
                "    post:\n" +
                "      operationId: insertnotnull\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Book'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "  /notnullparameter/new_reqBody_required:\n" +
                "    post:\n" +
                "      operationId: insert\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Book'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "components:\n" +
                "  schemas:\n" +
                "    Book:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        foo:\n" +
                "          type: string\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    /*
    TODO: in a scenario like the one in ticket 2793, currently no NPE is thrown
    but map is still not supported. When solved, update expected yaml in test case accordingly
     */
    @Test(description = "no NPE resolving map")
    public void testTicket2793() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(Ticket2793Resource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /distances:\n" +
                "    get:\n" +
                "      operationId: getDistances\n" +
                "      responses:\n" +
                "        200:\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/DistancesResponse'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    DistancesResponse:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        empty:\n" +
                "          type: boolean\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "test ticket #2818 @Parameter annotation")
    public void test2818() {
        Reader reader = new Reader(new OpenAPI());
        OpenAPI openAPI = reader.read(Ticket2818Resource.class);
        Paths paths = openAPI.getPaths();
        assertEquals(paths.size(), 1);
        PathItem pathItem = paths.get("/bookstore/{id}");
        assertNotNull(pathItem);
        Operation operation = pathItem.getGet();
        assertNotNull(operation);
        assertEquals(operation.getParameters().get(0).getSchema().getType(), "integer");
        assertEquals(operation.getParameters().get(0).getSchema().getFormat(), "int32");

    }

    @Test(description = "Responses with ref")
    public void testResponseWithRef() {
        Components components = new Components();
        components.addResponses("invalidJWT", new ApiResponse().description("when JWT token invalid/expired"));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);

        OpenAPI openAPI = reader.read(RefResponsesResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "        object\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "        default:\n" +
                "          description: boo\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/GenericError'\n" +
                "        401:\n" +
                "          $ref: '#/components/responses/invalidJWT'\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  schemas:\n" +
                "    GenericError:\n" +
                "      type: object\n" +
                "    SampleResponseSchema:\n" +
                "      type: object\n" +
                "  responses:\n" +
                "    invalidJWT:\n" +
                "      description: when JWT token invalid/expired";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Responses with filter")
    public void testResponseWithFilter() {
        Components components = new Components();
        components.addResponses("invalidJWT", new ApiResponse().description("when JWT token invalid/expired"));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);
        Reader reader = new Reader(oas);

        OpenAPI openAPI = reader.read(SimpleResponsesResource.class);


        OpenAPISpecFilter filterImpl = new RefResponseFilter();
        SpecFilter f = new SpecFilter();
        openAPI = f.filter(openAPI, filterImpl, null, null, null);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "        object\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      parameters: []\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "        default:\n" +
                "          description: boo\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/GenericError'\n" +
                "        401:\n" +
                "          $ref: '#/components/responses/invalidJWT'\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  schemas:\n" +
                "    GenericError:\n" +
                "      type: object\n" +
                "    SampleResponseSchema:\n" +
                "      type: object\n" +
                "  responses:\n" +
                "    invalidJWT:\n" +
                "      description: when JWT token invalid/expired";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    class RefResponseFilter extends AbstractSpecFilter {

        @Override
        public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params, Map<String, String> cookies, Map<String, List<String>> headers) {
            if ("getWithPayloadResponse".equals(operation.getOperationId())) {
                final ApiResponses apiResponses = (operation.getResponses() == null) ? new ApiResponses() : operation.getResponses();
                apiResponses.addApiResponse("401", new ApiResponse().$ref("#/components/responses/invalidJWT"));
                operation.setResponses(apiResponses);
                return Optional.of(operation);
            }
            return super.filterOperation(operation, api, params, cookies, headers);
        }
    }

    @Test(description = "array schema required property")
    public void testTicket2848() {
        Reader reader = new Reader(new OpenAPI());

        OpenAPI openAPI = reader.read(Ticket2848Resource.class);
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      operationId: getter\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Town'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    Town:\n" +
                "      required:\n" +
                "      - streets\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        streets:\n" +
                "          minItems: 1\n" +
                "          uniqueItems: true\n" +
                "          type: array\n" +
                "          items:\n" +
                "            type: string\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "RequestBody with ref")
    public void testRequestBodyWithRef() {
        Components components = new Components();
        components.addRequestBodies("User", new RequestBody().description("Test RequestBody"));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefRequestBodyResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with a payload complex input object\n" +
                "      operationId: sendPayload\n" +
                "      requestBody:\n" +
                "        $ref: '#/components/requestBodies/User'\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  schemas:\n" +
                "    User:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        username:\n" +
                "          type: string\n" +
                "        firstName:\n" +
                "          type: string\n" +
                "        lastName:\n" +
                "          type: string\n" +
                "        email:\n" +
                "          type: string\n" +
                "        password:\n" +
                "          type: string\n" +
                "        phone:\n" +
                "          type: string\n" +
                "        userStatus:\n" +
                "          type: integer\n" +
                "          description: User Status\n" +
                "          format: int32\n" +
                "      xml:\n" +
                "        name: User\n" +
                "  requestBodies:\n" +
                "    User:\n" +
                "      description: Test RequestBody\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "RequestBody with filter")
    public void testRequestBodyWithFilter() {
        Components components = new Components();
        components.addRequestBodies("User", new RequestBody());
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(SimpleRequestBodyResource.class);

        OpenAPISpecFilter filterImpl = new RefRequestBodyFilter();
        SpecFilter f = new SpecFilter();
        openAPI = f.filter(openAPI, filterImpl, null, null, null);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with a payload complex input object\n" +
                "      operationId: sendPayload\n" +
                "      parameters: []\n" +
                "      requestBody:\n" +
                "        $ref: '#/components/requestBodies/User'\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  schemas:\n" +
                "    User:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        username:\n" +
                "          type: string\n" +
                "        firstName:\n" +
                "          type: string\n" +
                "        lastName:\n" +
                "          type: string\n" +
                "        email:\n" +
                "          type: string\n" +
                "        password:\n" +
                "          type: string\n" +
                "        phone:\n" +
                "          type: string\n" +
                "        userStatus:\n" +
                "          type: integer\n" +
                "          description: User Status\n" +
                "          format: int32\n" +
                "      xml:\n" +
                "        name: User\n" +
                "  requestBodies:\n" +
                "    User: {}\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    class RefRequestBodyFilter extends AbstractSpecFilter {
        @Override
        public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params,
                                                   Map<String, String> cookies, Map<String, List<String>> headers) {
            if ("sendPayload".equals(operation.getOperationId())) {
                final RequestBody requestBody = new RequestBody();
                requestBody.set$ref("#/components/requestBodies/User");
                operation.setRequestBody(requestBody);
                return Optional.of(operation);
            }
            return super.filterOperation(operation, api, params, cookies, headers);
        }
    }

    @Test(description = "Parameter with ref")
    public void testParameterWithRef() {
        Components components = new Components();
        components.addParameters("id", new Parameter()
                .description("Id Description")
                .schema(new IntegerSchema())
                .in(ParameterIn.QUERY.toString())
                .example(1)
                .required(true));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefParameterResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with a payload complex input object\n" +
                "      operationId: sendPayload\n" +
                "      parameters:\n" +
                "      - $ref: '#/components/parameters/id'\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  parameters: \n" +
                "    id:\n" +
                "      in: query\n" +
                "      description: Id Description\n" +
                "      required: true\n" +
                "      schema:\n" +
                "        type: integer\n" +
                "        format: int32\n" +
                "      example: 1\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Responses with filter")
    public void testParameterWithFilter() {
        Components components = new Components();
        components.addParameters("id", new Parameter()
                .description("Id Description")
                .schema(new IntegerSchema())
                .in(ParameterIn.QUERY.toString())
                .example(1)
                .required(true));

        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(SimpleParameterResource.class);

        OpenAPISpecFilter filterImpl = new RefParameterFilter();
        SpecFilter f = new SpecFilter();
        openAPI = f.filter(openAPI, filterImpl, null, null, null);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with a payload complex input object\n" +
                "      operationId: sendPayload\n" +
                "      parameters:\n" +
                "      - $ref: '#/components/parameters/id'\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  parameters: \n" +
                "    id:\n" +
                "      in: query\n" +
                "      description: Id Description\n" +
                "      required: true\n" +
                "      schema:\n" +
                "        type: integer\n" +
                "        format: int32\n" +
                "      example: 1\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    class RefParameterFilter extends AbstractSpecFilter {
        @Override
        public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params,
                                                   Map<String, String> cookies, Map<String, List<String>> headers) {
            if ("sendPayload".equals(operation.getOperationId())) {
                final Parameter parameter = new Parameter();
                parameter.set$ref("#/components/parameters/id");
                operation.getParameters().clear();
                operation.addParametersItem(parameter);
                return Optional.of(operation);
            }
            return super.filterOperation(operation, api, params, cookies, headers);
        }
    }

    @Test(description = "Example with ref")
    public void testExampleWithRef() {
        Components components = new Components();
        components.addExamples("Id", new Example().description("Id Example").summary("Id Example").value("1"));

        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefExamplesResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /example:\n" +
                "    post:\n" +
                "      description: subscribes a client to updates relevant to the requestor's account\n" +
                "      operationId: subscribe\n" +
                "      parameters:\n" +
                "      - name: subscriptionId\n" +
                "        in: path\n" +
                "        required: true\n" +
                "        style: simple\n" +
                "        schema:\n" +
                "          type: string\n" +
                "          description: Schema\n" +
                "          example: Subscription example\n" +
                "        examples:\n" +
                "          subscriptionId_1:\n" +
                "            summary: Subscription number 12345\n" +
                "            description: subscriptionId_1\n" +
                "            value: 12345\n" +
                "            externalValue: Subscription external value 1\n" +
                "            $ref: '#/components/examples/Id'\n" +
                "        example: example\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SubscriptionResponse'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    SubscriptionResponse:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        subscriptionId:\n" +
                "          type: string\n" +
                "  examples:\n" +
                "    Id:\n" +
                "      summary: Id Example\n" +
                "      description: Id Example\n" +
                "      value: \"1\"\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Example with Ref Filter")
    public void testExampleWithFilter() {
        Components components = new Components();
        components.addExamples("Id", new Example().description("Id Example").summary("Id Example").value("1"));

        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(SimpleExamplesResource.class);

        OpenAPISpecFilter filterImpl = new RefExampleFilter();
        SpecFilter f = new SpecFilter();
        openAPI = f.filter(openAPI, filterImpl, null, null, null);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /example:\n" +
                "    post:\n" +
                "      description: subscribes a client to updates relevant to the requestor's account\n" +
                "      operationId: subscribe\n" +
                "      parameters:\n" +
                "      - example:\n" +
                "          $ref: '#/components/examples/Id'\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SubscriptionResponse'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    SubscriptionResponse:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        subscriptionId:\n" +
                "          type: string\n" +
                "  examples:\n" +
                "    Id:\n" +
                "      summary: Id Example\n" +
                "      description: Id Example\n" +
                "      value: \"1\"\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    class RefExampleFilter extends AbstractSpecFilter {
        @Override
        public Optional<Operation> filterOperation(Operation operation, ApiDescription api, Map<String, List<String>> params,
                                                   Map<String, String> cookies, Map<String, List<String>> headers) {
            if ("subscribe".equals(operation.getOperationId())) {
                final Parameter parameter = new Parameter();
                parameter.setExample(new Example().$ref("#/components/examples/Id"));
                operation.getParameters().clear();
                operation.addParametersItem(parameter);
                return Optional.of(operation);
            }
            return super.filterOperation(operation, api, params, cookies, headers);
        }
    }

    @Test(description = "Header with Ref")
    public void testHeaderWithRef() {
        Components components = new Components();
        components.addHeaders("Header", new Header().description("Header Description"));

        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefHeaderResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /path:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          headers:\n" +
                "            Rate-Limit-Limit:\n" +
                "              description: The number of allowed requests in the current period\n" +
                "              $ref: '#/components/headers/Header'\n" +
                "              style: simple\n" +
                "              schema:\n" +
                "                type: integer\n" +
                "      deprecated: true\n" +
                "components:\n" +
                "  headers:\n" +
                "    Header:\n" +
                "      description: Header Description\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "SecurityScheme with REf")
    public void testSecuritySchemeWithRef() {
        Components components = new Components();
        components.addSecuritySchemes("Security", new SecurityScheme().description("Security Example").
                name("Security").type(SecurityScheme.Type.OAUTH2).$ref("myOauth2Security").in(SecurityScheme.In.HEADER));

        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefSecurityResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      description: description\n" +
                "      operationId: Operation Id\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      security:\n" +
                "      - security_key:\n" +
                "        - write:pets\n" +
                "        - read:pets\n" +
                "components:\n" +
                "  securitySchemes:\n" +
                "    Security:\n" +
                "      type: oauth2\n" +
                "      description: Security Example\n" +
                "    myOauth2Security:\n" +
                "      type: oauth2\n" +
                "      description: myOauthSecurity Description\n" +
                "      $ref: '#/components/securitySchemes/Security'\n" +
                "      in: header\n" +
                "      flows:\n" +
                "        implicit:\n" +
                "          authorizationUrl: http://x.com\n" +
                "          scopes:\n" +
                "            write:pets: modify pets in your account\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Link with Ref")
    public void testLinkWithRef() {
        Components components = new Components();
        components.addLinks("Link", new Link().description("Link Description").operationId("id"));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefLinksResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /links:\n" +
                "    get:\n" +
                "      operationId: getUserWithAddress\n" +
                "      parameters:\n" +
                "      - name: userId\n" +
                "        in: query\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: test description\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "          links:\n" +
                "            address:\n" +
                "              operationId: getAddress\n" +
                "              parameters:\n" +
                "                userId: $request.query.userId\n" +
                "              $ref: '#/components/links/Link'\n" +
                "components:\n" +
                "  links:\n" +
                "    Link:\n" +
                "      operationId: id\n" +
                "      description: Link Description\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }

    @Test(description = "Callback with Ref")
    public void testCallbackWithRef() {
        Components components = new Components();
        components.addCallbacks("Callback", new Callback().addPathItem("/post", new PathItem().description("Post Path Item")));
        OpenAPI oas = new OpenAPI()
                .info(new Info().description("info"))
                .components(components);

        Reader reader = new Reader(oas);
        OpenAPI openAPI = reader.read(RefCallbackResource.class);

        String yaml = "openapi: 3.0.1\n" +
                "info:\n" +
                "  description: info\n" +
                "paths:\n" +
                "  /simplecallback:\n" +
                "    get:\n" +
                "      summary: Simple get operation\n" +
                "      operationId: getWithNoParameters\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "      callbacks:\n" +
                "        testCallback1:\n" +
                "          $ref: '#/components/callbacks/Callback'\n" +
                "components:\n" +
                "  callbacks:\n" +
                "    Callback:\n" +
                "      /post:\n" +
                "        description: Post Path Item\n";
        SerializationMatchers.assertEqualsToYaml(openAPI, yaml);
    }
}<file_sep>package io.swagger.v3.core.deserialization.properties;

import io.swagger.v3.core.util.Yaml;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.testng.annotations.Test;

import static org.testng.Assert.*;

public class ArrayPropertyDeserializerTest {
    private static final String yaml =
            "      operationId: something\n" +
                    "      responses:\n" +
                    "        200:\n" +
                    "          content:\n" +
                    "            '*/*':\n" +
                    "              examples:\n" +
                    "                simple:\n" +
                    "                  value: Array example\n" +
                    "                more:\n" +
                    "                  value: with two items\n" +
                    "              description: OK\n" +
                    "              schema:\n" +
                    "                type: array\n" +
                    "                minItems: 3\n" +
                    "                maxItems: 100\n" +
                    "                items:\n" +
                    "                  type: string\n";

    @Test(description = "it should includes the example in the arrayproperty")
    public void testArrayDeserialization() throws Exception {

        Operation operation = Yaml.mapper().readValue(yaml, Operation.class);
        ApiResponse response = operation.getResponses().get("200");
        assertNotNull(response);

        MediaType media = response.getContent().get("*/*");
        Schema responseSchema = media.getSchema();
        assertTrue(media.getExamples().size() == 2);
        assertNotNull(responseSchema);
        assertTrue(responseSchema instanceof ArraySchema);

        ArraySchema mp = (ArraySchema) responseSchema;
        assertEquals(mp.getMinItems(), new Integer(3));
        assertEquals(mp.getMaxItems(), new Integer(100));
    }
}<file_sep>package io.swagger.v3.core.serialization;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.oas.models.Car;
import io.swagger.v3.core.oas.models.Manufacturers;
import io.swagger.v3.core.oas.models.ReadOnlyModel;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.ExternalDocumentation;
import io.swagger.v3.oas.models.media.*;
import org.testng.annotations.Test;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.Map;

import static org.testng.Assert.*;

public class ModelSerializerTest {
    private final ObjectMapper m = Json.mapper();

    @Test(description = "it should convert a model")
    public void convertModel() throws JsonProcessingException {
        final Schema pet = new Schema();
        final Map<String, Schema> props = new LinkedHashMap<String, Schema>();
        props.put("intValue", new IntegerSchema());
        props.put("longValue", new IntegerSchema().format("int64"));
        props.put("dateValue", new DateSchema());
        props.put("dateTimeValue", new DateTimeSchema());
        pet.setProperties(props);
        pet.setRequired(Arrays.asList("intValue", "name"));
        final String json = "{\n" +
                "   \"required\":[\n" +
                "      \"intValue\"\n" +
                "   ],\n" +
                "   \"properties\":{\n" +
                "      \"intValue\":{\n" +
                "         \"type\":\"integer\",\n" +
                "         \"format\":\"int32\"\n" +
                "      },\n" +
                "      \"longValue\":{\n" +
                "         \"type\":\"integer\",\n" +
                "         \"format\":\"int64\"\n" +
                "      },\n" +
                "      \"dateValue\":{\n" +
                "         \"type\":\"string\",\n" +
                "         \"format\":\"date\"\n" +
                "      },\n" +
                "      \"dateTimeValue\":{\n" +
                "         \"type\":\"string\",\n" +
                "         \"format\":\"date-time\"\n" +
                "      }\n" +
                "   }\n" +
                "}";

        SerializationMatchers.assertEqualsToJson(pet, json);
    }

    @Test(description = "it should deserialize a model")
    public void deserializeModel() throws IOException {
        final String json =
                "{\n" +
                        "   \"required\":[\n" +
                        "      \"intValue\"\n" +
                        "   ],\n" +
                        "   \"type\":\"object\",\n" +
                        "   \"properties\":{\n" +
                        "      \"dateValue\":{\n" +
                        "         \"type\":\"string\",\n" +
                        "         \"format\":\"date\"\n" +
                        "      },\n" +
                        "      \"longValue\":{\n" +
                        "         \"type\":\"integer\",\n" +
                        "         \"format\":\"int64\"\n" +
                        "      },\n" +
                        "      \"dateTimeValue\":{\n" +
                        "         \"type\":\"string\",\n" +
                        "         \"format\":\"date-time\"\n" +
                        "      },\n" +
                        "      \"intValue\":{\n" +
                        "         \"type\":\"integer\",\n" +
                        "         \"format\":\"int32\"\n" +
                        "      },\n" +
                        "      \"byteArrayValue\":{\n" +
                        "         \"type\":\"string\",\n" +
                        "         \"format\":\"binary\"\n" +
                        "      }\n" +
                        "   }\n" +
                        "}";

        final Schema p = m.readValue(json, Schema.class);
        SerializationMatchers.assertEqualsToJson(p, json);
    }

    @Test(description = "it should serialize an array model")
    public void serializeArrayModel() throws IOException {
        final ArraySchema model = new ArraySchema();
        model.setItems(new Schema().$ref("Pet"));
        assertEquals(m.writeValueAsString(model), "{\"type\":\"array\",\"items\":{\"$ref\":\"#/components/schemas/Pet\"}}");
    }

    @Test(description = "it should deserialize an array model")
    public void deserializeArrayModel() throws IOException {
        final String json = "{\"type\":\"array\",\"items\":{\"$ref\":\"#/definitions/Pet\"}}";
        final Schema p = m.readValue(json, Schema.class);
        assertTrue(p instanceof ArraySchema);
        assertEquals(m.writeValueAsString(p), json);
    }

    @Test(description = "it should not create an xml object for $ref")
    public void shouldNotCreateXmlObjectForRef() throws IOException {
        final Schema model = new Schema().$ref("Monster");
        model.setDescription("oops");
        model.setExternalDocs(new ExternalDocumentation()
                .description("external docs")
                .url("http://swagger.io"));
        assertEquals(Json.mapper().writeValueAsString(model), "{\"$ref\":\"#/components/schemas/Monster\"}");
    }

    @Test(description = "it should make a field readOnly by annotation")
    public void makeFieldReadOnly() throws IOException {
        final Map<String, Schema> schemas = ModelConverters.getInstance().read(Car.class);
        final String json =
                "{\n" +
                        "   \"Car\":{\n" +
                        "      \"type\":\"object\",\n" +
                        "      \"properties\":{\n" +
                        "         \"wheelCount\":{\n" +
                        "            \"type\":\"integer\",\n" +
                        "            \"format\":\"int32\",\n" +
                        "            \"readOnly\":true\n" +
                        "         }\n" +
                        "      }\n" +
                        "   }\n" +
                        "}";
        SerializationMatchers.assertEqualsToJson(schemas, json);
    }

    @Test(description = "it should serialize a model with a Set")
    public void serializeModelWithSet() throws IOException {
        final Map<String, Schema> schemas = ModelConverters.getInstance().read(Manufacturers.class);
        final String json = "{\n" +
                "   \"Manufacturers\":{\n" +
                "      \"type\":\"object\",\n" +
                "      \"properties\":{\n" +
                "         \"countries\":{\n" +
                "            \"type\":\"array\",\n" +
                "            \"uniqueItems\":true,\n" +
                "            \"items\":{\n" +
                "               \"type\":\"string\"\n" +
                "            }\n" +
                "         }\n" +
                "      }\n" +
                "   }\n" +
                "}";
        SerializationMatchers.assertEqualsToJson(schemas, json);
    }

    @Test(description = "it should deserialize a model with object example")
    public void deserializeModelWithObjectExample() throws IOException {
        final String json = "{\n" +
                "   \"title\":\"Error\",\n" +
                "   \"type\":\"object\",\n" +
                "   \"properties\":{\n" +
                "      \"code\":{\n" +
                "         \"type\":\"integer\",\n" +
                "         \"format\":\"int32\"\n" +
                "      },\n" +
                "      \"message\":{\n" +
                "         \"type\":\"string\"\n" +
                "      },\n" +
                "      \"fields\":{\n" +
                "         \"type\":\"string\"\n" +
                "      }\n" +
                "   },\n" +
                "   \"example\":{\n" +
                "      \"code\":1,\n" +
                "      \"message\":\"hello\",\n" +
                "      \"fields\":\"abc\"\n" +
                "   }\n" +
                "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);
        assertEquals(Json.mapper().writeValueAsString(model.getExample()), "{\"code\":1,\"message\":\"hello\",\"fields\":\"abc\"}");
    }

    @Test(description = "it should deserialize a model with read-only property")
    public void deserializeModelWithReadOnlyProperty() throws IOException {
        final String json = "{\n" +
                "   \"properties\":{\n" +
                "      \"id\":{\n" +
                "         \"type\":\"integer\",\n" +
                "         \"format\":\"int32\",\n" +
                "         \"readOnly\":true\n" +
                "      }\n" +
                "   }\n" +
                "}";
        final Schema model = Json.mapper().readValue(json, Schema.class);
        Schema property = (Schema) model.getProperties().get("id");
        assertTrue(property.getReadOnly());
    }

    @Test(description = "it should generate a JSON with read-only from pojo, #1161")
    public void readOnlyJsonGeneration() throws IOException {
        Map<String, Schema> models = ModelConverters.getInstance().read(ReadOnlyModel.class);

        Schema model = models.get("ReadOnlyModel");

        Schema id = (Schema) model.getProperties().get("id");
        assertTrue(id.getReadOnly());

        Schema readWriteId = (Schema) model.getProperties().get("readWriteId");
        assertNull(readWriteId.getReadOnly());
    }

    @Test(description = "it should generate an integer field with enum")
    public void integerEnumGeneration() throws IOException {
        final String json = "{\n" +
                "   \"properties\":{\n" +
                "      \"id\":{\n" +
                "         \"description\":\"fun!\",\n" +
                "         \"type\":\"integer\",\n" +
                "         \"format\":\"int32\",\n" +
                "         \"readOnly\":true,\n" +
                "         \"enum\": [ 0, 1]\n" +
                "      }\n" +
                "   }\n" +
                "}";
        final Schema model = Json.mapper().readValue(json, Schema.class);
        IntegerSchema p = (IntegerSchema) model.getProperties().get("id");

        assertNotNull(p.getEnum());
        assertEquals(p.getEnum().get(0), new Integer(0));
        assertEquals(p.getEnum().get(1), new Integer(1));
    }

    @Test(description = "it retains enums per ")
    public void testEnumParser() throws IOException {
        String json = "{\n" +
                "  \"properties\": {\n" +
                "    \"AdvStateType\": {\n" +
                "      \"description\": \"Advertising State\",\n" +
                "      \"enum\": [\n" +
                "        \"off\",\n" +
                "        \"on\"\n" +
                "      ],\n" +
                "      \"type\": \"string\"\n" +
                "    }\n" +
                "  }\n" +
                "}";
        final Schema model = Json.mapper().readValue(json, Schema.class);
        StringSchema p = (StringSchema) model.getProperties().get("AdvStateType");

        assertNotNull(p.getEnum());
        assertEquals(p.getEnum().get(0), "off");
        assertEquals(p.getEnum().get(1), "on");
    }

    @Test
    public void testPrimitiveModel() throws Exception {
        String json = "{\n" +
                "  \"type\": \"string\",\n" +
                "  \"enum\": [\n" +
                "    \"a\",\n" +
                "    \"b\",\n" +
                "    \"c\"\n" +
                "  ]\n" +
                "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);

        assertNotNull(model.getEnum());
        assertTrue(model.getEnum().size() == 3);
    }

    @Test
    public void testIssue1852() throws Exception {
        String json = "{\n" +
                "  \"type\": \"integer\",\n" +
                "  \"minimum\": 10,\n" +
                "  \"maximum\": 20,\n" +
                "  \"default\": 15\n" +
                "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);

        assertEquals(model.getMinimum().intValue(), 10);
        assertEquals(model.getMaximum().intValue(), 20);
        assertEquals(model.getDefault(), 15);
    }

    @Test
    public void testIssue2064Neg() throws Exception {
        String json =
                "{\n" +
                        "  \"type\": \"string\",\n" +
                        "  \"uniqueItems\": false\n" +
                        "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);

        assertFalse(model.getUniqueItems());
    }

    @Test
    public void testIssue2064() throws Exception {
        String json =
                "{\n" +
                        "  \"type\": \"string\",\n" +
                        "  \"uniqueItems\": true\n" +
                        "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);

        assertTrue(model.getUniqueItems());
    }

    @Test
    public void testIssue2064Ip() throws Exception {
        String json =
                "{\n" +
                        "  \"type\": \"object\",\n" +
                        "  \"properties\": {\n" +
                        "    \"id\": {\n" +
                        "      \"type\": \"integer\",\n" +
                        "      \"format\": \"int32\",\n" +
                        "      \"multipleOf\": 3.0\n" +
                        "    }\n" +
                        "  }\n" +
                        "}";

        final Schema model = Json.mapper().readValue(json, Schema.class);

        IntegerSchema ip = (IntegerSchema) model.getProperties().get("id");
        assertEquals(ip.getMultipleOf(), new BigDecimal("3.0"));

    }
}<file_sep>package io.swagger.v3.core.resolving;

import com.google.common.base.Functions;
import com.google.common.collect.Collections2;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverterContextImpl;
import io.swagger.v3.core.jackson.ModelResolver;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.testng.annotations.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;

import static org.testng.Assert.*;

public class EnumTest extends SwaggerTestBase {

    @Test
    public void testEnum() throws Exception {
        final ModelResolver modelResolver = new ModelResolver(mapper());
        final ModelConverterContextImpl context = new ModelConverterContextImpl(modelResolver);

        final Schema model = context.resolve((new AnnotatedType().type(Currency.class)));
        assertNotNull(model);
        assertTrue(model instanceof StringSchema);
        final StringSchema strModel = (StringSchema) model;
        assertNotNull(strModel.getEnum());
        final Collection<String> modelValues =
                new ArrayList<String>(Collections2.transform(Arrays.asList(Currency.values()), Functions.toStringFunction()));
        assertEquals(strModel.getEnum(), modelValues);


        final Schema property = context.resolve(new AnnotatedType().type(Currency.class).schemaProperty(true));
        assertNotNull(property);
        assertTrue(property instanceof StringSchema);
        final StringSchema strProperty = (StringSchema) property;
        assertNotNull(strProperty.getEnum());
        final Collection<String> values =
                new ArrayList<String>(Collections2.transform(Arrays.asList(Currency.values()), Functions.toStringFunction()));
        assertEquals(strProperty.getEnum(), values);
    }

    public enum Currency {
        USA, CANADA
    }
}
<file_sep>package io.swagger.v3.core.util;

import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.converter.ResolvedSchema;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.Schema;

import java.lang.reflect.Type;
import java.util.Map;

public class OpenAPIBuilderToolbox {
	// add additional model and its respective dependencies to OpenAPI
	public static void addModel(OpenAPI api, Type type) {
		if (api.getComponents() == null) {
			api.components(new Components());
		}

		ResolvedSchema resolvedSchema = ModelConverters.getInstance()
				.resolveAsResolvedSchema(
						new AnnotatedType(type).resolveAsRef(true)
				);

		if (resolvedSchema.schema != null) {
			Map<String, Schema> schemaMap = resolvedSchema.referencedSchemas;
			if (schemaMap != null) {
				schemaMap.forEach((key, schema) -> api.getComponents().addSchemas(key, schema));
			}
		}
	}
}
<file_sep>/**
 * Copyright 2017 SmartBear Software
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.swagger.v3.oas.annotations.security;

import io.swagger.v3.oas.annotations.enums.SecuritySchemeIn;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.extensions.Extension;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.TYPE;

/**
 * The annotation may be used at class level (also on multiple classes) to add securitySchemes to spec
 * components section.
 *
 * @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#securitySchemeObject">Security Scheme (OpenAPI specification)</a>
 * @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#componentsObject">Components (OpenAPI specification)</a>
 **/
@Target({TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SecuritySchemes.class)
@Inherited
public @interface SecurityScheme {
    /**
     * The type of the security scheme. Valid values are "apiKey", "http", "oauth2", "openIdConnect".
     *
     * @return String type
     **/
    SecuritySchemeType type();

    /**
     * The name identifying this security scheme
     *
     * @return String name
     **/
    String name() default "";

    /**
     * A short description for security scheme. CommonMark syntax can be used for rich text representation.
     *
     * @return String description
     **/
    String description() default "";

    /**
     * The name of the header or query parameter to be used. Applies to apiKey type.
     * Maps to "name" property of <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#securitySchemeObject">Security Scheme (OpenAPI specification)</a>
     *
     * @return String paramName
     **/
    String paramName() default "";

    /**
     * The location of the API key. Valid values are "query" or "header".  Applies to apiKey type.
     *
     * @return String in
     **/
    SecuritySchemeIn in() default SecuritySchemeIn.DEFAULT;

    /**
     * The name of the HTTP Authorization scheme to be used in the Authorization header as defined in RFC 7235.  Applies to http type.
     *
     * @return String scheme
     **/
    String scheme() default "";

    /**
     * A hint to the client to identify how the bearer token is formatted. Bearer tokens are usually generated by an
     * authorization server, so this information is primarily for documentation purposes.  Applies to http ("bearer") type.
     *
     * @return String bearerFormat
     **/
    String bearerFormat() default "";

    /**
     * Required. An object containing configuration information for the flow types supported.  Applies to oauth2 type.
     *
     * @return OAuthFlows flows
     **/
    OAuthFlows flows() default @OAuthFlows;

    /**
     * Required. OpenId Connect URL to discover OAuth2 configuration values. This MUST be in the form of a URL.  Applies to openIdConnect.
     *
     * @return String openIdConnectUrl
     **/
    String openIdConnectUrl() default "";

    /**
     * The list of optional extensions
     *
     * @return an optional array of extensions
     */
    Extension[] extensions() default {};

    /**
     * A reference to a SecurityScheme defined in components securitySchemes.
     *
     * @since 2.0.3
     * @return the reference
     **/
    String ref() default "";

}
<file_sep>package io.swagger.v3.core.filter;

import com.google.common.collect.Sets;
import io.swagger.v3.core.filter.resources.*;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.ResourceUtils;
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.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.lang3.StringUtils;
import org.testng.annotations.Test;

import java.io.IOException;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static org.testng.Assert.*;

public class SpecFilterTest {

    private static final String RESOURCE_RECURSIVE_MODELS = "specFiles/recursivemodels.json";
    private static final String RESOURCE_PATH = "specFiles/petstore-3.0-v2.json";
    private static final String RESOURCE_REFERRED_SCHEMAS = "specFiles/petstore-3.0-referred-schemas.json";
    private static final String RESOURCE_PATH_WITHOUT_MODELS = "specFiles/petstore-3.0-v2_withoutModels.json";
    private static final String RESOURCE_DEPRECATED_OPERATIONS = "specFiles/deprecatedoperationmodel.json";
    private static final String CHANGED_OPERATION_ID = "Changed Operation";
    private static final String CHANGED_OPERATION_DESCRIPTION = "Changing some attributes of the operation";
    private static final String NEW_OPERATION_ID = "New Operation";
    private static final String NEW_OPERATION_DESCRIPTION = "Replaced Operation";
    private static final String QUERY = "query";
    private static final String PET_MODEL = "Pet";
    private static final String TAG_MODEL = "/Tag";
    private static final String PET_TAG = "pet";
    private static final String STORE_TAG = "store";
    private static final String USER_TAG = "user";

    @Test(description = "it should clone everything")
    public void cloneEverything() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoOpOperationsFilter(), null, null, null);

        assertEquals(Json.pretty(filtered), Json.pretty(openAPI));
    }

    @Test(description = "it should filter away get operations in a resource")
    public void filterAwayGetOperations() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final NoGetOperationsFilter filter = new NoGetOperationsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);

        if (filtered.getPaths() != null) {
            for (Map.Entry<String, PathItem> entry : filtered.getPaths().entrySet()) {
                assertNull(entry.getValue().getGet());
            }
        } else {
            fail("paths should not be null");
        }
    }

    @Test(description = "it should filter away the pet resource")
    public void filterAwayPetResource() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final NoPetOperationsFilter filter = new NoPetOperationsFilter();

        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);
        if (filtered.getPaths() != null) {
            for (Map.Entry<String, PathItem> entry : filtered.getPaths().entrySet()) {
                assertNull(entry.getValue().getDelete());
                assertNull(entry.getValue().getPost());
                assertNull(entry.getValue().getPut());
                assertNull(entry.getValue().getGet());
                assertNull(entry.getValue().getHead());
                assertNull(entry.getValue().getOptions());
            }
        } else {
            fail("paths should not be null");
        }
    }

    @Test(description = "it should replace away with a new operation")
    public void replaceGetResources() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        OpenAPI filter = new SpecFilter().filter(openAPI, new ReplaceGetOperationsFilter(), null, null, null);
        assertOperations(filter, NEW_OPERATION_ID, NEW_OPERATION_DESCRIPTION);
    }

    @Test(description = "it should change away with a new operation")
    public void changeGetResources() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        OpenAPI filter = new SpecFilter().filter(openAPI, new ChangeGetOperationsFilter(), null, null, null);
        assertOperations(filter, CHANGED_OPERATION_ID, CHANGED_OPERATION_DESCRIPTION);
    }

    private void assertOperations(OpenAPI filtered, String operationId, String description) {
        if (filtered.getPaths() != null) {
            for (Map.Entry<String, PathItem> entry : filtered.getPaths().entrySet()) {
                Operation get = entry.getValue().getGet();
                if (get != null) {
                    assertEquals(get.getOperationId(), operationId);
                    assertEquals(get.getDescription(), description);
                }
            }
        } else {
            fail("paths should not be null");
        }
    }

    @Test(description = "it should filter an openAPI object")
    public void filterAwayOpenAPI() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoOpenAPIFilter(), null, null, null);
        assertNull(filtered);
    }

    @Test(description = "it should filter any PathItem objects without Ref")
    public void filterAwayPathItemWithoutRef() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoPathItemFilter(), null, null, null);
        assertEquals(0, filtered.getPaths().size());
    }

    @Test(description = "it should filter any query parameter")
    public void filterAwayQueryParameters() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoParametersWithoutQueryInFilter(), null, null, null);
        if (filtered.getPaths() != null) {
            for (Map.Entry<String, PathItem> entry : filtered.getPaths().entrySet()) {
                validateParameters(entry.getValue().getGet());
                validateParameters(entry.getValue().getPost());
                validateParameters(entry.getValue().getPut());
                validateParameters(entry.getValue().getPatch());
                validateParameters(entry.getValue().getHead());
                validateParameters(entry.getValue().getDelete());
                validateParameters(entry.getValue().getOptions());
            }
        }
    }

    private void validateParameters(Operation operation) {
        if (operation != null) {
            for (Parameter parameter : operation.getParameters()) {
                assertNotEquals(QUERY, parameter.getIn());
            }
        }
    }

    @Test(description = "it should clone everything concurrently")
    public void cloneEverythingConcurrent() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);

        ThreadGroup tg = new ThreadGroup("SpecFilterTest" + "|" + System.currentTimeMillis());
        final Map<String, OpenAPI> filteredMap = new ConcurrentHashMap<>();
        for (int i = 0; i < 10; i++) {
            final int id = i;
            new Thread(tg, "SpecFilterTest") {
                public void run() {
                    try {
                        filteredMap.put("filtered " + id, new SpecFilter().filter(openAPI, new NoOpOperationsFilter(), null, null, null));
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }.start();
        }

        new Thread(new FailureHandler(tg, filteredMap, openAPI)).start();
    }

    class FailureHandler implements Runnable {
        ThreadGroup tg;
        Map<String, OpenAPI> filteredMap;
        private OpenAPI openAPI;

        private FailureHandler(ThreadGroup tg, Map<String, OpenAPI> filteredMap, OpenAPI openAPI) {
            this.tg = tg;
            this.filteredMap = filteredMap;
            this.openAPI = openAPI;
        }

        @Override
        public void run() {
            try {
                Thread[] thds = new Thread[tg.activeCount()];
                tg.enumerate(thds);
                for (Thread t : thds) {
                    if (t != null) {
                        t.join(10000);
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            for (OpenAPI filtered : filteredMap.values()) {
                assertEquals(Json.pretty(openAPI), Json.pretty(filtered));
            }
        }
    }

    @Test(description = "it should clone everything from JSON without models")
    public void cloneWithoutModels() throws IOException {
        final String json = ResourceUtils.loadClassResource(getClass(), RESOURCE_PATH_WITHOUT_MODELS);
        final OpenAPI openAPI = Json.mapper().readValue(json, OpenAPI.class);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoOpOperationsFilter(), null, null, null);

        SerializationMatchers.assertEqualsToJson(filtered, json);
    }

    @Test
    public void shouldRemoveBrokenRefs() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        openAPI.getPaths().get("/pet/{petId}").getGet().getResponses().getDefault().getHeaders().remove("X-Rate-Limit-Limit");
        assertNotNull(openAPI.getComponents().getSchemas().get("PetHeader"));
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        assertNull(filtered.getComponents().getSchemas().get("PetHeader"));
        assertNotNull(filtered.getComponents().getSchemas().get("Category"));
        assertNotNull(filtered.getComponents().getSchemas().get("Pet"));
    }

    @Test
    public void shouldNotRemoveGoodRefs() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        assertNotNull(openAPI.getComponents().getSchemas().get("PetHeader"));
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("PetHeader"));
        assertNotNull(filtered.getComponents().getSchemas().get("Category"));
    }

    @Test(description = "it should filter any Pet Ref in Schemas")
    public void filterAwayPetRefInSchemas() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final OpenAPI filtered = new SpecFilter().filter(openAPI, new NoPetRefSchemaFilter(), null, null, null);
        validateSchemasInComponents(filtered.getComponents(), PET_MODEL);
    }

    private void validateSchemasInComponents(Components components, String model) {
        if (components != null) {
            if (components.getSchemas() != null) {
                components.getSchemas().forEach((k, v) -> assertNotEquals(model, k));
            }
        }
    }

    @Test(description = "it should filter away secret parameters")
    public void filterAwaySecretParameters() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final RemoveInternalParamsFilter filter = new RemoveInternalParamsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);

        if (filtered.getPaths() != null) {
            for (Map.Entry<String, PathItem> entry : filtered.getPaths().entrySet()) {
                final Operation get = entry.getValue().getGet();
                if (get != null) {
                    for (Parameter param : get.getParameters()) {
                        final String description = param.getDescription();
                        if (StringUtils.isNotBlank(description)) {
                            assertFalse(description.startsWith("secret"));
                        }
                    }
                }
            }
        } else {
            fail("paths should not be null");
        }
    }

    @Test(description = "it should filter away internal model properties")
    public void filterAwayInternalModelProperties() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        final InternalModelPropertiesRemoverFilter filter = new InternalModelPropertiesRemoverFilter();

        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);
        for (Map.Entry<String, Schema> entry : filtered.getComponents().getSchemas().entrySet()) {
            for (String propName : (Set<String>) entry.getValue().getProperties().keySet()) {
                assertFalse(propName.startsWith("_"));
            }
        }
    }

    @Test(description = "it should retain non-broken reference model composed properties")
    public void retainNonBrokenReferenceModelComposedProperties() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_REFERRED_SCHEMAS);

        assertNotNull(openAPI.getComponents().getSchemas().get("User"));

        final NoOpOperationsFilter noOperationsFilter = new NoOpOperationsFilter();
        OpenAPI filtered = new SpecFilter().filter(openAPI, noOperationsFilter, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("User"));

        final RemoveUnreferencedDefinitionsFilter refFilter = new RemoveUnreferencedDefinitionsFilter();
        filtered = new SpecFilter().filter(openAPI, refFilter, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("User"));
        assertNotNull(filtered.getComponents().getSchemas().get("Pet"));

    }

    @Test(description = "recursive models, e.g. A-> A or A-> B and B -> A should not result in stack overflow")
    public void removeUnreferencedDefinitionsOfRecuriveModels() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_RECURSIVE_MODELS);
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("SelfReferencingModel"));
        assertNotNull(filtered.getComponents().getSchemas().get("IndirectRecursiveModelA"));
        assertNotNull(filtered.getComponents().getSchemas().get("IndirectRecursiveModelB"));
    }

    @Test(description = "broken references should not result in NPE")
    public void removeUnreferencedModelOverride() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_REFERRED_SCHEMAS);
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("Order"));
    }

    @Test(description = "Retain models referenced from additonalProperties")
    public void retainModelsReferencesFromAdditionalProperties() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_REFERRED_SCHEMAS);
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        assertNotNull(filtered.getComponents().getSchemas().get("Order"));
        assertNotNull(filtered.getComponents().getSchemas().get("ReferredOrder"));
    }

    @Test(description = "Clone should retain any 'deperecated' flags present on operations")
    public void cloneRetainDeperecatedFlags() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_DEPRECATED_OPERATIONS);
        final RemoveUnreferencedDefinitionsFilter remover = new RemoveUnreferencedDefinitionsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, remover, null, null, null);

        Operation operation = filtered.getPaths().get("/test").getGet();

        Boolean deprectedFlag = operation.getDeprecated();
        assertNotNull(deprectedFlag);
        assertEquals(deprectedFlag, Boolean.TRUE);
    }

    @Test(description = "it should contain all tags in the top level OpenAPI object")
    public void shouldContainAllTopLevelTags() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_REFERRED_SCHEMAS);
        final NoOpOperationsFilter filter = new NoOpOperationsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);
        assertEquals(getTagNames(filtered), Sets.newHashSet(PET_TAG, USER_TAG, STORE_TAG));
    }

    @Test(description = "it should not contain user tags in the top level OpenAPI object")
    public void shouldNotContainTopLevelUserTags() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_REFERRED_SCHEMAS);
        final NoPetOperationsFilter filter = new NoPetOperationsFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);
        assertEquals(getTagNames(filtered), Sets.newHashSet(USER_TAG, STORE_TAG));

    }

    @Test(description = "it should filter with null definitions")
    public void filterWithNullDefinitions() throws IOException {
        final OpenAPI openAPI = getOpenAPI(RESOURCE_PATH);
        openAPI.getComponents().setSchemas(null);

        final InternalModelPropertiesRemoverFilter filter = new InternalModelPropertiesRemoverFilter();
        final OpenAPI filtered = new SpecFilter().filter(openAPI, filter, null, null, null);
        assertNotNull(filtered);
    }

    private Set getTagNames(OpenAPI openAPI) {
        Set<String> result = new HashSet<>();
        if (openAPI.getTags() != null) {
            for (Tag item : openAPI.getTags()) {
                result.add(item.getName());
            }
        }
        return result;
    }

    private OpenAPI getOpenAPI(String path) throws IOException {
        final String json = ResourceUtils.loadClassResource(getClass(), path);
        return Json.mapper().readValue(json, OpenAPI.class);
    }
}
<file_sep>package io.swagger.v3.jaxrs2.it.resources;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/users")
@Produces("application/json")
public class UrlEncodedResource {
    @POST
    @Path("/add")
    @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
    public Response addUser(@FormParam("id") final String id, @FormParam("name") final String name,
                            @FormParam("gender") final String gender) {
        return Response.status(200).entity("Adding user  " + id + " to the system.").build();
    }
}
<file_sep>package io.swagger.v3.core.deserialization.properties;

import io.swagger.v3.core.util.TestUtils;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.MapSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.testng.annotations.Test;

import java.util.List;
import java.util.Map;

import static org.testng.Assert.*;

public class JsonPropertiesDeserializationTest {

    @Test(description = "should deserialize a string property with constraints")
    public void testDeserializeConstrainedStringProperty() throws Exception {

        OpenAPI oas = TestUtils.deserializeJsonFileFromClasspath("specFiles/propertiesWithConstraints.json", OpenAPI.class);

        StringSchema property = (StringSchema) oas.getComponents().getSchemas().get("Health").getProperties().get("string_with_constraints");

        assertEquals(property.getMinLength(), Integer.valueOf(10));
        assertEquals(property.getMaxLength(), Integer.valueOf(100));
        assertEquals(property.getPattern(), "apattern");
    }

    @Test(description = "should deserialize an array property with constraints")
    public void testDeserializeConstrainedArrayProperties() throws Exception {

        OpenAPI oas = TestUtils.deserializeJsonFileFromClasspath("specFiles/propertiesWithConstraints.json", OpenAPI.class);

        Map<String, Schema> properties = oas.getComponents().getSchemas().get("Health").getProperties();

        ArraySchema withMin = (ArraySchema) properties.get("array_with_min");

        assertEquals(withMin.getMinItems(), Integer.valueOf(5));
        assertNull(withMin.getMaxItems());
        assertNull(withMin.getUniqueItems());

        ArraySchema withMax = (ArraySchema) properties.get("array_with_max");

        assertNull(withMax.getMinItems());
        assertEquals(withMax.getMaxItems(), Integer.valueOf(10));
        assertNull(withMax.getUniqueItems());

        ArraySchema withUnique = (ArraySchema) properties.get("array_with_unique");

        assertNull(withUnique.getMinItems());
        assertNull(withUnique.getMaxItems());
        assertEquals(withUnique.getUniqueItems(), Boolean.TRUE);

        ArraySchema withAll = (ArraySchema) properties.get("array_with_all");

        assertEquals(withAll.getMinItems(), Integer.valueOf(1));
        assertEquals(withAll.getMaxItems(), Integer.valueOf(10));
        assertEquals(withAll.getUniqueItems(), Boolean.TRUE);
    }

    @Test(description = "should deserialize a property with vendor extensions of different types")
    public void testDeserializePropertyWithVendorExtensions() throws Exception {

        OpenAPI oas = TestUtils.deserializeJsonFileFromClasspath("specFiles/propertyWithVendorExtensions.json", OpenAPI.class);

        Map<String, Object> oasVendorExtensions = oas.getExtensions();
        Map<String, Object> vendorExtensions = ((Schema) oas.getComponents().getSchemas().get("Health").getProperties().get("status")).getExtensions();

        assertVendorExtensions(oasVendorExtensions);
        assertVendorExtensions(vendorExtensions);

        //check for vendor extensions in array property types
        vendorExtensions = ((Schema) oas.getComponents().getSchemas().get("Health").getProperties().get("array")).getExtensions();

        String xStringValue = (String) vendorExtensions.get("x-string-value");
        assertNotNull(xStringValue);
        assertEquals(xStringValue, "string_value");
    }

    private void assertVendorExtensions(Map<String, Object> vendorExtensions) {
        assertNotNull(vendorExtensions);
        assertEquals(6, vendorExtensions.size());

        String xStringValue = (String) vendorExtensions.get("x-string-value");
        assertNotNull(xStringValue);
        assertEquals(xStringValue, "Hello World");

        assertTrue(vendorExtensions.containsKey("x-null-value"));
        assertNull(vendorExtensions.get("x-null-value"));

        Map<String, String> xMapValue = (Map) vendorExtensions.get("x-map-value");
        assertNotNull(xMapValue);
        assertEquals(xMapValue.get("hello"), "world");
        assertEquals(xMapValue.get("foo"), "bar");

        List<String> xListValue = (List) vendorExtensions.get("x-list-value");
        assertNotNull(xListValue);
        assertEquals(xListValue.get(0), "Hello");
        assertEquals(xListValue.get(1), "World");

        Integer xNumberValue = (Integer) vendorExtensions.get("x-number-value");
        assertNotNull(xNumberValue);
        assertEquals(xNumberValue.intValue(), 123);

        Boolean xBooleanValue = (Boolean) vendorExtensions.get("x-boolean-value");
        assertNotNull(xBooleanValue);
        assertTrue(xBooleanValue);

        assertFalse(vendorExtensions.containsKey("not-an-extension"));

    }

    @Test
    public void shouldDeserializeArrayPropertyMinItems() throws Exception {
        String path = "json-schema-validation/array.json";
        ArraySchema property = (ArraySchema) TestUtils.deserializeJsonFileFromClasspath(path, Schema.class);

        assertNotNull(property.getMinItems());
        assertEquals(property.getMinItems().intValue(), 1);
    }

    @Test
    public void shouldDeserializeArrayPropertyMaxItems() throws Exception {
        String path = "json-schema-validation/array.json";
        ArraySchema property = (ArraySchema) TestUtils.deserializeJsonFileFromClasspath(path, Schema.class);

        assertNotNull(property.getMaxItems());
        assertEquals(property.getMaxItems().intValue(), 10);
    }

    @Test
    public void shouldDeserializeArrayPropertyUniqueItems() throws Exception {
        String path = "json-schema-validation/array.json";
        ArraySchema property = (ArraySchema) TestUtils.deserializeJsonFileFromClasspath(path, Schema.class);

        assertNotNull(property.getUniqueItems());
        assertTrue(property.getUniqueItems());
    }

    @Test
    public void givenMapProperty_shouldDeserializeMinProperties() {
        String path = "json-schema-validation/map.json";
        MapSchema property = (MapSchema) TestUtils.deserializeJsonFileFromClasspath(path, Schema.class);

        assertNotNull(property.getMinProperties());
        assertEquals(property.getMinProperties().intValue(), 1);
    }

    @Test
    public void givenMapProperty_shouldDeserializeMaxProperties() {
        String path = "json-schema-validation/map.json";
        MapSchema property = (MapSchema) TestUtils.deserializeJsonFileFromClasspath(path, Schema.class);

        assertNotNull(property.getMaxProperties());
        assertEquals(property.getMaxProperties().intValue(), 10);
    }
}
<file_sep>package io.swagger.v3.core.resolving;

import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.oas.models.media.IntegerSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.testng.annotations.Test;

import java.util.Map;

import static org.testng.Assert.*;

public class HiddenFieldTest {

    @Test(description = "it should ignore a hidden field")
    public void testHiddenField() {
        final Map<String, Schema> models = ModelConverters.getInstance().read(ModelWithHiddenFields.class);

        final Schema model = models.get("ModelWithHiddenFields");
        assertNotNull(model);
        assertEquals(model.getProperties().size(), 2);

        final Schema idValue = (Schema) model.getProperties().get("id");
        assertTrue(idValue instanceof IntegerSchema);

        assertTrue(model.getRequired().contains("id"));

        final Schema nameValue = (Schema) model.getProperties().get("name");
        assertTrue(nameValue instanceof StringSchema);
    }

    class ModelWithHiddenFields {
        @io.swagger.v3.oas.annotations.media.Schema(required = true)
        public Long id = null;

        @io.swagger.v3.oas.annotations.media.Schema(required = true, hidden = false)
        public String name = null;

        @io.swagger.v3.oas.annotations.media.Schema(hidden = true)
        public String password = null;
    }
}
<file_sep>package io.swagger.v3.core.converting;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.oas.models.Person;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.core.util.OutputReplacer;
import io.swagger.v3.core.util.ResourceUtils;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.QueryParameter;
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 org.testng.annotations.Test;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.testng.Assert.*;

public class SwaggerSerializerTest {
    ObjectMapper m = Json.mapper();

    @Test(description = "it should convert a spec")
    public void convertSpec() throws IOException {
        final Schema personModel = ModelConverters.getInstance().read(Person.class).get("Person");
        final Schema errorModel = ModelConverters.getInstance().read(Error.class).get("Error");
        final Info info = new Info()
                .version("1.0.0")
                .title("Swagger Petstore");

        final Contact contact = new Contact()
                .name("Swagger API Team")
                .email("<EMAIL>")
                .url("http://swagger.io");

        info.setContact(contact);

        final Map<String, Object> map = new HashMap<String, Object>();
        map.put("name", "value");
        info.addExtension("x-test2", map);
        info.addExtension("x-test", "value");

        final OpenAPI swagger = new OpenAPI()
                .info(info)
                .addServersItem(new Server()
                        .url("http://petstore.swagger.io"))

//                .securityDefinition("api-key", new ApiKeyAuthDefinition("key", In.HEADER))
//                .consumes("application/json")
//                .produces("application/json")
                .schema("Person", personModel)
                .schema("Error", errorModel);

        final Operation get = new Operation()
//                .produces("application/json")
                .summary("finds pets in the system")
                .description("a longer description")
                .addTagsItem("Pet Operations")
                .operationId("get pet by id")
                .deprecated(true);

        get.addParametersItem(new Parameter()
                .in("query")
                .name("tags")
                .description("tags to filter by")
                .required(false)
                .schema(new StringSchema())
        );

        get.addParametersItem(new Parameter()
                .in("path")
                .name("petId")
                .description("pet to fetch")
                .schema(new IntegerSchema().format("int64"))
        );

        final ApiResponse response = new ApiResponse()
                .description("pets returned")
                .content(new Content()
                        .addMediaType("application/json", new MediaType()
                                .schema(new Schema().$ref("Person"))
                                .example("fun")));

        final ApiResponse errorResponse = new ApiResponse()
                .description("error response")
                .link("myLink", new Link()
                        .description("a link")
                        .operationId("theLinkedOperationId")
                        .parameters("userId", "gah")
                )
                .content(new Content()
                        .addMediaType("application/json", new MediaType()
                                .schema(new Schema().$ref("Error"))));

        get.responses(new ApiResponses()
                .addApiResponse("200", response)
                .addApiResponse("default", errorResponse));

        final Operation post = new Operation()
                .summary("adds a new pet")
                .description("you can add a new pet this way")
                .addTagsItem("Pet Operations")
                .operationId("add pet")
                .responses(new ApiResponses()
                        .addApiResponse("default", errorResponse))
                .requestBody(new RequestBody()
                        .description("the pet to add")
                        .content(new Content().addMediaType("*/*", new MediaType()
                                .schema(new Schema().$ref("Person")))));

        swagger.paths(new Paths().addPathItem("/pets", new PathItem()
                .get(get).post(post)));
        final String swaggerJson = Json.mapper().writeValueAsString(swagger);
        Json.prettyPrint(swagger);
        final OpenAPI rebuilt = Json.mapper().readValue(swaggerJson, OpenAPI.class);
        SerializationMatchers.assertEqualsToJson(rebuilt, swaggerJson);
    }

    @Test(description = "it should read the uber api")
    public void readUberApi() throws IOException {
        final String jsonString = ResourceUtils.loadClassResource(getClass(), "uber.json");
        final OpenAPI swagger = Json.mapper().readValue(jsonString, OpenAPI.class);
        assertNotNull(swagger);
    }

    @Test(description = "it should write a spec with parameter references")
    public void writeSpecWithParameterReferences() throws IOException {
        final Schema personModel = ModelConverters.getInstance().read(Person.class).get("Person");

        final Info info = new Info()
                .version("1.0.0")
                .title("Swagger Petstore");

        final Contact contact = new Contact()
                .name("Swagger API Team")
                .email("<EMAIL>")
                .url("http://swagger.io");
        info.setContact(contact);

        final OpenAPI swagger = new OpenAPI()
                .info(info)
                .addServersItem(new Server().url("http://petstore.swagger.io"))
                //.consumes("application/json")
                //.produces("application/json")
                .schema("Person", personModel);

        final QueryParameter parameter = (QueryParameter) new QueryParameter()
                .name("id")
                .description("a common get parameter")
                .schema(new IntegerSchema());

        final Operation get = new Operation()
                //.produces("application/json")
                .summary("finds pets in the system")
                .description("a longer description")
                //.tag("Pet Operations")
                .operationId("get pet by id")
                .addParametersItem(new Parameter().$ref("#/parameters/Foo"));

        swagger
                .components(new Components().addParameters("Foo", parameter))
                .path("/pets", new PathItem().get(get));

        final String swaggerJson = Json.mapper().writeValueAsString(swagger);
        final OpenAPI rebuilt = Json.mapper().readValue(swaggerJson, OpenAPI.class);
        assertEquals(Json.pretty(rebuilt), Json.pretty(swagger));
    }

    @Test
    public void prettyPrintTest() throws IOException {
        final String json = ResourceUtils.loadClassResource(getClass(), "uber.json");
        final OpenAPI swagger = Json.mapper().readValue(json, OpenAPI.class);
        final String outputStream = OutputReplacer.OUT.run(new OutputReplacer.Function() {
            @Override
            public void run() {
                Json.prettyPrint(swagger);
            }
        });
        SerializationMatchers.assertEqualsToJson(swagger, outputStream);
    }

    @Test
    public void exceptionsTest() throws IOException {
        final String outputStream1 = OutputReplacer.ERROR.run(new OutputReplacer.Function() {
            @Override
            public void run() {
                Json.pretty(new ThrowHelper());
            }
        });
        assertTrue(outputStream1.contains(ThrowHelper.MESSAGE));

        final String outputStream2 = OutputReplacer.ERROR.run(new OutputReplacer.Function() {
            @Override
            public void run() {
                Json.prettyPrint(new ThrowHelper());
            }
        });
        assertTrue(outputStream2.contains(ThrowHelper.MESSAGE));
    }

    static class ThrowHelper {

        public static final String MESSAGE = "Test exception";

        public String getValue() throws IOException {
            throw new IOException(MESSAGE);
        }

        public void setValue(String value) {

        }
    }
}
<file_sep>package io.swagger.v3.jaxrs2.annotations.operations;

import io.swagger.v3.jaxrs2.annotations.AbstractAnnotationTest;
import io.swagger.v3.jaxrs2.resources.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.headers.Header;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import org.testng.annotations.Test;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.io.IOException;

import static org.testng.Assert.assertEquals;

public class AnnotatedOperationMethodTest extends AbstractAnnotationTest {
    @Test
    public void testSimpleGetOperation() {
        String openApiYAML = readIntoYaml(SimpleGetOperationTest.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.length() - 1;

        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex\n" +
                "      operationId: getWithNoParameters\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "      deprecated: true";
        String extractedYAML = openApiYAML.substring(start, end);

        assertEquals(extractedYAML, expectedYAML);
    }

    static class SimpleGetOperationTest {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex",
                operationId = "getWithNoParameters",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!")
                }
        )
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testSimpleGetOperationWithoutResponses() {
        String openApiYAML = readIntoYaml(SimpleGetWithoutResponses.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.length() - 1;

        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs or responses\n" +
                "      operationId: getWithNoParametersAndNoResponses\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            '*/*': {}\n" +
                "      deprecated: true";
        String extractedYAML = openApiYAML.substring(start, end);

        assertEquals(extractedYAML, expectedYAML);
    }

    static class SimpleGetWithoutResponses {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs or responses",
                operationId = "getWithNoParametersAndNoResponses",
                deprecated = true
        )
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testGetOperationWithResponsePayloadAndAlternateCodes() {
        String openApiYAML = readIntoYaml(GetOperationWithResponsePayloadAndAlternateCodes.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.indexOf("components:");
        String extractedYAML = openApiYAML.substring(start, end);
        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "        default:\n" +
                "          description: boo\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/GenericError'\n" +
                "              examples:\n" +
                "                boo:\n" +
                "                  summary: example of boo\n" +
                "                  description: boo\n" +
                "                  value: example\n" +
                "                  externalValue: example of external value\n" +
                "      deprecated: true\n";

        assertEquals(extractedYAML, expectedYAML);
    }

    static class GetOperationWithResponsePayloadAndAlternateCodes {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex",
                operationId = "getWithPayloadResponse",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!",
                                content = @Content(
                                        mediaType = "application/json",
                                        schema = @Schema(implementation = SampleResponseSchema.class)
                                )
                        ),
                        @ApiResponse(
                                description = "boo",
                                content = @Content(
                                        mediaType = "*/*",
                                        schema = @Schema(implementation = GenericError.class),
                                        examples = {
                                                @ExampleObject(name = "boo", value = "example",
                                                        summary = "example of boo", externalValue = "example of external value")
                                        }

                                )
                        )
                }
        )
        @Path("/path")
        @GET
        public void simpleGet() {
        }
    }

    static class SampleResponseSchema {
        @Schema(description = "the user id")
        public String id;
    }

    static class GenericError {
        public int code;
        public String message;
    }

    @Test(description = "reads an operation with response examples defined")
    public void testOperationWithResponseExamples() {
        String openApiYAML = readIntoYaml(GetOperationWithResponseExamples.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.indexOf("components:");
        String extractedYAML = openApiYAML.substring(start, end);
        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "              examples:\n" +
                "                basic:\n" +
                "                  summary: shows a basic example\n" +
                "                  description: basic\n" +
                "                  value: '{id: 19877734}'\n" +
                "      deprecated: true\n";
        assertEquals(extractedYAML, expectedYAML);
    }

    @Test(description = "reads an operation with response examples defined")
    public void testOperationWithParameterExample() {
        String openApiYAML = readIntoYaml(GetOperationWithParameterExample.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.indexOf("components:");
        String extractedYAML = openApiYAML.substring(start, end);
        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with a parameter example\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      parameters:\n" +
                "      - in: query\n" +
                "        schema:\n" +
                "          type: string\n" +
                "        example:\n" +
                "          id: 19877734\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SampleResponseSchema'\n" +
                "              examples:\n" +
                "                basic:\n" +
                "                  summary: shows a basic example\n" +
                "                  description: basic\n" +
                "                  value: '{id: 19877734}'\n" +
                "      deprecated: true\n";
        assertEquals(extractedYAML, expectedYAML);
    }

    static class GetOperationWithResponseExamples {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex output",
                operationId = "getWithPayloadResponse",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!",
                                content = @Content(
                                        mediaType = "application/json",
                                        schema = @Schema(implementation = SampleResponseSchema.class),
                                        examples = {
                                                @ExampleObject(
                                                        name = "basic",
                                                        summary = "shows a basic example",
                                                        value = "{id: 19877734}")
                                        }
                                )
                        )
                }
        )
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    static class GetOperationWithParameterExample {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with a parameter example",
                operationId = "getWithPayloadResponse",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!",
                                content = @Content(
                                        mediaType = "application/json",
                                        schema = @Schema(implementation = SampleResponseSchema.class),
                                        examples = {
                                                @ExampleObject(
                                                        name = "basic",
                                                        summary = "shows a basic example",
                                                        value = "{id: 19877734}")
                                        }
                                )
                        )
                }
        )
        @GET
        @Path("/path")
        public void simpleGet(@Parameter(in = ParameterIn.QUERY, example = "{\"id\": 19877734}") String exParam) {
        }
    }

    static class GetOperationWithResponseSingleHeader {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex output",
                operationId = "getWithPayloadResponse",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!",
                                headers = {@Header(
                                        name = "Rate-Limit-Limit",
                                        description = "The number of allowed requests in the current period",
                                        schema = @Schema(type = "integer"))})
                })
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testOperationWithResponseSingleHeader() {
        String openApiYAML = readIntoYaml(GetOperationWithResponseSingleHeader.class);
        int start = openApiYAML.indexOf("get:");
        String extractedYAML = openApiYAML.substring(start);
        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          headers:\n" +
                "            Rate-Limit-Limit:\n" +
                "              description: The number of allowed requests in the current period\n" +
                "              style: simple\n" +
                "              schema:\n" +
                "                type: integer\n" +
                "      deprecated: true\n";
        assertEquals(expectedYAML, extractedYAML);
    }

    static class GetOperationWithResponseMultipleHeaders {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex output",
                operationId = "getWithPayloadResponse",
                deprecated = true,
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!",
                                headers = {@Header(
                                        name = "Rate-Limit-Limit",
                                        description = "The number of allowed requests in the current period",
                                        schema = @Schema(type = "integer")),
                                        @Header(
                                                name = "X-Rate-Limit-Desc",
                                                description = "The description of rate limit",
                                                schema = @Schema(type = "string"))})
                })
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testOperationWithResponseMultipleHeaders() {
        String openApiYAML = readIntoYaml(GetOperationWithResponseMultipleHeaders.class);
        int start = openApiYAML.indexOf("get:");
        String extractedYAML = openApiYAML.substring(start);
        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex output\n" +
                "      operationId: getWithPayloadResponse\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "          headers:\n" +
                "            X-Rate-Limit-Desc:\n" +
                "              description: The description of rate limit\n" +
                "              style: simple\n" +
                "              schema:\n" +
                "                type: string\n" +
                "            Rate-Limit-Limit:\n" +
                "              description: The number of allowed requests in the current period\n" +
                "              style: simple\n" +
                "              schema:\n" +
                "                type: integer\n" +
                "      deprecated: true\n";
        assertEquals(expectedYAML, extractedYAML);
    }

    @Test(description = "reads the pet resource from sample")
    public void testCompletePetResource() throws IOException {
        String expectedYAML = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /pet/findByTags:\n" +
                "    get:\n" +
                "      summary: Finds Pets by tags\n" +
                "      description: Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.\n" +
                "      operationId: findPetsByTags\n" +
                "      parameters:\n" +
                "      - name: tags\n" +
                "        in: query\n" +
                "        description: Tags to filter by\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: Pets matching criteria\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Pet'\n" +
                "        400:\n" +
                "          description: Invalid tag value\n" +
                "  /pet/findByCategory/{category}:\n" +
                "    get:\n" +
                "      summary: Finds Pets by category\n" +
                "      operationId: findPetsByCategory\n" +
                "      parameters:\n" +
                "      - name: category\n" +
                "        in: path\n" +
                "        description: Category value that need to be considered for filter\n" +
                "        required: true\n" +
                "        style: matrix\n" +
                "        schema:\n" +
                "          $ref: '#/components/schemas/Category'\n" +
                "      - name: skip\n" +
                "        in: query\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      - name: limit\n" +
                "        in: query\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Pet'\n" +
                "        400:\n" +
                "          description: Invalid category value\n" +
                "  /pet/{petId}:\n" +
                "    get:\n" +
                "      summary: Find pet by ID\n" +
                "      description: Returns a pet when 0 < ID <= 10.  ID > 10 or nonintegers will simulate API error conditions\n" +
                "      operationId: getPetById\n" +
                "      parameters:\n" +
                "      - name: petId\n" +
                "        in: path\n" +
                "        description: ID of pet that needs to be fetched\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: The pet\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Pet'\n" +
                "            application/xml:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Pet'\n" +
                "        400:\n" +
                "          description: Invalid ID supplied\n" +
                "        404:\n" +
                "          description: Pet not found\n" +
                "  /pet/bodynoannotation:\n" +
                "    post:\n" +
                "      summary: Add a new pet to the store no annotation\n" +
                "      operationId: addPetNoAnnotation\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Pet'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Pet'\n" +
                "      responses:\n" +
                "        405:\n" +
                "          description: Invalid input\n" +
                "  /pet/bodyid:\n" +
                "    post:\n" +
                "      summary: Add a new pet to the store passing an integer with generic parameter annotation\n" +
                "      operationId: addPetByInteger\n" +
                "      requestBody:\n" +
                "        description: Pet object that needs to be added to the store\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        405:\n" +
                "          description: Invalid input\n" +
                "  /pet/bodyidnoannotation:\n" +
                "    post:\n" +
                "      summary: Add a new pet to the store passing an integer without parameter annotation\n" +
                "      operationId: addPetByIntegerNoAnnotation\n" +
                "      requestBody:\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              type: integer\n" +
                "              format: int32\n" +
                "      responses:\n" +
                "        405:\n" +
                "          description: Invalid input\n" +
                "  /pet:\n" +
                "    put:\n" +
                "      summary: Update an existing pet\n" +
                "      operationId: updatePet\n" +
                "      requestBody:\n" +
                "        description: Pet object that needs to be added to the store\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Pet'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        400:\n" +
                "          description: Invalid ID supplied\n" +
                "        404:\n" +
                "          description: Pet not found\n" +
                "        405:\n" +
                "          description: Validation exception\n" +
                "    post:\n" +
                "      summary: Add a new pet to the store\n" +
                "      operationId: addPet\n" +
                "      requestBody:\n" +
                "        description: Pet object that needs to be added to the store\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Pet'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/Pet'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        405:\n" +
                "          description: Invalid input\n" +
                "  /pet/findByStatus:\n" +
                "    get:\n" +
                "      summary: Finds Pets by status\n" +
                "      description: Multiple status values can be provided with comma separated strings\n" +
                "      operationId: findPetsByStatus\n" +
                "      parameters:\n" +
                "      - name: status\n" +
                "        in: query\n" +
                "        description: Status values that need to be considered for filter\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      - name: skip\n" +
                "        in: query\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      - name: limit\n" +
                "        in: query\n" +
                "        schema:\n" +
                "          type: integer\n" +
                "          format: int32\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Pet'\n" +
                "        400:\n" +
                "          description: Invalid status value\n" +
                "components:\n" +
                "  schemas:\n" +
                "    Category:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        name:\n" +
                "          type: string\n" +
                "      xml:\n" +
                "        name: Category\n" +
                "    Tag:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        name:\n" +
                "          type: string\n" +
                "      xml:\n" +
                "        name: Tag\n" +
                "    Pet:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        category:\n" +
                "          $ref: '#/components/schemas/Category'\n" +
                "        name:\n" +
                "          type: string\n" +
                "        photoUrls:\n" +
                "          type: array\n" +
                "          xml:\n" +
                "            wrapped: true\n" +
                "          items:\n" +
                "            type: string\n" +
                "            xml:\n" +
                "              name: photoUrl\n" +
                "        tags:\n" +
                "          type: array\n" +
                "          xml:\n" +
                "            wrapped: true\n" +
                "          items:\n" +
                "            $ref: '#/components/schemas/Tag'\n" +
                "        status:\n" +
                "          type: string\n" +
                "          description: pet status in the store\n" +
                "          enum:\n" +
                "          - available,pending,sold\n" +
                "      xml:\n" +
                "        name: Pet";

        compareAsYaml(PetResource.class, expectedYAML);
        compareAsYaml(PetResourceSlashesinPath.class, expectedYAML);

    }

    @Test(description = "reads the resource with generic response from sample")
    public void testGenericResponseResource() throws IOException {
        String yaml = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /:\n" +
                "    get:\n" +
                "      summary: Returns a list of somethings\n" +
                "      operationId: getSomethings\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/SomethingResponse'\n" +
                "  /overridden:\n" +
                "    get:\n" +
                "      summary: Returns a list of somethings\n" +
                "      operationId: getSomethingsOverridden\n" +
                "      responses:\n" +
                "        default:\n" +
                "          content:\n" +
                "            '*/*':\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/Something'\n" +
                "components:\n" +
                "  schemas:\n" +
                "    SomethingResponse:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        data:\n" +
                "          $ref: '#/components/schemas/DataSomething'\n" +
                "    DataSomething:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        items:\n" +
                "          type: array\n" +
                "          items:\n" +
                "            $ref: '#/components/schemas/Something'\n" +
                "    Something:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: string\n";
        compareAsYaml(GenericResponsesResource.class, yaml);
    }

    @Test(description = "reads the user resource from sample")
    public void testCompleteUserResource() throws IOException {
        String expectedYAML = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /user:\n" +
                "    post:\n" +
                "      summary: Create user\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUser\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "  /user/createWithArray:\n" +
                "    post:\n" +
                "      summary: Creates list of users with given input array\n" +
                "      operationId: createUsersWithArrayInput\n" +
                "      requestBody:\n" +
                "        description: List of user object\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              type: array\n" +
                "              items:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "  /user/createWithList:\n" +
                "    post:\n" +
                "      summary: Creates list of users with given input array\n" +
                "      operationId: createUsersWithListInput\n" +
                "      requestBody:\n" +
                "        description: List of user object\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              type: array\n" +
                "              items:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "  /user/{username}:\n" +
                "    get:\n" +
                "      summary: Get user by user name\n" +
                "      operationId: getUserByName\n" +
                "      parameters:\n" +
                "      - name: username\n" +
                "        in: path\n" +
                "        description: 'The name that needs to be fetched. Use user1 for testing. '\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: The user\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "        400:\n" +
                "          description: User not found\n" +
                "    put:\n" +
                "      summary: Updated user\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: updateUser\n" +
                "      parameters:\n" +
                "      - name: username\n" +
                "        in: path\n" +
                "        description: name that need to be deleted\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "        examples:\n" +
                "          example2:\n" +
                "            summary: Summary example 2\n" +
                "            description: example2\n" +
                "            value: example2\n" +
                "            externalValue: external value 2\n" +
                "          example1:\n" +
                "            summary: Summary example 1\n" +
                "            description: example1\n" +
                "            value: example1\n" +
                "            externalValue: external value 1\n" +
                "      requestBody:\n" +
                "        description: Updated user object\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: user updated\n" +
                "        400:\n" +
                "          description: Invalid user supplied\n" +
                "        404:\n" +
                "          description: User not found\n" +
                "    delete:\n" +
                "      summary: Delete user\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: deleteUser\n" +
                "      parameters:\n" +
                "      - name: username\n" +
                "        in: path\n" +
                "        description: The name that needs to be deleted\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: user deteled\n" +
                "        400:\n" +
                "          description: Invalid username supplied\n" +
                "        404:\n" +
                "          description: User not found\n" +
                "  /user/login:\n" +
                "    get:\n" +
                "      summary: Logs user into the system\n" +
                "      operationId: loginUser\n" +
                "      parameters:\n" +
                "      - name: username\n" +
                "        in: query\n" +
                "        description: The user name for login\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      - name: password\n" +
                "        in: query\n" +
                "        description: The password for login in clear text\n" +
                "        required: true\n" +
                "        schema:\n" +
                "          type: string\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: Successfully logged in\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                type: string\n" +
                "            application/xml:\n" +
                "              schema:\n" +
                "                type: string\n" +
                "        400:\n" +
                "          description: Invalid username/password supplied\n" +
                "  /user/logout:\n" +
                "    get:\n" +
                "      summary: Logs out current logged in user session\n" +
                "      operationId: logoutUser\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "components:\n" +
                "  schemas:\n" +
                "    User:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        username:\n" +
                "          type: string\n" +
                "        firstName:\n" +
                "          type: string\n" +
                "        lastName:\n" +
                "          type: string\n" +
                "        email:\n" +
                "          type: string\n" +
                "        password:\n" +
                "          type: string\n" +
                "        phone:\n" +
                "          type: string\n" +
                "        userStatus:\n" +
                "          type: integer\n" +
                "          description: User Status\n" +
                "          format: int32\n" +
                "      xml:\n" +
                "        name: User";
        compareAsYaml(UserResource.class, expectedYAML);
    }

    @Test(description = "reads the simple user resource from sample")
    public void testSimpleUserResource() throws IOException {

        String expectedYAML = "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /user:\n" +
                "    post:\n" +
                "      summary: Create user\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUser\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "  /user/createUserWithReturnType:\n" +
                "    post:\n" +
                "      summary: Create user with return type\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUserWithReturnType\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "            application/xml:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "  /user/createUserWithResponseAnnotation:\n" +
                "    post:\n" +
                "      summary: Create user with response annotation\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUserWithResponseAnnotation\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: aaa\n" +
                "          content:\n" +
                "            application/json:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "            application/xml:\n" +
                "              schema:\n" +
                "                $ref: '#/components/schemas/User'\n" +
                "  /user/createUserWithReturnTypeAndResponseAnnotation:\n" +
                "    post:\n" +
                "      summary: Create user with return type and response annotation\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUserWithReturnTypeAndResponseAnnotation\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          application/json:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "          application/xml:\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/User'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: aaa\n" +
                "components:\n" +
                "  schemas:\n" +
                "    User:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        id:\n" +
                "          type: integer\n" +
                "          format: int64\n" +
                "        username:\n" +
                "          type: string\n" +
                "        firstName:\n" +
                "          type: string\n" +
                "        lastName:\n" +
                "          type: string\n" +
                "        email:\n" +
                "          type: string\n" +
                "        password:\n" +
                "          type: string\n" +
                "        phone:\n" +
                "          type: string\n" +
                "        userStatus:\n" +
                "          type: integer\n" +
                "          description: User Status\n" +
                "          format: int32\n" +
                "      xml:\n" +
                "        name: User";
        compareAsYaml(SimpleUserResource.class, expectedYAML);
    }

    @Test(description = "reads and skips the hidden user resource")
    public void testHiddenUserResource() {
        String openApiYAML = readIntoYaml(HiddenUserResource.class);
        assertEquals(openApiYAML, "openapi: 3.0.1\n");
    }

    @Test(description = "reads and skips the hidden user resource")
    public void testHiddenAnnotatedUserResource() throws IOException {
        String openApiYAML = readIntoYaml(HiddenAnnotatedUserResource.class);
        assertEquals(openApiYAML, "openapi: 3.0.1\n");
        compareAsYaml(HiddenAnnotatedUserResource.HiddenAnnotatedUserResourceMethodAndData.class, "openapi: 3.0.1\n" +
                "paths:\n" +
                "  /user/2:\n" +
                "    post:\n" +
                "      summary: Create user\n" +
                "      description: This can only be done by the logged in user.\n" +
                "      operationId: createUserWithHiddenBeanProperty\n" +
                "      requestBody:\n" +
                "        description: Created user object\n" +
                "        content:\n" +
                "          '*/*':\n" +
                "            schema:\n" +
                "              $ref: '#/components/schemas/UserResourceBean'\n" +
                "        required: true\n" +
                "      responses:\n" +
                "        default:\n" +
                "          description: default response\n" +
                "          content:\n" +
                "            'application/json': {}\n" +
                "            'application/xml': {}\n" +
                "components:\n" +
                "  schemas:\n" +
                "    UserResourceBean:\n" +
                "      type: object\n" +
                "      properties:\n" +
                "        foo:\n" +
                "          type: string");

    }

    @Test
    public void testSimpleGetOperationWithSecurity() {

        String openApiYAML = readIntoYaml(SimpleGetOperationWithSecurity.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.length() - 1;

        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex\n" +
                "      operationId: getWithNoParameters\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "      security:\n" +
                "      - petstore-auth:\n" +
                "        - write:pets";
        String extractedYAML = openApiYAML.substring(start, end);

        assertEquals(expectedYAML, extractedYAML);
    }

    static class SimpleGetOperationWithSecurity {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex",
                operationId = "getWithNoParameters",
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!")
                },
                security = @SecurityRequirement(
                        name = "petstore-auth",
                        scopes = "write:pets"))
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testSimpleGetOperationWithMultipleSecurity() {

        String openApiYAML = readIntoYaml(SimpleGetOperationWithMultipleSecurityScopes.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.length() - 1;

        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex\n" +
                "      operationId: getWithNoParameters\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "      security:\n" +
                "      - petstore-auth:\n" +
                "        - write:pets\n" +
                "        - read:pets";
        String extractedYAML = openApiYAML.substring(start, end);

        assertEquals(extractedYAML, expectedYAML);
    }

    static class SimpleGetOperationWithMultipleSecurityScopes {
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex",
                operationId = "getWithNoParameters",
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!")
                },
                security = @SecurityRequirement(
                        name = "petstore-auth",
                        scopes = {"write:pets", "read:pets"}))
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }

    @Test
    public void testSimpleGetOperationWithMultipleSecurityAnnotations() {

        String openApiYAML = readIntoYaml(SimpleGetOperationWithMultipleSecurityAnnotations.class);
        int start = openApiYAML.indexOf("get:");
        int end = openApiYAML.length() - 1;

        String expectedYAML = "get:\n" +
                "      summary: Simple get operation\n" +
                "      description: Defines a simple get operation with no inputs and a complex\n" +
                "      operationId: getWithNoParameters\n" +
                "      responses:\n" +
                "        200:\n" +
                "          description: voila!\n" +
                "      security:\n" +
                "      - review-auth:\n" +
                "        - write:review\n" +
                "      - petstore-auth:\n" +
                "        - write:pets\n" +
                "        - read:pets\n" +
                "      - api_key: []";
        String extractedYAML = openApiYAML.substring(start, end);

        assertEquals(extractedYAML, expectedYAML);
    }

    static class SimpleGetOperationWithMultipleSecurityAnnotations {
        @SecurityRequirement(name = "review-auth", scopes = {"write:review"})
        @Operation(
                summary = "Simple get operation",
                description = "Defines a simple get operation with no inputs and a complex",
                operationId = "getWithNoParameters",
                responses = {
                        @ApiResponse(
                                responseCode = "200",
                                description = "voila!")
                },
                security = {@SecurityRequirement(
                        name = "petstore-auth",
                        scopes = {"write:pets", "read:pets"}),
                        @SecurityRequirement(
                                name = "api_key",
                                scopes = {}),})
        @GET
        @Path("/path")
        public void simpleGet() {
        }
    }
}
<file_sep>package io.swagger.v3.jaxrs2.resources;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Arrays;

@Path("/bookstore")
public class BookStoreTicket2646 {
    @Produces({ MediaType.APPLICATION_JSON })
    @GET
    public Response getBooks(
            @QueryParam("page") @DefaultValue("1") int page) {
        return Response.ok(
                Arrays.asList(
                        new Book(),
                        new Book()
                )
        ).build();
    }

    @Produces({ MediaType.APPLICATION_JSON })
    @Path("/{id}")
    @GET
    public Book getBook(@PathParam("id") Long id) {
        return new Book();
    }

    @Path("/{id}")
    @DELETE
    public Response delete(@PathParam("id") String id) {
        return Response.ok().build();
    }

    public static class Book {
        public String foo;
    }
}<file_sep>package io.swagger.v3.core.serialization;

import io.swagger.v3.core.converter.ModelConverters;
import io.swagger.v3.core.matchers.SerializationMatchers;
import io.swagger.v3.core.oas.models.Person;
import io.swagger.v3.core.util.ResourceUtils;
import io.swagger.v3.core.util.TestUtils;
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.Contact;
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.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.*;
import io.swagger.v3.oas.models.servers.Server;
import org.testng.annotations.Test;

import java.io.IOException;

public class SecurityDefinitionTest {

    @Test(description = "it should serialize correctly security")
    public void serializeSecurity() throws IOException {
        final OpenAPI oas = TestUtils.deserializeJsonFileFromClasspath("specFiles/securityDefinitions.json", OpenAPI.class);
        final String json = ResourceUtils.loadClassResource(getClass(), "specFiles/securityDefinitions.json");
        SerializationMatchers.assertEqualsToJson(oas, json);
    }

    @Test(description = "it should create a model with security requirements")
    public void createModelWithSecurityRequirements() throws IOException {
        final Schema personModel = ModelConverters.getInstance().read(Person.class).get("Person");
        final Schema errorModel = ModelConverters.getInstance().read(Error.class).get("Error");

        final Info info = new Info()
                .version("1.0.0")
                .title("Swagger Petstore");

        final Contact contact = new Contact()
                .name("Swagger API Team")
                .email("<EMAIL>")
                .url("http://swagger.io");
        info.setContact(contact);

        final OpenAPI oas = new OpenAPI()
                .info(info)
                .addServersItem(new Server()
                        .url("http://petstore.swagger.io"))
                .schema("Person", personModel)
                .schema("Error", errorModel);

        oas.schemaRequirement("githubAccessCode",
                new SecurityScheme()
                        .flows(new OAuthFlows()
                                .authorizationCode(new OAuthFlow()
                                        .scopes(new Scopes().addString("user:email", "Grants read access to a user’s email addresses.")))));

        final Operation get = new Operation()
                .summary("finds pets in the system")
                .description("a longer description")
                .addTagsItem("Pet Operations")
                .operationId("get pet by id");

        get.addParametersItem(new Parameter()
                .in("query")
                .name("tags")
                .description("tags to filter by")
                .required(false)
                .schema(new StringSchema()));
        get.addParametersItem(new Parameter()
                .in("path")
                .name("petId")
                .description("pet to fetch")
                .schema(new IntegerSchema().format("int64")));

        final ApiResponse response = new ApiResponse()
                .description("pets returned")
                .content(new Content().addMediaType("*/*", new MediaType().schema(new Schema().$ref("Person"))));

        final ApiResponse errorResponse = new ApiResponse()
                .description("error response")
                .content(new Content().addMediaType("*/*", new MediaType().schema(new Schema().$ref("Error"))));

        get.responses(new ApiResponses()
                .addApiResponse("200", response)
                .addApiResponse("default", errorResponse))
                .addSecurityItem(new SecurityRequirement()
                        .addList("internal_oauth2", "user:email"))
                .addSecurityItem(new SecurityRequirement()
                        .addList("api_key"));

        oas.path("/pets", new PathItem().get(get));

        final String json = ResourceUtils.loadClassResource(getClass(), "ModelWithSecurityRequirements.json");
        SerializationMatchers.assertEqualsToJson(oas, json);
    }
}
<file_sep>/**
 * Copyright 2017 SmartBear Software
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.swagger.v3.oas.annotations.security;

import java.lang.annotation.*;

import static java.lang.annotation.ElementType.*;

/**
 * The annotation may be applied at class or method level, or in {@link io.swagger.v3.oas.annotations.Operation#security()} ()} to define security requirements for the
 * single operation (when applied at method level) or for all operations of a class (when applied at class level).
 * <p>It can also be used in {@link io.swagger.v3.oas.annotations.OpenAPIDefinition#security()} to define spec level security.</p>
 *
 * @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#securityRequirementObject">Security Requirement (OpenAPI specification)</a>
 * @see io.swagger.v3.oas.annotations.OpenAPIDefinition
 * @see io.swagger.v3.oas.annotations.Operation
 **/
@Target({METHOD, TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(SecurityRequirements.class)
@Inherited
public @interface SecurityRequirement {
    /**
     * This name must correspond to a declared SecurityRequirement.
     *
     * @return String name
     */
    String name();

    /**
     * If the security scheme is of type "oauth2" or "openIdConnect", then the value is a list of scope names required for the execution.
     * For other security scheme types, the array must be empty.
     *
     * @return String array of scopes
     */
    String[] scopes() default {};
}
<file_sep>package io.swagger.v3.core.deserialization;

import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.testng.annotations.Test;

import java.io.IOException;

import static org.testng.Assert.*;

public class ObjectPropertyTest {
    @Test(description = "convert a model with object properties")
    public void readModelWithObjectProperty() throws IOException {
        String json = "{" +
                "   \"properties\":{" +
                "      \"id\":{" +
                "         \"type\":\"string\"" +
                "      }," +
                "      \"someObject\":{" +
                "         \"type\":\"object\"," +
                "        \"x-foo\": \"vendor x\"," +
                "         \"properties\":{" +
                "            \"innerId\":{" +
                "               \"type\":\"string\"" +
                "            }" +
                "         }" +
                "      }" +
                "   }" +
                "}";

        Schema model = Json.mapper().readValue(json, Schema.class);

        Schema p = (Schema) model.getProperties().get("someObject");
        assertTrue(p instanceof ObjectSchema);

        ObjectSchema op = (ObjectSchema) p;

        Schema sp = op.getProperties().get("innerId");
        assertTrue(sp instanceof StringSchema);

        assertTrue(op.getExtensions() != null);
        assertNotNull(op.getExtensions().get("x-foo"));
        assertEquals(op.getExtensions().get("x-foo"), "vendor x");
    }
}
<file_sep>package io.swagger.v3.jaxrs2;

import io.swagger.v3.jaxrs2.resources.model.ListOfStringsBeanParam;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.testng.Assert;
import org.testng.annotations.Test;

import javax.ws.rs.BeanParam;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import java.util.List;

public class BeanParamTest {

    @Path("/")
    private static class MyBeanParamResource {
        @GET
        public String getWithBeanParam(@BeanParam ListOfStringsBeanParam listOfStringsBean) {
            return "result";
        }
    }

    @Test(description = "check array type of serialized BeanParam containing QueryParams") // tests issue #2466
    public void shouldSerializeTypeParameter() {
        OpenAPI openApi = new Reader(new OpenAPI()).read(MyBeanParamResource.class);
        List<Parameter> getOperationParams = openApi.getPaths().get("/").getGet().getParameters();
        Assert.assertEquals(getOperationParams.size(), 1);
        Parameter param = getOperationParams.get(0);
        Assert.assertEquals(param.getName(), "listOfStrings");
        Schema<?> schema = param.getSchema();
        // These are the important checks:
        Assert.assertEquals(schema.getClass(), ArraySchema.class);
        Assert.assertEquals(((ArraySchema) schema).getItems().getType(), "string");   
    }

}<file_sep>package io.swagger.v3.jaxrs2.resources;

import io.swagger.v3.jaxrs2.resources.model.Pet;
import io.swagger.v3.oas.annotations.Operation;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

@Path("test")
@Produces(MediaType.APPLICATION_JSON)
public class TestResource {
    @Path("/status")
    @GET
    @Operation(description = "Get status")
    public String getStatus() {
        return "{\"status\":\"OK!\"}";
    }

    @Path("/more")
    @Operation(description = "Get more")
    @Produces({MediaType.APPLICATION_XML})
    public TestSubResource getSubResource(
            @QueryParam("qp") Integer qp) {
        return new TestSubResource();
    }

    @Path("/evenmore")
    @Operation(description = "Get even more")
    @Produces({MediaType.APPLICATION_XML})
    @Consumes(MediaType.APPLICATION_JSON)
    public TestSubResource getEvenMoreSubResource(Pet pet) {
        return new TestSubResource();
    }
}
<file_sep>/**
 * Copyright 2017 SmartBear Software
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.swagger.v3.oas.annotations.links;

import io.swagger.v3.oas.annotations.extensions.Extension;
import io.swagger.v3.oas.annotations.servers.Server;

import java.lang.annotation.*;

/**
 * The annotation may be applied in {@link io.swagger.v3.oas.annotations.responses.ApiResponse#links()} to add OpenAPI links to a response.
 *
 * @see <a target="_new" href="https://github.com/OAI/OpenAPI-Specification/blob/3.0.1/versions/3.0.1.md#linkObject">Link (OpenAPI specification)</a>
 * @see io.swagger.v3.oas.annotations.responses.ApiResponse
 **/
@Target({ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface Link {
    /**
     * The name of this link.
     *
     * @return the link's name
     **/
    String name() default "";

    /**
     * A relative or absolute reference to an OAS operation. This field is mutually exclusive of the operationId field, and must point to an Operation Object. Relative operationRef values may be used to locate an existing Operation Object in the OpenAPI definition.  Ignored if the operationId property is specified.
     *
     * @return an operation reference
     **/
    String operationRef() default "";

    /**
     * The name of an existing, resolvable OAS operation, as defined with a unique operationId. This field is mutually exclusive of the operationRef field.
     *
     * @return an operation ID
     **/
    String operationId() default "";

    /**
     * Array of parameters to pass to an operation as specified with operationId or identified via operationRef.
     *
     * @return the list of parameters for this link
     **/
    LinkParameter[] parameters() default {};

    /**
     * A description of the link. CommonMark syntax may be used for rich text representation.
     *
     * @return the link's description
     **/
    String description() default "";

    /**
     * A literal value or {expression} to use as a request body when calling the target operation.
     *
     * @return the request body of this link
     **/
    String requestBody() default "";

    /**
     * An alternative server to service this operation.
     *
     * @return the server associated to this link
     **/
    Server server() default @Server;

    /**
     * The list of optional extensions
     *
     * @return an optional array of extensions
     */
    Extension[] extensions() default {};

    /**
     * A reference to a link defined in components links.
     *
     * @since 2.0.3
     * @return the reference
     **/
    String ref() default "";

}
<file_sep>package io.swagger.v3.oas.models;

public class OpenAPIBuilderOptions {
	// THUAN - Configurations - Miscellaneous
	public static boolean USE_FULLNAME = false; // include package name in schemas
	public static boolean USE_ENUMNAME = false; // use Enum's name() instead of toString()
	public static boolean OMIT_GENERIC = false; // remove generic part in schemas' name
	public static boolean RECYCLE_ENUM = false; // make enums reusable
	public static boolean HIDE_PARENTS = false; // hide all properties from ancestors provided in allOf
}
<file_sep>package io.swagger.test;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.util.DefaultPrettyPrinter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.parameters.QueryParameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.tags.Tag;
import org.testng.annotations.Test;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class SimpleBuilderTest {
    @Test
    public void testBuilder() throws Exception {
        // basic metadata
        OpenAPI oai =
                new OpenAPI()
                        .info(new Info()
                                .contact(new Contact()
                                        .email("<EMAIL>")
                                        .name("<NAME>")
                                        .url("https://foo.bar")))
                        .externalDocs(new ExternalDocumentation()
                                .description("read more here")
                                .url("http://swagger.io"))
                        .addTagsItem(new Tag()
                                .name("funky dunky")
                                .description("all about neat things"))
                        .extensions(new HashMap<String, Object>() {{
                            put("x-fancy-extension", "something");
                        }});

        Map<String, Schema> schemas = new HashMap<>();

        schemas
                .put("StringSchema", new StringSchema()
                        .description("simple string schema")
                        .minLength(3)
                        .maxLength(100)
                        .example("it works")
                );

        schemas.put("IntegerSchema", new IntegerSchema()
                .description("simple integer schema")
                .multipleOf(new BigDecimal(3))
                .minimum(new BigDecimal(6))
        );

        oai.components(new Components()
                .schemas(schemas));

        schemas.put("Address", new Schema()
                .description("address object")
                .addProperties("street", new StringSchema()
                        .description("the street number"))
                .addProperties("city", new StringSchema()
                        .description("city"))
                .addProperties("state", new StringSchema()
                        .description("state")
                        .minLength(2)
                        .maxLength(2))
                .addProperties("zip", new StringSchema()
                        .description("zip code")
                        .pattern("^\\d{5}(?:[-\\s]\\d{4})?$")
                        .minLength(2)
                        .maxLength(2))
                .addProperties("country", new StringSchema()
                        ._enum(new ArrayList<String>() {{
                            this.add("US");
                        }}))
                .description("2-digit country code")
                .minLength(2)
                .maxLength(2)

        );

        oai.paths(new Paths()
                .addPathItem("/foo", new PathItem()
                        .description("the foo path")
                        .get(new Operation()
                                .addParametersItem(new QueryParameter()
                                        .description("Records to skip")
                                        .required(false)
                                        .schema(new IntegerSchema()
                                        ))
                                .responses(new ApiResponses()
                                        .addApiResponse("200", new ApiResponse()
                                                .description("it worked")
                                                .content(new Content()
                                                        .addMediaType("application/json",
                                                                new MediaType().schema(new Schema()
                                                                        .$ref("#/components/schemas/Address")))
                                                )
                                                .link("funky", new Link()
                                                        .operationId("getFunky")))
                                )
                        )
                )
        );

        System.out.println(writeJson(oai));
    }

    public static String writeJson(Object value) throws Exception {
        ObjectMapper mapper = new ObjectMapper();

        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
        mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

        return mapper.writer(new DefaultPrettyPrinter()).writeValueAsString(value);
    }
}
<file_sep>/**
 * Copyright 2016 SmartBear Software
 * <p>
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.swagger.v3.jaxrs2.resources;

import io.swagger.v3.jaxrs2.resources.data.PetData;
import io.swagger.v3.jaxrs2.resources.exception.NotFoundException;
import io.swagger.v3.jaxrs2.resources.model.Category;
import io.swagger.v3.jaxrs2.resources.model.Pet;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;

import javax.ws.rs.*;
import javax.ws.rs.core.Response;

@Consumes("application/json")
@Path("/pet")
@Produces({"application/json", "application/xml"})
public class PetResource {
    static PetData petData = new PetData();

    @GET
    @Path("/{petId}")
    @Operation(summary = "Find pet by ID",
            description = "Returns a pet when 0 < ID <= 10.  ID > 10 or nonintegers will simulate API error conditions",
            responses = {
                    @ApiResponse(
                            description = "The pet", content = @Content(
                            schema = @Schema(implementation = Pet.class)
                    )),
                    @ApiResponse(responseCode = "400", description = "Invalid ID supplied"),
                    @ApiResponse(responseCode = "404", description = "Pet not found")
            })
    public Response getPetById(
            @Parameter(description = "ID of pet that needs to be fetched"/*, _enum = "range[1,10]"*/, required = true)
            @PathParam("petId") Long petId) throws NotFoundException {
        Pet pet = petData.getPetById(petId);
        if (null != pet) {
            return Response.ok().entity(pet).build();
        } else {
            throw new NotFoundException(404, "Pet not found");
        }
    }

    @POST
    @Consumes({"application/json", "application/xml"})
    @Operation(summary = "Add a new pet to the store",
            responses = {
                    @ApiResponse(responseCode = "405", description = "Invalid input")
            })
    public Response addPet(
            @Parameter(description = "Pet object that needs to be added to the store", required = true) Pet pet) {
        petData.addPet(pet);
        return Response.ok().entity("SUCCESS").build();
    }

    @POST
    @Path("/bodynoannotation")
    @Consumes({"application/json", "application/xml"})
    @Produces({"application/json", "application/xml"})
    @Operation(summary = "Add a new pet to the store no annotation",
            responses = {
                    @ApiResponse(responseCode = "405", description = "Invalid input")
            })
    public Response addPetNoAnnotation(Pet pet) {
        petData.addPet(pet);
        return Response.ok().entity("SUCCESS").build();
    }

    @POST
    @Path("/bodyid")
    @Consumes({"application/json", "application/xml"})
    @Operation(summary = "Add a new pet to the store passing an integer with generic parameter annotation",
            responses = {
                    @ApiResponse(responseCode = "405", description = "Invalid input")
            })
    public Response addPetByInteger(
            @Parameter(description = "Pet object that needs to be added to the store", required = true) int petId) {
        return Response.ok().entity("SUCCESS").build();
    }

    @POST
    @Path("/bodyidnoannotation")
    @Consumes({"application/json", "application/xml"})
    @Operation(summary = "Add a new pet to the store passing an integer without parameter annotation",
            responses = {
                    @ApiResponse(responseCode = "405", description = "Invalid input")
            })
    public Response addPetByIntegerNoAnnotation(int petId) {
        return Response.ok().entity("SUCCESS").build();
    }

    @PUT
    @Operation(summary = "Update an existing pet",
            responses = {
                    @ApiResponse(responseCode = "400", description = "Invalid ID supplied"),
                    @ApiResponse(responseCode = "404", description = "Pet not found"),
                    @ApiResponse(responseCode = "405", description = "Validation exception")})
    public Response updatePet(
            @Parameter(description = "Pet object that needs to be added to the store", required = true) Pet pet) {
        petData.addPet(pet);
        return Response.ok().entity("SUCCESS").build();
    }

    @GET
    @Path("/findByStatus")
    @Produces("application/xml")
    @Operation(summary = "Finds Pets by status",
            description = "Multiple status values can be provided with comma separated strings",
            responses = {
                    @ApiResponse(
                            content = @Content(mediaType = "application/json",
                                    schema = @Schema(implementation = Pet.class))),
                    @ApiResponse(
                            responseCode = "400", description = "Invalid status value"
                    )}
    )
    public Response findPetsByStatus(
            @Parameter(description = "Status values that need to be considered for filter", required = true/*, defaultValue = "available", allowableValues = "available,pending,sold", allowMultiple = true*/) @QueryParam("status") String status,
            @BeanParam QueryResultBean qr
    ) {
        return Response.ok(petData.findPetByStatus(status)).build();
    }

    @GET
    @Path("/findByCategory/{category}")
    @Produces("application/xml")
    @Operation(summary = "Finds Pets by category",
            responses = {
                    @ApiResponse(
                            content = @Content(mediaType = "application/json",
                                    schema = @Schema(implementation = Pet.class))),
                    @ApiResponse(
                            responseCode = "400", description = "Invalid category value"
                    )}
    )
    public Response findPetsByCategory(
            @Parameter(description = "Category value that need to be considered for filter", required = true) @MatrixParam("category") Category category,
            @BeanParam QueryResultBean qr
    ) {
        return Response.ok(petData.findPetByCategory(category)).build();
    }

    @GET
    @Path("/findByTags")
    @Produces("application/json")
    @Operation(summary = "Finds Pets by tags",
            description = "Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing.",
            responses = {
                    @ApiResponse(description = "Pets matching criteria",
                            content = @Content(schema = @Schema(implementation = Pet.class))
                    ),
                    @ApiResponse(description = "Invalid tag value", responseCode = "400")
            })
    @Deprecated
    public Response findPetsByTags(
            @Parameter(description = "Tags to filter by", required = true/*, allowMultiple = true*/) @QueryParam("tags") String tags) {
        return Response.ok(petData.findPetByTags(tags)).build();
    }
}
<file_sep>package io.swagger.v3.jaxrs2.resources;

import io.swagger.v3.oas.annotations.parameters.RequestBody;

import javax.validation.constraints.NotNull;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Arrays;

@Path("/notnullparameter")
public class Ticket2794Resource
{
    @Produces({ MediaType.APPLICATION_JSON })
    @GET
    public Response getBooks(
            @QueryParam("page") @NotNull int page) {
        return Response.ok(
                Arrays.asList(
                        new Book(),
                        new Book()
                )
        ).build();
    }

    @Path("/new_reqBody_required")
    @POST
    public Response insert(@RequestBody(required = true) Book book) {
        return Response.ok().build();
    }

    @Path("/newnotnull")
    @POST
    public Response insertnotnull(@NotNull Book book) {
        return Response.ok().build();
    }

    public static class Book {
        public String foo;
    }
}<file_sep>package io.swagger.v3.jaxrs2.resources;

import io.swagger.v3.jaxrs2.resources.model.ModelWithJsonIdentity;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;

import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

@Path("/pet")
@Produces({"application/json", "application/xml"})
public class JsonIdentityResource {

    @POST
    @Operation(description = "Add a single object")
    public Response test(
            @Parameter(required = true) ModelWithJsonIdentity model) {
        return Response.ok().entity("SUCCESS").build();
    }
}<file_sep>package io.swagger.v3.jaxrs2;

import io.swagger.v3.jaxrs2.annotations.AbstractAnnotationTest;
import io.swagger.v3.jaxrs2.matchers.SerializationMatchers;
import io.swagger.v3.jaxrs2.petstore.EmptyPetResource;
import io.swagger.v3.jaxrs2.petstore.callback.ComplexCallbackResource;
import io.swagger.v3.jaxrs2.petstore.callback.MultipleCallbacksTestWithOperationResource;
import io.swagger.v3.jaxrs2.petstore.callback.RepeatableCallbackResource;
import io.swagger.v3.jaxrs2.petstore.callback.SimpleCallbackWithOperationResource;
import io.swagger.v3.jaxrs2.petstore.example.ExamplesResource;
import io.swagger.v3.jaxrs2.petstore.link.LinksResource;
import io.swagger.v3.jaxrs2.petstore.openapidefintion.OpenAPIDefinitionResource;
import io.swagger.v3.jaxrs2.petstore.operation.*;
import io.swagger.v3.jaxrs2.petstore.parameter.*;
import io.swagger.v3.jaxrs2.petstore.requestbody.RequestBodyMethodPriorityResource;
import io.swagger.v3.jaxrs2.petstore.requestbody.RequestBodyParameterPriorityResource;
import io.swagger.v3.jaxrs2.petstore.requestbody.RequestBodyResource;
import io.swagger.v3.jaxrs2.petstore.responses.*;
import io.swagger.v3.jaxrs2.petstore.security.SecurityResource;
import io.swagger.v3.jaxrs2.petstore.tags.*;
import io.swagger.v3.oas.models.OpenAPI;
import org.testng.annotations.Test;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.*;

import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.fail;

/**
 * Pet Resource Test Class
 * Adding a lot of tests of different pet resource examples
 */
public class PetResourceTest extends AbstractAnnotationTest {
    private static final String PETSTORE_SOURCE = "petstore/";
    private static final String TAGS_SOURCE = "petstore/tags/";
    private static final String OPERATIONS_SOURCE = "petstore/operation/";
    private static final String CALLBACKS_SOURCE = "petstore/callbacks/";
    private static final String RESPONSES_SOURCE = "petstore/responses/";
    private static final String PARAMETERS_SOURCE = "petstore/parameters/";
    private static final String LINKS_SOURCE = "petstore/links/";
    private static final String EXAMPLES_SOURCE = "petstore/example/";
    private static final String REQUEST_BODIES_SOURCE = "petstore/requestbody/";
    private static final String YAML_EXTENSION = ".yaml";
    private static final String PETSTORE_PACKAGE = "io.swagger.v3.jaxrs2.petstore";
    private static final char DOT = '.';
    private static final char SLASH = '/';

    @Test(description = "Test an empty resource class (Without operations or annotations)")
    public void testEmptyPetResource() {
        compare(EmptyPetResource.class, PETSTORE_SOURCE);
    }

    @Test(description = "Test a resource with examples)")
    public void testExamplesResource() {
        compare(ExamplesResource.class, EXAMPLES_SOURCE);
    }

    @Test(description = "Test a resource with Links)")
    public void testLinksResource() {
        compare(LinksResource.class, LINKS_SOURCE);
    }

    @Test(description = "Test some resources with Callbacks)")
    public void testCallBacksResources() {
        compare(SimpleCallbackWithOperationResource.class, CALLBACKS_SOURCE);
        compare(MultipleCallbacksTestWithOperationResource.class, CALLBACKS_SOURCE);
        compare(RepeatableCallbackResource.class, CALLBACKS_SOURCE);
        compare(ComplexCallbackResource.class, CALLBACKS_SOURCE);
    }

    @Test(description = "Test some resources with different Operations scenarios)")
    public void testOperationsResources() {
        compare(HiddenOperationResource.class, OPERATIONS_SOURCE);
        compare(OperationWithoutAnnotationResource.class, OPERATIONS_SOURCE);
        compare(FullyAnnotatedOperationResource.class, OPERATIONS_SOURCE);
        compare(AnnotatedSameNameOperationResource.class, OPERATIONS_SOURCE);
        compare(NotAnnotatedSameNameOperationResource.class, OPERATIONS_SOURCE);
        compare(ExternalDocumentationResource.class, OPERATIONS_SOURCE);
        compare(ServerOperationResource.class, OPERATIONS_SOURCE);
        compare(SubResource.class, OPERATIONS_SOURCE);
        compare(OperationResource.class, OPERATIONS_SOURCE);
    }

    @Test(description = "Test OpenAPIDefinition resource)")
    public void testOpenAPIDefinitionResource() {
        compare(OpenAPIDefinitionResource.class, PETSTORE_SOURCE);
    }

    @Test(description = "Test RequestBody resource)")
    public void tetRequestBodyResource() {
        compare(RequestBodyResource.class, REQUEST_BODIES_SOURCE);
        compare(RequestBodyParameterPriorityResource.class, REQUEST_BODIES_SOURCE);
        compare(RequestBodyMethodPriorityResource.class, REQUEST_BODIES_SOURCE);
    }

    @Test(description = "Test Parameters resources)")
    public void testParametersResource() {
        compare(ParametersResource.class, PARAMETERS_SOURCE);
        compare(RepeatableParametersResource.class, PARAMETERS_SOURCE);
        compare(ArraySchemaResource.class, PARAMETERS_SOURCE);
        compare(SingleNotAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(MultipleNotAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(SingleJaxRSAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(OpenAPIJaxRSAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(OpenAPIWithContentJaxRSAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(OpenAPIWithImplementationJaxRSAnnotatedParameter.class, PARAMETERS_SOURCE);
        compare(ComplexParameterResource.class, PARAMETERS_SOURCE);
        compare(ComplexParameterWithOperationResource.class, PARAMETERS_SOURCE);
    }

    @Test(description = "Test ApiResponses resource)")
    public void testResponsesResource() {
        compare(MethodResponseResource.class, RESPONSES_SOURCE);
        compare(OperationResponseResource.class, RESPONSES_SOURCE);
        compare(NoResponseResource.class, RESPONSES_SOURCE);
        compare(ImplementationResponseResource.class, RESPONSES_SOURCE);
        compare(NoImplementationResponseResource.class, RESPONSES_SOURCE);
        compare(PriorityResponseResource.class, RESPONSES_SOURCE);
        compare(ComplexResponseResource.class, RESPONSES_SOURCE);
    }

    @Test(description = "Test Security resource)")
    public void testSecurityResource() {
        compare(SecurityResource.class, PETSTORE_SOURCE);
    }

    @Test(description = "Test Tags resource)")
    public void testTagsResource() {
        compare(CompleteTagResource.class, TAGS_SOURCE);
        compare(TagOpenAPIDefinitionResource.class, TAGS_SOURCE);
        compare(TagClassResource.class, TAGS_SOURCE);
        compare(TagMethodResource.class, TAGS_SOURCE);
        compare(TagOperationResource.class, TAGS_SOURCE);
    }

    @Test(description = "Test a full set of classes")
    public void testSetOfClasses() {
        final Reader reader = new Reader(new OpenAPI());
        final OpenAPI openAPI = reader.read(getSetOfClassesFromPackage(PETSTORE_PACKAGE));
        assertNotNull(openAPI);
        try {
            SerializationMatchers.assertEqualsToYaml(openAPI, getOpenAPIAsString(PETSTORE_SOURCE + "FullPetResource.yaml"));
        } catch (IOException e) {
            fail();
        }
    }

    /**
     * Extract a set of classes from a package name
     *
     * @param packageName target to scan the classes
     * @return Set<Class>
     */
    private Set<Class<?>> getSetOfClassesFromPackage(final String packageName) {
        final Set<Class<?>> classSet = new HashSet<>();
        try {
            final Class[] classes = getClasses(packageName);
            for (final Class aClass : classes) {
                classSet.add(aClass);
            }
        } catch (final ClassNotFoundException | IOException e) {
            fail();
        }
        return classSet;
    }

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     */
    private static Class[] getClasses(final String packageName)
            throws ClassNotFoundException, IOException {
        final ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        final String path = packageName.replace(DOT, SLASH);
        final Enumeration<URL> resources = classLoader.getResources(path);
        final List<File> dirs = new ArrayList<>();
        while (resources.hasMoreElements()) {
            final URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        final ArrayList<Class> classes = new ArrayList<>();
        for (final File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirectories.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     */
    private static List<Class> findClasses(final File directory, final String packageName) throws ClassNotFoundException {
        final List<Class> classes = new ArrayList<>();
        if (!directory.exists()) {
            return classes;
        }
        final File[] files = directory.listFiles();
        if (files != null) {
            for (final File file : files) {
                if (file.isDirectory()) {
                    assert !file.getName().contains(".");
                    classes.addAll(findClasses(file, packageName + "." + file.getName()));
                } else if (file.getName().endsWith(".class")) {
                    classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
                }
            }
        }
        return classes;
    }

    /**
     * Compare a class that were read and parsed to a yaml against a yaml file.
     *
     * @param clazz  to read.
     * @param source where is the yaml.
     */
    private void compare(final Class clazz, final String source) {
        final String file = source + clazz.getSimpleName() + YAML_EXTENSION;
        try {
            compareAsYaml(clazz, getOpenAPIAsString(file));
        } catch (IOException e) {
            e.printStackTrace();
            fail();
        }
    }
}
<file_sep>package io.swagger.v3.jaxrs2.cdi2;

import io.swagger.v3.jaxrs2.SwaggerSerializers;
import io.swagger.v3.jaxrs2.integration.resources.OpenApiResource;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.testng.AssertJUnit;
import org.testng.annotations.Test;

import javax.enterprise.inject.spi.Extension;
import javax.inject.Inject;

public class CDIAutodiscoveryTest extends Arquillian {

    @Inject
    DiscoveryTestExtension ext;

    @Deployment
    public static JavaArchive createDeployment() {
        return ShrinkWrap.create(JavaArchive.class)
                .addClasses(SwaggerSerializers.class)
                .addPackage(OpenApiResource.class.getPackage())
                .addAsServiceProviderAndClasses(Extension.class, DiscoveryTestExtension.class)
                .addAsManifestResource("META-INF/beans.xml");
    }

    @Test
    void confirmPathClassesWereDiscovered() {
        String[] expected = {
                "io.swagger.v3.jaxrs2.integration.resources.AcceptHeaderOpenApiResource",
                "io.swagger.v3.jaxrs2.integration.resources.OpenApiResource"
                };
        Object[] found = ext.getResources().stream()
                .map(resource -> resource.getName())
                .sorted()
                .toArray();
        AssertJUnit.assertArrayEquals(expected, found);
    }

    @Test
    void confirmProviderClassesWereDiscovered() {
        String[] expected = {
                "io.swagger.v3.jaxrs2.SwaggerSerializers"
                };
        Object[] found = ext.getProviders().stream()
                .map(resource -> resource.getName())
                .toArray();
        AssertJUnit.assertArrayEquals(expected, found);
    }

}
