Preserve async API version resolver order

Prior to this commit, the `DefaultApiVersionStrategy` reactive variant
would attempt to resolve the API version with the first resolver that
replies with a version. This contradicts the API that registers
resolvers in order.
This commit ensures that each resolver is called in order.

Signed-off-by: Gimin Kim <138752849+Gimini-3@users.noreply.github.com>
This commit is contained in:
Gimin Kim
2026-08-28 18:33:41 +02:00
committed by Brian Clozel
parent 6e5cf0ce45
commit 74a4b1a694
2 changed files with 34 additions and 1 deletions
@@ -169,7 +169,7 @@ public class DefaultApiVersionStrategy implements ApiVersionStrategy {
@Override
public Mono<String> resolveApiVersion(ServerWebExchange exchange) {
return Flux.fromIterable(this.versionResolvers)
.flatMap(resolver -> resolver instanceof AsyncApiVersionResolver asyncResolver ?
.concatMap(resolver -> resolver instanceof AsyncApiVersionResolver asyncResolver ?
asyncResolver.resolveVersionAsync(exchange) :
Mono.justOrEmpty(resolver.resolveVersion(exchange)))
.next();
@@ -23,6 +23,7 @@ import org.jspecify.annotations.Nullable;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import reactor.test.StepVerifier;
import reactor.test.publisher.TestPublisher;
import org.springframework.web.accept.InvalidApiVersionException;
import org.springframework.web.accept.MissingApiVersionException;
@@ -128,10 +129,42 @@ class DefaultApiVersionStrategiesTests {
.withMessage("versionRequired cannot be set to true if a defaultVersion is also configured");
}
@Test
void resolveVersionWithResolversInOrder() {
TestPublisher<String> firstResolverPublisher = TestPublisher.create();
ApiVersionStrategy strategy = apiVersionStrategy(List.of(
(AsyncApiVersionResolver) exchange -> firstResolverPublisher.flux().next(),
(AsyncApiVersionResolver) exchange -> Mono.just("2.0")));
StepVerifier.create(strategy.resolveApiVersion(
MockServerWebExchange.from(MockServerHttpRequest.get("/"))))
.then(() -> firstResolverPublisher.emit("1.0"))
.expectNext("1.0")
.verifyComplete();
}
@Test
void resolveVersionPropagatesErrorFromEarlierResolver() {
TestPublisher<String> firstResolverPublisher = TestPublisher.create();
ApiVersionStrategy strategy = apiVersionStrategy(List.of(
(AsyncApiVersionResolver) exchange -> firstResolverPublisher.flux().next(),
(AsyncApiVersionResolver) exchange -> Mono.just("2.0")));
StepVerifier.create(strategy.resolveApiVersion(
MockServerWebExchange.from(MockServerHttpRequest.get("/"))))
.then(() -> firstResolverPublisher.error(new IllegalStateException("test")))
.expectErrorMessage("test")
.verify();
}
private static DefaultApiVersionStrategy apiVersionStrategy() {
return apiVersionStrategy(null, false, null);
}
private static DefaultApiVersionStrategy apiVersionStrategy(List<ApiVersionResolver> resolvers) {
return new DefaultApiVersionStrategy(resolvers, parser, null, null, false, null, null);
}
private static DefaultApiVersionStrategy apiVersionStrategy(
@Nullable String defaultVersion, boolean detectSupportedVersions,
@Nullable Predicate<Comparable<?>> supportedVersionPredicate) {