Skip to content

CAMEL-24367: Add camel-rest-postman component - #25390

Open
christosgkoros wants to merge 3 commits into
apache:mainfrom
christosgkoros:feat/camel-rest-postman-component
Open

CAMEL-24367: Add camel-rest-postman component#25390
christosgkoros wants to merge 3 commits into
apache:mainfrom
christosgkoros:feat/camel-rest-postman-component

Conversation

@christosgkoros

@christosgkoros christosgkoros commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

JIRA: https://issues.apache.org/jira/browse/CAMEL-24367

What this adds

A new camel-rest-postman component that configures REST producers and contract-first REST consumers from a Postman Collection instead of an OpenAPI specification. It is the Postman counterpart of camel-rest-openapi: it performs no HTTP itself and delegates to a component implementing RestProducerFactory.

The motivation is that a large number of teams keep a Postman Collection as the only machine-readable description of their API, and today Camel has no way to consume that.

The collection is loaded either from a Collection v2.1 JSON document (classpath:, file:, http:) or, by its uid, from the Postman cloud.

Usage

// invoke one request
from("direct:start")
    .to("rest-postman:petstore.json#getPetById");

// run every request of a folder, or of the whole collection, like Postman's collection runner
from("timer:smoke?period=60000")
    .to("rest-postman:petstore.json#pets");

// serve the collection's requests, dispatching each to direct:<requestId>
from("rest-postman:petstore.json")
    .to("direct:dummy");

Multi-request runs return a List<PostmanRunResult> (status, body, headers, per-request failure), with runFailFast controlling whether the first failure aborts the run.

Design notes

Addressing requests. Postman items have a human name rather than an operation id, so the name is slugified (Get Pet By IdgetPetById), folder-qualified (pets/getPetById) when a name is not unique. item.id is accepted too, but note it is optional in the v2.1 schema and Postman's exporter strips it, so exported collections are normally addressed by slug and cloud-fetched ones by id. Both work.

Two credentials, deliberately named apart. postmanApiKey authenticates against Postman in order to download a collection; it is never sent to the API the collection describes. The collection's own auth block authenticates against that API and is governed by collectionAuth, which defaults to ignore (with a startup warning naming the type found) because those values are usually unresolved {{placeholders}}, and silently attaching a credential found in a config file to outbound requests is surprising. An e2e test asserts the separation.

Security. Redirects from postmanApiUrl are rejected rather than followed, since following one would replay the API key to the redirect target; postmanApiUrl must be HTTPS except for loopback; reads are bounded (8 MiB, 5000 items, 64 folder levels); apiContextPath serves the collection with every auth block and every type: secret variable removed, unconditionally. Postman event scripts are never parsed or executed.

No new third-party dependency. The collection is parsed with camel-util-json, already on the classpath via camel-support.

Testing

  • 167 tests in camel-rest-postman
  • 8 contract-first consumer tests in camel-platform-http-vertx, following the precedent that rest-openapi's consumer tests live there because PlatformHttpComponent is the only RestOpenApiConsumerFactory implementation
  • mvn clean install -Psourcecheck passes on both modified modules

Review feedback addressed

Changes since the first push, in response to @davsclaus's review:

  • JIRACAMEL-24367 now referenced in the commit message and PR title.
  • Upgrade guide — the new-component section has been removed; camel-4x-upgrade-guide-4_22.adoc is now byte-identical to main. The component's own .adoc page carries that documentation.
  • consumerComponentName description — no longer names the OpenAPI SPI class; it now describes the capability ("must be able to service contract-first REST consumers, as platform-http does").
  • Exception wrapping — the two new RuntimeException(e) sites in RestPostmanProcessor now use RuntimeCamelException.wrapRuntimeCamelException(e).

Known gap

For a path the collection does describe, a wrong-method request currently gets a 405 from the vert.x router before this component's processor runs, and the router leaves Allow empty. rest-openapi populates Allow in the equivalent case, so the difference is mine; the processor's own 404/405 handling (with Allow) still applies to paths the router has no route for. I would appreciate a pointer here if the cause is obvious to someone who knows platform-http well.


This contribution was AI-assisted: written with Claude Code (Claude Opus) on behalf of @christosgkoros, who reviewed the design decisions. Commits carry a Co-Authored-By trailer.

@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 for this well-designed component, @christosgkoros — the security measures (redirect rejection, HTTPS enforcement, bounded reads, auth stripping, variable recursion limits) are all substantiated by the code, the test suite is thorough (122 test methods, all AssertJ, all package-private, no Thread.sleep), and there are no new runtime dependencies.

Two things need addressing before this can merge, plus a few suggestions below.

Blocking

  1. Missing JIRA ticket — no CAMEL-XXXXX issue is linked anywhere (PR title, description, commits, branch name). Per project guidelines, a JIRA ticket is required. Please create one and update the branch/commits accordingly (feature/CAMEL-XXXXX-rest-postman, CAMEL-XXXXX: Add camel-rest-postman component).

  2. Upgrade guide misuse — the 37-line new-component section added to camel-4x-upgrade-guide-4_22.adoc should be removed. Per project conventions, the upgrade guide is for migration only — new features should not be documented there. The component's own .adoc page (which is well-written) is the right place.

