diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..cd2015181 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +tab_width = 4 +indent_style = tab + +[*.json] +indent_style = space +indent_size = 2 diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/api/annotations/ParameterObject.java b/springdoc-openapi-common/src/main/java/org/springdoc/api/annotations/ParameterObject.java new file mode 100644 index 000000000..9cb4ed3a8 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/api/annotations/ParameterObject.java @@ -0,0 +1,10 @@ +package org.springdoc.api.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.PARAMETER) +@Retention(RetentionPolicy.RUNTIME) +public @interface ParameterObject {} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java index 3ce4c0b38..20d132640 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/AbstractRequestBuilder.java @@ -53,6 +53,8 @@ import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import org.apache.commons.lang3.StringUtils; +import org.springdoc.api.annotations.ParameterObject; +import org.springdoc.core.converters.AdditionalModelsConverter; import org.springdoc.core.customizers.OperationCustomizer; import org.springdoc.core.customizers.ParameterCustomizer; @@ -160,22 +162,35 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, // requests String[] pNames = this.localSpringDocParameterNameDiscoverer.getParameterNames(handlerMethod.getMethod()); MethodParameter[] parameters = handlerMethod.getMethodParameters(); - String[] reflectionParametersNames = Arrays.stream(parameters).map(MethodParameter::getParameterName).toArray(String[]::new); - if (pNames == null) - pNames = reflectionParametersNames; + + List explodedParameters = new ArrayList<>(); + for (int i = 0; i < parameters.length; ++i) { + MethodParameter p = parameters[i]; + if (p.hasParameterAnnotation(ParameterObject.class)) { + Class paramClass = AdditionalModelsConverter.getReplacement(p.getParameterType()); + Stream.of(paramClass.getDeclaredFields()) + .map(f -> DelegatingMethodParameter.fromGetterOfField(paramClass, f)) + .filter(Objects::nonNull) + .forEach(explodedParameters::add); + } + else { + String name = pNames != null ? pNames[i] : p.getParameterName(); + explodedParameters.add(new DelegatingMethodParameter(p, name, null)); + } + } + parameters = explodedParameters.toArray(new MethodParameter[0]); + RequestBodyInfo requestBodyInfo = new RequestBodyInfo(); List operationParameters = (operation.getParameters() != null) ? operation.getParameters() : new ArrayList<>(); Map parametersDocMap = getApiParameters(handlerMethod.getMethod()); Components components = openAPI.getComponents(); - for (int i = 0; i < pNames.length; i++) { + for (MethodParameter methodParameter : parameters) { // check if query param Parameter parameter = null; - final String pName = pNames[i] == null ? reflectionParametersNames[i] : pNames[i]; - MethodParameter methodParameter = parameters[i]; io.swagger.v3.oas.annotations.Parameter parameterDoc = methodParameter.getParameterAnnotation(io.swagger.v3.oas.annotations.Parameter.class); if (parameterDoc == null) - parameterDoc = parametersDocMap.get(pName); + parameterDoc = parametersDocMap.get(methodParameter.getParameterName()); // use documentation as reference if (parameterDoc != null) { if (parameterDoc.hidden()) @@ -185,7 +200,7 @@ public Operation build(HandlerMethod handlerMethod, RequestMethod requestMethod, } if (!isParamToIgnore(methodParameter)) { - ParameterInfo parameterInfo = new ParameterInfo(pName, methodParameter, parameter); + ParameterInfo parameterInfo = new ParameterInfo(methodParameter.getParameterName(), methodParameter, parameter); parameter = buildParams(parameterInfo, components, requestMethod, methodAttributes.getJsonViewAnnotation()); // Merge with the operation parameters @@ -205,7 +220,7 @@ else if (!RequestMethod.GET.equals(requestMethod)) { } LinkedHashMap map = getParameterLinkedHashMap(components, methodAttributes, operationParameters, parametersDocMap); - setParams(operation, new ArrayList(map.values()), requestBodyInfo); + setParams(operation, new ArrayList<>(map.values()), requestBodyInfo); // allow for customisation return customiseOperation(operation, handlerMethod); } @@ -297,7 +312,7 @@ else if (pathVar != null) { String name = StringUtils.isBlank(pathVar.value()) ? pName : pathVar.value(); parameterInfo.setpName(name); // check if PATH PARAM - requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), Boolean.TRUE, null); + requestInfo = new RequestInfo(ParameterIn.PATH.toString(), pathVar.value(), !methodParameter.isOptional(), null); parameter = buildParam(parameterInfo, components, requestInfo, jsonView); } else if (cookieValue != null) { @@ -307,7 +322,7 @@ else if (cookieValue != null) { } // By default if (RequestMethod.GET.equals(requestMethod) || (parameterInfo.getParameterModel() != null && ParameterIn.PATH.toString().equals(parameterInfo.getParameterModel().getIn()))) - parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, Boolean.TRUE, null, jsonView); + parameter = this.buildParam(QUERY_PARAM, components, parameterInfo, !methodParameter.isOptional(), null, jsonView); return parameter; } diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java new file mode 100644 index 000000000..b865e5072 --- /dev/null +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/DelegatingMethodParameter.java @@ -0,0 +1,133 @@ +package org.springdoc.core; + +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Executable; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.util.Objects; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; + +import org.springframework.core.MethodParameter; +import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; + +/** + * @author zarebski.m + */ +class DelegatingMethodParameter extends MethodParameter { + private volatile MethodParameter delegate; + + private Annotation[] additionalParameterAnnotations; + + private String parameterName; + + DelegatingMethodParameter(MethodParameter delegate, String parameterName, Annotation[] additionalParameterAnnotations) { + super(delegate); + this.delegate = delegate; + this.additionalParameterAnnotations = additionalParameterAnnotations; + this.parameterName = parameterName; + } + + @Nullable + static MethodParameter fromGetterOfField(Class paramClass, Field field) { + try { + return Stream.of(Introspector.getBeanInfo(paramClass).getPropertyDescriptors()) + .filter(d -> d.getName().equals(field.getName())) + .map(PropertyDescriptor::getReadMethod) + .filter(Objects::nonNull) + .findFirst() + .map(method -> new MethodParameter(method, -1)) + .map(param -> new DelegatingMethodParameter(param, field.getName(), field.getDeclaredAnnotations())) + .orElse(null); + } + catch (IntrospectionException e) { + return null; + } + } + + @Override + @NonNull + public Annotation[] getParameterAnnotations() { + return ArrayUtils.addAll(delegate.getParameterAnnotations(), additionalParameterAnnotations); + } + + @Override + public String getParameterName() { + return parameterName; + } + + @Override + public Method getMethod() { + return delegate.getMethod(); + } + + @Override + public Constructor getConstructor() { + return delegate.getConstructor(); + } + + @Override + public Class getDeclaringClass() { + return delegate.getDeclaringClass(); + } + + @Override + public Member getMember() { + return delegate.getMember(); + } + + @Override + public AnnotatedElement getAnnotatedElement() { + return delegate.getAnnotatedElement(); + } + + @Override + public Executable getExecutable() { + return delegate.getExecutable(); + } + + @Override + public MethodParameter withContainingClass(Class containingClass) { + return delegate.withContainingClass(containingClass); + } + + @Override + public Class getContainingClass() { + return delegate.getContainingClass(); + } + + @Override + public Class getParameterType() { + return delegate.getParameterType(); + } + + @Override + public Type getGenericParameterType() { + return delegate.getGenericParameterType(); + } + + @Override + public Class getNestedParameterType() { + return delegate.getNestedParameterType(); + } + + @Override + public Type getNestedGenericParameterType() { + return delegate.getNestedGenericParameterType(); + } + + @Override + public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) { + delegate.initParameterNameDiscovery(parameterNameDiscoverer); + } +} diff --git a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java index 6d7d55c7a..02847972a 100644 --- a/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java +++ b/springdoc-openapi-common/src/main/java/org/springdoc/core/converters/AdditionalModelsConverter.java @@ -18,7 +18,6 @@ package org.springdoc.core.converters; - import java.util.HashMap; import java.util.Iterator; import java.util.Map; @@ -44,6 +43,10 @@ public static void replaceWithSchema(Class source, Schema target) { modelToSchemaMap.put(source, target); } + public static Class getReplacement(Class clazz) { + return modelToClassMap.getOrDefault(clazz, clazz); + } + @Override public Schema resolve(AnnotatedType type, ModelConverterContext context, Iterator chain) { JavaType javaType = Json.mapper().constructType(type.getType()); diff --git a/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/Pageable.java b/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/Pageable.java index 0e7f924ac..e3ab1f786 100644 --- a/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/Pageable.java +++ b/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/Pageable.java @@ -18,47 +18,54 @@ package org.springdoc.core.converters; -import java.util.List; -import java.util.Objects; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Schema; +import org.springframework.lang.Nullable; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; +import java.util.List; +import java.util.Objects; @NotNull public class Pageable { - @NotNull + @Nullable @Min(0) - private int page; + @Schema(description = "Zero-based page index (0..N)", defaultValue = "0") + private Integer page; - @NotNull + @Nullable @Min(1) @Max(2000) - private int size; + @Schema(description = "The size of the page to be returned", defaultValue = "20") + private Integer size; - @NotNull + @Nullable + @ArraySchema(arraySchema = @Schema(description = "Sorting criteria in the format: property(,asc|desc). " + + "Default sort order is ascending. " + "Multiple sort criteria are supported.")) private List sort; - public Pageable(@NotNull @Min(0) int page, @NotNull @Min(1) @Max(2000) int size, List sort) { + public Pageable(int page, int size, List sort) { this.page = page; this.size = size; this.sort = sort; } - public int getPage() { + public Integer getPage() { return page; } - public void setPage(int page) { + public void setPage(Integer page) { this.page = page; } - public int getSize() { + public Integer getSize() { return size; } - public void setSize(int size) { + public void setSize(Integer size) { this.size = size; } @@ -67,10 +74,11 @@ public List getSort() { } public void setSort(List sort) { - if (sort == null) + if (sort == null) { this.sort.clear(); - else + } else { this.sort = sort; + } } public void addSort(String sort) { @@ -82,8 +90,8 @@ public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Pageable pageable = (Pageable) o; - return page == pageable.page && - size == pageable.size && + return Objects.equals(page, pageable.page) && + Objects.equals(size, pageable.size) && Objects.equals(sort, pageable.sort); } diff --git a/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/PageableAsQueryParam.java b/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/PageableAsQueryParam.java index dbdb2275f..e2d1f73cd 100644 --- a/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/PageableAsQueryParam.java +++ b/springdoc-openapi-data-rest/src/main/java/org/springdoc/core/converters/PageableAsQueryParam.java @@ -18,18 +18,23 @@ package org.springdoc.core.converters; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.media.ArraySchema; import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; -@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE }) +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @deprecated Use {@link org.springdoc.api.annotations.ParameterObject} annotation + * on {@link org.springframework.data.domain.Pageable} method parameter instead. + */ +@Deprecated +@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Parameter(in = ParameterIn.QUERY , description = "Zero-based page index (0..N)" diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/HelloController.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/HelloController.java new file mode 100644 index 000000000..69e7926d5 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/HelloController.java @@ -0,0 +1,38 @@ +/* + * + * * Copyright 2019-2020 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.app7; + +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.data.domain.Pageable; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@RestController +public class HelloController { + + @GetMapping(value = "/search", produces = { "application/xml", "application/json" }) + public ResponseEntity> getAllPets(@NotNull @ParameterObject Pageable pageable) { + return null; + } + +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/PersonDTO.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/PersonDTO.java new file mode 100644 index 000000000..e7d21b4de --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/PersonDTO.java @@ -0,0 +1,63 @@ +/* + * + * * Copyright 2019-2020 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.app7; + +import io.swagger.v3.oas.annotations.media.Schema; + +@Schema +public class PersonDTO { + private String email; + + private String firstName; + + private String lastName; + + public PersonDTO() { + } + + public PersonDTO(final String email, final String firstName, final String lastName) { + this.email = email; + this.firstName = firstName; + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(final String email) { + this.email = email; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(final String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(final String lastName) { + this.lastName = lastName; + } +} diff --git a/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/SpringDocApp7Test.java b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/SpringDocApp7Test.java new file mode 100644 index 000000000..692a1f837 --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/java/test/org/springdoc/api/app7/SpringDocApp7Test.java @@ -0,0 +1,29 @@ +/* + * + * * Copyright 2019-2020 the original author or authors. + * * + * * Licensed under the Apache License, Version 2.0 (the "License"); + * * you may not use this file except in compliance with the License. + * * You may obtain a copy of the License at + * * + * * https://www.apache.org/licenses/LICENSE-2.0 + * * + * * Unless required by applicable law or agreed to in writing, software + * * distributed under the License is distributed on an "AS IS" BASIS, + * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * * See the License for the specific language governing permissions and + * * limitations under the License. + * + */ + +package test.org.springdoc.api.app7; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import test.org.springdoc.api.AbstractSpringDocTest; + +public class SpringDocApp7Test extends AbstractSpringDocTest { + + @SpringBootApplication + static class SpringDocTestApp {} + +} \ No newline at end of file diff --git a/springdoc-openapi-data-rest/src/test/resources/results/app2.json b/springdoc-openapi-data-rest/src/test/resources/results/app2.json index 2ca21bb45..2b5b16677 100644 --- a/springdoc-openapi-data-rest/src/test/resources/results/app2.json +++ b/springdoc-openapi-data-rest/src/test/resources/results/app2.json @@ -56,26 +56,26 @@ "components": { "schemas": { "Pageable": { - "required": [ - "page", - "size", - "sort" - ], "type": "object", "properties": { "page": { "minimum": 0, "type": "integer", - "format": "int32" + "description": "Zero-based page index (0..N)", + "format": "int32", + "default": 0 }, "size": { "maximum": 2000, "minimum": 1, "type": "integer", - "format": "int32" + "description": "The size of the page to be returned", + "format": "int32", + "default": 20 }, "sort": { "type": "array", + "description": "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", "items": { "type": "string" } diff --git a/springdoc-openapi-data-rest/src/test/resources/results/app7.json b/springdoc-openapi-data-rest/src/test/resources/results/app7.json new file mode 100644 index 000000000..b1027316a --- /dev/null +++ b/springdoc-openapi-data-rest/src/test/resources/results/app7.json @@ -0,0 +1,103 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/search": { + "get": { + "tags": [ + "hello-controller" + ], + "operationId": "getAllPets", + "parameters": [ + { + "name": "page", + "in": "query", + "required": false, + "schema": { + "minimum": 0, + "type": "integer", + "description": "Zero-based page index (0..N)", + "format": "int32", + "default": 0 + } + }, + { + "name": "size", + "in": "query", + "required": false, + "schema": { + "maximum": 2000, + "minimum": 1, + "type": "integer", + "description": "The size of the page to be returned", + "format": "int32", + "default": 20 + } + }, + { + "name": "sort", + "in": "query", + "required": false, + "schema": { + "type": "array", + "description": "Sorting criteria in the format: property(,asc|desc). Default sort order is ascending. Multiple sort criteria are supported.", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "description": "default response", + "content": { + "application/xml": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PersonDTO" + } + } + }, + "application/json": { + "schema": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PersonDTO" + } + } + } + } + } + } + } + } + }, + "components": { + "schemas": { + "PersonDTO": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/RequestParams.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/RequestParams.java new file mode 100644 index 000000000..31818012b --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/RequestParams.java @@ -0,0 +1,29 @@ +package test.org.springdoc.api.app102; + +import io.swagger.v3.oas.annotations.Parameter; +import org.springframework.lang.Nullable; + +public class RequestParams { + @Nullable + @Parameter(description = "string parameter") + private String stringParam; + + @Parameter(description = "int parameter") + private int intParam; + + public String getStringParam() { + return stringParam; + } + + public void setStringParam(String stringParam) { + this.stringParam = stringParam; + } + + public int getIntParam() { + return intParam; + } + + public void setIntParam(int intParam) { + this.intParam = intParam; + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/SpringDocApp102Test.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/SpringDocApp102Test.java new file mode 100644 index 000000000..7eb99a7c9 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/SpringDocApp102Test.java @@ -0,0 +1,9 @@ +package test.org.springdoc.api.app102; + +import org.springframework.boot.autoconfigure.SpringBootApplication; +import test.org.springdoc.api.AbstractSpringDocTest; + +public class SpringDocApp102Test extends AbstractSpringDocTest { + @SpringBootApplication + static class SpringDocTestApp {} +} diff --git a/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/TestController.java b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/TestController.java new file mode 100644 index 000000000..84117b501 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/java/test/org/springdoc/api/app102/TestController.java @@ -0,0 +1,14 @@ +package test.org.springdoc.api.app102; + +import org.springdoc.api.annotations.ParameterObject; +import org.springframework.lang.Nullable; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class TestController { + @GetMapping("test") + public void getTest(@RequestParam @Nullable String param, @ParameterObject RequestParams requestParams) { + } +} diff --git a/springdoc-openapi-webmvc-core/src/test/resources/results/app102.json b/springdoc-openapi-webmvc-core/src/test/resources/results/app102.json new file mode 100644 index 000000000..611245280 --- /dev/null +++ b/springdoc-openapi-webmvc-core/src/test/resources/results/app102.json @@ -0,0 +1,58 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "OpenAPI definition", + "version": "v0" + }, + "servers": [ + { + "url": "http://localhost", + "description": "Generated server url" + } + ], + "paths": { + "/test": { + "get": { + "tags": [ + "test-controller" + ], + "operationId": "getTest", + "parameters": [ + { + "name": "param", + "in": "query", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "stringParam", + "in": "query", + "description": "string parameter", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "intParam", + "in": "query", + "description": "int parameter", + "required": true, + "schema": { + "type": "integer", + "format": "int32" + } + } + ], + "responses": { + "200": { + "description": "default response" + } + } + } + } + }, + "components": {} +} \ No newline at end of file