chore: Add Citrus catalog generator implementation#84
Conversation
📝 WalkthroughWalkthroughRefactors catalog generation to runtime-specific generators: adds Camel and Citrus generator implementations and builders, replaces monolithic generator classes with functional interfaces, removes static Citrus JSON catalog files from the repo and build, and wires Citrus support into CLI and exports. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor CLI
participant GenerateCmd as GenerateCommand
participant Builder as CatalogGeneratorBuilder
participant Generator as CatalogGenerator
participant Maven as Maven Central
participant FS as Filesystem
CLI->>GenerateCmd: run (runtime + versions)
GenerateCmd->>Builder: select builder (Camel/Citrus) with versions
Builder->>Generator: build()
GenerateCmd->>Generator: generate()
alt Citrus path
Generator->>Maven: resolve Citrus catalog artifact (GAV)
Maven-->>Generator: artifact (zip/resources)
Generator->>FS: write .json / .xsd files + index
else Camel path
Generator->>Maven: load Camel catalogs, kamelets, CRDs, schemas
Maven-->>Generator: catalog + schema resources
Generator->>FS: write per-catalog files + index
end
Generator-->>GenerateCmd: CatalogDefinition (with fileName)
GenerateCmd->>FS: add file to library / output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
- Remove Maven based Citrus catalog loading mechanism and replace with catalog generator implementation - Introduce interface for CatalogGenerator and CatalogGeneratorBuilder - Use catalog generator implementations for Citrus and Camel based on the catalog runtime - Citrus catalog files are added just like any other catalog runtime (Main, Quarkus, SpringBoot) - Generator loads Citrus catalog resources from Maven central - index.js holds the Citrus versions to include into catalog (at the moment 4.9.2)
293c357 to
cea7f57
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (10)
index.js (1)
1-15: Consider updating the file header documentation.The header currently describes "Camel catalogs and Kamelets" and lists Camel JBang commands for Main, Quarkus, and Spring Boot runtimes. With the addition of Citrus, consider updating the description to reflect that Citrus is also included, and note that Citrus catalog versions are sourced differently (from Maven Central directly rather than via Camel JBang).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@index.js` around lines 1 - 15, Update the file header comment to mention that the module exports versions for Camel catalogs, Kamelets, and Citrus; replace or augment the phrase "Camel catalogs and Kamelets" with something like "Camel catalogs, Kamelets, and Citrus" and add a short note that Citrus catalog versions are sourced from Maven Central directly (not via Camel JBang). Keep the existing Camel JBang command examples for Main/Quarkus/Spring Boot but add one sentence clarifying the different source for Citrus versions so readers know to fetch Citrus from Maven Central rather than using the Camel JBang commands.src/main/java/io/kaoto/camelcatalog/generator/CatalogGeneratorBuilder.java (1)
1-7: Missing license header.Consistent with the observation for
CatalogGenerator.java, this file is also missing the Apache License 2.0 header that other files in the codebase include.📝 Suggested fix
+/* + * Copyright (C) 2023 Red Hat, Inc. + * + * Licensed 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 io.kaoto.camelcatalog.generator;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/CatalogGeneratorBuilder.java` around lines 1 - 7, Add the standard Apache License 2.0 file header to this source so it matches other files in the repo; update the top of the CatalogGeneratorBuilder interface (the CatalogGeneratorBuilder type and its build() method declaration) to include the same multi-line Apache License 2.0 comment block used elsewhere in the project before the package declaration.src/main/java/io/kaoto/camelcatalog/generator/CatalogGenerator.java (1)
1-9: Missing license header.Other files in this codebase include the Apache License 2.0 header (e.g.,
CatalogRuntime.java). This file should include the same license header for consistency.📝 Suggested fix
+/* + * Copyright (C) 2023 Red Hat, Inc. + * + * Licensed 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 io.kaoto.camelcatalog.generator;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/CatalogGenerator.java` around lines 1 - 9, The file is missing the project's Apache License 2.0 header; add the same Apache License 2.0 comment block used across the codebase (identical to other files like CatalogRuntime) at the very top of the file, above the package declaration, so the CatalogGenerator interface (and its generate() method returning CatalogDefinition) has the consistent license header as other source files.src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGeneratorBuilder.java (2)
1-33: Missing license header.This new file should include the Apache License 2.0 header consistent with other files in the codebase.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGeneratorBuilder.java` around lines 1 - 33, Add the missing Apache License 2.0 file header at the top of the new CitrusCatalogGeneratorBuilder class file: insert the exact license block used across the project (including copyright line, year and owner) above the package declaration so it matches other files in the codebase; ensure the header appears before "package io.kaoto.camelcatalog.generator.citrus;" and keep the rest of the CitrusCatalogGeneratorBuilder class unchanged.
29-32: Add validation for required parameters inbuild().The
build()method passescatalogVersionandoutputDirectorytoCitrusCatalogGeneratorwithout validation. SinceoutputDirectory.getAbsolutePath()is called in the generator without null checks, a nulloutputDirectorywill cause a NullPointerException at runtime. Similarly, nullcatalogVersioncould cause issues when used in string formatting. Validating these parameters at build time follows the fail-fast principle.💡 Suggested enhancement
`@Override` public CatalogGenerator build() { + if (catalogVersion == null || catalogVersion.isBlank()) { + throw new IllegalStateException("catalogVersion is required"); + } + if (outputDirectory == null) { + throw new IllegalStateException("outputDirectory is required"); + } return new CitrusCatalogGenerator(catalogVersion, outputDirectory, verbose); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGeneratorBuilder.java` around lines 29 - 32, The build() method should validate required fields before constructing CitrusCatalogGenerator: check that catalogVersion is not null/empty and outputDirectory is not null (and optionally that outputDirectory.exists() && outputDirectory.isDirectory()), and if any validation fails throw an IllegalStateException or IllegalArgumentException with a clear message; update the CitrusCatalogGeneratorBuilder.build() to perform these checks on catalogVersion and outputDirectory before calling new CitrusCatalogGenerator(catalogVersion, outputDirectory, verbose).src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandOptionsTest.java (1)
68-76: Consider adding explicit assertion for Citrus version.The test verifies the option is recognized and that the catalog version set is non-empty, but doesn't explicitly verify the Citrus version was added correctly. While this follows the existing pattern, a more thorough test would validate all expected versions are present.
💡 Suggested enhancement
generateCommandOptions.configure(args); assertEquals("catalogName", configBean.getCatalogsName()); assertEquals("kameletsVersion", configBean.getKameletsVersion()); assertFalse(configBean.getCatalogVersionSet().isEmpty()); + assertEquals(4, configBean.getCatalogVersionSet().size()); + assertTrue(configBean.getCatalogVersionSet().stream() + .anyMatch(arg -> arg.getRuntime() == CatalogRuntime.Citrus && "citrusVersion".equals(arg.getVersion()))); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandOptionsTest.java` around lines 68 - 76, Add an explicit assertion that the Citrus version was parsed and stored: in GenerateCommandOptionsTest, after configure(args) add an assertion that configBean contains "citrusVersion" (e.g. assertTrue(configBean.getCatalogVersionSet().contains("citrusVersion")) or, if available, assertEquals("citrusVersion", configBean.getCitrusVersion())); this ensures the Citrus option is verified alongside getCatalogsName(), getKameletsVersion(), and getCatalogVersionSet().src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java (1)
187-213: Use UTF‑8 for deterministic Kamelet hashing/output.
OutputStreamWriteruses the platform default charset; specifying UTF‑8 keeps hashes/filenames stable across environments.♻️ Deterministic encoding
-import java.io.*; +import java.io.*; +import java.nio.charset.StandardCharsets; @@ - var writer = new OutputStreamWriter(outputStream); + var writer = new OutputStreamWriter(outputStream, StandardCharsets.UTF_8);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java` around lines 187 - 213, The JSON is written via an OutputStreamWriter without specifying charset in getKameletsEntry, causing platform-dependent byte output and unstable Util.generateHash results; update the writer creation to use UTF-8 (e.g., create OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) so the bytes written (and therefore the generated hash/filename) are deterministic across environments and ensure any imports (StandardCharsets) are added.src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGeneratorBuilder.java (1)
85-91: Consider adding null validation in the build() method for required fields.The
runtime,outputDirectory, andcatalogVersionparameters can currently be null, causing failures later inCamelCatalogVersionLoaderorCamelCatalogGeneratorwith less context. While all current usages correctly set these fields, adding precondition checks makes intent explicit and prevents subtle bugs.Suggested guardrails
+import java.util.Objects; @@ `@Override` public CatalogGenerator build() { + Objects.requireNonNull(runtime, "runtime"); + Objects.requireNonNull(catalogVersion, "catalogVersion"); + Objects.requireNonNull(outputDirectory, "outputDirectory"); var catalogGenerator = new CamelCatalogGenerator(new CamelCatalogVersionLoader(runtime, verbose), outputDirectory); catalogGenerator.setCamelCatalogVersion(catalogVersion); catalogGenerator.setKameletsVersion(kameletsVersion); catalogGenerator.setCamelKCRDsVersion(camelKCRDsVersion); return catalogGenerator; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGeneratorBuilder.java` around lines 85 - 91, The build() method can pass null runtime, outputDirectory, or catalogVersion into CamelCatalogVersionLoader/CamelCatalogGenerator causing later confusing failures; add explicit null checks at the start of build() (e.g., guard against runtime, outputDirectory, and catalogVersion being null) and throw an IllegalStateException or NullPointerException with a clear message identifying the missing field; reference the build() method and ensure the check runs before constructing new CamelCatalogGenerator or CamelCatalogVersionLoader so errors fail fast and give context.src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandTest.java (1)
102-142: Add a test for the Citrus runtime path. GenerateCommand now selects builders by runtime (lines 47-61), with Citrus using CitrusCatalogGeneratorBuilder instead of CamelCatalogGeneratorBuilder. The current tests cover only the Main runtime branch. A Citrus-specific test would verify the correct builder is instantiated, the expected method calls are made, and output folder conventions are correct, preventing regressions in the Citrus path.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandTest.java` around lines 102 - 142, Add a new unit test mirroring the existing Main-runtime test but exercise the Citrus branch: construct a test that sets the GenerateCommand to use CatalogRuntime.CITRUS, mockConstruction(CitrusCatalogGeneratorBuilder.class) (similar to how CamelCatalogGeneratorBuilder is mocked) to return a mocked CitrusCatalogGenerator whose generate() returns the catalogDefinition, and mockConstruction(CatalogLibrary.class) the same way; call generateCommand.run(), assert the Citrus builder was constructed, the CatalogLibrary was created once, the created CatalogLibraryEntry has runtime "Citrus" and the name/version match the fixture, and assert the fileName uses forward slashes and the expected Citrus output folder pattern (e.g., contains "/4.8.0/index.json" and includes the citrus runtime folder segment).src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.java (1)
57-66: Create parent directories when writing resources.If any resource name includes subfolders (e.g., from nested JAR entries), the write will fail with
NoSuchFileExceptionsince parent directories aren't created automatically. WhileFiles.writeString()already uses UTF‑8 by default, making it explicit improves clarity.🛠️ Proposed fix
catalogFiles.forEach((k, v) -> { try { + var outputPath = Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix); + Files.createDirectories(outputPath.getParent()); - Files.writeString(Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix), v); + Files.writeString(outputPath, v, StandardCharsets.UTF_8); } catch (IOException e) { throw new RuntimeException(e); } });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.java` around lines 57 - 66, loadAndWriteCatalogFiles currently writes each entry from catalogFiles directly with Files.writeString which fails for nested paths; update the loop in loadAndWriteCatalogFiles to ensure parent directories exist before writing (create parent dirs for Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix).getParent()) and call Files.writeString with an explicit charset (StandardCharsets.UTF_8) when writing the value v; keep using resourceLoader and catalogFiles as before and wrap IOExceptions as currently done.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/io/kaoto/camelcatalog/commands/GenerateCommandOptions.java`:
- Line 78: The Citrus option description and the default-version handling are
inconsistent: update either the description or add Citrus default handling in
addDefaultVersions so behavior matches other runtimes. Option A: modify the
citrusVersionOption description to state that Citrus will be skipped when not
specified (instead of claiming it uses the generator installed version). Option
B: add Citrus fallback logic in addDefaultVersions (similar to
Main/Quarkus/SpringBoot) so addDefaultVersions assigns a default version for
CatalogRuntime.Citrus when none provided; locate references to
addRuntimeVersions, addDefaultVersions, citrusVersionOption and
CatalogRuntime.Citrus to implement the chosen change.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`:
- Around line 166-185: In CamelCatalogGenerator, both processKameletBoundaries
and processKamelets call getKameletsEntry and assume a non-null
CatalogDefinitionEntry; update each method to null-check the returned value from
getKameletsEntry(), and if it is null log a clear error via LOGGER (include
context like the kamelet type) and skip adding to index.getCatalogs() (or return
early) to avoid NullPointerException; do this for the call in
processKameletBoundaries (KAMELET_BOUNDARIES_KEY/KAMELET_BOUNDARIES_FILENAME)
and the call in processKamelets (KAMELETS/KAMELETS_AGGREGATE).
- Around line 57-95: The generate() method currently logs exceptions and returns
null which leads to NPEs downstream; instead, catch the Exception, log useful
context (including camelCatalogVersion and runtime label from
camelCatalogVersionLoader.getRuntime().getLabel()) and rethrow an unchecked
exception (e.g., IllegalStateException) with a clear message so callers (like
GenerateCommand) fail fast; remove the final return null and ensure the thrown
exception wraps the original (use e as the cause) and references key symbols
catalogDefinition, outputDirectory, jsonMapper, and Util.generateHash for
context.
---
Nitpick comments:
In `@index.js`:
- Around line 1-15: Update the file header comment to mention that the module
exports versions for Camel catalogs, Kamelets, and Citrus; replace or augment
the phrase "Camel catalogs and Kamelets" with something like "Camel catalogs,
Kamelets, and Citrus" and add a short note that Citrus catalog versions are
sourced from Maven Central directly (not via Camel JBang). Keep the existing
Camel JBang command examples for Main/Quarkus/Spring Boot but add one sentence
clarifying the different source for Citrus versions so readers know to fetch
Citrus from Maven Central rather than using the Camel JBang commands.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`:
- Around line 187-213: The JSON is written via an OutputStreamWriter without
specifying charset in getKameletsEntry, causing platform-dependent byte output
and unstable Util.generateHash results; update the writer creation to use UTF-8
(e.g., create OutputStreamWriter(outputStream, StandardCharsets.UTF_8)) so the
bytes written (and therefore the generated hash/filename) are deterministic
across environments and ensure any imports (StandardCharsets) are added.
In
`@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGeneratorBuilder.java`:
- Around line 85-91: The build() method can pass null runtime, outputDirectory,
or catalogVersion into CamelCatalogVersionLoader/CamelCatalogGenerator causing
later confusing failures; add explicit null checks at the start of build()
(e.g., guard against runtime, outputDirectory, and catalogVersion being null)
and throw an IllegalStateException or NullPointerException with a clear message
identifying the missing field; reference the build() method and ensure the check
runs before constructing new CamelCatalogGenerator or CamelCatalogVersionLoader
so errors fail fast and give context.
In `@src/main/java/io/kaoto/camelcatalog/generator/CatalogGenerator.java`:
- Around line 1-9: The file is missing the project's Apache License 2.0 header;
add the same Apache License 2.0 comment block used across the codebase
(identical to other files like CatalogRuntime) at the very top of the file,
above the package declaration, so the CatalogGenerator interface (and its
generate() method returning CatalogDefinition) has the consistent license header
as other source files.
In `@src/main/java/io/kaoto/camelcatalog/generator/CatalogGeneratorBuilder.java`:
- Around line 1-7: Add the standard Apache License 2.0 file header to this
source so it matches other files in the repo; update the top of the
CatalogGeneratorBuilder interface (the CatalogGeneratorBuilder type and its
build() method declaration) to include the same multi-line Apache License 2.0
comment block used elsewhere in the project before the package declaration.
In
`@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.java`:
- Around line 57-66: loadAndWriteCatalogFiles currently writes each entry from
catalogFiles directly with Files.writeString which fails for nested paths;
update the loop in loadAndWriteCatalogFiles to ensure parent directories exist
before writing (create parent dirs for
Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix).getParent()) and
call Files.writeString with an explicit charset (StandardCharsets.UTF_8) when
writing the value v; keep using resourceLoader and catalogFiles as before and
wrap IOExceptions as currently done.
In
`@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGeneratorBuilder.java`:
- Around line 1-33: Add the missing Apache License 2.0 file header at the top of
the new CitrusCatalogGeneratorBuilder class file: insert the exact license block
used across the project (including copyright line, year and owner) above the
package declaration so it matches other files in the codebase; ensure the header
appears before "package io.kaoto.camelcatalog.generator.citrus;" and keep the
rest of the CitrusCatalogGeneratorBuilder class unchanged.
- Around line 29-32: The build() method should validate required fields before
constructing CitrusCatalogGenerator: check that catalogVersion is not null/empty
and outputDirectory is not null (and optionally that outputDirectory.exists() &&
outputDirectory.isDirectory()), and if any validation fails throw an
IllegalStateException or IllegalArgumentException with a clear message; update
the CitrusCatalogGeneratorBuilder.build() to perform these checks on
catalogVersion and outputDirectory before calling new
CitrusCatalogGenerator(catalogVersion, outputDirectory, verbose).
In
`@src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandOptionsTest.java`:
- Around line 68-76: Add an explicit assertion that the Citrus version was
parsed and stored: in GenerateCommandOptionsTest, after configure(args) add an
assertion that configBean contains "citrusVersion" (e.g.
assertTrue(configBean.getCatalogVersionSet().contains("citrusVersion")) or, if
available, assertEquals("citrusVersion", configBean.getCitrusVersion())); this
ensures the Citrus option is verified alongside getCatalogsName(),
getKameletsVersion(), and getCatalogVersionSet().
In `@src/test/java/io/kaoto/camelcatalog/commands/GenerateCommandTest.java`:
- Around line 102-142: Add a new unit test mirroring the existing Main-runtime
test but exercise the Citrus branch: construct a test that sets the
GenerateCommand to use CatalogRuntime.CITRUS,
mockConstruction(CitrusCatalogGeneratorBuilder.class) (similar to how
CamelCatalogGeneratorBuilder is mocked) to return a mocked
CitrusCatalogGenerator whose generate() returns the catalogDefinition, and
mockConstruction(CatalogLibrary.class) the same way; call generateCommand.run(),
assert the Citrus builder was constructed, the CatalogLibrary was created once,
the created CatalogLibraryEntry has runtime "Citrus" and the name/version match
the fixture, and assert the fileName uses forward slashes and the expected
Citrus output folder pattern (e.g., contains "/4.8.0/index.json" and includes
the citrus runtime folder segment).
| addRuntimeVersions(configBean, cmd, camelMainVersionOption, CatalogRuntime.Main); | ||
| addRuntimeVersions(configBean, cmd, camelQuarkusVersionOption, CatalogRuntime.Quarkus); | ||
| addRuntimeVersions(configBean, cmd, camelSpringbootVersionOption, CatalogRuntime.SpringBoot); | ||
| addRuntimeVersions(configBean, cmd, citrusVersionOption, CatalogRuntime.Citrus); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's look at the GenerateCommandOptions.java file to understand the structure
cat -n src/main/java/io/kaoto/camelcatalog/commands/GenerateCommandOptions.java | head -150Repository: KaotoIO/camel-catalog
Length of output: 7022
🏁 Script executed:
# Search for the addDefaultVersions method and how it handles different runtimes
rg -n "addDefaultVersions" --type java -A 30Repository: KaotoIO/camel-catalog
Length of output: 4961
🏁 Script executed:
# Check how Citrus is referenced in the codebase
rg -n "Citrus" --type java -B 2 -A 2Repository: KaotoIO/camel-catalog
Length of output: 9382
🏁 Script executed:
# Look for addRuntimeVersions to understand the pattern
rg -n "addRuntimeVersions" --type java -B 1 -A 3Repository: KaotoIO/camel-catalog
Length of output: 1765
Update Citrus description or add default version handling to match other runtimes.
The Citrus option description states "If not specified, it will use the generator installed version," but addDefaultVersions() (lines 100-118) provides no fallback for Citrus—only for Main, Quarkus, and SpringBoot. Either add Citrus default version handling or update the description to clarify that Citrus catalog generation is skipped when not explicitly specified.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/commands/GenerateCommandOptions.java` at
line 78, The Citrus option description and the default-version handling are
inconsistent: update either the description or add Citrus default handling in
addDefaultVersions so behavior matches other runtimes. Option A: modify the
citrusVersionOption description to state that Citrus will be skipped when not
specified (instead of claiming it uses the generator installed version). Option
B: add Citrus fallback logic in addDefaultVersions (similar to
Main/Quarkus/SpringBoot) so addDefaultVersions assigns a default version for
CatalogRuntime.Citrus when none provided; locate references to
addRuntimeVersions, addDefaultVersions, citrusVersionOption and
CatalogRuntime.Citrus to implement the chosen change.
| @Override | ||
| public CatalogDefinition generate() { | ||
| camelCatalogVersionLoader.loadKameletBoundaries(); | ||
| camelCatalogVersionLoader.loadCamelCatalog(camelCatalogVersion); | ||
| camelCatalogVersionLoader.loadKamelets(kameletsVersion); | ||
| camelCatalogVersionLoader.loadKubernetesSchema(); | ||
| camelCatalogVersionLoader.loadCamelKCRDs(camelKCRDsVersion); | ||
| camelCatalogVersionLoader.loadLocalSchemas(); | ||
| camelCatalogVersionLoader.loadKaotoPatterns(); | ||
| camelCatalogVersionLoader.loadCamelYamlDsl(camelCatalogVersion); | ||
|
|
||
| var catalogDefinition = new CatalogDefinition(); | ||
| var yamlDslSchemaProcessor = processCamelSchema(); | ||
| processCatalog(yamlDslSchemaProcessor, catalogDefinition); | ||
| processKameletBoundaries(catalogDefinition); | ||
| processKamelets(catalogDefinition); | ||
| processSchemas(catalogDefinition); | ||
|
|
||
| try { | ||
| catalogDefinition | ||
| .setName("Camel " + camelCatalogVersionLoader.getRuntime().getLabel() + " " + camelCatalogVersion); | ||
| catalogDefinition.setVersion(camelCatalogVersion); | ||
| catalogDefinition.setRuntime(camelCatalogVersionLoader.getRuntime()); | ||
|
|
||
| String content = jsonMapper.writeValueAsString(catalogDefinition); | ||
| String filename = String.format("%s-%s.json", "index", | ||
| Util.generateHash(content)); | ||
|
|
||
| File indexFile = outputDirectory.toPath().resolve(filename).toFile(); | ||
| catalogDefinition.setFileName(indexFile.getName()); | ||
|
|
||
| Util.createTabWriter(jsonMapper).writeValue(indexFile, catalogDefinition); | ||
|
|
||
| return catalogDefinition; | ||
| } catch (Exception e) { | ||
| LOGGER.log(Level.SEVERE, e.toString(), e); | ||
| } | ||
|
|
||
| return null; |
There was a problem hiding this comment.
Don’t return null from generate()—fail fast with context.
The method logs and returns null, which will be dereferenced downstream (e.g., in GenerateCommand). Prefer throwing a clear exception so failures are explicit.
💥 Suggested failure handling
- } catch (Exception e) {
- LOGGER.log(Level.SEVERE, e.toString(), e);
- }
-
- return null;
+ } catch (Exception e) {
+ throw new RuntimeException("Error generating Camel catalog", e);
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`
around lines 57 - 95, The generate() method currently logs exceptions and
returns null which leads to NPEs downstream; instead, catch the Exception, log
useful context (including camelCatalogVersion and runtime label from
camelCatalogVersionLoader.getRuntime().getLabel()) and rethrow an unchecked
exception (e.g., IllegalStateException) with a clear message so callers (like
GenerateCommand) fail fast; remove the final return null and ensure the thrown
exception wraps the original (use e as the cause) and references key symbols
catalogDefinition, outputDirectory, jsonMapper, and Util.generateHash for
context.
| private void processKameletBoundaries(CatalogDefinition index) { | ||
| if (camelCatalogVersionLoader.getKameletBoundaries().isEmpty()) { | ||
| LOGGER.severe("Kamelet boundaries are not loaded"); | ||
| return; | ||
| } | ||
|
|
||
| var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKameletBoundaries(), KAMELET_BOUNDARIES_KEY, | ||
| KAMELET_BOUNDARIES_FILENAME, "Aggregated Kamelet boundaries in JSON"); | ||
| index.getCatalogs().put(indexEntry.name(), indexEntry); | ||
| } | ||
|
|
||
| private void processKamelets(CatalogDefinition index) { | ||
| if (camelCatalogVersionLoader.getKamelets().isEmpty()) { | ||
| LOGGER.severe("Kamelets are not loaded"); | ||
| } | ||
|
|
||
| var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKamelets(), KAMELETS, KAMELETS_AGGREGATE, | ||
| "Aggregated Kamelets in JSON"); | ||
| index.getCatalogs().put(indexEntry.name(), indexEntry); | ||
| } |
There was a problem hiding this comment.
Guard against null CatalogDefinitionEntry results.
getKameletsEntry() returns null on errors, but callers unconditionally dereference it, which can throw NullPointerException and hide the root cause.
🛡️ Null-safe handling
- var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKameletBoundaries(), KAMELET_BOUNDARIES_KEY,
- KAMELET_BOUNDARIES_FILENAME, "Aggregated Kamelet boundaries in JSON");
- index.getCatalogs().put(indexEntry.name(), indexEntry);
+ var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKameletBoundaries(), KAMELET_BOUNDARIES_KEY,
+ KAMELET_BOUNDARIES_FILENAME, "Aggregated Kamelet boundaries in JSON");
+ if (indexEntry != null) {
+ index.getCatalogs().put(indexEntry.name(), indexEntry);
+ } else {
+ LOGGER.severe("Failed to generate kamelet boundaries entry");
+ }
@@
- var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKamelets(), KAMELETS, KAMELETS_AGGREGATE,
- "Aggregated Kamelets in JSON");
- index.getCatalogs().put(indexEntry.name(), indexEntry);
+ var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKamelets(), KAMELETS, KAMELETS_AGGREGATE,
+ "Aggregated Kamelets in JSON");
+ if (indexEntry != null) {
+ index.getCatalogs().put(indexEntry.name(), indexEntry);
+ } else {
+ LOGGER.severe("Failed to generate kamelets entry");
+ }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`
around lines 166 - 185, In CamelCatalogGenerator, both processKameletBoundaries
and processKamelets call getKameletsEntry and assume a non-null
CatalogDefinitionEntry; update each method to null-check the returned value from
getKameletsEntry(), and if it is null log a clear error via LOGGER (include
context like the kamelet type) and skip adding to index.getCatalogs() (or return
early) to avoid NullPointerException; do this for the call in
processKameletBoundaries (KAMELET_BOUNDARIES_KEY/KAMELET_BOUNDARIES_FILENAME)
and the call in processKamelets (KAMELETS/KAMELETS_AGGREGATE).
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java (1)
235-249: Comparator exception handling can violate contract and cause non-deterministic ordering.Returning
0on exception (line 247) violates the transitivity requirement ofComparator, potentially causing inconsistent sort results across runs. Additionally, each kamelet YAML is parsed twice—once here for sorting and again inprocessKameletFile—which is inefficient.Consider parsing once and sorting the parsed objects, or at minimum, re-throw the exception wrapped in
RuntimeExceptionto fail fast rather than produce non-deterministic output.♻️ Alternative: parse once and sort by extracted name
private List<String> sortKamelets(List<String> kamelets) { record KameletWithName(String content, String name) {} return kamelets.stream() .map(k -> { try { JsonNode node = yamlMapper.readTree(k); String name = node.get("metadata").get("name").asText().toLowerCase(); return new KameletWithName(k, name); } catch (Exception e) { throw new RuntimeException("Failed to parse kamelet for sorting", e); } }) .sorted(Comparator.comparing(KameletWithName::name)) .map(KameletWithName::content) .toList(); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java` around lines 235 - 249, The comparator in sortKamelets swallows parse exceptions and returns 0 which breaks Comparator contracts and leads to non-deterministic sorting; change sortKamelets to parse each YAML once, extract the metadata.name (use yamlMapper.readTree) into a small holder (e.g., record or pair) alongside the original content, throw a RuntimeException if parsing fails instead of returning 0, then sort the holders by the extracted lowercase name and map back to the original String content; reference the method sortKamelets, yamlMapper.readTree, and the metadata.name extraction to locate where to implement this.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`:
- Around line 69-70: processCamelSchema() can return null and passing it
directly into processCatalog(yamlDslSchemaProcessor, catalogDefinition) risks an
NPE inside CamelCatalogProcessor; add a null guard after calling
processCamelSchema() (check yamlDslSchemaProcessor) and handle the failure path
(e.g., log an error via the existing logger and return/abort the generation or
throw a clear exception) instead of calling processCatalog when
yamlDslSchemaProcessor is null so CamelCatalogProcessor never receives a null
schema.
- Around line 223-233: In processKameletFile, avoid the chained
.get("metadata").get("name") call on the node returned by
yamlMapper.readTree(kamelet); first validate that kameletNode != null and that
kameletNode.has("metadata") and kameletNode.get("metadata").has("name") (or
check for non-null JsonNode objects) before calling .asText(); if either is
missing, log a clear SEVERE message via LOGGER including a short identifier
(e.g. the missing field and a snippet of the kamelet string) and skip
processing, otherwise proceed to call KameletProcessor.process((ObjectNode)
kameletNode) and targetObject.putIfAbsent(lowerFileName, kameletNode).
- Around line 177-185: The method processKamelets logs when
camelCatalogVersionLoader.getKamelets() is empty but continues and may call
getKameletsEntry() and insert a null/empty value into index; update
processKamelets to return early after logging if
camelCatalogVersionLoader.getKamelets().isEmpty(), and after calling
getKameletsEntry(...) check the resulting indexEntry (CatalogDefinition
indexEntry) for null before calling index.getCatalogs().put(indexEntry.name(),
indexEntry) to avoid inserting nulls.
In
`@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.java`:
- Around line 39-66: The write path in loadAndWriteCatalogFiles may fail because
parent directories from resourceLoader.loadResourcesFromFolderAsString are not
created; before calling Files.writeString(Paths.get(...)) ensure the parent
directory exists (e.g., resolve the target Path, call
Files.createDirectories(path.getParent()) when non-null). Also avoid hardcoding
catalogDefinition.setFileName("index.json") in generate: after
loadAndWriteCatalogFiles runs, verify that "index.json" exists under
outputDirectory and otherwise set the fileName to a valid generated file (for
example the first key written + suffix) so setFileName reflects an actually
produced file. Use the existing symbols loadAndWriteCatalogFiles, generate,
resourceLoader.loadResourcesFromFolderAsString, outputDirectory, and
catalogDefinition.setFileName to locate and apply changes.
---
Duplicate comments:
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`:
- Around line 166-175: processKameletBoundaries unconditionally dereferences the
result of getKameletsEntry into indexEntry which can be null; change
processKameletBoundaries to check whether the CatalogDefinitionEntry returned by
getKameletsEntry (indexEntry) is null before calling
index.getCatalogs().put(...), and if null log an error (using LOGGER.severe or
similar) and return (or otherwise handle the error path) so you avoid a
NullPointerException; reference getKameletsEntry, processKameletBoundaries,
indexEntry, and index.getCatalogs().put when making the fix.
- Around line 57-96: The generate() method currently catches all exceptions,
logs them and returns null which leads to downstream NPEs; instead, rethrow a
meaningful unchecked exception after logging so callers like GenerateCommand can
fail fast. In the catch(Exception e) block of CamelCatalogGenerator.generate(),
build and throw a RuntimeException (or a custom IllegalStateException) that
includes contextual info (e.g. camelCatalogVersion and
camelCatalogVersionLoader.getRuntime().getLabel()) and chain the original
exception (pass e as the cause) so the stacktrace is preserved; remove the final
"return null" path so generate() never silently returns null on failure.
---
Nitpick comments:
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`:
- Around line 235-249: The comparator in sortKamelets swallows parse exceptions
and returns 0 which breaks Comparator contracts and leads to non-deterministic
sorting; change sortKamelets to parse each YAML once, extract the metadata.name
(use yamlMapper.readTree) into a small holder (e.g., record or pair) alongside
the original content, throw a RuntimeException if parsing fails instead of
returning 0, then sort the holders by the extracted lowercase name and map back
to the original String content; reference the method sortKamelets,
yamlMapper.readTree, and the metadata.name extraction to locate where to
implement this.
| var yamlDslSchemaProcessor = processCamelSchema(); | ||
| processCatalog(yamlDslSchemaProcessor, catalogDefinition); |
There was a problem hiding this comment.
Guard against null yamlDslSchemaProcessor before passing to processCatalog.
processCamelSchema() returns null on failure (lines 125, 136). The null value is then passed directly to processCatalog(), which forwards it to CamelCatalogProcessor. This will likely cause an NPE inside CamelCatalogProcessor if the schema loading fails.
🛡️ Suggested null check
var catalogDefinition = new CatalogDefinition();
var yamlDslSchemaProcessor = processCamelSchema();
+ if (yamlDslSchemaProcessor == null) {
+ throw new IllegalStateException("Failed to process Camel YAML DSL schema");
+ }
processCatalog(yamlDslSchemaProcessor, catalogDefinition);📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| var yamlDslSchemaProcessor = processCamelSchema(); | |
| processCatalog(yamlDslSchemaProcessor, catalogDefinition); | |
| var yamlDslSchemaProcessor = processCamelSchema(); | |
| if (yamlDslSchemaProcessor == null) { | |
| throw new IllegalStateException("Failed to process Camel YAML DSL schema"); | |
| } | |
| processCatalog(yamlDslSchemaProcessor, catalogDefinition); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`
around lines 69 - 70, processCamelSchema() can return null and passing it
directly into processCatalog(yamlDslSchemaProcessor, catalogDefinition) risks an
NPE inside CamelCatalogProcessor; add a null guard after calling
processCamelSchema() (check yamlDslSchemaProcessor) and handle the failure path
(e.g., log an error via the existing logger and return/abort the generation or
throw a clear exception) instead of calling processCatalog when
yamlDslSchemaProcessor is null so CamelCatalogProcessor never receives a null
schema.
| private void processKamelets(CatalogDefinition index) { | ||
| if (camelCatalogVersionLoader.getKamelets().isEmpty()) { | ||
| LOGGER.severe("Kamelets are not loaded"); | ||
| } | ||
|
|
||
| var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKamelets(), KAMELETS, KAMELETS_AGGREGATE, | ||
| "Aggregated Kamelets in JSON"); | ||
| index.getCatalogs().put(indexEntry.name(), indexEntry); | ||
| } |
There was a problem hiding this comment.
Missing early return when kamelets are empty.
Unlike processKameletBoundaries() (line 169), this method logs the error but continues processing. This will call getKameletsEntry() with an empty list, likely resulting in an empty or null entry being added to the index. Additionally, the null guard issue for indexEntry from a prior review remains unaddressed.
🐛 Proposed fix: add early return and null guard
private void processKamelets(CatalogDefinition index) {
if (camelCatalogVersionLoader.getKamelets().isEmpty()) {
LOGGER.severe("Kamelets are not loaded");
+ return;
}
var indexEntry = getKameletsEntry(camelCatalogVersionLoader.getKamelets(), KAMELETS, KAMELETS_AGGREGATE,
"Aggregated Kamelets in JSON");
- index.getCatalogs().put(indexEntry.name(), indexEntry);
+ if (indexEntry != null) {
+ index.getCatalogs().put(indexEntry.name(), indexEntry);
+ } else {
+ LOGGER.severe("Failed to generate kamelets entry");
+ }
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`
around lines 177 - 185, The method processKamelets logs when
camelCatalogVersionLoader.getKamelets() is empty but continues and may call
getKameletsEntry() and insert a null/empty value into index; update
processKamelets to return early after logging if
camelCatalogVersionLoader.getKamelets().isEmpty(), and after calling
getKameletsEntry(...) check the resulting indexEntry (CatalogDefinition
indexEntry) for null before calling index.getCatalogs().put(indexEntry.name(),
indexEntry) to avoid inserting nulls.
| private void processKameletFile(String kamelet, ObjectNode targetObject) { | ||
| try { | ||
| JsonNode kameletNode = yamlMapper.readTree(kamelet); | ||
| String lowerFileName = kameletNode.get("metadata").get("name").asText().toLowerCase(); | ||
|
|
||
| KameletProcessor.process((ObjectNode) kameletNode); | ||
| targetObject.putIfAbsent(lowerFileName, kameletNode); | ||
| } catch (Exception e) { | ||
| LOGGER.log(Level.SEVERE, e.toString(), e); | ||
| } | ||
| } |
There was a problem hiding this comment.
Potential NPE on malformed kamelet YAML.
Line 226 chains .get("metadata").get("name") without null checks. If a kamelet file is missing the metadata field, this will throw NullPointerException that's caught and logged, but silently drops the kamelet from output without clear indication of which file failed.
🛡️ Suggested defensive check
private void processKameletFile(String kamelet, ObjectNode targetObject) {
try {
JsonNode kameletNode = yamlMapper.readTree(kamelet);
- String lowerFileName = kameletNode.get("metadata").get("name").asText().toLowerCase();
+ JsonNode metadata = kameletNode.get("metadata");
+ if (metadata == null || metadata.get("name") == null) {
+ LOGGER.warning("Skipping kamelet with missing metadata or name");
+ return;
+ }
+ String lowerFileName = metadata.get("name").asText().toLowerCase();
KameletProcessor.process((ObjectNode) kameletNode);
targetObject.putIfAbsent(lowerFileName, kameletNode);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/main/java/io/kaoto/camelcatalog/generator/CamelCatalogGenerator.java`
around lines 223 - 233, In processKameletFile, avoid the chained
.get("metadata").get("name") call on the node returned by
yamlMapper.readTree(kamelet); first validate that kameletNode != null and that
kameletNode.has("metadata") and kameletNode.get("metadata").has("name") (or
check for non-null JsonNode objects) before calling .asText(); if either is
missing, log a clear SEVERE message via LOGGER including a short identifier
(e.g. the missing field and a snippet of the kamelet string) and skip
processing, otherwise proceed to call KameletProcessor.process((ObjectNode)
kameletNode) and targetObject.putIfAbsent(lowerFileName, kameletNode).
| @Override | ||
| public CatalogDefinition generate() { | ||
| String gav = String.format("org.citrusframework:citrus-catalog-schema:%s", catalogVersion); | ||
| boolean shouldFetchTransitive = false; | ||
| boolean shouldUseSnapshots = catalogVersion.endsWith("SNAPSHOT"); | ||
|
|
||
| kaotoMavenVersionManager.resolve(gav, shouldUseSnapshots, shouldFetchTransitive); | ||
| loadAndWriteCatalogFiles(".json"); | ||
| loadAndWriteCatalogFiles(".xsd"); | ||
|
|
||
| var catalogDefinition = new CatalogDefinition(); | ||
| catalogDefinition.setName("Citrus " + catalogVersion); | ||
| catalogDefinition.setRuntime(CatalogRuntime.Citrus); | ||
| catalogDefinition.setVersion(catalogVersion); | ||
| catalogDefinition.setFileName("index.json"); | ||
| return catalogDefinition; | ||
| } | ||
|
|
||
| private void loadAndWriteCatalogFiles(String fileSuffix) { | ||
| final Map<String, String> catalogFiles = new HashMap<>(); | ||
| resourceLoader.loadResourcesFromFolderAsString("org/citrusframework/schema/citrus/%s".formatted(catalogVersion), catalogFiles, fileSuffix); | ||
| catalogFiles.forEach((k, v) -> { | ||
| try { | ||
| Files.writeString(Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix), v); | ||
| } catch (IOException e) { | ||
| throw new RuntimeException(e); | ||
| } | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
python - <<'PY'
import io, zipfile, urllib.request
version = "4.9.2"
url = f"https://repo1.maven.org/maven2/org/citrusframework/citrus-catalog-schema/{version}/citrus-catalog-schema-{version}.jar"
try:
data = urllib.request.urlopen(url).read()
z = zipfile.ZipFile(io.BytesIO(data))
prefix = f"org/citrusframework/schema/citrus/{version}/"
entries = [e for e in z.namelist() if e.startswith(prefix) and not e.endswith('/')]
print("=== Citrus Catalog Schema JAR Analysis ===")
print(f"Total entries in {prefix}: {len(entries)}")
# Check for index.json
has_index = any(e == prefix + "index.json" for e in entries)
print(f"Has index.json: {has_index}")
# Check for nested entries
nested = [e for e in entries if "/" in e[len(prefix):]]
print(f"Nested entries count: {len(nested)}")
if nested:
print("Nested entries (first 15):")
for e in nested[:15]:
print(f" {e}")
# Show all entries if count is small
if len(entries) <= 20:
print("\nAll entries:")
for e in entries:
print(f" {e}")
except Exception as e:
print(f"Error: {e}")
import traceback
traceback.print_exc()
PYRepository: KaotoIO/camel-catalog
Length of output: 2798
🏁 Script executed:
find . -type f -name "*CitrusCatalogGenerator*" | head -20Repository: KaotoIO/camel-catalog
Length of output: 237
🏁 Script executed:
find . -type f -name "*.java" | xargs grep -l "CitrusCatalogGenerator" | head -10Repository: KaotoIO/camel-catalog
Length of output: 305
🏁 Script executed:
find . -type f \( -name "*.md" -o -name "*.txt" \) | xargs grep -l -i "citrus" 2>/dev/null | head -10Repository: KaotoIO/camel-catalog
Length of output: 47
🏁 Script executed:
# Check for test files and look for citrus-catalog-schema references
rg "citrus-catalog-schema" --type java -B 2 -A 2Repository: KaotoIO/camel-catalog
Length of output: 1331
🏁 Script executed:
# Find the test directory structure
find . -type d -name "test" | head -5Repository: KaotoIO/camel-catalog
Length of output: 207
🏁 Script executed:
# Read the full CitrusCatalogGenerator.java file
cat -n ./src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.javaRepository: KaotoIO/camel-catalog
Length of output: 3358
🏁 Script executed:
# Check for test files related to Citrus generator
find . -path "*/test/*" -name "*Citrus*" -type fRepository: KaotoIO/camel-catalog
Length of output: 47
🏁 Script executed:
# Look at ResourceLoader to understand how it loads resources
find . -type f -name "ResourceLoader.java"Repository: KaotoIO/camel-catalog
Length of output: 128
🏁 Script executed:
# Check how other generators handle file writing
find . -path "*/generator/*" -name "*.java" -type f | grep -v Citrus | head -5Repository: KaotoIO/camel-catalog
Length of output: 457
🏁 Script executed:
# Look at the structure of the project to understand if there are any integration tests or fixtures
ls -la ./src/test/Repository: KaotoIO/camel-catalog
Length of output: 284
🏁 Script executed:
# Read ResourceLoader.java to understand resource loading
cat -n ./src/main/java/io/kaoto/camelcatalog/maven/ResourceLoader.javaRepository: KaotoIO/camel-catalog
Length of output: 6456
🏁 Script executed:
# Check test files for any Citrus-related tests
ls -la ./src/test/java/Repository: KaotoIO/camel-catalog
Length of output: 221
🏁 Script executed:
# Look for any test resources
ls -la ./src/test/resources/Repository: KaotoIO/camel-catalog
Length of output: 572
Create parent directories when writing catalog files to prevent NoSuchFileException with nested resources.
The loadResourcesFromFolderAsString method in ResourceLoader preserves nested paths from the JAR; if the Citrus schema JAR contains resources in subdirectories (e.g., schemas/file.json), the file write at line 62 will fail because parent directories are not created. Additionally, catalogDefinition.setFileName("index.json") is hardcoded without validating the file was generated.
Suggested hardening
catalogFiles.forEach((k, v) -> {
try {
- Files.writeString(Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix), v);
+ var target = Paths.get(outputDirectory.getAbsolutePath(), k + fileSuffix);
+ Files.createDirectories(target.getParent());
+ Files.writeString(target, v, StandardCharsets.UTF_8);
} catch (IOException e) {
throw new RuntimeException(e);
}
});🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@src/main/java/io/kaoto/camelcatalog/generator/citrus/CitrusCatalogGenerator.java`
around lines 39 - 66, The write path in loadAndWriteCatalogFiles may fail
because parent directories from resourceLoader.loadResourcesFromFolderAsString
are not created; before calling Files.writeString(Paths.get(...)) ensure the
parent directory exists (e.g., resolve the target Path, call
Files.createDirectories(path.getParent()) when non-null). Also avoid hardcoding
catalogDefinition.setFileName("index.json") in generate: after
loadAndWriteCatalogFiles runs, verify that "index.json" exists under
outputDirectory and otherwise set the fileName to a valid generated file (for
example the first key written + suffix) so setFileName reflects an actually
produced file. Use the existing symbols loadAndWriteCatalogFiles, generate,
resourceLoader.loadResourcesFromFolderAsString, outputDirectory, and
catalogDefinition.setFileName to locate and apply changes.
Summary by CodeRabbit
New Features
Chores