Design note

Reusing RestOpenApiConsumerFactory — this is fine, no need for a new SPI. The contract is generic enough and PlatformHttpComponent is its only implementation. Just be aware the parameter description for consumerComponentName says "RestOpenApiConsumerFactory" which may confuse users — consider describing the capability generically instead of naming the SPI class.


This review covers project rules and conventions. It does not replace specialised tools (CodeRabbit, SonarCloud) for deep static analysis.

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

Claude Code on behalf of davsclaus

@davsclaus

Copy link
Copy Markdown
Contributor

Configures REST producers and contract-first REST consumers from a Postman
Collection, as the Postman counterpart of camel-rest-openapi. Like it, this
component performs no HTTP itself and delegates to a component implementing
RestProducerFactory.

The collection is loaded either from a Collection v2.1 JSON document
(classpath:, file: or http:) or, by its uid, from the Postman cloud.

Producer:
- a fragment naming a request invokes it, sending the exchange body and headers
- a fragment naming a folder, or no fragment at all, runs every request in turn
  like Postman's collection runner and returns a List<PostmanRunResult>

Consumer:
- serves the collection's requests, dispatching each to direct:<requestId>
- missingRequest=fail|ignore|mock, where mock replays the collection's own saved
  example responses before falling back to mockIncludePattern
- apiContextPath serves the collection with every auth block and every secret
  variable removed

Requests are addressed by their slugified name, folder-qualified when a name is
not unique, and by item.id when the collection records one. item.id is optional
in the v2.1 schema and Postman's exporter strips it, so exported collections are
normally addressed by slug.

Two separate credentials are kept apart by their option names: postmanApiKey
authenticates against Postman in order to download a collection and is never
sent to the API the collection describes, while the collection's own auth block
is governed by collectionAuth, which defaults to ignore. Redirects from
postmanApiUrl are rejected rather than followed, since following one would send
the key to the redirect target.

No new third-party dependency is introduced: the collection is parsed with
camel-util-json.

Co-Authored-By: Claude <noreply@anthropic.com>
@christosgkoros
christosgkoros force-pushed the feat/camel-rest-postman-component branch from 73a054b to c348957 Compare August 6, 2026 23:34
@christosgkoros
christosgkoros deleted the feat/camel-rest-postman-component branch August 6, 2026 23:34
@christosgkoros
christosgkoros restored the feat/camel-rest-postman-component branch August 6, 2026 23:35
@christosgkoros christosgkoros reopened this Aug 6, 2026
@christosgkoros christosgkoros changed the title Add camel-rest-postman component CAMEL-24367: Add camel-rest-postman component Aug 6, 2026
@christosgkoros

Copy link
Copy Markdown
Contributor Author

Thanks for the review @davsclaus, and for creating CAMEL-24367. All four points are addressed in the force-pushed commit c348957.

1. JIRA — the commit message and PR title are now CAMEL-24367: Add camel-rest-postman component.

On the branch name: I tried renaming it to feature/CAMEL-24367-rest-postman and that closed this PR — GitHub does not carry a pull request across a branch rename when the head is on a fork. I renamed it back and reopened, so the branch is still feat/camel-rest-postman-component and this thread is intact. If you would rather have the branch name match the convention, say so and I will open a fresh PR from a correctly named branch and link back to this one; I did not want to throw away the review thread unilaterally.

2. Upgrade guide — the 37-line section is removed. camel-4x-upgrade-guide-4_22.adoc is now byte-identical to main, and the documentation lives only in rest-postman-component.adoc.

One observation while doing this, purely FYI: the same file currently has === camel-clickhouse (new component) and === camel-duckdb (new component) sections, which is what I patterned mine on. Happy to leave those alone — just flagging in case they should also be cleaned up.

3. consumerComponentName description — good catch, it no longer names the OpenAPI SPI:

Name of the Camel component that will service the requests. The component must be present in Camel registry and it must be able to service contract-first REST consumers, as platform-http does. If not set CLASSPATH is searched for a single component with that capability.

4. RuntimeException wrapping — both sites in RestPostmanProcessor now use RuntimeCamelException.wrapRuntimeCamelException(e).

Thanks also for confirming the RestOpenApiConsumerFactory reuse is acceptable — that was the design call I was least sure about.

Rebuilt and re-verified after the changes: 167 tests in camel-rest-postman and 8 consumer tests in camel-platform-http-vertx all pass, and mvn clean install -Psourcecheck is clean on both modules.

Claude Code on behalf of @christosgkoros

@davsclaus

Copy link
Copy Markdown
Contributor

Ad 2)
that was a mistake both has been removed

@github-actions

github-actions Bot commented Aug 7, 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.

@davsclaus

Copy link
Copy Markdown
Contributor

[05:56:02.037] WARN (asciidoctor): skipping reference to missing attribute: petid
file: /home/runner/work/camel/camel/camel/docs/components/modules/ROOT/pages/rest-postman-component.adoc
source: /home/runner/work/camel/camel/camel (branch: HEAD | start path: docs/components)
[05:56:02.041] WARN (asciidoctor): skipping reference to missing attribute: baseurl
file: /home/runner/work/camel/camel/camel/docs/components/modules/ROOT/pages/rest-postman-component.adoc
source: /home/runner/work/camel/camel/camel (branch: HEAD | start path: docs/components)
[05:56:02.042] WARN (asciidoctor): skipping reference to missing attribute: variable
file: /home/runner/work/camel/camel/camel/docs/components/modules/ROOT/pages/rest-postman-component.adoc
source: /home/runner/work/camel/camel/camel (branch: HEAD | start path: docs/components)
[05:56:02.043] WARN (asciidoctor): skipping reference to missing attribute: placeholders
file: /home/runner/work/camel/camel/camel/docs/components/modules/ROOT/pages/rest-postman-component.adoc
source: /home/runner/work/camel/camel/camel (branch: HEAD | start path: docs/components)

The documentation build treats asciidoctor warnings as failures, and the four
brace spans in rest-postman-component.adoc were being parsed as AsciiDoc
attribute references rather than literal text, producing "skipping reference to
missing attribute" for petid, baseurl, variable and placeholders.

Wrap them in an inline passthrough so no substitution is applied. The doubled
Postman braces make this preferable to backslash escaping.

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

Copy link
Copy Markdown
Contributor Author

The Validate documentation job failure was caused by this PR — pushed a fix in 6607c56.

The site build produced exactly four asciidoctor warnings and all four were in rest-postman-component.adoc; the job treats warnings as failures:

WARN (asciidoctor): skipping reference to missing attribute: petid
WARN (asciidoctor): skipping reference to missing attribute: baseurl
WARN (asciidoctor): skipping reference to missing attribute: variable
WARN (asciidoctor): skipping reference to missing attribute: placeholders

AsciiDoc was parsing the brace spans as attribute references rather than literal text. They are now wrapped in an inline passthrough (`+{petId}+`, `+{{baseUrl}}+`), which suppresses substitution. I used a passthrough rather than the backslash escaping used in rest-openapi-component.adoc, because Postman's doubled braces make \{\{baseUrl}} awkward to read — happy to switch if you prefer consistency with the existing page.

Note that the same job log also contains unix-dgram / node-gyp native build errors from the camel-website toolchain. Those are unrelated to this PR and did not fail the job — the Antora build ran to completion afterwards.

I have left this as a separate commit so the change since your review is visible; it should be squashed at merge.

Claude Code on behalf of @christosgkoros

@davsclaus

Copy link
Copy Markdown
Contributor

There are uncommitted changes
HEAD detached at pull/25390/merge
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/rest-postman-component.adoc
modified: core/camel-util/src/main/java/org/apache/camel/util/SensitiveUtils.java
modified: dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/RestPostmanComponentBuilderFactory.java
modified: dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/RestPostmanEndpointBuilderFactory.java

The consumerComponentName description reword and the AsciiDoc passthrough fix
were not propagated to the files derived from them, which left the tree dirty
after a build:

- catalog copy of rest-postman-component.adoc
- component and endpoint DSL builder factories

Also reverts an unintended re-indentation of the SENSITIVE-PATTERN marker in
SensitiveUtils, which the formatter had applied but the generator does not
produce, so that file now differs from main only by the two postmanapikey
entries.

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

Copy link
Copy Markdown
Contributor Author

Fixed in d4c5f8e. You were right — those four files were derived from the two changes I made in response to your review, and I had regenerated only some of the downstream artefacts.

File Why it was stale
catalog copy of rest-postman-component.adoc not regenerated after the AsciiDoc passthrough fix
RestPostmanComponentBuilderFactory not regenerated after the consumerComponentName reword
RestPostmanEndpointBuilderFactory same
SensitiveUtils see below

SensitiveUtils was a different problem: my commit carried an unintended re-indentation of the // SENSITIVE-PATTERN: END marker that the generator does not produce. It is reverted, so that file now differs from main only by the two postmanapikey entries. I confirmed formatter:format and impsort:sort leave it alone, so it should not drift again.

To make sure this is actually fixed rather than just locally plausible, I reproduced what CI does end to end:

mvn install -Dquickly                                    # BUILD SUCCESS
mvn install -DskipTests -pl catalog/camel-catalog,core/camel-util,\
    dsl/camel-componentdsl,dsl/camel-endpointdsl,dsl/camel-kamelet-main,docs
                                                         # BUILD SUCCESS
git status --short                                       # empty

Also confirmed on the previous run that this was the only real failure: on 6607c56 the JDK 25 maven build step passed and only Fail if there are uncommitted changes failed; JDK 17 was cancelled by fail-fast rather than failing on its own. The Validate documentation job went green, so the asciidoctor fix worked.

While I was at it I checked that RestOpenApiConsumerFactory no longer appears in any user-facing text — docs, catalog json, or the generated DSL builders. It remains only in the Java code, where it is the actual API being called.

Claude Code on behalf of @christosgkoros

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants