Skip to content

CAMEL-25361: camel-couchbase - use the view value as the body and document the fullDocument default - #27588

Merged
davsclaus merged 3 commits into
apache:mainfrom
allthingssecurity:camel-couchbase-view-body-and-fulldocument-default
Oct 9, 2026
Merged

davsclaus merged 3 commits into
apache:mainfrom
allthingssecurity:camel-couchbase-view-body-and-fulldocument-default

Conversation

@allthingssecurity

@allthingssecurity allthingssecurity commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Description

CAMEL-25361

Follow-up to #27347 (CAMEL-25221), with two small defects found while preparing it.

  • View body. With useView=true&fullDocument=false the body was row.valueAs(Object.class), which the Couchbase SDK 3 returns as an Optional, so routes received Optional[value] (or Optional.empty when the view emitted null). The body is now the value, or null when the view emitted none. The SQL++ path is not affected (it uses the row as a JSON string). The code dates from CAMEL-15792 (3.7), so 4.14.x and 4.18.x have it too; as it changes what routes receive, the upgrade guide mentions it (a new ==== useView=true with fullDocument=false subsection of the existing === camel-couchbase section).
  • fullDocument default. The field defaults to true, but @UriParam said defaultValue = "false", so the component JSON, catalog and endpoint DSL documented the wrong default. true is the intended default: it keeps the behaviour from before the option existed (the consumer always fetched the document), and the SQL++ section of the component page already says so. Only the annotation changes; the generated files are regenerated. No runtime change.
  • Upgrade note asked for by @davsclaus in the review of CAMEL-25221: camel-couchbase - remove a document only after its exchange completed #27347, under the existing ==== consumerProcessedStrategy=delete: a view or SQL++ query that returns several rows for one document now delivers only the first of these rows with delete. I checked that against the code: the first row marks the document in progress, the later rows are skipped, and the completed exchange removes the document. In 4.22 the next row of the already removed document failed the whole poll.

Not included: the other suggestion from that review, clearing the in-flight set in doStop/doStart. I said in #27347 that I would add it, but stopRoute/startRoute does not reuse the consumer: RouteService sets the route up again and DefaultRoute.initializeServices calls endpoint.createConsumer, so the set starts empty after a route restart. The set survives only a stop and start of the same consumer instance (for example a route policy that stops and starts the consumer). There, clearing it would let the next poll read again a document whose exchange is still being processed, so I left it as is; happy to clear it in doStart if you prefer bounding the set over that duplicate.

Tests: new CouchbaseConsumerBodyTest (mocked cluster). theValueEmittedByTheViewIsTheBodyWithoutFullDocument and theBodyIsNullWhenTheViewEmitsNoValue fail without the change (two runs; expected: <[{name=Alice}, Bob]> but was: <[Optional[{name=Alice}], Optional[Bob]]>). Two control tests pass with and without it: theQueryRowIsTheBodyWithoutFullDocument (the SQL++ row as a JSON string) and theFullDocumentIsTheBodyByDefault (without the option the consumer fetches the document, which pins the fullDocument=true default). Module: 61 tests, 0 failures (the Docker ITs are skipped).

Target

  • I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

Apache Camel coding standards and style

  • I checked that each commit in the pull request has a meaningful subject line and body.
  • I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.
    (I built and tested the affected module, including the formatter and import-sort plugins, and regenerated the catalog and endpoint DSL files for camel-couchbase. I did not run the full root build.)

AI-assisted contributions

  • If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.
    This PR was prepared with Claude Code (Claude Opus 5.5). The commit carries a Co-Authored-By trailer.

Claude Code on behalf of allthingssecurity

🤖 Generated with Claude Code

…ument the fullDocument default

With useView=true and fullDocument=false the consumer set the message body to
row.valueAs(Object.class), which the Couchbase SDK 3 returns as an Optional, so
the body was Optional[value] (Optional.empty when the view emitted null) instead
of the value. The body is now the value, or null when the view emitted none.
The SQL++ path is not affected: it uses the query row as a JSON string.

The fullDocument option was documented with defaultValue false since it was
added (CAMEL-15792), but the field has always defaulted to true, which keeps
the behaviour from before the option existed (the consumer always fetched the
document). The annotation now says true, and the component JSON, the catalog
and the endpoint DSL are regenerated. No change at runtime.

Upgrade guide: the body change, and, as asked in the review of apache#27347
(CAMEL-25221), that a view or SQL++ query returning several rows for one
document delivers only the first of them with consumerProcessedStrategy=delete.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-couchbase
  • docs
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 9 of 704 tested, 24 compile-only — current: 9 all tested

Maveniverse Scalpel detected 9 affected modules (current approach: 9).

Skip-tests mode would test 9 modules (4 direct + 8 downstream), skip tests for 24 (generated code, meta-modules)

Modules Scalpel would test (9)
  • camel-couchbase ← components/camel-couchbase/src/generated/resources/META-INF/org/apache/camel/component/couchbase/couchbase.json, components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseConsumer.java, components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseEndpoint.java, components/camel-couchbase/src/test/java/org/apache/camel/component/couchbase/CouchbaseConsumerBodyTest.java
  • camel-jbang-mcp ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-mcp ← downstream of org.apache.camel:camel-jbang-core
  • camel-jbang-plugin-route-parser ← downstream of org.apache.camel:camel-route-parser
  • camel-jbang-plugin-tui ← downstream of org.apache.camel:camel-catalog
  • camel-jbang-plugin-validate ← downstream of org.apache.camel:camel-yaml-dsl-validator
  • camel-launcher-container ← downstream of org.apache.camel:camel-launcher
  • camel-yaml-dsl-validator ← downstream of org.apache.camel:camel-catalog
  • camel-yaml-dsl-validator-maven-plugin ← downstream of org.apache.camel:camel-yaml-dsl-validator
Modules with tests skipped (24)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

⚠️ Some tests are disabled on GitHub Actions (@DisabledIfSystemProperty(named = "ci.env.name")) and require manual verification:

  • components/camel-couchbase: 6 test(s) disabled on GitHub Actions
All tested modules (36 modules, 5m 58s total)

Total reactor time: 5m 58s

Module Duration Status
Camel :: Launcher 52.2s SUCCESS
Camel :: JBang :: Plugin :: TUI 44.2s SUCCESS
Camel :: JBang :: MCP 40.2s SUCCESS
Camel :: Component DSL 23.6s SUCCESS
Camel :: Catalog :: Camel Catalog 21.9s SUCCESS
Camel :: Couchbase 21.1s SUCCESS
Camel :: YAML DSL :: Validator 18.6s SUCCESS
Camel :: YAML DSL 17.7s SUCCESS
Camel :: JBang :: Plugin :: Validate 15.2s SUCCESS
Camel :: Docs 14.9s SUCCESS
Camel :: JBang :: Plugin :: Testing 13.6s SUCCESS
Camel :: Kamelet Main 11.5s SUCCESS
Camel :: JBang :: Plugin :: Kubernetes 10.6s SUCCESS
Camel :: YAML DSL :: Deserializers 8.1s SUCCESS
Camel :: Catalog :: Camel Route Parser 7.6s SUCCESS
Camel :: Catalog :: Camel Report Maven Plugin 6.3s SUCCESS
Camel :: All Components Sync point 4.9s SUCCESS
Camel :: YAML DSL :: Validator Maven Plugin 4.9s SUCCESS
Camel :: YAML DSL :: Maven Plugins 3.6s SUCCESS
Camel :: Catalog :: Maven 2.6s SUCCESS
Camel :: Catalog :: Suggest (deprecated) 2.4s SUCCESS
Camel :: JBang :: Plugin :: Edit 1.6s SUCCESS
Camel :: Coverage 1.5s SUCCESS
Camel :: Assembly 1.4s SUCCESS
Camel :: JBang :: Main 1.4s SUCCESS
Camel :: Catalog :: Dummy Component 1.1s SUCCESS
Camel :: Catalog :: Console 1.0s SUCCESS
Camel :: JBang :: Integration tests 1.0s SUCCESS
Camel :: JBang :: Plugin :: Generate 0.9s SUCCESS
Camel :: JBang :: Plugin :: MCP 0.8s SUCCESS
Camel :: Endpoint DSL :: Support 0.8s SUCCESS
Camel :: Launcher :: Container 0.6s SUCCESS
Camel :: JBang :: Plugin :: Route Parser 0.5s SUCCESS
Camel :: Endpoint DSL n/a
Camel :: Integration Tests n/a
Camel :: JBang :: Core n/a

Top 20 slowest modules:

  • Camel :: Launcher (52.2s)
  • Camel :: JBang :: Plugin :: TUI (44.2s)
  • Camel :: JBang :: MCP (40.2s)
  • Camel :: Component DSL (23.6s)
  • Camel :: Catalog :: Camel Catalog (21.9s)
  • Camel :: Couchbase (21.1s)
  • Camel :: YAML DSL :: Validator (18.6s)
  • Camel :: YAML DSL (17.7s)
  • Camel :: JBang :: Plugin :: Validate (15.2s)
  • Camel :: Docs (14.9s)
  • Camel :: JBang :: Plugin :: Testing (13.6s)
  • Camel :: Kamelet Main (11.5s)
  • Camel :: JBang :: Plugin :: Kubernetes (10.6s)
  • Camel :: YAML DSL :: Deserializers (8.1s)
  • Camel :: Catalog :: Camel Route Parser (7.6s)
  • Camel :: Catalog :: Camel Report Maven Plugin (6.3s)
  • Camel :: All Components Sync point (4.9s)
  • Camel :: YAML DSL :: Validator Maven Plugin (4.9s)
  • Camel :: YAML DSL :: Maven Plugins (3.6s)
  • Camel :: Catalog :: Maven (2.6s)

