This is an automated email from the ASF dual-hosted git repository.

mpochatkin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


The following commit(s) were added to refs/heads/main by this push:
     new 5f256c844b7 IGNITE-23421 Not found URLs respond with 409 instead of 
404 (#7577)
5f256c844b7 is described below

commit 5f256c844b72c6cdfde30ed3e056ab08a143e47b
Author: Vadim Pakhnushev <[email protected]>
AuthorDate: Fri Feb 13 14:32:47 2026 +0300

    IGNITE-23421 Not found URLs respond with 409 instead of 404 (#7577)
---
 ...lusterStateHttpServerFilterInitializedTest.java | 21 +++-------
 ...terStateHttpServerFilterNotInitializedTest.java | 45 +++++++++++++---------
 .../ClusterStateHttpServerFilter.java              | 17 +++++++-
 .../ignite/internal/rest/RestComponentTest.java    | 17 ++++++++
 4 files changed, 66 insertions(+), 34 deletions(-)

diff --git 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
index 90dd83a39ca..c8c27461ebe 100644
--- 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
+++ 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterInitializedTest.java
@@ -17,6 +17,8 @@
 
 package org.apache.ignite.internal.rest;
 
+import static 
org.apache.ignite.internal.rest.ItClusterStateHttpServerFilterNotInitializedTest.disabledEndpoints;
+import static 
org.apache.ignite.internal.rest.ItClusterStateHttpServerFilterNotInitializedTest.enabledEndpoints;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
 import io.micronaut.http.HttpRequest;
@@ -28,7 +30,6 @@ import java.util.stream.Stream;
 import org.apache.ignite.internal.ClusterConfiguration;
 import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
 import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
 /** Tests that after cluster is initialized, all endpoints are available. */
@@ -40,23 +41,13 @@ public class ItClusterStateHttpServerFilterInitializedTest 
extends ClusterPerCla
     @Client(NODE_URL + "/management/v1")
     HttpClient client;
 
-    private static Stream<Arguments> endpoints() {
-        return Stream.of(
-                Arguments.of("deployment/cluster/units"),
-                Arguments.of("node/state"),
-                Arguments.of("configuration/cluster"),
-                Arguments.of("configuration/node"),
-                Arguments.of("configuration/node/ignite.rest"),
-                Arguments.of("cluster/topology/logical"),
-                Arguments.of("cluster/topology/physical")
-        );
+    private static Stream<HttpRequest<String>> endpoints() {
+        return Stream.concat(disabledEndpoints(), enabledEndpoints());
     }
 
     @ParameterizedTest
     @MethodSource("endpoints")
-    void clusterEndpointsEnabled(String path) {
-        assertDoesNotThrow(
-                () -> client.toBlocking().retrieve(HttpRequest.GET(path))
-        );
+    void clusterEndpointsEnabled(HttpRequest<String> request) {
+        assertDoesNotThrow(() -> client.toBlocking().exchange(request));
     }
 }
diff --git 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
index b3603017951..1a82432b5aa 100644
--- 
a/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
+++ 
b/modules/rest/src/integrationTest/java/org/apache/ignite/internal/rest/ItClusterStateHttpServerFilterNotInitializedTest.java
@@ -20,11 +20,12 @@ package org.apache.ignite.internal.rest;
 import static io.micronaut.http.HttpRequest.GET;
 import static io.micronaut.http.HttpRequest.PATCH;
 import static io.micronaut.http.HttpStatus.CONFLICT;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
+import static io.micronaut.http.MediaType.TEXT_PLAIN;
 import static 
org.apache.ignite.internal.rest.matcher.MicronautHttpResponseMatcher.assertThrowsProblem;
 import static org.apache.ignite.internal.rest.matcher.ProblemMatcher.isProblem;
 import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 
-import com.fasterxml.jackson.databind.ObjectMapper;
 import io.micronaut.http.HttpRequest;
 import io.micronaut.http.client.HttpClient;
 import io.micronaut.http.client.annotation.Client;
@@ -34,9 +35,9 @@ import java.util.stream.Stream;
 import org.apache.ignite.internal.ClusterConfiguration;
 import org.apache.ignite.internal.ClusterPerClassIntegrationTest;
 import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.TestInfo;
 import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
 
 /** Tests that before cluster is initialized, only a subset of endpoints are 
available. */
@@ -44,28 +45,27 @@ import org.junit.jupiter.params.provider.MethodSource;
 public class ItClusterStateHttpServerFilterNotInitializedTest extends 
ClusterPerClassIntegrationTest {
     private static final String NODE_URL = "http://localhost:"; + 
ClusterConfiguration.DEFAULT_BASE_HTTP_PORT;
 
-    private final ObjectMapper mapper = new ObjectMapper();
-
     @Inject
     @Client(NODE_URL + "/management/v1")
     HttpClient client;
 
-    private static Stream<Arguments> disabledEndpoints() {
+    static Stream<HttpRequest<String>> disabledEndpoints() {
         return Stream.of(
-                Arguments.of(GET("deployment/units")),
-                Arguments.of(GET("cluster/state")),
-                Arguments.of(GET("configuration/cluster")),
-                Arguments.of(PATCH("configuration/cluster", 
"any.key=any-value")),
-                Arguments.of(GET("cluster/topology/logical"))
+                GET("deployment/cluster/units"),
+                GET("cluster/state"),
+                GET("configuration/cluster"),
+                PATCH("configuration/cluster", 
"ignite.system.idleSafeTimeSyncIntervalMillis=2000").contentType(TEXT_PLAIN),
+                GET("cluster/topology/logical")
         );
     }
 
-    private static Stream<Arguments> enabledEndpoints() {
+    static Stream<HttpRequest<String>> enabledEndpoints() {
         return Stream.of(
-                Arguments.of("node/state"),
-                Arguments.of("configuration/node"),
-                Arguments.of("configuration/node/ignite.rest"),
-                Arguments.of("cluster/topology/physical")
+                GET("node/state"),
+                GET("configuration/node"),
+                GET("configuration/node/ignite.rest"),
+                PATCH("configuration/node", 
"ignite.deployment.location=deployment").contentType(TEXT_PLAIN),
+                GET("cluster/topology/physical")
         );
     }
 
@@ -99,10 +99,19 @@ public class 
ItClusterStateHttpServerFilterNotInitializedTest extends ClusterPer
 
     @ParameterizedTest
     @MethodSource("enabledEndpoints")
-    void nodeConfigAndClusterInitAreEnabled(String path) {
+    void nodeConfigAndClusterInitAreEnabled(HttpRequest<String> request) {
         // But node config and cluster init endpoints are enabled
-        assertDoesNotThrow(
-                () -> client.toBlocking().retrieve(GET(path))
+        assertDoesNotThrow(() -> client.toBlocking().exchange(request));
+    }
+
+    @Test
+    void nonExistentUrlReturns404WhenNotInitialized() {
+        assertThrowsProblem(
+                () -> client.toBlocking().retrieve("nonExistentEndpoint"),
+                isProblem()
+                        .withStatus(NOT_FOUND)
+                        .withTitle("Not Found")
+                        .withDetail("Requested resource not found: 
/management/v1/nonExistentEndpoint")
         );
     }
 }
diff --git 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
index a014e160dc4..8909fde3a79 100644
--- 
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
+++ 
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/authentication/ClusterStateHttpServerFilter.java
@@ -23,6 +23,9 @@ import io.micronaut.http.MutableHttpResponse;
 import io.micronaut.http.annotation.Filter;
 import io.micronaut.http.filter.HttpServerFilter;
 import io.micronaut.http.filter.ServerFilterChain;
+import io.micronaut.web.router.Router;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
 import org.apache.ignite.internal.rest.ResourceHolder;
 import org.apache.ignite.internal.rest.RestManager;
 import org.apache.ignite.internal.rest.api.Problem;
@@ -37,14 +40,26 @@ import reactor.core.publisher.Mono;
 @Filter(Filter.MATCH_ALL_PATTERN)
 @Requires(property = "ignite.endpoints.filter-non-initialized", value = 
"true", defaultValue = "true")
 public class ClusterStateHttpServerFilter implements HttpServerFilter, 
ResourceHolder {
+    private static final IgniteLogger LOG = 
Loggers.forClass(ClusterStateHttpServerFilter.class);
+
     private RestManager restManager;
 
-    public ClusterStateHttpServerFilter(RestManager restManager) {
+    private final Router router;
+
+    public ClusterStateHttpServerFilter(RestManager restManager, Router 
router) {
         this.restManager = restManager;
+        this.router = router;
     }
 
     @Override
     public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, 
ServerFilterChain chain) {
+        // If no route matches this path, skip state filtering and let the 
chain return 404. findAllClosest is the method used in the
+        // RoutingInBoundHandler to find the route.
+        if (router.findAllClosest(request).isEmpty()) {
+            LOG.debug("No route found for request {}, skip availability 
check", request);
+            return chain.proceed(request);
+        }
+
         return 
Mono.just(restManager.pathAvailability(request.getPath())).<MutableHttpResponse<?>>flatMap(availability
 -> {
             if (!availability.isAvailable()) {
                 return Mono.just(HttpProblemResponse.from(
diff --git 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
 
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
index 76b1e7a05c1..25719833021 100644
--- 
a/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
+++ 
b/modules/rest/src/test/java/org/apache/ignite/internal/rest/RestComponentTest.java
@@ -18,6 +18,7 @@
 package org.apache.ignite.internal.rest;
 
 import static io.micronaut.http.HttpStatus.CONFLICT;
+import static io.micronaut.http.HttpStatus.NOT_FOUND;
 import static io.micronaut.http.HttpStatus.OK;
 import static 
org.apache.ignite.configuration.annotation.ConfigurationType.LOCAL;
 import static org.apache.ignite.internal.rest.RestState.INITIALIZATION;
@@ -62,6 +63,8 @@ import 
org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.EnumSource;
 import org.mockito.Mockito;
 
 /**
@@ -128,6 +131,20 @@ public class RestComponentTest extends 
BaseIgniteAbstractTest {
         assertThat(restComponent.stopAsync(new ComponentContext()), 
willCompleteSuccessfully());
     }
 
+    @ParameterizedTest
+    @EnumSource(RestState.class)
+    public void nonExistentEndpoint(RestState state) {
+        restManager.setState(state);
+
+        assertThrowsProblem(
+                () -> client.toBlocking().retrieve("nonExistentEndpoint"),
+                isProblem()
+                        .withStatus(NOT_FOUND)
+                        .withTitle("Not Found")
+                        .withDetail("Requested resource not found: 
/management/v1/nonExistentEndpoint")
+        );
+    }
+
     @Test
     public void nodeConfigTest() {
         assertThat(getConfig(), hasStatus(OK));

Reply via email to