Skip to content

Fix contract-first REST DSL 404 when OpenAPI base path is root - #25752

Merged
davsclaus merged 2 commits into
apache:mainfrom
tools400:fix/rest-openapi-root-basepath-404
Aug 27, 2026
Merged

Fix contract-first REST DSL 404 when OpenAPI base path is root#25752
davsclaus merged 2 commits into
apache:mainfrom
tools400:fix/rest-openapi-root-basepath-404

Conversation

@tools400

Copy link
Copy Markdown
Contributor

RestOpenApiHelper.determineBasePath() always falls back to DEFAULT_BASE_PATH ("/") when the OpenAPI servers[0].url has no path segment, so the effective base path is never truly empty. In VertxPlatformHttpConsumer.startRestServicesContractFirst(), the Vert.x route path was built by plain concatenation of basePath + baseUrl ("/" + "/hello" -> "//hello"), which Vert.x normalizes away when matching incoming requests, making every contract-first route permanently unreachable whenever the contract's base path is root.

Normalize the join via a new buildNormalizedEndpoint() helper that strips a trailing slash from the base path before concatenation, used at both call sites (operation routes and the api-specification route).

Add regression tests covering a contract-first spec without a path segment in servers[0].url.

Description

The merge request fixes the problem, that Contract First APIs always return 404, if no base path has been set in servers[0].url.

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.

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.

RestOpenApiHelper.determineBasePath() always falls back to
DEFAULT_BASE_PATH ("/") when the OpenAPI servers[0].url has no path
segment, so the effective base path is never truly empty. In
VertxPlatformHttpConsumer.startRestServicesContractFirst(), the Vert.x
route path was built by plain concatenation of basePath + baseUrl
("/" + "/hello" -> "//hello"), which Vert.x normalises away when
matching incoming requests, making every contract-first route
permanently unreachable whenever the contract's base path is root.

Normalise the join via a new buildNormalizedEndpoint() helper that
strips a trailing slash from the base path before concatenation, used
at both call sites (operation routes and the api-specification route).

Add regression tests covering a contract-first spec without a path
segment in servers[0].url.

Co-authored-by: Claude <noreply@anthropic.com>

@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.

This review focuses on rules/conventions (per project guidelines) and is not a substitute for CodeRabbit, Sourcery, SonarCloud, or similar specialized tools/static analysis.

Thanks for the fix and the clear write-up — the root cause analysis is spot on. I checked out the branch locally and verified it directly: reverting just the buildNormalizedEndpoint change reproduces the exact bug (404 instead of 200, "//pet/{petId}" instead of "/pet/{petId}"), and all 5 tests pass with the fix in place. Good, targeted regression coverage at both the unit level (buildNormalizedEndpoint) and the integration level (actual HTTP request through Vert.x).

Two minor, non-blocking notes below.

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

@github-actions

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 Aug 26, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-platform-http-vertx

🔬 Scalpel shadow comparison — Scalpel: 18 tested, 29 compile-only — current: 18 all tested

Maveniverse Scalpel detected 47 affected modules (current approach: 18).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • 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
  • docs
  • dummy-component

Skip-tests mode would test 18 modules (1 direct + 17 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (18)
  • camel-a2a
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-jsonpath
  • camel-knative-http
  • camel-launcher-container
  • camel-mcp-server
  • camel-micrometer-prometheus
  • camel-oauth
  • camel-observability-services
  • camel-openapi-validator
  • camel-platform-http-main
  • camel-platform-http-vertx
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • 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
  • docs
  • dummy-component

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

All tested modules (47 modules)
  • Camel :: AI :: A2A
  • Camel :: AI :: MCP Server
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: JSon Path
  • Camel :: Kamelet Main
  • Camel :: Knative HTTP
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Micrometer :: Prometheus
  • Camel :: OAuth
  • Camel :: Observability Services
  • Camel :: OpenAPI :: Validator
  • Camel :: Platform HTTP :: Main
  • Camel :: Platform HTTP :: Vert.x
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@gnodet gnodet 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.

Correct fix for a real bug: when the OpenAPI base path is root ("/"), the contract-first REST DSL concatenated "/" + "/hello""//hello", which Vert.x normalized away, making every contract-first route permanently unreachable (404). The fix properly strips the trailing slash from basePath before concatenation via a new buildNormalizedEndpoint() helper, applied consistently to both call sites (operation routes and API specification routes).

Test coverage is solid — both focused unit tests (VertxPlatformHttpConsumerNormalizedEndpointTest) for the normalization logic and an end-to-end integration test (RestOpenApiContractFirstRootBasePathTest) that reproduces the actual 404. CI is green.

Minor observations (non-blocking):

  • The new test files use public on classes/methods and JUnit assertions instead of AssertJ. The project convention recommends package-private visibility and AssertJ for new tests, but practically every other test in this module follows the same public/JUnit pattern, so this is consistent with existing module practice.
  • No JIRA ticket is linked — consider filing one so the commit message can follow the CAMEL-XXXX: format.

📋 PR Metadata

Aspect Current Suggested
Category (unlabeled) bug
Labels components + bug
Milestone (none) 4.22.0

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.

Claude Code on behalf of Guillaume Nodet

base can never be null here — both call sites only reach this method after target.equals(r.getBasePath()) matched a non-null target.
@tools400

Copy link
Copy Markdown
Contributor Author

Removed the dead code. mvn clean install -DskipTests builds cleanly, and the relevant tests pass:

  • mvn test -pl components/camel-platform-http-vertx -Dtest=RestOpenApiContractFirstRootBasePathTest -am -q-
  • mvn test -pl components/camel-platform-http-vertx -Dtest=VertxPlatformHttpConsumerNormalizedEndpointTest -am -q

@davsclaus davsclaus added this to the 4.23.0 milestone Aug 27, 2026
@davsclaus davsclaus added the bug Something isn't working label Aug 27, 2026
@davsclaus
davsclaus merged commit 3c83431 into apache:main Aug 27, 2026
5 checks passed
davsclaus added a commit that referenced this pull request Aug 27, 2026
RestOpenApiHelper.determineBasePath() always falls back to DEFAULT_BASE_PATH
("/") when the OpenAPI servers[0].url has no path segment, so the effective
base path is never truly empty. In
VertxPlatformHttpConsumer.startRestServicesContractFirst(), the Vert.x route
path was built by plain concatenation of basePath + baseUrl ("/" + "/hello"
-> "//hello"), which Vert.x normalizes away when matching incoming requests,
making every contract-first route permanently unreachable whenever the
contract's base path is root.

Normalize the join via a new buildNormalizedEndpoint() helper that strips a
trailing slash from the base path before concatenation, used at both call
sites (operation routes and the api-specification route). Adds regression
tests covering a contract-first spec without a path segment in
servers[0].url.

Backport of #25752 to camel-4.22.x.

Closes #25792
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working components

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants