Skip to content

Commit

Permalink
Provide support of OpenApi annotations with resteasy extension
Browse files Browse the repository at this point in the history
- Support for JaxRs friendly annotations from other extension with a new build item
- Refactor existing friendly package prefix with the new build item
- Add test case for OpenApi annotations
  • Loading branch information
vietk committed Aug 20, 2020
1 parent 0f567e9 commit 4a7a8fe
Show file tree
Hide file tree
Showing 13 changed files with 98 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import io.quarkus.resteasy.server.common.spi.AdditionalJaxRsResourceDefiningAnnotationBuildItem;
import io.quarkus.resteasy.server.common.spi.AdditionalJaxRsResourceMethodAnnotationsBuildItem;
import io.quarkus.resteasy.server.common.spi.AdditionalJaxRsResourceMethodParamAnnotations;
import io.quarkus.resteasy.server.common.spi.AllowedJaxRsAnnotationPrefixBuildItem;
import io.quarkus.runtime.annotations.ConfigItem;
import io.quarkus.runtime.annotations.ConfigRoot;
import io.quarkus.undertow.deployment.FilterBuildItem;
Expand Down Expand Up @@ -174,6 +175,7 @@ public void build(
List<AdditionalJaxRsResourceDefiningAnnotationBuildItem> additionalJaxRsResourceDefiningAnnotations,
List<AdditionalJaxRsResourceMethodAnnotationsBuildItem> additionalJaxRsResourceMethodAnnotations,
List<AdditionalJaxRsResourceMethodParamAnnotations> additionalJaxRsResourceMethodParamAnnotations,
List<AllowedJaxRsAnnotationPrefixBuildItem> friendlyJaxRsAnnotationPrefixes,
List<ResteasyDeploymentCustomizerBuildItem> deploymentCustomizers,
JaxrsProvidersToRegisterBuildItem jaxrsProvidersToRegisterBuildItem,
CombinedIndexBuildItem combinedIndexBuildItem,
Expand Down Expand Up @@ -283,7 +285,8 @@ public void build(

// generate default constructors for suitable concrete @Path classes that don't have them
// see https://issues.jboss.org/browse/RESTEASY-2183
generateDefaultConstructors(transformers, withoutDefaultCtor, additionalJaxRsResourceDefiningAnnotations);
generateDefaultConstructors(transformers, withoutDefaultCtor, additionalJaxRsResourceDefiningAnnotations,
friendlyJaxRsAnnotationPrefixes);

checkParameterNames(beanArchiveIndexBuildItem.getIndex(), additionalJaxRsResourceMethodParamAnnotations);

Expand Down Expand Up @@ -387,6 +390,18 @@ public void transform(TransformationContext context) {
resteasyDeployment.produce(new ResteasyDeploymentBuildItem(path, deployment));
}

@BuildStep
List<AllowedJaxRsAnnotationPrefixBuildItem> registerCompatibleAnnotationPrefixes() {
List<AllowedJaxRsAnnotationPrefixBuildItem> prefixes = new ArrayList<>();
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem(packageName(ResteasyDotNames.PATH)));
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("kotlin")); // make sure the annotation that the Kotlin compiler adds don't interfere with creating a default constructor
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("lombok")); // same for lombok
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("io.quarkus.security")); // same for the security annotations
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("javax.annotation.security"));
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("jakarta.annotation.security"));
return prefixes;
}

