ammachado commented on code in PR #26920:
URL: https://github.com/apache/camel/pull/26920#discussion_r4118273533


##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java:
##########
@@ -114,6 +114,8 @@ public static List<String> validate(
                 msgs.addAll(validateYamlBeanRefs(content, declarations, 
catalog));
                 msgs.addAll(validateResourceRefs(content, directory));
                 
msgs.addAll(GroovyImportChecks.validateYamlGroovyImports(content, null, 
declarations.javaClasses()));
+                // a direct: or seda: endpoint no route of the application 
consumes (CAMEL-24955)
+                
msgs.addAll(EndpointConsumerChecks.validateYamlConsumers(content, directory, 
fileName));

Review Comment:
   Fixed in df7284f. A new `SourceValidator.validate(..., boolean 
checkConsumers)` overload lets `AuthoringTools.writeFile` (used by both 
`camel_write_file` and `camel_edit_file`) leave the consumer check out, so a 
missing consumer never refuses a write. It still runs in 
`camel_validate_source`, `camel validate source` and the load-failure report. 
The TUI write (`SourceEditAssist.validateSource`) validates without a 
directory, so it never ran this check. Test: 
`AuthoringToolsTest.aMissingConsumerIsReportedButDoesNotRefuseTheWrite` 
(reported by validate, written by write).
   
   _Claude Code on behalf of Adriano Machado (@ammachado)_
   
   _This was generated by an AI agent and may contain inaccuracies. Please 
verify before relying on it._



##########
dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/EndpointConsumers.java:
##########
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.yaml.validator;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+
+import static org.apache.camel.dsl.yaml.validator.RouteGraph.Route;
+import static org.apache.camel.dsl.yaml.validator.RouteGraph.normalize;
+import static org.apache.camel.dsl.yaml.validator.RouteGraph.routes;
+import static org.apache.camel.dsl.yaml.validator.RouteGraph.scheme;
+import static org.apache.camel.dsl.yaml.validator.RouteGraph.sendsTo;
+
+/**
+ * A {@code direct:} or {@code seda:} endpoint a route sends to, and no route 
of the application consumes (CAMEL-24955).
+ * With {@code direct:} the route fails to start - <i>No consumers available 
on endpoint</i>; with {@code seda:} nothing
+ * fails, and the message is queued and never read.
+ * <p/>
+ * The routes of an application are spread over files, so a file on its own 
cannot answer: the caller scans the other
+ * route files of the directory and passes what they consume. Without them the 
check says nothing.
+ */
+public final class EndpointConsumers {
+
+    /** The components whose consumer is a route of the same application. */
+    private static final Set<String> CHECKED = Set.of("direct", "seda");
+
+    private static final ObjectMapper MAPPER = new ObjectMapper(new 
YAMLFactory());
+
+    private EndpointConsumers() {
+    }
+
+    /**
+     * The {@code direct:} and {@code seda:} endpoints the routes of a YAML 
file consume, without their options. Empty
+     * when the file is not YAML the routes can be read from; null when a 
route consumes an endpoint only known at
+     * runtime ({@code from: direct:{{name}}}), which could be any of them.
+     */
+    public static Set<String> consumed(String yaml) {
+        JsonNode target = read(yaml);
+        return target != null ? consumed(routes(target)) : Set.of();
+    }
+
+    /**
+     * @param  yaml              the YAML DSL source
+     * @param  consumedElsewhere the endpoints the other route files of the 
application consume, as returned by
+     *                           {@link #consumed(String)}; null when they are 
not known, which keeps the check quiet
+     * @return                   a message for each endpoint a route sends to 
and no route consumes
+     */
+    public static List<String> check(String yaml, Set<String> 
consumedElsewhere) {
+        if (consumedElsewhere == null) {
+            return List.of();
+        }
+        JsonNode target = read(yaml);
+        if (target == null) {
+            return List.of();
+        }
+        List<Route> routes = routes(target);
+        Set<String> own = consumed(routes);
+        if (own == null) {
+            return List.of();
+        }
+        Set<String> consumed = new HashSet<>(consumedElsewhere);
+        consumed.addAll(own);
+        Set<String> messages = new LinkedHashSet<>();
+        for (Route r : routes) {
+            for (String uri : sendsTo(r.steps())) {
+                String endpoint = endpoint(uri);
+                if (endpoint == null || consumed.contains(endpoint)) {
+                    continue;
+                }
+                messages.add((r.id() != null ? "route " + r.id() + ": " : "")
+                             + "sends to " + endpoint + ", and no route 
consumes it - not in this file, nor in the"
+                             + " other route files of the directory: "
+                             + ("direct".equals(scheme(endpoint))
+                                     ? "the route fails to start with No 
consumers available on endpoint"
+                                     : "nothing fails, the messages are queued 
and never read")
+                             + "; add a route with from: " + endpoint + ", or 
correct the name");
+            }
+        }
+        return new ArrayList<>(messages);
+    }
+
+    private static Set<String> consumed(List<Route> routes) {

Review Comment:
   Fixed in df7284f. A YAML file with `routeTemplate` / `route-template` / 
`templatedRoute` / `templated-route` entries, or a `kind: Kamelet`, is now 
"consumers unknown": `check()` returns no messages for it and `consumed()` 
returns null, so a sibling template file also keeps the check quiet. Tests: 
`testARouteTemplateKeepsTheCheckQuiet` (your exact scenario) and 
`testAKameletConsumesWhatIsNotKnown`.
   
   _Claude Code on behalf of Adriano Machado (@ammachado)_
   
   _This was generated by an AI agent and may contain inaccuracies. Please 
verify before relying on it._



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.dsl.yaml.validator.EndpointConsumers;
+
+/**
+ * A {@code direct:} or {@code seda:} endpoint a YAML route sends to, and no 
route of the application consumes
+ * (CAMEL-24955). The routes of an application are spread over the files of 
its directory, so the endpoints the other
+ * route files consume - YAML, Java and XML - are read from them and handed to 
the check.
+ */
+public final class EndpointConsumerChecks {
+
+    /** A Java DSL route input: from( not called on something else, so not 
Instant.from( or List.from(. */
+    private static final Pattern JAVA_FROM = 
Pattern.compile("(?<![.\\w])from\\s*\\(\\s*(\"([^\"]*)\")?");

Review Comment:
   Fixed in df7284f. A Java source containing `routeTemplate(` or `fromF(` 
returns null (unknown). `JAVA_FROM` now reads the literal only when it is the 
whole argument (followed by `)` or `,`), so `from("direct:" + NAME)` is a 
non-literal input and also returns null; this also avoids `from("direct:foo" + 
SUFFIX)` being read as `direct:foo`. Test: 
`javaRouteInputsTheScanCannotReadKeepTheCheckQuiet` covers all three forms.
   
   _Claude Code on behalf of Adriano Machado (@ammachado)_
   
   _This was generated by an AI agent and may contain inaccuracies. Please 
verify before relying on it._



##########
dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointConsumerChecks.java:
##########
@@ -0,0 +1,128 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.dsl.jbang.core.commands.ai;
+
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.camel.dsl.yaml.validator.EndpointConsumers;
+
+/**
+ * A {@code direct:} or {@code seda:} endpoint a YAML route sends to, and no 
route of the application consumes
+ * (CAMEL-24955). The routes of an application are spread over the files of 
its directory, so the endpoints the other
+ * route files consume - YAML, Java and XML - are read from them and handed to 
the check.
+ */
+public final class EndpointConsumerChecks {
+
+    /** A Java DSL route input: from( not called on something else, so not 
Instant.from( or List.from(. */
+    private static final Pattern JAVA_FROM = 
Pattern.compile("(?<![.\\w])from\\s*\\(\\s*(\"([^\"]*)\")?");
+    private static final Pattern XML_FROM = 
Pattern.compile("<from\\s[^>]*?\\buri\\s*=\\s*[\"']([^\"']*)[\"']");
+
+    private EndpointConsumerChecks() {
+    }
+
+    /**
+     * @param  content     the YAML route file
+     * @param  directory   the directory of the application's route files; 
null says nothing
+     * @param  excludeFile the file being validated, whose routes come from 
the content
+     * @return             the messages, one per endpoint no route consumes
+     */
+    public static List<String> validateYamlConsumers(String content, Path 
directory, String excludeFile) {
+        return EndpointConsumers.check(content, consumed(directory, 
excludeFile));
+    }
+
+    /**
+     * The {@code direct:} and {@code seda:} endpoints the route files of the 
directory consume, leaving out the file
+     * being validated; null when they cannot be known: no directory, or a 
route input the scan cannot read (a Java
+     * {@code from(} with no literal, a placeholder), which could be any 
endpoint.
+     */
+    static Set<String> consumed(Path directory, String excludeFile) {
+        if (directory == null || !Files.isDirectory(directory)) {
+            return null;
+        }
+        Set<String> answer = new HashSet<>();
+        try (var stream = Files.list(directory)) {

Review Comment:
   Fixed in df7284f. The scan now walks the directory tree with the same limits 
as the file tools (`AuthoringTools.MAX_DEPTH`, `SKIPPED_DIRS`, `SCAN_LIMIT`, 
hidden dirs skipped), and returns null when it cannot see every file (depth or 
file limit reached, unreadable entry). The validated file is left out by 
`directory.resolve(file)`, so `routes/a.camel.yaml` matches. For the Maven 
layout: from the project root, `src/main/java` RouteBuilders are scanned; a 
directory without its own `pom.xml`/`build.gradle(.kts)` that sits inside a 
project that has one (e.g. `src/main/resources/camel`, as `camel validate` and 
the load-failure report pass) keeps the check quiet. Tests: 
`aRouteFileInASubdirectoryConsumesIt`, 
`theFileBeingValidatedIsLeftOutByItsPathUnderTheDirectory`, 
`aMavenProjectIsScannedFromItsRootOnly`. `BeanDeclarations.scan` has the same 
flat-listing gap; I left it out of this PR.
   
   _Claude Code on behalf of Adriano Machado (@ammachado)_
   
   _This was generated by an AI agent and may contain inaccuracies. Please 
verify before relying on it._



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to