⚙️ View full build and test results

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the fix is right: ViewRow.valueAs(Class) returns Optional<T> in SDK 3, so with useView=true&fullDocument=false the body has been an Optional since CAMEL-15792. The fullDocument annotation also contradicted the field default (true) and the SQL++ docs. Both upgrade-guide entries are accurate: the body change is a real change for routes that unwrapped the Optional, and the multi-row note matches the in-flight skip added in CAMEL-25221. On the in-flight set: agreed, stopRoute/startRoute builds a new consumer, so leaving it as it is makes sense. The tests follow the module's mock-based style. One optional nit inline.

This review was generated by an AI agent (Claude Code on behalf of davsclaus) and may contain inaccuracies. Please verify all suggestions before applying.

…nd the fullDocument default

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@oscerd oscerd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. Both defects independently confirmed.

1. The Optional body is real. I decompiled the SDK actually on the classpath (couchbase-client-version = 3.12.3, parent/pom.xml:140):

public <T> java.util.Optional<T> valueAs(java.lang.Class<T>);

So doc = row.valueAs(Object.class) on main (CouchbaseConsumer.java:256) genuinely set the body to an Optional. The .orElse(null) fix is correct — and the surrounding code already used the right idiom two lines later (row.keyAs(String.class).orElse(null)), which makes this an obvious copy-paste miss rather than intent. git log confirms: the call dates from the SDK-3 migration and no commit has touched it since.

2. The defaultValue mismatch is real. CouchbaseEndpoint.java:152-153 had @UriParam(defaultValue = "false") over private boolean fullDocument = true. The component page already contradicted the annotation — couchbase-component.adoc:119 says "When fullDocument is true (the default)". The annotation was the outlier; changing it to true is documentation-only with no runtime effect, and does not relax a default.

Generated files are complete. All three mirrors of the default are regenerated: component JSON, catalog JSON, endpoint DSL Javadoc. I checked the .adoc pair separately — the prose at :119 and the example at :164 already said true, so no .adoc edit (and therefore no catalog-doc mirror) is due. CI's uncommitted-changes check passing agrees.

Test review. CouchbaseConsumerBodyTest is well-constructed: it drives poll() directly with startScheduler=false instead of racing a scheduler, so no Thread.sleep and no Awaitility needed. theFullDocumentIsTheBodyByDefault is a good regression pin — I verified CouchbaseCollectionOperation.getDocument(...) returns collection.get(id, options), i.e. the GetResult itself (CouchbaseCollectionOperation.java:79-85), so assertSame(document, bodies.get(0)) is sound. JUnit style matches the module (7 test files use org.junit.jupiter.api.Assertions, zero AssertJ).

Non-blocking

[Suggestion] The mocked-ViewRow helper stubs valueAs(Object.class) only. If someone later switches the consumer to valueAs(TypeRef), the mock returns null and the test NPEs rather than failing with a clear message. A lenient() default or a comment pinning the overload would make the intent explicit.

[Question] The PR deliberately does not clear the in-flight set in doStart/doStop, contrary to what was said in #27347. The reasoning (route restart creates a fresh consumer via DefaultRoute.initializeServices → endpoint.createConsumer, so the set is already empty) matches the isInFlight Javadoc at CouchbaseConsumer.java:288+, and clearing it would reintroduce the duplicate-delivery window CAMEL-25221 closed. I agree with leaving it — and it's spelled out in the PR description, so this is already handled transparently. Flagging only so @davsclaus can ack it explicitly.

Checklist: tests ✅ · docs ✅ already correct, verified · upgrade guide ✅ ==== useView=true with fullDocument=false correctly nested under === camel-couchbase · generated catalog ✅ all 3 · commit convention ✅ · public API ⚠️ body-type change, documented as breaking · CI all green ✅


Reviewed with Claude Code (Claude Opus 5) on behalf of @oscerd. This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying. It is a rules-and-conventions review and does not replace CodeRabbit, Sourcery, or SonarCloud.

… overloads in a comment

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@allthingssecurity

Copy link
Copy Markdown
Contributor Author

Thanks @oscerd. Addressed in a048d0b:

  • [Suggestion] mocked ViewRow stubs valueAs(Object.class) only — done as a comment in the viewRow(...) helper. It pins the valueAs(Class)/keyAs(Class) overloads that CouchbaseConsumer calls, and says that a move to valueAs(TypeRef) must be stubbed there too, since an unstubbed call returns null and fails as an NPE, not as a clear assertion. Test-only change.
  • [Question] not clearing the in-flight set in doStart/doStop — no change, as explained in the PR description; that ack is for @davsclaus.

Tests: camel-couchbase install, 61 unit tests, 0 failures (ITs skipped without a container).

Claude Code on behalf of allthingssecurity

@davsclaus davsclaus added this to the 4.23.0 milestone Oct 9, 2026
@davsclaus davsclaus added the bug Something isn't working label Oct 9, 2026
@davsclaus
davsclaus merged commit d613868 into apache:main Oct 9, 2026
6 checks passed
@allthingssecurity
allthingssecurity deleted the camel-couchbase-view-body-and-fulldocument-default branch October 9, 2026 19:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants