Skip to content

Commit

Permalink
Merge branch 'main' of github.com:martin-kofoed-jyskebank-dk/quarkus
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-kofoed-jyskebank-dk committed Nov 22, 2023
2 parents e24cbb4 + 1323552 commit c322c2a
Show file tree
Hide file tree
Showing 42 changed files with 1,956 additions and 196 deletions.
56 changes: 0 additions & 56 deletions .github/develocity-preapproved-developers.json

This file was deleted.

9 changes: 0 additions & 9 deletions .github/workflows/develocity-publish-build-scans.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,8 @@ jobs:
pull-requests: write
checks: write
steps:
- uses: actions/checkout@v4
- name: Extract preapproved developers list
id: extract-preapproved-developers
run: |
echo "preapproved-developpers<<EOF" >> $GITHUB_OUTPUT
cat .github/develocity-preapproved-developers.json >> $GITHUB_OUTPUT
echo >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Publish Maven Build Scans
uses: gradle/github-actions/maven-build-scan/publish@v1-beta
if: ${{ contains(fromJson(steps.extract-preapproved-developers.outputs.preapproved-developpers).preapproved-developers, github.event.workflow_run.actor.login) }}
with:
develocity-url: 'https://ge.quarkus.io'
develocity-access-key: ${{ secrets.GRADLE_ENTERPRISE_ACCESS_KEY }}
Expand Down
20 changes: 20 additions & 0 deletions .mvn/gradle-enterprise-custom-user-data.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,23 @@ if (System.env.GITHUB_ACTIONS) {
}
}

// Check runtime Maven version and Maven Wrapper version are aligned
def runtimeInfo = (org.apache.maven.rtinfo.RuntimeInformation) session.lookup("org.apache.maven.rtinfo.RuntimeInformation")
def runtimeMavenVersion = runtimeInfo?.getMavenVersion()
Properties mavenWrapperProperties = new Properties()
File mavenWrapperPropertiesFile = new File(".mvn/wrapper/maven-wrapper.properties")
if(mavenWrapperPropertiesFile.exists()) {
mavenWrapperPropertiesFile.withInputStream {
mavenWrapperProperties.load(it)
}
// assuming the wrapper properties contains:
// distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/VERSION/apache-maven-VERSION-bin.zip
if(regexp = mavenWrapperProperties."distributionUrl" =~ /.*\/apache-maven-(.*)-bin\.zip/) {
def wrapperMavenVersion = regexp.group(1)
if (runtimeMavenVersion && wrapperMavenVersion && wrapperMavenVersion != runtimeMavenVersion) {
log.warn("Maven Wrapper is configured with a different version (" + wrapperMavenVersion + ") than the runtime version (" + runtimeMavenVersion + "). This will negatively impact build consistency and build caching.")
buildScan.tag("misaligned-maven-version")
buildScan.value("wrapper-maven-version", wrapperMavenVersion)
}
}
}
2 changes: 1 addition & 1 deletion .mvn/gradle-enterprise.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
</local>
<remote>
<enabled>true</enabled>
<storeEnabled>#{env['CI'] != null}</storeEnabled>
<storeEnabled>#{env['CI'] != null and env['GRADLE_ENTERPRISE_ACCESS_KEY'] != null and env['GRADLE_ENTERPRISE_ACCESS_KEY'] != ''}</storeEnabled>
</remote>
</buildCache>
</gradleEnterprise>
4 changes: 2 additions & 2 deletions bom/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@
<hamcrest.version>2.2</hamcrest.version><!-- The version needs to be compatible with both REST Assured and Awaitility -->
<junit.jupiter.version>5.10.0</junit.jupiter.version>
<junit-pioneer.version>1.5.0</junit-pioneer.version>
<infinispan.version>14.0.20.Final</infinispan.version>
<infinispan.version>14.0.21.Final</infinispan.version>
<infinispan.protostream.version>4.6.5.Final</infinispan.protostream.version>
<caffeine.version>3.1.5</caffeine.version>
<netty.version>4.1.100.Final</netty.version>
Expand Down Expand Up @@ -211,7 +211,7 @@
<docker-java.version>3.3.3</docker-java.version> <!-- must be the version Testcontainers use -->
<!-- Check the compatibility matrix (https://github.com/opensearch-project/opensearch-testcontainers) before upgrading: -->
<opensearch-testcontainers.version>2.0.0</opensearch-testcontainers.version>
<com.dajudge.kindcontainer>1.3.0</com.dajudge.kindcontainer>
<com.dajudge.kindcontainer>1.4.4</com.dajudge.kindcontainer>
<aesh.version>2.7</aesh.version>
<aesh-readline.version>2.4</aesh-readline.version>
<jansi.version>2.4.0</jansi.version> <!-- Keep in sync with aesh-readline and dekorate -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,9 @@ private RuntimeUpdatesProcessor setWatchedFilePathsInternal(Map<String, Boolean>
// First find all matching paths from all roots
try (final Stream<Path> walk = Files.walk(root)) {
walk.forEach(path -> {
if (path.equals(root)) {
if (path.equals(root)
// Never watch directories
|| Files.isDirectory(path)) {
return;
}
// Use the relative path to match the watched file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,5 @@ protected void assertAppModel(ApplicationModel model) throws Exception {
expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "dep-g", "1"),
DependencyFlags.RUNTIME_CP, DependencyFlags.DEPLOYMENT_CP));
assertEquals(expected, getDependenciesWithFlag(model, DependencyFlags.RUNTIME_CP));

expected = new HashSet<>();
expected.add(new ArtifactDependency(ArtifactCoords.jar("io.quarkus.bootstrap.test", "ext-a-dep", "1"),
DependencyFlags.REMOVED));
expected.add(new ArtifactDependency(ArtifactCoords.jar("org.banned", "dep-e", "1"), DependencyFlags.REMOVED));
expected.add(new ArtifactDependency(ArtifactCoords.jar("org.banned.too", "dep-d", "1"), DependencyFlags.REMOVED));
expected.add(new ArtifactDependency(ArtifactCoords.jar("org.banned", "dep-f", "1"), DependencyFlags.REMOVED));
assertEquals(expected, getDependenciesWithFlag(model, DependencyFlags.REMOVED));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,10 @@ private static boolean getRootlessStateFor(ContainerRuntime containerRuntime) {
final Predicate<String> stringPredicate;
// Docker includes just "rootless" under SecurityOptions, while podman includes "rootless: <boolean>"
if (containerRuntime == ContainerRuntime.DOCKER) {
stringPredicate = line -> line.trim().equals("rootless");
// We also treat Docker Desktop as "rootless" since the way it binds mounts does not
// transparently map the host user ID and GID
// see https://docs.docker.com/desktop/faqs/linuxfaqs/#how-do-i-enable-file-sharing
stringPredicate = line -> line.trim().equals("rootless") || line.contains("desktop-linux");
} else {
stringPredicate = line -> line.trim().equals("rootless: true");
}
Expand Down
101 changes: 101 additions & 0 deletions docs/src/main/asciidoc/rest-client-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,107 @@ If you use a `CompletionStage`, you would need to call the service's method to r
This difference comes from the laziness aspect of Mutiny and its subscription protocol.
More details about this can be found in https://smallrye.io/smallrye-mutiny/latest/reference/uni-and-multi/[the Mutiny documentation].

=== Server-Sent Event (SSE) support

Consuming SSE events is possible simply by declaring the result type as a `io.smallrye.mutiny.Multi`.

The simplest example is:

[source, java]
----
package org.acme.rest.client;
import io.smallrye.mutiny.Multi;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
Multi<String> get();
}
----

[NOTE]
====
All the IO involved in streaming the SSE results is done in a non-blocking manner.
====

Results are not limited to strings - for example when the server returns JSON payload for each event, Quarkus automatically deserializes it into the generic type used in the `Multi`.

[TIP]
====
Users can also access the entire SSE event by using the `org.jboss.resteasy.reactive.client.SseEvent` type.
A simple example where the event payloads are `Long` values is the following:
[source, java]
----
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.client.SseEvent;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
Multi<SseEvent<Long>> get();
}
----
====

==== Filtering out events

On occasion, the stream of SSE events may contain some events that should not be returned by the client - an example of this is having the server send heartbeat events in order to keep the underlying TCP connection open.
The REST Client supports filtering out such events by providing the `@org.jboss.resteasy.reactive.client.SseEventFilter`.

Here is an example of filtering out heartbeat events:

[source,java]
----
package org.acme.rest.client;
import io.smallrye.mutiny.Uni;
import java.util.function.Predicate;
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import org.jboss.resteasy.reactive.client.SseEvent;
import org.jboss.resteasy.reactive.client.SseEventFilter;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.QueryParam;
@Path("/sse")
@RegisterRestClient(configKey = "some-api")
public interface SseClient {
@GET
@Produces(MediaType.SERVER_SENT_EVENTS)
@SseEventFilter(HeartbeatFilter.class)
Multi<SseEvent<Long>> get();
class HeartbeatFilter implements Predicate<SessionEvent<String>> {
@Override
public boolean test(SseEvent<String> event) {
return !"heartbeat".equals(event.id());
}
}
}
----

== Custom headers support

There are a few ways in which you can specify custom headers for your REST calls:
Expand Down
4 changes: 4 additions & 0 deletions docs/src/main/asciidoc/resteasy-reactive.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3006,6 +3006,10 @@ public class RuntimeResource {
}
}
----
[IMPORTANT]
====
This feature does not work when using native build.
====


== RESTEasy Reactive client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ It is an exact path match because it does not end with `*`.
<3> This permission set references the previously defined policy.
`roles1` is an example name; you can call the permission sets whatever you want.

WARNING: The `/forbidden` exact path in the example above will not secure the `/forbidden/` path. Don't forget to add new exact path for the `/forbidden/` path.

=== Custom HttpSecurityPolicy

Sometimes it might be useful to register your own named policy. You can get it done by creating application scoped CDI
Expand Down Expand Up @@ -123,10 +125,12 @@ Otherwise, it queries for an exact match and only matches that specific path:

[source,properties]
----
quarkus.http.auth.permission.permit1.paths=/public/*,/css/*,/js/*,/robots.txt
quarkus.http.auth.permission.permit1.paths=/public*,/css/*,/js/*,/robots.txt <1>
quarkus.http.auth.permission.permit1.policy=permit
quarkus.http.auth.permission.permit1.methods=GET,HEAD
----
<1> The `$$*$$` wildcard at the end of the path matches zero or more path segments, but never any word starting from the `/public` path.
For that reason, a path like `/public-info` is not matched by this pattern.

=== Matching a path but not a method

Expand Down Expand Up @@ -170,6 +174,59 @@ quarkus.http.auth.permission.public.policy=permit
----
====

=== Matching multiple sub-paths: longest path to the `*` wildcard wins

Previous examples shown how you can match all sub-paths when a path ends with the `$$*$$` wildcard.
The `$$*$$` wildcard can also be used in the middle of the path, in which case it represents exactly one path segment.
You can't combine this wildcard with any other path segment character, therefore the `$$*$$` wildcard will always be
enclosed with path separators as in the `/public/$$*$$/about-us` path.

What happens if multiple path patterns matches same request path?
Matching is always done on the "longest sub-path to the `$$*$$` wildcard wins" basis.
Every path segment character is considered more specific than the `$$*$$` wildcard.

Here is a simple example:

[source,properties]
----
quarkus.http.auth.permission.secured.paths=/api/*/detail <1>
quarkus.http.auth.permission.secured.policy=authenticated
quarkus.http.auth.permission.public.paths=/api/public-product/detail <2>
quarkus.http.auth.permission.public.policy=permit
----
<1> Request paths like `/api/product/detail` can only be accessed by authenticated users.
<2> The path `/api/public-product/detail` is more specific, therefore accessible by anyone.

[IMPORTANT]
====
All paths secured with the authorization using configuration should be tested.
Writing path patterns with multiple wildcards can be cumbersome.
Please make sure paths are authorized as you intended.
====

In the following example, paths are ordered from the most specific to the least specific one:

.Request path `/one/two/three/four/five` matches ordered from the most specific to the least specific path

[source, text]
----
/one/two/three/four/five
/one/two/three/four/*
/one/two/three/*/five
/one/two/three/*/*
/one/two/*/four/five
/one/*/three/four/five
/*/two/three/four/five
/*/two/three/*/five
/*
----

[IMPORTANT]
====
The `$$*$$` wildcard at the end of the path matches zero or more path segments.
The `$$*$$` wildcard placed anywhere else matches exactly one path segment.
====

=== Matching multiple paths: most specific method wins

When a path is registered with multiple permission sets, the permission sets explicitly specifying an HTTP method that matches the request take precedence.
Expand Down
Loading

0 comments on commit c322c2a

Please sign in to comment.