Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to exclude classes in REST Data with Panache with annotations #34999

Merged
merged 1 commit into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions docs/src/main/asciidoc/rest-data-panache.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -596,4 +596,36 @@
* Link: < http://example.com/people?page=2&size=2 >; rel="last"
* Link: < http://example.com/people?page=1&size=2 >; rel="next"

A `previous` link header (and hal link) would not be included, because the previous page does not exist.

Check warning on line 599 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Spelling] Use correct American English spelling. Did you really mean 'hal'? Raw Output: {"message": "[Quarkus.Spelling] Use correct American English spelling. Did you really mean 'hal'?", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 599, "column": 31}}}, "severity": "WARNING"}

== Include/Exclude Jakarta REST classes

Check warning on line 601 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.Headings] Use sentence-style capitalization in 'Using Build time conditions'. Raw Output: {"message": "[Quarkus.Headings] Use sentence-style capitalization in 'Using Build time conditions'.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 601, "column": 24}}}, "severity": "INFO"}

=== Using Build time conditions

Check warning on line 603 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.OxfordComma] Use the Oxford comma in 'Quarkus enables the inclusion or exclusion of Jakarta REST Resources, Providers and'. Raw Output: {"message": "[Quarkus.OxfordComma] Use the Oxford comma in 'Quarkus enables the inclusion or exclusion of Jakarta REST Resources, Providers and'.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 603, "column": 12}}}, "severity": "INFO"}

Quarkus enables the inclusion or exclusion of Jakarta REST Resources, Providers and Features directly thanks to build time conditions in the same that it does for CDI beans.

Check warning on line 605 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.SentenceLength] Try to keep sentences to an average of 32 words or fewer. Raw Output: {"message": "[Quarkus.SentenceLength] Try to keep sentences to an average of 32 words or fewer.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 605, "column": 153}}}, "severity": "INFO"}

Check warning on line 605 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'therefore' rather than 'Thus' unless updating existing content that uses it. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'therefore' rather than 'Thus' unless updating existing content that uses it.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 605, "column": 153}}}, "severity": "WARNING"}
Thus, the REST Data with Panache interfaces can be annotated with profile conditions (`@io.quarkus.arc.profile.IfBuildProfile` or `@io.quarkus.arc.profile.UnlessBuildProfile`) and/or with property conditions (`io.quarkus.arc.properties.IfBuildProperty` or `io.quarkus.arc.properties.UnlessBuildProperty`) to indicate to Quarkus at build time under which conditions the generated Jakarta REST classes should be included.

Check warning on line 606 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsWarnings] Consider using 'a or b' or 'a, b, or both' rather than 'and/or' unless updating existing content that uses it. Raw Output: {"message": "[Quarkus.TermsWarnings] Consider using 'a or b' or 'a, b, or both' rather than 'and/or' unless updating existing content that uses it.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 606, "column": 155}}}, "severity": "WARNING"}

Check warning on line 606 in docs/src/main/asciidoc/rest-data-panache.adoc

View workflow job for this annotation

GitHub Actions / Linting with Vale

[vale] reported by reviewdog 🐶 [Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'. Raw Output: {"message": "[Quarkus.TermsSuggestions] Depending on the context, consider using ', which (non restrictive clause preceded by a comma)' or 'that (restrictive clause without a comma)' rather than 'which'.", "location": {"path": "docs/src/main/asciidoc/rest-data-panache.adoc", "range": {"start": {"line": 606, "column": 326}}}, "severity": "INFO"}

In the following example, Quarkus will include the generated resource from the `PeopleResource` interface if and only if the build profile `app1` has been enabled.

[source,java]
----
@IfBuildProfile("app1")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
----


=== Using a runtime property

IMPORTANT: This option is only available when using the RESTEasy Reactive Quarkus extension.

Quarkus can also conditionally disable the generated Jakarta REST Resources based on the value of runtime properties using the `@io.quarkus.resteasy.reactive.server.EndpointDisabled` annotation.

In the following example, Quarkus will exclude the generated resource from the `PeopleResource` interface at runtime if the application has `some.property` configured to `"disable"`.

[source,java]
----
@EndpointDisabled(name = "some.property", stringValue = "disable")
public interface PeopleResource extends PanacheEntityResource<Person, Long> {
}
----
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.build;

import static io.restassured.RestAssured.given;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class BuildConditionsWithResourceDisabledTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Collection.class, CollectionsResource.class));

