This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d9e143c3b3f9 CAMEL-24711: camel-jbang - camel validate yaml/source
--camel-version and --runtime
d9e143c3b3f9 is described below
commit d9e143c3b3f973b095eaec2d17a4476a005ab155
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Sep 16 10:12:14 2026 +0200
CAMEL-24711: camel-jbang - camel validate yaml/source --camel-version and
--runtime
camel validate yaml and camel validate source checked a file against the
CLI's own catalog and YAML DSL schema, whatever version the project runs.
They now take --camel-version and --runtime like camel catalog and camel
doc: the catalog of that version and runtime (main, spring-boot, quarkus
with its platform), and the YAML DSL schema read from the camel-yaml-dsl
jar of that version through the Maven downloader. A component the runtime
catalog does not have while Camel has it is an error (no Quarkus extension
or Spring Boot starter). camel_validate_source answers for its camelVersion
on both halves, one schema validator per version; the TUI and
camel_write_file get it through the catalog version they already set.
--canonical with a version before 4.22 is an error, not a silent fallback.
Simple, Java, XSLT and XML checks stay on the CLI's own classpath, as the
docs say.
Closes #26488
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
.../modules/ROOT/pages/camel-jbang-devtools.adoc | 34 +++++++
.../modules/ROOT/pages/camel-jbang-mcp.adoc | 4 +-
.../dsl/jbang/core/commands/ai/EndpointChecks.java | 46 ++++++++-
.../jbang/core/commands/ai/SourceValidator.java | 67 ++++++++++++-
.../camel/dsl/jbang/core/common/CatalogLoader.java | 48 +++++++++
.../commands/ai/SourceValidatorVersionTest.java | 107 +++++++++++++++++++++
.../commands/ai/fakequarkus/components/log.json | 68 +++++++++++++
.../commands/ai/fakequarkus/components/timer.json | 52 ++++++++++
.../camel-jbang-plugin-validate/pom.xml | 12 +++
.../commands/validate/CatalogVersionMixin.java | 98 +++++++++++++++++++
.../commands/validate/SourceValidateCommand.java | 12 ++-
.../commands/validate/YamlValidateCommand.java | 11 ++-
.../validate/ValidateCamelVersionTest.java | 100 +++++++++++++++++++
.../src/test/resources/no-extension.yaml | 21 ++++
.../src/test/resources/route.yaml | 28 ++++++
.../camel/dsl/yaml/validator/YamlValidator.java | 24 ++++-
.../validator/YamlValidatorSchemaDocumentTest.java | 59 ++++++++++++
17 files changed, 776 insertions(+), 15 deletions(-)
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-devtools.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-devtools.adoc
index d8f1bda0b31c..574f18cfc282 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-devtools.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-devtools.adoc
@@ -421,3 +421,37 @@ When all files pass:
$ camel validate yaml cheese.yaml bad.yaml
Validation success (files:2)
----
+
+`camel validate yaml` checks the YAML DSL schema, then the endpoint URIs,
Simple expressions and bean references
+against the Camel catalog (`--catalog=false` for the schema only);
`--canonical` validates against the canonical
+schema and reports the deprecated compact notation. `camel validate source`
validates any file `camel run` would
+load: YAML routes, `.properties` files (the `camel.*` keys), Java (compiles
it), XSLT and XML.
+
+=== Validating against another Camel version or runtime
+
+Both commands validate against the CLI's own Camel version. A project that
runs another version, or Camel
+Spring Boot or Camel Quarkus, is validated against the catalog and the YAML
DSL schema of that version with
+`--camel-version` and `--runtime`, the same options `camel catalog` and `camel
doc` take:
+
+[source,bash]
+----
+$ camel validate yaml --camel-version=4.18.0 orders.yaml
+$ camel validate source --runtime=quarkus --quarkus-version=3.30.1 orders.yaml
application.properties
+Validation error detected (errors:1)
+
+ File: orders.yaml
+ line 4: atmosphere-websocket: Camel Quarkus has no extension
for this component (no camel-quarkus-atmosphere-websocket); pick a component
that has one, camel_catalog_find lists them
+----
+
+The catalog of that version is downloaded as for `camel catalog`, and the
schema is read from the
+`camel-yaml-dsl` jar of that version (`--repos` and `--download=false` apply
to both). With `--runtime=quarkus`
+the Quarkus platform is resolved from the Camel version through the Quarkus
extension registry (cached for a
+day under `~/.camel`), or given with `--quarkus-version`; a component of the
route that has no Camel Quarkus
+extension, or no Camel Spring Boot starter with `--runtime=spring-boot`, is an
error. `--canonical` needs Camel
+4.22 or later, the first version with the canonical schema.
+
+The Simple expressions are parsed by the CLI's own Simple language (the
catalog of the other version supplies
+its functions), and the Java, XSLT and XML checks of `camel validate source`
use the CLI's classpath.
+
+The `camel_validate_source` tool of the MCP servers
(xref:camel-jbang-mcp.adoc[]) does the same when it is
+given a `camelVersion`: the catalog and the schema of that version.
diff --git a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
index 3a53d0cacf74..6b272ccadbd8 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-jbang-mcp.adoc
@@ -310,7 +310,9 @@ project `directory` as an argument, the runtime tools take
the integration `name
| `camel_validate_source`
| Validates Camel YAML DSL or `.properties` source without writing: the YAML
DSL schema (a misspelled option
such as `logLevel` instead of `loggingLevel`), endpoint URIs, simple
expressions, and `camel.*` options.
- Takes the content, or reads the file from the project directory.
+ Takes the content, or reads the file from the project directory. With
`camelVersion` (or the version of the
+ selected integration in the TUI) the catalog and the YAML DSL schema of that
Camel version answer, the schema
+ read from its `camel-yaml-dsl` jar; without it the CLI's own, with nothing
to download.
| `camel_get_files`
| The source files of a project directory (name, size, type), or the content
of one of them.
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
index f992dd13de86..ccb1273dbef8 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/EndpointChecks.java
@@ -26,7 +26,9 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.catalog.EndpointValidationResult;
+import org.apache.camel.catalog.RuntimeProvider;
import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.YAML_URI_PATTERN;
import static
org.apache.camel.dsl.jbang.core.commands.ai.YamlLines.countLeadingSpaces;
@@ -179,8 +181,15 @@ final class EndpointChecks {
try {
EndpointValidationResult result
= catalog.validateEndpointProperties(fullUri, false,
consumerOnly, producerOnly);
+ String scheme = fullUri.contains(":") ? fullUri.substring(0,
fullUri.indexOf(':')) : fullUri;
+ if (result.getUnknownComponent() != null) {
+ // a warning to the catalog, an error when the runtime is
what lacks the component
+ String missing = missingInRuntime(catalog, scheme);
+ if (missing != null) {
+ errors.add(linePrefix(i) + missing);
+ }
+ }
if (!result.isSuccess()) {
- String scheme = fullUri.contains(":") ?
fullUri.substring(0, fullUri.indexOf(':')) : fullUri;
collectEndpointErrors(errors, result, scheme, i,
optionLineMap);
}
checkRegexOptions(errors, fullUri, i, optionLineMap);
@@ -191,6 +200,41 @@ final class EndpointChecks {
return errors;
}
+ private static volatile CamelCatalog defaultCatalog;
+
+ /**
+ * The message for a component the catalog of a runtime (Camel Quarkus,
Camel Spring Boot) does not have while Camel
+ * has it: the runtime has no extension or starter for it. Null for the
default catalog, whose unknown components
+ * are not reported: a project can register a component of its own
(CAMEL-24711).
+ */
+ static String missingInRuntime(CamelCatalog catalog, String scheme) {
+ RuntimeProvider provider = catalog.getRuntimeProvider();
+ String name = provider != null ? provider.getProviderName() : null;
+ if (name == null || "default".equals(name)) {
+ return null;
+ }
+ CamelCatalog plain = defaultCatalog;
+ if (plain == null) {
+ synchronized (EndpointChecks.class) {
+ plain = defaultCatalog;
+ if (plain == null) {
+ plain = new DefaultCamelCatalog();
+ defaultCatalog = plain;
+ }
+ }
+ }
+ if (plain.componentModel(scheme) == null) {
+ return null;
+ }
+ return switch (name) {
+ case "quarkus" -> scheme + ": Camel Quarkus has no extension for
this component (no camel-quarkus-" + scheme
+ + "); pick a component that has one,
camel_catalog_find lists them";
+ case "springboot" -> scheme + ": Camel Spring Boot has no starter
for this component (no camel-" + scheme
+ + "-starter)";
+ default -> scheme + ": the " + name + " runtime has no support for
this component";
+ };
+ }
+
/** Options models write that the component does not have, and what the
component does instead. */
static final Map<String, String> INVENTED_OPTIONS = Map.ofEntries(
Map.entry("file:mkdir", "directories are created by default
(autoCreate=true); remove the option"),
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
index 463e416d2284..939c3672a0a1 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidator.java
@@ -26,12 +26,15 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.networknt.schema.Error;
import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
import org.apache.camel.dsl.yaml.validator.YamlValidator;
import static
org.apache.camel.dsl.jbang.core.commands.ai.JavaChecks.JAVA_CLASS_PATTERN;
@@ -47,7 +50,10 @@ import static
org.apache.camel.dsl.jbang.core.commands.ai.JavaChecks.withSibling
*/
public final class SourceValidator {
+ private static final String BUILTIN_VERSION = new
DefaultCamelCatalog().getCatalogVersion();
private static volatile YamlValidator yamlValidator;
+ /** The schema validators of other Camel versions, by version
(CAMEL-24711). */
+ private static final Map<String, YamlValidator> VERSION_VALIDATORS = new
ConcurrentHashMap<>();
private SourceValidator() {
}
@@ -83,9 +89,19 @@ public final class SourceValidator {
public static List<String> validate(
String fileName, String content, CamelCatalog catalog,
Function<String, String> extraPropertyLine,
Path directory) {
+ return validate(fileName, content, catalog, extraPropertyLine,
directory, null);
+ }
+
+ /**
+ * As {@link #validate(String, String, CamelCatalog, Function, Path)},
with the YAML DSL schema validator to use:
+ * one built for the schema of another Camel version, or null for the
schema of the catalog's version.
+ */
+ public static List<String> validate(
+ String fileName, String content, CamelCatalog catalog,
Function<String, String> extraPropertyLine,
+ Path directory, YamlValidator schemaValidator) {
String name = fileName == null ? "" :
fileName.toLowerCase(Locale.ROOT);
if (name.endsWith(".yaml") || name.endsWith(".yml")) {
- List<String> msgs = validateCamelYaml(content, catalog);
+ List<String> msgs = validateCamelYaml(content, catalog,
schemaValidator);
if (directory != null && msgs.isEmpty()) {
msgs = new ArrayList<>(msgs);
msgs.addAll(validateYamlBeanRefs(content,
BeanDeclarations.scan(directory, fileName), catalog));
@@ -114,15 +130,33 @@ public final class SourceValidator {
/**
* Validates Camel YAML DSL source: the YAML DSL schema first, then
endpoint URIs and simple expressions against the
- * catalog. Returns the messages, empty when the source is valid.
+ * catalog. The schema is the one of the catalog's Camel version: the
CLI's own, or for a catalog of another version
+ * the schema read from the {@code camel-yaml-dsl} jar of that version.
Returns the messages, empty when the source
+ * is valid.
*/
public static List<String> validateCamelYaml(String content, CamelCatalog
catalog) {
+ return validateCamelYaml(content, catalog, null);
+ }
+
+ /**
+ * As {@link #validateCamelYaml(String, CamelCatalog)} with the schema
validator to use, null for the one of the
+ * catalog's version.
+ */
+ public static List<String> validateCamelYaml(String content, CamelCatalog
catalog, YamlValidator schemaValidator) {
List<String> msgs = new ArrayList<>();
if (content == null || content.isBlank()) {
return msgs;
}
+ YamlValidator validator;
+ try {
+ validator = schemaValidator != null ? schemaValidator :
yamlValidator(catalog);
+ } catch (Exception e) {
+ msgs.add("Cannot validate against the YAML DSL schema of Camel " +
catalog.getCatalogVersion() + ": "
+ + e.getMessage());
+ return msgs;
+ }
try {
- msgs.addAll(formatSchemaErrors(yamlValidator().validate(content)));
+ msgs.addAll(formatSchemaErrors(validator.validate(content)));
} catch (Exception e) {
msgs.add("Invalid YAML: " + e.getMessage());
return msgs;
@@ -131,6 +165,33 @@ public final class SourceValidator {
return msgs;
}
+ /**
+ * The schema validator of the catalog's Camel version: the CLI's own when
the catalog is the built-in one, else one
+ * for the schema of the version the catalog was loaded for (CAMEL-24711),
built once per version.
+ */
+ static YamlValidator yamlValidator(CamelCatalog catalog) throws Exception {
+ String version = catalog != null ? catalog.getCatalogVersion() : null;
+ if (version == null || version.equals(BUILTIN_VERSION)) {
+ return yamlValidator();
+ }
+ YamlValidator v = VERSION_VALIDATORS.get(version);
+ if (v == null) {
+ synchronized (VERSION_VALIDATORS) {
+ v = VERSION_VALIDATORS.get(version);
+ if (v == null) {
+ String schema = CatalogLoader.loadYamlDslSchema(null,
version, false, true);
+ if (schema == null) {
+ return yamlValidator();
+ }
+ v = new YamlValidator(false, schema, catalog);
+ v.init();
+ VERSION_VALIDATORS.put(version, v);
+ }
+ }
+ }
+ return v;
+ }
+
/**
* The catalog checks of a YAML route without the schema: endpoint URIs
and Simple expressions. For a caller that
* has already validated the schema (the CLI, the schema tool).
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java
index e581eb20479d..ab8b18e666f4 100644
---
a/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java
+++
b/dsl/camel-jbang/camel-jbang-core/src/main/java/org/apache/camel/dsl/jbang/core/common/CatalogLoader.java
@@ -19,8 +19,11 @@ package org.apache.camel.dsl.jbang.core.common;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.Function;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
@@ -240,6 +243,51 @@ public final class CatalogLoader {
return answer;
}
+ /**
+ * The YAML DSL JSON schema of a Camel version, read from the {@code
org.apache.camel:camel-yaml-dsl} jar of that
+ * version (every 4.x release ships {@code schema/camelYamlDsl.json}; the
canonical schema exists from 4.22).
+ *
+ * @param repos extra Maven repositories, comma separated; null for
the defaults
+ * @param version the Camel version
+ * @param canonical whether to read the canonical schema
+ * @param download whether to download when the jar is not in the local
repository
+ * @return the schema document as JSON, or null when the version
is the one of the CLI, whose schema is on
+ * the classpath
+ * @throws Exception when the jar cannot be downloaded or has no such
schema
+ */
+ public static String loadYamlDslSchema(String repos, String version,
boolean canonical, boolean download)
+ throws Exception {
+ if (version == null || version.isBlank() || version.equals(new
DefaultCamelCatalog().getCatalogVersion())) {
+ return null;
+ }
+ String entry = canonical ? "schema/camelYamlDsl-canonical.json" :
"schema/camelYamlDsl.json";
+ MavenDependencyDownloader downloader = new MavenDependencyDownloader();
+ downloader.setRepositories(repos);
+ downloader.setDownload(download);
+ try {
+ downloader.start();
+ MavenArtifact ma = downloader.downloadArtifact("org.apache.camel",
"camel-yaml-dsl", version);
+ if (ma == null || ma.getFile() == null) {
+ throw new IOException("Cannot download
org.apache.camel:camel-yaml-dsl:" + version);
+ }
+ try (ZipFile jar = new ZipFile(ma.getFile())) {
+ ZipEntry ze = jar.getEntry(entry);
+ if (ze == null) {
+ throw new IOException(
+ canonical
+ ? "Camel " + version + " has no canonical
YAML DSL schema (it exists from Camel 4.22)"
+ : "org.apache.camel:camel-yaml-dsl:" +
version + " has no " + entry);
+ }
+ try (InputStream is = jar.getInputStream(ze)) {
+ return new String(is.readAllBytes(),
StandardCharsets.UTF_8);
+ }
+ }
+ } finally {
+ downloader.stop();
+ downloader.close();
+ }
+ }
+
public static String resolveCamelVersionFromSpringBoot(String repos,
String camelSpringBootVersion, boolean download)
throws Exception {
DependencyDownloaderClassLoader cl = new
DependencyDownloaderClassLoader(CatalogLoader.class.getClassLoader());
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorVersionTest.java
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorVersionTest.java
new file mode 100644
index 000000000000..b105e6e0c8e4
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/java/org/apache/camel/dsl/jbang/core/commands/ai/SourceValidatorVersionTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.util.List;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.catalog.DefaultRuntimeProvider;
+import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
+import org.apache.camel.dsl.yaml.validator.YamlValidator;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * The validation of a source for another Camel version or runtime than the
CLI's own (CAMEL-24711): the schema of that
+ * version, and a component the runtime has no extension for.
+ */
+class SourceValidatorVersionTest {
+
+ private static final String ROUTE = """
+ - from:
+ uri: timer:tick
+ steps:
+ - to: kafka:orders
+ - to: log:done
+ """;
+
+ /** A catalog that knows timer and log only, the way the
camel-quarkus-catalog knows the extensions. */
+ static final class FakeQuarkusProvider extends DefaultRuntimeProvider {
+ @Override
+ public String getProviderName() {
+ return "quarkus";
+ }
+
+ @Override
+ public String getComponentJSonSchemaDirectory() {
+ return
"org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components";
+ }
+ }
+
+ @Test
+ void aComponentWithoutAnExtensionIsAnErrorOnTheRuntimeCatalog() {
+ CamelCatalog quarkus = new DefaultCamelCatalog();
+ quarkus.setRuntimeProvider(new FakeQuarkusProvider());
+
+ List<String> msgs = SourceValidator.validateYamlEndpoints(ROUTE,
quarkus);
+ assertThat(msgs).hasSize(1);
+ assertThat(msgs.get(0)).contains("kafka: Camel Quarkus has no
extension").contains("camel-quarkus-kafka");
+
+ // the plain catalog says nothing about a scheme it does not know: a
project can register a component of its own
+
assertThat(SourceValidator.validateYamlEndpoints(ROUTE.replace("kafka:orders",
"mine:orders"),
+ new DefaultCamelCatalog())).isEmpty();
+ // and neither does the runtime catalog, only Camel components without
an extension are reported
+
assertThat(SourceValidator.validateYamlEndpoints(ROUTE.replace("kafka:orders",
"mine:orders"), quarkus)).isEmpty();
+ }
+
+ @Test
+ void theBuiltInCatalogUsesTheBuiltInSchema() throws Exception {
+ YamlValidator v = SourceValidator.yamlValidator(new
DefaultCamelCatalog());
+ assertThat(v).isSameAs(SourceValidator.yamlValidator(null));
+ }
+
+ @Test
+ @DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
+ disabledReason = "Runs only local — requires the
camel-yaml-dsl jar of another version")
+ void aCatalogOfAnotherVersionGetsTheSchemaOfThatVersion() throws Exception
{
+ CamelCatalog older = CatalogLoader.loadCatalog(null, "4.18.0", true);
+ assertThat(older.getCatalogVersion()).isEqualTo("4.18.0");
+
+ YamlValidator v = SourceValidator.yamlValidator(older);
+ assertThat(v).isNotSameAs(SourceValidator.yamlValidator(null));
+ assertThat(SourceValidator.yamlValidator(older)).as("one validator per
version").isSameAs(v);
+
+ // a route both versions accept validates; the schema read from the
4.18.0 jar is the one that answers
+ assertThat(SourceValidator.validateCamelYaml(ROUTE, older)).isEmpty();
+ assertThat(CatalogLoader.loadYamlDslSchema(null, "4.18.0", false,
true)).contains("\"$schema\"");
+ // the CLI's own version has its schema on the classpath: nothing to
read
+ assertThat(CatalogLoader.loadYamlDslSchema(null, new
DefaultCamelCatalog().getCatalogVersion(), false, true))
+ .isNull();
+ }
+
+ @Test
+ @DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
+ disabledReason = "Runs only local — requires the
camel-yaml-dsl jar of another version")
+ void theCanonicalSchemaExistsFromCamel422() {
+ org.assertj.core.api.Assertions.assertThatThrownBy(() ->
CatalogLoader.loadYamlDslSchema(null, "4.18.0", true, true))
+ .hasMessageContaining("no canonical YAML DSL schema")
+ .hasMessageContaining("4.22");
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/log.json
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/log.json
new file mode 100644
index 000000000000..553eac237c7d
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/log.json
@@ -0,0 +1,68 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "log",
+ "title": "Log Data",
+ "description": "Prints data from the routed message (such as body and
headers) to the logger.",
+ "deprecated": false,
+ "firstVersion": "1.1.0",
+ "label": "core,monitoring",
+ "javaType": "org.apache.camel.component.log.LogComponent",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-log",
+ "version": "4.23.0-SNAPSHOT",
+ "scheme": "log",
+ "extendsScheme": "",
+ "syntax": "log:loggerName",
+ "async": false,
+ "api": false,
+ "consumerOnly": false,
+ "producerOnly": true,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": false
+ },
+ "componentProperties": {
+ "lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Whether the producer should be started lazy (on the first message). By
starting lazy you can use this to allow CamelContext and routes to startup in
situations where a producer may otherwise fail [...]
+ "sourceLocationLoggerName": { "index": 1, "kind": "property",
"displayName": "Source Location Logger Name", "group": "producer", "label": "",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "If enabled then the source location of where the log endpoint
is used in Camel routes, would be used as logger name, instead of the given
name. However, if the source location is disabled [...]
+ "autowiredEnabled": { "index": 2, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ "exchangeFormatter": { "index": 3, "kind": "property", "displayName":
"Exchange Formatter", "group": "advanced", "label": "advanced", "required":
false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter",
"deprecated": false, "autowired": true, "secret": false, "description": "Sets a
custom ExchangeFormatter to convert the Exchange to a String suitable for
logging. If not specified, we default to DefaultExchangeFormatter." }
+ },
+ "properties": {
+ "loggerName": { "index": 0, "kind": "path", "displayName": "Logger Name",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "Name of the logging
category to use" },
+ "groupActiveOnly": { "index": 1, "kind": "parameter", "displayName":
"Group Active Only", "group": "producer", "label": "", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "If true, will
hide stats when no new messages have been received for a time interval, if
false, show stats regardless of message traffic." },
+ "groupDelay": { "index": 2, "kind": "parameter", "displayName": "Group
Delay", "group": "producer", "label": "", "required": false, "type": "integer",
"javaType": "java.lang.Long", "deprecated": false, "autowired": false,
"secret": false, "description": "Set the initial delay for stats (in millis)" },
+ "groupInterval": { "index": 3, "kind": "parameter", "displayName": "Group
Interval", "group": "producer", "label": "", "required": false, "type":
"integer", "javaType": "java.lang.Long", "deprecated": false, "autowired":
false, "secret": false, "description": "If specified will group message stats
by this time interval (in millis)" },
+ "groupSize": { "index": 4, "kind": "parameter", "displayName": "Group
Size", "group": "producer", "label": "", "required": false, "type": "integer",
"javaType": "java.lang.Integer", "deprecated": false, "autowired": false,
"secret": false, "description": "An integer that specifies a group size for
throughput logging." },
+ "level": { "index": 5, "kind": "parameter", "displayName": "Level",
"group": "producer", "label": "", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "TRACE", "DEBUG", "INFO", "WARN",
"ERROR", "OFF" ], "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "INFO", "description": "Logging level to use. The default value
is INFO." },
+ "logMask": { "index": 6, "kind": "parameter", "displayName": "Log Mask",
"group": "producer", "label": "", "required": false, "type": "boolean",
"javaType": "java.lang.Boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "If true, mask sensitive
information like password or passphrase in the log." },
+ "marker": { "index": 7, "kind": "parameter", "displayName": "Marker",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "An optional Marker name to use." },
+ "plain": { "index": 8, "kind": "parameter", "displayName": "Plain",
"group": "producer", "label": "", "required": false, "type": "boolean",
"javaType": "boolean", "deprecated": false, "autowired": false, "secret":
false, "defaultValue": false, "description": "If enabled only the body will be
printed out" },
+ "sourceLocationLoggerName": { "index": 9, "kind": "parameter",
"displayName": "Source Location Logger Name", "group": "producer", "label": "",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "If enabled then the source location of where the log endpoint
is used in Camel routes, would be used as logger name, instead of the given
name. However, if the source location is disabled [...]
+ "lazyStartProducer": { "index": 10, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
+ "exchangeFormatter": { "index": 11, "kind": "parameter", "displayName":
"Exchange Formatter", "group": "advanced", "label": "advanced", "required":
false, "type": "object", "javaType": "org.apache.camel.spi.ExchangeFormatter",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom exchange formatter" },
+ "maxChars": { "index": 12, "kind": "parameter", "displayName": "Max
Chars", "group": "formatting", "label": "formatting", "required": false,
"type": "integer", "javaType": "int", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": 10000, "description": "Limits the number of
characters logged per line." },
+ "multiline": { "index": 13, "kind": "parameter", "displayName":
"Multiline", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "If enabled then
each information is outputted on a newline." },
+ "showAll": { "index": 14, "kind": "parameter", "displayName": "Show All",
"group": "formatting", "label": "formatting", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": false, "description": "Quick option for
turning all options on. (multiline, maxChars has to be manually set if to be
used)" },
+ "showAllProperties": { "index": 15, "kind": "parameter", "displayName":
"Show All Properties", "group": "formatting", "label": "formatting",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "Show all of the exchange properties (both internal and
custom)." },
+ "showBody": { "index": 16, "kind": "parameter", "displayName": "Show
Body", "group": "formatting", "label": "formatting", "required": false, "type":
"boolean", "javaType": "boolean", "deprecated": false, "autowired": false,
"secret": false, "defaultValue": true, "description": "Show the message body."
},
+ "showBodyType": { "index": 17, "kind": "parameter", "displayName": "Show
Body Type", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": true, "description": "Show the body
Java type." },
+ "showCachedStreams": { "index": 18, "kind": "parameter", "displayName":
"Show Cached Streams", "group": "formatting", "label": "formatting",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Whether Camel should show cached stream bodies or not
(org.apache.camel.StreamCache)." },
+ "showCaughtException": { "index": 19, "kind": "parameter", "displayName":
"Show Caught Exception", "group": "formatting", "label": "formatting",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "If the exchange has a caught exception, show the exception
message (no stack trace). A caught exception is stored as a property on the
exchange (using the key org.apache.camel.Exchange# [...]
+ "showException": { "index": 20, "kind": "parameter", "displayName": "Show
Exception", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "If the exchange
has an exception, show the exception message (no stacktrace)" },
+ "showExchangeId": { "index": 21, "kind": "parameter", "displayName": "Show
Exchange Id", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show the unique
exchange ID." },
+ "showExchangePattern": { "index": 22, "kind": "parameter", "displayName":
"Show Exchange Pattern", "group": "formatting", "label": "formatting",
"required": false, "type": "boolean", "javaType": "boolean", "deprecated":
false, "autowired": false, "secret": false, "defaultValue": false,
"description": "Shows the Message Exchange Pattern (or MEP for short)." },
+ "showFiles": { "index": 23, "kind": "parameter", "displayName": "Show
Files", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "If enabled Camel
will output files" },
+ "showFuture": { "index": 24, "kind": "parameter", "displayName": "Show
Future", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "If enabled Camel
will on Future objects wait for it to complete to obtain the payload to be
logged." },
+ "showHeaders": { "index": 25, "kind": "parameter", "displayName": "Show
Headers", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show the message
headers." },
+ "showProperties": { "index": 26, "kind": "parameter", "displayName": "Show
Properties", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show the
exchange properties (only custom). Use showAllProperties to show both internal
and custom properties." },
+ "showRouteGroup": { "index": 27, "kind": "parameter", "displayName": "Show
Route Group", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show route
Group." },
+ "showRouteId": { "index": 28, "kind": "parameter", "displayName": "Show
Route Id", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show route ID."
},
+ "showStackTrace": { "index": 29, "kind": "parameter", "displayName": "Show
Stack Trace", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show the stack
trace, if an exchange has an exception. Only effective if one of showAll,
showException or showCaughtException are enabled." },
+ "showStreams": { "index": 30, "kind": "parameter", "displayName": "Show
Streams", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether Camel
should show stream bodies or not (eg such as java.io.InputStream). Beware if
you enable this option then you may not be able later to access the message
body as the stream have already bee [...]
+ "showVariables": { "index": 31, "kind": "parameter", "displayName": "Show
Variables", "group": "formatting", "label": "formatting", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Show the
variables." },
+ "skipBodyLineSeparator": { "index": 32, "kind": "parameter",
"displayName": "Skip Body Line Separator", "group": "formatting", "label":
"formatting", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Whether to skip line separators when logging the message body.
This allows to log the message body in one line, setting this option to false
will preserve any line separators from t [...]
+ "style": { "index": 33, "kind": "parameter", "displayName": "Style",
"group": "formatting", "label": "formatting", "required": false, "type":
"enum", "javaType":
"org.apache.camel.support.processor.DefaultExchangeFormatter.OutputStyle",
"enum": [ "Default", "Tab", "Fixed" ], "deprecated": false, "autowired": false,
"secret": false, "defaultValue": "Default", "description": "Sets the outputs
style to use." }
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/timer.json
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/timer.json
new file mode 100644
index 000000000000..30d99544faab
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-core/src/test/resources/org/apache/camel/dsl/jbang/core/commands/ai/fakequarkus/components/timer.json
@@ -0,0 +1,52 @@
+{
+ "component": {
+ "kind": "component",
+ "name": "timer",
+ "title": "Timer",
+ "description": "Generate messages in specified intervals using
java.util.Timer.",
+ "deprecated": false,
+ "firstVersion": "1.0.0",
+ "label": "core,scheduling",
+ "javaType": "org.apache.camel.component.timer.TimerComponent",
+ "supportLevel": "Stable",
+ "groupId": "org.apache.camel",
+ "artifactId": "camel-timer",
+ "version": "4.23.0-SNAPSHOT",
+ "scheme": "timer",
+ "extendsScheme": "",
+ "syntax": "timer:timerName",
+ "async": false,
+ "api": false,
+ "consumerOnly": true,
+ "producerOnly": false,
+ "lenientProperties": false,
+ "browsable": false,
+ "remote": false
+ },
+ "componentProperties": {
+ "bridgeErrorHandler": { "index": 0, "kind": "property", "displayName":
"Bridge Error Handler", "group": "consumer", "label": "consumer", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": false, "description":
"Allows for bridging the consumer to the Camel routing Error Handler, which
mean any exceptions (if possible) occurred while the Camel consumer is trying
to pickup incoming messages, or the like [...]
+ "includeMetadata": { "index": 1, "kind": "property", "displayName":
"Include Metadata", "group": "consumer", "label": "", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
include metadata in the exchange such as fired time, timer name, timer count
etc." },
+ "autowiredEnabled": { "index": 2, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ },
+ "headers": {
+ "CamelTimerFiredTime": { "index": 0, "kind": "header", "displayName": "",
"group": "consumer", "label": "", "required": false, "javaType": "Date",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The fired time", "constantName":
"org.apache.camel.component.timer.TimerConstants#HEADER_FIRED_TIME" },
+ "CamelMessageTimestamp": { "index": 1, "kind": "header", "displayName":
"", "group": "consumer", "label": "", "required": false, "javaType": "long",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The timestamp of the message", "constantName":
"org.apache.camel.component.timer.TimerConstants#HEADER_MESSAGE_TIMESTAMP" }
+ },
+ "properties": {
+ "timerName": { "index": 0, "kind": "path", "displayName": "Timer Name",
"group": "consumer", "label": "", "required": true, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The name of the timer" },
+ "delay": { "index": 1, "kind": "parameter", "displayName": "Delay",
"group": "consumer", "label": "", "required": false, "type": "duration",
"javaType": "long", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "1000", "description": "The number of milliseconds to wait
before the first event is generated. Should not be used in conjunction with the
time option. The default value is 1000." },
+ "fixedRate": { "index": 2, "kind": "parameter", "displayName": "Fixed
Rate", "group": "consumer", "label": "", "required": false, "type": "boolean",
"javaType": "boolean", "deprecated": false, "autowired": false, "secret":
false, "defaultValue": false, "description": "Events take place at
approximately regular intervals, separated by the specified period." },
+ "includeMetadata": { "index": 3, "kind": "parameter", "displayName":
"Include Metadata", "group": "consumer", "label": "", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Whether to
include metadata in the exchange such as fired time, timer name, timer count
etc." },
+ "period": { "index": 4, "kind": "parameter", "displayName": "Period",
"group": "consumer", "label": "", "required": false, "type": "duration",
"javaType": "long", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": "1000", "description": "Generate periodic events every period.
Must be zero or positive value. The default value is 1000." },
+ "repeatCount": { "index": 5, "kind": "parameter", "displayName": "Repeat
Count", "group": "consumer", "label": "", "required": false, "type": "integer",
"javaType": "long", "deprecated": false, "autowired": false, "secret": false,
"description": "Specifies a maximum limit for the number of fires. Therefore,
if you set it to 1, the timer will only fire once. If you set it to 5, it will
only fire five times. A value of zero or negative means fire forever." },
+ "bridgeErrorHandler": { "index": 6, "kind": "parameter", "displayName":
"Bridge Error Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Allows for bridging the consumer to the
Camel routing Error Handler, which mean any exceptions (if possible) occurred
while the Camel consumer is trying to pickup incoming [...]
+ "exceptionHandler": { "index": 7, "kind": "parameter", "displayName":
"Exception Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "object", "javaType":
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.",
"deprecated": false, "autowired": false, "secret": false, "description": "To
let the consumer use a custom ExceptionHandler. Notice if the option
bridgeErrorHandler is enabled then this option is not in use. By def [...]
+ "exchangePattern": { "index": 8, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." },
+ "daemon": { "index": 9, "kind": "parameter", "displayName": "Daemon",
"group": "advanced", "label": "advanced", "required": false, "type": "boolean",
"javaType": "boolean", "deprecated": false, "autowired": false, "secret":
false, "defaultValue": true, "description": "Specifies whether the thread
associated with the timer endpoint runs as a daemon. The default value is
true." },
+ "pattern": { "index": 10, "kind": "parameter", "displayName": "Pattern",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "Allows you to specify a custom Date pattern to
use for setting the time option using URI syntax." },
+ "synchronous": { "index": 11, "kind": "parameter", "displayName":
"Synchronous", "group": "advanced", "label": "advanced", "required": false,
"type": "boolean", "javaType": "boolean", "deprecated": false, "autowired":
false, "secret": false, "defaultValue": false, "description": "Sets whether
synchronous processing should be strictly used" },
+ "time": { "index": 12, "kind": "parameter", "displayName": "Time",
"group": "advanced", "label": "advanced", "required": false, "type": "string",
"javaType": "java.util.Date", "deprecated": false, "autowired": false,
"secret": false, "description": "A java.util.Date the first event should be
generated. If using the URI, the pattern expected is: yyyy-MM-dd HH:mm:ss or
yyyy-MM-dd'T'HH:mm:ss." },
+ "timer": { "index": 13, "kind": "parameter", "displayName": "Timer",
"group": "advanced", "label": "advanced", "required": false, "type": "object",
"javaType": "java.util.Timer", "deprecated": false, "autowired": false,
"secret": false, "description": "To use a custom Timer" },
+ "runLoggingLevel": { "index": 14, "kind": "parameter", "displayName": "Run
Logging Level", "group": "scheduler", "label": "consumer,scheduler",
"required": false, "type": "enum", "javaType": "org.apache.camel.LoggingLevel",
"enum": [ "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "OFF" ], "deprecated":
false, "autowired": false, "secret": false, "defaultValue": "TRACE",
"description": "The consumer logs a start\/complete log line when it polls.
This option allows you to configure the log [...]
+ }
+}
diff --git a/dsl/camel-jbang/camel-jbang-plugin-validate/pom.xml
b/dsl/camel-jbang/camel-jbang-plugin-validate/pom.xml
index 8a281548b22a..d108e6dfdf2b 100644
--- a/dsl/camel-jbang/camel-jbang-plugin-validate/pom.xml
+++ b/dsl/camel-jbang/camel-jbang-plugin-validate/pom.xml
@@ -49,5 +49,17 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-yaml-dsl-validator</artifactId>
</dependency>
+
+ <!-- test -->
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-test-junit6</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.assertj</groupId>
+ <artifactId>assertj-core</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
new file mode 100644
index 000000000000..4336a9c46092
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/CatalogVersionMixin.java
@@ -0,0 +1,98 @@
+/*
+ * 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.validate;
+
+import org.apache.camel.catalog.CamelCatalog;
+import org.apache.camel.catalog.DefaultCamelCatalog;
+import org.apache.camel.dsl.jbang.core.commands.MavenResolverMixin;
+import org.apache.camel.dsl.jbang.core.commands.QuarkusPlatformMixin;
+import org.apache.camel.dsl.jbang.core.common.CatalogLoader;
+import org.apache.camel.dsl.jbang.core.common.QuarkusHelper;
+import org.apache.camel.dsl.jbang.core.common.RuntimeCompletionCandidates;
+import org.apache.camel.dsl.jbang.core.common.RuntimeType;
+import org.apache.camel.dsl.jbang.core.common.RuntimeTypeConverter;
+import org.apache.camel.dsl.yaml.validator.YamlValidator;
+import picocli.CommandLine;
+
+/**
+ * The Camel version and runtime a validation answers for (CAMEL-24711): the
catalog of that version and runtime, as
+ * {@code camel catalog} and {@code camel doc} load it, and the YAML DSL
schema of that Camel version read from its
+ * {@code camel-yaml-dsl} jar. Without options the CLI's own version, with
nothing to download.
+ */
+public class CatalogVersionMixin {
+
+ @CommandLine.Option(names = { "--camel-version" },
+ description = "Validate against another Camel version
than the CLI's own: its catalog and YAML DSL schema")
+ String camelVersion;
+
+ @CommandLine.Option(names = { "--runtime" },
+ completionCandidates =
RuntimeCompletionCandidates.class,
+ converter = RuntimeTypeConverter.class,
+ description = "Runtime (${COMPLETION-CANDIDATES});
spring-boot and quarkus use the catalog of that"
+ + " runtime, so a component without a
starter or extension is an error")
+ RuntimeType runtime;
+
+ @CommandLine.Mixin
+ MavenResolverMixin mavenResolver;
+
+ @CommandLine.Mixin
+ QuarkusPlatformMixin quarkusPlatform;
+
+ /** The catalog and the Camel version it answers for. */
+ public record Loaded(CamelCatalog catalog, String camelVersion) {
+ }
+
+ /**
+ * Loads the catalog of the version and runtime asked for; the built-in
catalog when none is.
+ */
+ public Loaded load() throws Exception {
+ if (RuntimeType.springBoot == runtime) {
+ String version = camelVersion != null ? camelVersion : new
DefaultCamelCatalog().getCatalogVersion();
+ return new Loaded(
+ CatalogLoader.loadSpringBootCatalog(mavenResolver.repos(),
version, mavenResolver.download()), version);
+ }
+ if (RuntimeType.quarkus == runtime) {
+ QuarkusHelper.QuarkusPlatformBom bom =
quarkusPlatform.resolve(camelVersion,
+ mavenResolver.downloader()::resolveArtifact,
mavenResolver.download(), mavenResolver.fresh());
+ CamelCatalog catalog
+ = CatalogLoader.loadQuarkusCatalog(bom.quarkusCamelBom(),
mavenResolver.downloader()::resolveArtifact);
+ // the Camel version the platform pins is the one the schema is
read for
+ String version = bom.camelVersion() != null ? bom.camelVersion() :
camelVersion;
+ return new Loaded(catalog, version);
+ }
+ if (camelVersion == null || camelVersion.isBlank()) {
+ return new Loaded(new DefaultCamelCatalog(), null);
+ }
+ return new Loaded(
+ CatalogLoader.loadCatalog(mavenResolver.repos(), camelVersion,
mavenResolver.download()),
+ camelVersion);
+ }
+
+ /**
+ * The YAML DSL schema validator of the loaded Camel version: the CLI's
own schema for the CLI's version, else the
+ * schema read from the {@code camel-yaml-dsl} jar of that version.
+ *
+ * @throws Exception when the jar cannot be downloaded, or the version has
no canonical schema (before 4.22)
+ */
+ public YamlValidator yamlValidator(Loaded loaded, boolean canonical)
throws Exception {
+ String schema = CatalogLoader.loadYamlDslSchema(mavenResolver.repos(),
loaded.camelVersion(), canonical,
+ mavenResolver.download());
+ YamlValidator validator = new YamlValidator(canonical, schema,
loaded.catalog());
+ validator.init();
+ return validator;
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/SourceValidateCommand.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/SourceValidateCommand.java
index 71ec89464f83..b5fa97808a53 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/SourceValidateCommand.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/SourceValidateCommand.java
@@ -25,10 +25,10 @@ import java.util.Map;
import java.util.Stack;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.commands.ai.SourceValidator;
+import org.apache.camel.dsl.yaml.validator.YamlValidator;
import picocli.CommandLine;
/**
@@ -45,13 +45,19 @@ public class SourceValidateCommand extends CamelCommand {
parameterConsumer = FilesConsumer.class)
List<String> files = new ArrayList<>();
+ @CommandLine.Mixin
+ CatalogVersionMixin catalogVersion;
+
public SourceValidateCommand(CamelJBangMain main) {
super(main);
}
@Override
public Integer doCall() throws Exception {
- CamelCatalog catalog = new DefaultCamelCatalog();
+ // the catalog and schema of the Camel version and runtime asked for;
the CLI's own without options
+ CatalogVersionMixin.Loaded loaded = catalogVersion.load();
+ CamelCatalog catalog = loaded.catalog();
+ YamlValidator schemaValidator = loaded.camelVersion() != null ?
catalogVersion.yamlValidator(loaded, false) : null;
Map<String, List<String>> reports = new LinkedHashMap<>();
for (String n : files) {
File f = new File(n);
@@ -62,7 +68,7 @@ public class SourceValidateCommand extends CamelCommand {
String content = Files.readString(f.toPath());
File parent = f.getAbsoluteFile().getParentFile();
reports.put(n, SourceValidator.validate(f.getName(), content,
catalog, null,
- parent != null ? parent.toPath() : null));
+ parent != null ? parent.toPath() : null, schemaValidator));
}
int count = reports.values().stream().mapToInt(List::size).sum();
if (count > 0) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
index e0591e317f7a..68d8112311c9 100644
---
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/main/java/org/apache/camel/dsl/jbang/core/commands/validate/YamlValidateCommand.java
@@ -28,7 +28,6 @@ import java.util.Stack;
import com.networknt.schema.Error;
import org.apache.camel.catalog.CamelCatalog;
-import org.apache.camel.catalog.DefaultCamelCatalog;
import org.apache.camel.dsl.jbang.core.commands.CamelCommand;
import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
import org.apache.camel.dsl.jbang.core.commands.ai.SourceValidator;
@@ -51,6 +50,9 @@ public class YamlValidateCommand extends CamelCommand {
+ " (use --catalog=false for the schema
only)")
boolean catalog = true;
+ @CommandLine.Mixin
+ CatalogVersionMixin catalogVersion;
+
@CommandLine.Parameters(description = { "The Camel YAML source files to
parse." },
arity = "1..9",
paramLabel = "<files>",
@@ -63,10 +65,11 @@ public class YamlValidateCommand extends CamelCommand {
@Override
public Integer doCall() throws Exception {
- YamlValidator validator = new YamlValidator(canonical);
- validator.init();
+ // the catalog and schema of the Camel version and runtime asked for;
the CLI's own without options
+ CatalogVersionMixin.Loaded loaded = catalogVersion.load();
+ YamlValidator validator = catalogVersion.yamlValidator(loaded,
canonical);
- CamelCatalog camelCatalog = catalog ? new DefaultCamelCatalog() : null;
+ CamelCatalog camelCatalog = catalog ? loaded.catalog() : null;
Map<String, List<Error>> reports = new LinkedHashMap<>();
for (String n : files) {
if (matchFile(n)) {
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/java/org/apache/camel/dsl/jbang/core/commands/validate/ValidateCamelVersionTest.java
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/java/org/apache/camel/dsl/jbang/core/commands/validate/ValidateCamelVersionTest.java
new file mode 100644
index 000000000000..1d6b60b4ffc7
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/java/org/apache/camel/dsl/jbang/core/commands/validate/ValidateCamelVersionTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.validate;
+
+import java.nio.file.Path;
+
+import org.apache.camel.dsl.jbang.core.commands.CamelJBangMain;
+import org.apache.camel.dsl.jbang.core.common.StringPrinter;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
+import picocli.CommandLine;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * camel validate yaml and camel validate source against the catalog and YAML
DSL schema of another Camel version or
+ * runtime (CAMEL-24711). Without options the CLI's own version answers,
offline.
+ */
+class ValidateCamelVersionTest {
+
+ private static final String ROUTE =
Path.of("src/test/resources/route.yaml").toString();
+ private static final String NO_EXTENSION =
Path.of("src/test/resources/no-extension.yaml").toString();
+
+ /** The file based Quarkus extension registry of the camel-jbang-core
tests: no registry call. */
+ private static String quarkusExtRegistry() {
+ return "--quarkus-ext-registry="
+ +
Path.of("../camel-jbang-core/src/test/resources/registry.quarkus.io").toAbsolutePath().normalize().toUri();
+ }
+
+ private static StringPrinter printer;
+
+ private static int yaml(String... args) throws Exception {
+ printer = new StringPrinter();
+ YamlValidateCommand cmd = new YamlValidateCommand(new
CamelJBangMain().withPrinter(printer));
+ CommandLine.populateCommand(cmd, args);
+ return cmd.doCall();
+ }
+
+ private static int source(String... args) throws Exception {
+ printer = new StringPrinter();
+ SourceValidateCommand cmd = new SourceValidateCommand(new
CamelJBangMain().withPrinter(printer));
+ CommandLine.populateCommand(cmd, args);
+ return cmd.doCall();
+ }
+
+ @Test
+ void theOwnVersionValidatesOffline() throws Exception {
+ assertThat(yaml(ROUTE)).isZero();
+ assertThat(printer.getOutput()).contains("Validation success");
+ assertThat(source(ROUTE)).isZero();
+ assertThat(printer.getOutput()).contains("Validation success");
+
+ // the plain catalog knows every component: a Camel component without
an extension is not its business
+ assertThat(yaml(NO_EXTENSION)).isZero();
+ }
+
+ @Test
+ @DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
+ disabledReason = "Runs only local — downloads
the catalog and camel-yaml-dsl jar of another version")
+ void anotherCamelVersionValidatesWithItsSchemaAndCatalog() throws
Exception {
+ assertThat(yaml("--camel-version=4.18.0", ROUTE)).isZero();
+ assertThat(printer.getOutput()).contains("Validation success");
+ assertThat(source("--camel-version=4.18.0", ROUTE)).isZero();
+ assertThat(printer.getOutput()).contains("Validation success");
+
+ // the canonical schema exists from 4.22: an older version is an
error, not a silent fallback
+ assertThatThrownBy(() -> yaml("--canonical", "--camel-version=4.18.0",
ROUTE))
+ .hasMessageContaining("no canonical YAML DSL
schema").hasMessageContaining("4.22");
+ }
+
+ @Test
+ @DisabledIfSystemProperty(named = "ci.env.name", matches = ".*",
+ disabledReason = "Runs only local — downloads
the Quarkus platform BOM and catalog")
+ void quarkusReportsAComponentWithoutAnExtension() throws Exception {
+ // Quarkus platform 3.30.1 pins Camel 4.16.0: the catalog of
camel-quarkus-catalog 3.30.0, the schema of 4.16.0
+ assertThat(yaml("--runtime=quarkus", "--quarkus-version=3.30.1",
quarkusExtRegistry(), ROUTE)).isZero();
+ assertThat(source("--runtime=quarkus", "--quarkus-version=3.30.1",
quarkusExtRegistry(), ROUTE)).isZero();
+
+ assertThat(yaml("--runtime=quarkus", "--quarkus-version=3.30.1",
quarkusExtRegistry(), NO_EXTENSION)).isEqualTo(1);
+ assertThat(printer.getOutput()).contains("atmosphere-websocket: Camel
Quarkus has no extension");
+ assertThat(source("--runtime=quarkus", "--quarkus-version=3.30.1",
quarkusExtRegistry(), NO_EXTENSION))
+ .isEqualTo(1);
+ assertThat(printer.getOutput()).contains("atmosphere-websocket: Camel
Quarkus has no extension");
+ }
+}
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/no-extension.yaml
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/no-extension.yaml
new file mode 100644
index 000000000000..df6b67904d5b
--- /dev/null
+++
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/no-extension.yaml
@@ -0,0 +1,21 @@
+#
+# 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.
+#
+
+- from:
+ uri: timer:tick
+ steps:
+ - to: atmosphere-websocket:/chat
diff --git
a/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/route.yaml
b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/route.yaml
new file mode 100644
index 000000000000..a31e4a593053
--- /dev/null
+++ b/dsl/camel-jbang/camel-jbang-plugin-validate/src/test/resources/route.yaml
@@ -0,0 +1,28 @@
+#
+# 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.
+#
+
+- from:
+ uri: timer:tick
+ parameters:
+ period: 1000
+ steps:
+ - setBody:
+ expression:
+ simple:
+ expression: "Hello ${random(1,10)}"
+ - to: kafka:orders
+ - to: log:done
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
index 366604c23b5a..9a050d5ad4c0 100644
---
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/main/java/org/apache/camel/dsl/yaml/validator/YamlValidator.java
@@ -73,6 +73,8 @@ public class YamlValidator {
private final ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
private final boolean canonical;
+ private final String schemaJson;
+ private final CamelCatalog catalog;
private Schema schema;
private Map<String, OneOfGroup> oneOfGroups;
@@ -84,7 +86,22 @@ public class YamlValidator {
}
public YamlValidator(boolean canonical) {
+ this(canonical, null, null);
+ }
+
+ /**
+ * A validator for a schema document other than the one on the classpath:
the schema of another Camel version, read
+ * from the {@code camel-yaml-dsl} jar of that version.
+ *
+ * @param canonical whether the document is the canonical schema
+ * @param schemaJson the schema document as JSON; null for the schema on
the classpath
+ * @param catalog the catalog of the same Camel version, for the checks
the schema cannot express; null for the
+ * catalog on the classpath
+ */
+ public YamlValidator(boolean canonical, String schemaJson, CamelCatalog
catalog) {
this.canonical = canonical;
+ this.schemaJson = schemaJson;
+ this.catalog = catalog;
}
public boolean isCanonical() {
@@ -778,9 +795,9 @@ public class YamlValidator {
* array-typed options (e.g. "outputs") use "oneOf" to mean "each element
is one of these types", not "exactly one
* of these sibling keys must be present".
*/
- private static Map<String, OneOfGroup> loadOneOfGroups() {
+ private Map<String, OneOfGroup> loadOneOfGroups() {
Map<String, OneOfGroup> groups = new HashMap<>();
- CamelCatalog catalog = new DefaultCamelCatalog();
+ CamelCatalog catalog = this.catalog != null ? this.catalog : new
DefaultCamelCatalog();
for (String name : catalog.findModelNames()) {
EipModel model = catalog.eipModel(name);
if (model == null) {
@@ -1270,7 +1287,8 @@ public class YamlValidator {
public void init() throws Exception {
String location = canonical ? LOCATION_CANONICAL : LOCATION;
- var model =
mapper.readTree(YamlValidator.class.getResourceAsStream(location));
+ var model = schemaJson != null
+ ? mapper.readTree(schemaJson) :
mapper.readTree(YamlValidator.class.getResourceAsStream(location));
this.model = model;
this.topLevelEntries = new LinkedHashSet<>();
model.at("/items/properties").fieldNames().forEachRemaining(topLevelEntries::add);
diff --git
a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaDocumentTest.java
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaDocumentTest.java
new file mode 100644
index 000000000000..e0db8ddf47d9
--- /dev/null
+++
b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/YamlValidatorSchemaDocumentTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.io.File;
+import java.nio.charset.StandardCharsets;
+
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * A validator built for a schema document handed to it, as camel validate
--camel-version reads the schema of another
+ * Camel version from its camel-yaml-dsl jar (CAMEL-24711), validates against
that document and not the classpath.
+ */
+public class YamlValidatorSchemaDocumentTest {
+
+ private static String schema(String location) throws Exception {
+ try (var is = YamlValidator.class.getResourceAsStream(location)) {
+ return new String(is.readAllBytes(), StandardCharsets.UTF_8);
+ }
+ }
+
+ @Test
+ public void theGivenDocumentIsTheSchema() throws Exception {
+ File shorthand = new
File("src/test/resources/canonical-invalid-log-shorthand.yaml");
+
+ // the classpath schema accepts the log shorthand
+ YamlValidator classpath = new YamlValidator(false, null, null);
+ classpath.init();
+ assertThat(classpath.validate(shorthand)).isEmpty();
+
+ // the same validator given the canonical document as its schema
rejects it: the document is what counts
+ YamlValidator given = new YamlValidator(false,
schema("/schema/camelYamlDsl-canonical.json"), null);
+ given.init();
+ assertThat(given.validate(shorthand)).isNotEmpty();
+ assertThat(given.isCanonical()).isFalse();
+
+ // and the classic document given explicitly validates as the
classpath one does
+ YamlValidator same = new YamlValidator(false,
schema("/schema/camelYamlDsl.json"), null);
+ same.init();
+ assertThat(same.validate(shorthand)).isEmpty();
+ assertThat(same.validate(new
File("src/test/resources/canonical-valid.yaml"))).isEmpty();
+ }
+}