From e25dc553a5cd1e4022e00d5929d86f7b5053ce86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francesco=20Chicchiricc=C3=B2?= Date: Mon, 30 Nov 2015 16:13:17 +0100 Subject: [PATCH] [CXF-6695] Adding JAX-RS 2 extension for handling @BeanParam and @MatrixParam --- parent/pom.xml | 2 +- .../cxf/jaxrs/swagger/JaxRs2Extension.java | 151 ++++++++++++++++++ .../cxf/jaxrs/swagger/MatrixParameter.java | 28 ++++ .../io.swagger.jaxrs.ext.SwaggerExtension | 1 + .../cxf/jaxrs/swagger/SwaggerUtilsTest.java | 6 +- .../src/test/resources/swagger20.json | 15 +- 6 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java create mode 100644 rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java create mode 100644 rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension diff --git a/parent/pom.xml b/parent/pom.xml index 210c0b0128e..8ca9fdbb1b7 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -144,7 +144,7 @@ 1.2.1 1.3.1.RELEASE spring-test - 1.3.12 + 1.3.13 1.5.4 1.7 4.4.1 diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java new file mode 100644 index 00000000000..3c8ae9546f7 --- /dev/null +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/JaxRs2Extension.java @@ -0,0 +1,151 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import javax.ws.rs.BeanParam; +import javax.ws.rs.MatrixParam; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.introspect.AnnotatedField; +import com.fasterxml.jackson.databind.introspect.AnnotatedMethod; +import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition; + +import io.swagger.converter.ModelConverters; +import io.swagger.jaxrs.ext.AbstractSwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtension; +import io.swagger.jaxrs.ext.SwaggerExtensions; +import io.swagger.models.parameters.Parameter; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.Property; +import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.StringProperty; +import io.swagger.util.Json; +import io.swagger.util.ParameterProcessor; + +public class JaxRs2Extension extends AbstractSwaggerExtension { + + private final ObjectMapper mapper = Json.mapper(); + + @Override + public List extractParameters( + final List annotations, + final Type type, + final Set typesToSkip, + final Iterator chain) { + + if (shouldIgnoreType(type, typesToSkip)) { + return new ArrayList<>(); + } + + List parameters = new ArrayList<>(); + for (Annotation annotation : annotations) { + if (annotation instanceof MatrixParam) { + MatrixParam param = (MatrixParam) annotation; + MatrixParameter mp = new MatrixParameter().name(param.value()); + + Property schema = createProperty(type); + if (schema != null) { + mp.setProperty(schema); + } + parameters.add(mp); + } else if (annotation instanceof BeanParam) { + // Use Jackson's logic for processing Beans + final BeanDescription beanDesc = mapper.getSerializationConfig().introspect(constructType(type)); + final List properties = beanDesc.findProperties(); + + for (final BeanPropertyDefinition propDef : properties) { + final AnnotatedField field = propDef.getField(); + final AnnotatedMethod setter = propDef.getSetter(); + final List paramAnnotations = new ArrayList<>(); + final Iterator extensions = SwaggerExtensions.chain(); + Type paramType = null; + + // Gather the field's details + if (field != null) { + paramType = field.getGenericType(); + + for (final Annotation fieldAnnotation : field.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Gather the setter's details but only the ones we need + if (setter != null) { + // Do not set the param class/type from the setter if the values are already identified + if (paramType == null && setter.getGenericParameterTypes() != null) { + paramType = setter.getGenericParameterTypes()[0]; + } + + for (final Annotation fieldAnnotation : setter.annotations()) { + if (!paramAnnotations.contains(fieldAnnotation)) { + paramAnnotations.add(fieldAnnotation); + } + } + } + + // Re-process all Bean fields and let the default swagger-jaxrs processor do its thing + List extracted = + extensions.next().extractParameters(paramAnnotations, paramType, typesToSkip, extensions); + + // since downstream processors won't know how to introspect @BeanParam, process here + for (Parameter param : extracted) { + if (ParameterProcessor.applyAnnotations(null, param, paramType, paramAnnotations) != null) { + parameters.add(param); + } + } + } + } + } + + // Only call down to the other items in the chain if no parameters were produced + if (parameters.isEmpty()) { + parameters = super.extractParameters(annotations, type, typesToSkip, chain); + } + + return parameters; + } + + private Property createProperty(Type type) { + return enforcePrimitive(ModelConverters.getInstance().readAsProperty(type), 0); + } + + private Property enforcePrimitive(Property in, int level) { + if (in instanceof RefProperty) { + return new StringProperty(); + } + if (in instanceof ArrayProperty) { + if (level == 0) { + final ArrayProperty array = (ArrayProperty) in; + array.setItems(enforcePrimitive(array.getItems(), level + 1)); + } else { + return new StringProperty(); + } + } + return in; + } +} diff --git a/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java new file mode 100644 index 00000000000..f1472c2e1b4 --- /dev/null +++ b/rt/rs/description/src/main/java/org/apache/cxf/jaxrs/swagger/MatrixParameter.java @@ -0,0 +1,28 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.cxf.jaxrs.swagger; + +import io.swagger.models.parameters.AbstractSerializableParameter; + +public class MatrixParameter extends AbstractSerializableParameter { + + public MatrixParameter() { + super.setIn("matrix"); + } +} diff --git a/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension b/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension new file mode 100644 index 00000000000..9803485e96a --- /dev/null +++ b/rt/rs/description/src/main/resources/META-INF/services/io.swagger.jaxrs.ext.SwaggerExtension @@ -0,0 +1 @@ +org.apache.cxf.jaxrs.swagger.JaxRs2Extension diff --git a/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java b/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java index e9434e1dc29..5c3d6e9ca4c 100644 --- a/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java +++ b/rt/rs/description/src/test/java/org/apache/cxf/jaxrs/swagger/SwaggerUtilsTest.java @@ -57,7 +57,7 @@ public void testConvertSwagger20ToUserResource() { assertEquals("application/x-www-form-urlencoded", op.getConsumes()); assertEquals("application/json", op.getProduces()); - assertEquals(2, op.getParameters().size()); + assertEquals(3, op.getParameters().size()); Parameter param1 = op.getParameters().get(0); assertEquals("userName", param1.getName()); assertEquals(ParameterType.FORM, param1.getType()); @@ -66,5 +66,9 @@ public void testConvertSwagger20ToUserResource() { assertEquals("password", param2.getName()); assertEquals(ParameterType.FORM, param2.getType()); assertEquals(String.class, param2.getJavaType()); + Parameter param3 = op.getParameters().get(2); + assertEquals("type", param3.getName()); + assertEquals(ParameterType.MATRIX, param3.getType()); + assertEquals(String.class, param3.getJavaType()); } } diff --git a/rt/rs/description/src/test/resources/swagger20.json b/rt/rs/description/src/test/resources/swagger20.json index 44e7a324bba..f8f264df5db 100644 --- a/rt/rs/description/src/test/resources/swagger20.json +++ b/rt/rs/description/src/test/resources/swagger20.json @@ -23,7 +23,20 @@ "in": "formData", "name": "password", "type": "string" - } + }, + { + "name": "type", + "in": "matrix", + "required": true, + "type": "string", + "enum": [ + "PROPAGATION", + "NOTIFICATION", + "SCHEDULED", + "SYNCHRONIZATION", + "PUSH" + ] + } ] } }