@BuildStep
void processPathInterfaceImplementors(CombinedIndexBuildItem combinedIndexBuildItem,
BuildProducer<UnremovableBeanBuildItem> unremovableBeans,
Expand Down Expand Up @@ -579,15 +594,14 @@ private static void registerProviders(ResteasyDeployment deployment,

private static void generateDefaultConstructors(BuildProducer<BytecodeTransformerBuildItem> transformers,
Map<DotName, ClassInfo> withoutDefaultCtor,
List<AdditionalJaxRsResourceDefiningAnnotationBuildItem> additionalJaxRsResourceDefiningAnnotations) {
List<AdditionalJaxRsResourceDefiningAnnotationBuildItem> additionalJaxRsResourceDefiningAnnotations,
List<AllowedJaxRsAnnotationPrefixBuildItem> friendlyJaxRsAnnotationPrefixes) {

final Set<String> allowedAnnotationPrefixes = new HashSet<>(1 + additionalJaxRsResourceDefiningAnnotations.size());
allowedAnnotationPrefixes.add(packageName(ResteasyDotNames.PATH));
allowedAnnotationPrefixes.add("kotlin"); // make sure the annotation that the Kotlin compiler adds don't interfere with creating a default constructor
allowedAnnotationPrefixes.add("lombok"); // same for lombok
allowedAnnotationPrefixes.add("io.quarkus.security"); // same for the security annotations
allowedAnnotationPrefixes.add("javax.annotation.security");
allowedAnnotationPrefixes.add("jakarta.annotation.security");
friendlyJaxRsAnnotationPrefixes.stream()
.map(prefix -> prefix.getAnnotationPrefix())
.forEachOrdered(allowedAnnotationPrefixes::add);

for (AdditionalJaxRsResourceDefiningAnnotationBuildItem additionalJaxRsResourceDefiningAnnotation : additionalJaxRsResourceDefiningAnnotations) {
final String packageName = packageName(additionalJaxRsResourceDefiningAnnotation.getAnnotationClass());
if (packageName != null) {
Expand All @@ -605,7 +619,7 @@ private static void generateDefaultConstructors(BuildProducer<BytecodeTransforme
boolean hasNonJaxRSAnnotations = false;
for (AnnotationInstance instance : classInfo.classAnnotations()) {
final String packageName = packageName(instance.name());
if (packageName == null || !allowedAnnotationPrefixes.contains(packageName)) {
if (packageName == null || !isPackageAllowed(allowedAnnotationPrefixes, packageName)) {
hasNonJaxRSAnnotations = true;
break;
}
Expand Down Expand Up @@ -643,6 +657,10 @@ public void visit(int version, int access, String name, String signature, String
}
}

private static boolean isPackageAllowed(Set<String> allowedAnnotationPrefixes, String packageName) {
return allowedAnnotationPrefixes.stream().anyMatch(prefix -> packageName.startsWith(prefix));
}

private static String packageName(DotName dotName) {
final String className = dotName.toString();
final int index = className.lastIndexOf('.');
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package io.quarkus.resteasy.server.common.spi;

import io.quarkus.builder.item.MultiBuildItem;

/**
* The package prefix of any annotations that have to be compatible with JaxRs resource class
* to allow constructor injection.
*/
public final class AllowedJaxRsAnnotationPrefixBuildItem extends MultiBuildItem {

private final String prefix;

public AllowedJaxRsAnnotationPrefixBuildItem(String prefix) {
this.prefix = prefix;
}

public String getAnnotationPrefix() {
return prefix;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem;
import io.quarkus.deployment.logging.LogCleanupFilterBuildItem;
import io.quarkus.resteasy.server.common.spi.AllowedJaxRsAnnotationPrefixBuildItem;
import io.quarkus.resteasy.server.common.spi.ResteasyJaxrsConfigBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.smallrye.openapi.common.deployment.SmallRyeOpenApiConfig;
Expand Down Expand Up @@ -159,6 +160,13 @@ OpenApiFilteredIndexViewBuildItem smallryeOpenApiIndex(CombinedIndexBuildItem co
new OpenApiConfigImpl(ConfigProvider.getConfig())));
}

@BuildStep
public List<AllowedJaxRsAnnotationPrefixBuildItem> registerJaxRsSupportedAnnotation() {
List<AllowedJaxRsAnnotationPrefixBuildItem> prefixes = new ArrayList<>();
prefixes.add(new AllowedJaxRsAnnotationPrefixBuildItem("org.eclipse.microprofile.openapi.annotations"));
return prefixes;
}

@BuildStep
public void registerOpenApiSchemaClassesForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<ReflectiveHierarchyBuildItem> reflectiveHierarchy,
Expand Down Expand Up @@ -418,4 +426,4 @@ private OASFilter filter(OpenApiConfig openApiConfig) {
return OpenApiProcessor.getFilter(openApiConfig,
Thread.currentThread().getContextClassLoader());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class OpenApiDefaultPathTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class));
.addClasses(OpenApiResource.class, ResourceBean.class));

@Test
public void testOpenApiPathAccessResource() {
Expand All @@ -34,7 +34,11 @@ public void testOpenApiPathAccessResource() {
.header("Content-Type", "application/json;charset=UTF-8")
.body("openapi", Matchers.startsWith("3.0"))
.body("info.title", Matchers.equalTo("Generated API"))
.body("paths", Matchers.hasKey("/resource"));
.body("tags.name[0]", Matchers.equalTo("test"))
.body("paths.'/resource'.get.servers[0]", Matchers.hasKey("url"))
.body("paths.'/resource'.get.security[0]", Matchers.hasKey("securityRequirement"))
.body("paths.'/resource'.get", Matchers.hasKey("openApiExtension"));

RestAssured.given()
.when().options(OPEN_API_PATH)
.then().header("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class OpenApiHttpRootDefaultPathTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.http.root-path=/foo"), "application.properties"));

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class OpenApiPathWithSegmentsTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.smallrye-openapi.path=" + OPEN_API_PATH),
"application.properties"));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public class OpenApiPathWithoutSegmentsTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.smallrye-openapi.path=" + OPEN_API_PATH),
"application.properties"));

Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
package io.quarkus.smallrye.openapi.test.jaxrs;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

import org.eclipse.microprofile.openapi.annotations.extensions.Extension;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.servers.Server;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;

@Path("/resource")
@Tag(name = "test")
@Extension(name = "openApiExtension", value = "openApiExtensionValue")
@SecurityRequirement(name = "securityRequirement", scopes = "securityRequirementScope")
@Server(url = "serverUrl")
public class OpenApiResource {

private ResourceBean resourceBean;

@Inject
public OpenApiResource(ResourceBean resourceBean) {
this.resourceBean = resourceBean;
}

@GET
public String root() {
return "resource";
return resourceBean.toString();
}

@GET
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class OpenApiWithConfigTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsManifestResource("test-openapi.yaml", "openapi.yaml")
.addAsResource(new StringAsset("mp.openapi.scan.disable=true\nmp.openapi.servers=https://api.acme.org/"),
"application.properties"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class OpenApiWithResteasyPathHttpRootDefaultPathTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.http.root-path=/http-root-path\n" +
"quarkus.resteasy.path=/resteasy-path"),
"application.properties"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class OpenApiWithResteasyPathTestCase {
@RegisterExtension
static QuarkusUnitTest runner = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.resteasy.path=/foo/bar"),
"application.properties"));

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package io.quarkus.smallrye.openapi.test.jaxrs;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class ResourceBean {
@Override
public String toString() {
return "resource";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public class SwaggerAndOpenAPIWithCommonPrefixTest {
@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClass(OpenApiResource.class)
.addClasses(OpenApiResource.class, ResourceBean.class)
.addAsResource(new StringAsset("quarkus.smallrye-openapi.path=/swagger"), "application.properties"));

@Test
Expand Down

0 comments on commit 4a7a8fe

Please sign in to comment.