@Test
void shouldResourceNotBeFound() {
given().accept("application/json")
.when().get("/collections")
.then().statusCode(404);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.build;

import static io.restassured.RestAssured.given;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class BuildConditionsWithResourceEnabledTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.overrideConfigKey("collections.enabled", "true")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Collection.class, CollectionsResource.class));

@Test
void shouldResourceBeFound() {
given().accept("application/json")
.when().get("/collections")
.then().statusCode(200);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.build;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

import io.quarkus.hibernate.orm.panache.PanacheEntityBase;

@Entity
public class Collection extends PanacheEntityBase {

@Id
public String id;

public String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.hibernate.orm.rest.data.panache.deployment.build;

import io.quarkus.arc.properties.IfBuildProperty;
import io.quarkus.hibernate.orm.rest.data.panache.PanacheEntityResource;

@IfBuildProperty(name = "collections.enabled", stringValue = "true")
public interface CollectionsResource extends PanacheEntityResource<Collection, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.build;

import static io.restassured.RestAssured.given;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class BuildConditionsWithResourceDisabledTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.overrideConfigKey("collections.endpoint", "disable")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Collection.class, CollectionsResource.class));

@Test
void shouldResourceNotBeFound() {
given().accept("application/json")
.when().get("/collections")
.then().statusCode(404);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.build;

import static io.restassured.RestAssured.given;

import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.spec.JavaArchive;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.test.QuarkusUnitTest;

public class BuildConditionsWithResourceEnabledTest {
@RegisterExtension
static final QuarkusUnitTest TEST = new QuarkusUnitTest()
.overrideConfigKey("collections.endpoint", "enable")
.setArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
.addClasses(Collection.class, CollectionsResource.class));

@Test
void shouldResourceBeFound() {
given().accept("application/json")
.when().get("/collections")
.then().statusCode(200);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.build;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;

import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;

@Entity
public class Collection extends PanacheEntityBase {

@Id
public String id;

public String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.quarkus.hibernate.reactive.rest.data.panache.deployment.build;

import io.quarkus.hibernate.reactive.rest.data.panache.PanacheEntityResource;
import io.quarkus.resteasy.reactive.server.EndpointDisabled;

@EndpointDisabled(name = "collections.endpoint", stringValue = "disable")
public interface CollectionsResource extends PanacheEntityResource<Collection, String> {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,12 @@

import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationTarget;

import io.quarkus.arc.deployment.BuildTimeConditionBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanBuildItem;
import io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor;
import io.quarkus.deployment.Capabilities;
Expand Down Expand Up @@ -50,8 +55,11 @@ void supportingBuildItems(Capabilities capabilities,
}

@BuildStep
void implementResources(CombinedIndexBuildItem index, List<RestDataResourceBuildItem> resourceBuildItems,
List<ResourcePropertiesBuildItem> resourcePropertiesBuildItems, Capabilities capabilities,
void implementResources(CombinedIndexBuildItem index,
List<RestDataResourceBuildItem> resourceBuildItems,
List<ResourcePropertiesBuildItem> resourcePropertiesBuildItems,
List<BuildTimeConditionBuildItem> buildTimeConditions,
Capabilities capabilities,
BuildProducer<GeneratedBeanBuildItem> resteasyClassicImplementationsProducer,
BuildProducer<GeneratedJaxRsResourceBuildItem> resteasyReactiveImplementationsProducer) {

Expand All @@ -63,27 +71,30 @@ void implementResources(CombinedIndexBuildItem index, List<RestDataResourceBuild
"Reactive REST Data Panache does not work with 'quarkus-resteasy'. Only 'quarkus-resteasy-reactive' extensions are supported");
}

Set<String> excludedClasses = getExcludedClasses(buildTimeConditions);
ClassOutput classOutput = isResteasyClassic ? new GeneratedBeanGizmoAdaptor(resteasyClassicImplementationsProducer)
: new GeneratedJaxRsResourceGizmoAdaptor(resteasyReactiveImplementationsProducer);
JaxRsResourceImplementor jaxRsResourceImplementor = new JaxRsResourceImplementor(capabilities);
ResourcePropertiesProvider resourcePropertiesProvider = new ResourcePropertiesProvider(index.getIndex());

for (RestDataResourceBuildItem resourceBuildItem : resourceBuildItems) {
ResourceMetadata resourceMetadata = resourceBuildItem.getResourceMetadata();
ResourceProperties resourceProperties = getResourceProperties(resourcePropertiesProvider,
resourceMetadata, resourcePropertiesBuildItems);
if (resourceProperties.isHal()) {
if (isResteasyClassic && !hasAnyJsonCapabilityForResteasyClassic(capabilities)) {
throw new IllegalStateException("Cannot generate HAL endpoints without "
+ "either 'quarkus-resteasy-jsonb' or 'quarkus-resteasy-jackson'");
} else if (!isResteasyClassic && !hasAnyJsonCapabilityForResteasyReactive(capabilities)) {
throw new IllegalStateException("Cannot generate HAL endpoints without "
+ "either 'quarkus-resteasy-reactive-jsonb' or 'quarkus-resteasy-reactive-jackson'");
}
if (!excludedClasses.contains(resourceBuildItem.getResourceMetadata().getResourceInterface())) {
ResourceMetadata resourceMetadata = resourceBuildItem.getResourceMetadata();
ResourceProperties resourceProperties = getResourceProperties(resourcePropertiesProvider,
resourceMetadata, resourcePropertiesBuildItems);
if (resourceProperties.isHal()) {
if (isResteasyClassic && !hasAnyJsonCapabilityForResteasyClassic(capabilities)) {
throw new IllegalStateException("Cannot generate HAL endpoints without "
+ "either 'quarkus-resteasy-jsonb' or 'quarkus-resteasy-jackson'");
} else if (!isResteasyClassic && !hasAnyJsonCapabilityForResteasyReactive(capabilities)) {
throw new IllegalStateException("Cannot generate HAL endpoints without "
+ "either 'quarkus-resteasy-reactive-jsonb' or 'quarkus-resteasy-reactive-jackson'");
}

}
if (resourceProperties.isExposed()) {
jaxRsResourceImplementor.implement(classOutput, resourceMetadata, resourceProperties, capabilities);
}
if (resourceProperties.isExposed()) {
jaxRsResourceImplementor.implement(classOutput, resourceMetadata, resourceProperties, capabilities);
}
}
}
}
Expand All @@ -108,4 +119,13 @@ private boolean hasAnyJsonCapabilityForResteasyReactive(Capabilities capabilitie
return capabilities.isPresent(Capability.RESTEASY_REACTIVE_JSON_JSONB)
|| capabilities.isPresent(Capability.RESTEASY_REACTIVE_JSON_JACKSON);
}

private static Set<String> getExcludedClasses(List<BuildTimeConditionBuildItem> buildTimeConditions) {
return buildTimeConditions.stream()
.filter(item -> !item.isEnabled())
.map(BuildTimeConditionBuildItem::getTarget)
.filter(target -> target.kind() == AnnotationTarget.Kind.CLASS)
.map(target -> target.asClass().toString())
.collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ public class ResourcePropertiesProvider {
private static final DotName METHOD_PROPERTIES_ANNOTATION = DotName.createSimple(
io.quarkus.rest.data.panache.MethodProperties.class.getName());

private static final List<String> ANNOTATIONS_TO_COPY = List.of(RolesAllowed.class.getPackageName());
private static final List<String> ANNOTATIONS_TO_COPY = List.of(RolesAllowed.class.getPackageName(),
// To also support `@EndpointDisabled` if used
"io.quarkus.resteasy.reactive.server");

private final IndexView index;

Expand Down
Loading