This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.18.x by this push:
new 16bbc3a3452e [backport camel-4.18.x] CAMEL-25983: camel-github2 -
getCommitFile reads the file of the commit (#28374)
16bbc3a3452e is described below
commit 16bbc3a3452ea387034d726845f2c788f638dbd6
Author: Claus Ibsen <[email protected]>
AuthorDate: Sun Oct 25 08:42:03 2026 +0100
[backport camel-4.18.x] CAMEL-25983: camel-github2 - getCommitFile reads
the file of the commit (#28374)
Signed-off-by: Claus Ibsen <[email protected]>
Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
---
.../apache/camel/catalog/components/github2.json | 9 +-
.../apache/camel/component/github2/github2.json | 9 +-
.../src/main/docs/github2-component.adoc | 6 +-
.../camel/component/github2/GitHub2Constants.java | 9 +-
.../camel/component/github2/GitHub2Endpoint.java | 2 +-
.../github2/producer/GetCommitFileProducer.java | 74 +++++++---
.../producer/GetCommitFileProducerTest.java | 161 +++++++++++++++++++++
.../dsl/GitHub2EndpointBuilderFactory.java | 21 ++-
8 files changed, 252 insertions(+), 39 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json
index ace31bcf777e..c5c211880881 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/github2.json
@@ -39,9 +39,10 @@
"CamelGitHubIssueTitle": { "index": 3, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The title of the issue", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_ISSUE_TITLE" },
"CamelGitHubCommitAuthor": { "index": 4, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit author", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_AUTHOR" },
"CamelGitHubCommitCommitter": { "index": 5, "kind": "header",
"displayName": "", "group": "consumer", "label": "consumer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The committer name", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_COMMITTER" },
- "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit sha", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" },
- "CamelGitHubCommitUrl": { "index": 7, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit URL", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" },
- "CamelGitHubEventPayload": { "index": 8, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The event payload",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" }
+ "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "",
"group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The commit sha. The getCommitFile producer reads the
file at this commit (a commit id of 40 hexadecimal characters).",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" },
+ "CamelGitHubFilePath": { "index": 7, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The path of the file in the repository for the
getCommitFile producer. If not set, the body is the path.", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_FILE_PATH" },
+ "CamelGitHubCommitUrl": { "index": 8, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit URL", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" },
+ "CamelGitHubEventPayload": { "index": 9, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The event payload",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" }
},
"properties": {
"type": { "index": 0, "kind": "path", "displayName": "Type", "group":
"common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.github2.GitHub2Type", "enum": [ "CLOSEPULLREQUEST",
"PULLREQUESTCOMMENT", "COMMIT", "PULLREQUEST", "TAG", "PULLREQUESTSTATE",
"PULLREQUESTFILES", "GETCOMMITFILE", "CREATEISSUE", "EVENT" ], "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "What git operation to execute" },
@@ -56,7 +57,7 @@
"exceptionHandler": { "index": 9, "kind": "parameter", "displayName":
"Exception Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "object", "javaType":
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.",
"deprecated": false, "autowired": false, "secret": false, "description": "To
let the consumer use a custom ExceptionHandler. Notice if the option
bridgeErrorHandler is enabled then this option is not in use. By def [...]
"exchangePattern": { "index": 10, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." },
"pollStrategy": { "index": 11, "kind": "parameter", "displayName": "Poll
Strategy", "group": "consumer (advanced)", "label": "consumer,advanced",
"required": false, "type": "object", "javaType":
"org.apache.camel.spi.PollingConsumerPollStrategy", "deprecated": false,
"autowired": false, "secret": false, "description": "A pluggable
org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your
custom implementation to control error handling usually occurred during the po
[...]
- "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding",
"group": "producer", "label": "producer", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "To use the given encoding when getting a git
commit file" },
+ "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding",
"group": "producer", "label": "producer", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The charset of the content of the file for the
getCommitFile producer (the default charset of the JVM if not set)" },
"state": { "index": 13, "kind": "parameter", "displayName": "State",
"group": "producer", "label": "producer", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "error", "failure", "pending",
"success" ], "deprecated": false, "autowired": false, "secret": false,
"description": "To set git commit status state" },
"targetUrl": { "index": 14, "kind": "parameter", "displayName": "Target
Url", "group": "producer", "label": "producer", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "To set git commit status target url" },
"lazyStartProducer": { "index": 15, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
diff --git
a/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json
b/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json
index ace31bcf777e..c5c211880881 100644
---
a/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json
+++
b/components/camel-github2/src/generated/resources/META-INF/org/apache/camel/component/github2/github2.json
@@ -39,9 +39,10 @@
"CamelGitHubIssueTitle": { "index": 3, "kind": "header", "displayName":
"", "group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The title of the issue", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_ISSUE_TITLE" },
"CamelGitHubCommitAuthor": { "index": 4, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit author", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_AUTHOR" },
"CamelGitHubCommitCommitter": { "index": 5, "kind": "header",
"displayName": "", "group": "consumer", "label": "consumer", "required": false,
"javaType": "String", "deprecated": false, "deprecationNote": "", "autowired":
false, "secret": false, "description": "The committer name", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_COMMITTER" },
- "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit sha", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" },
- "CamelGitHubCommitUrl": { "index": 7, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit URL", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" },
- "CamelGitHubEventPayload": { "index": 8, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The event payload",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" }
+ "CamelGitHubCommitSha": { "index": 6, "kind": "header", "displayName": "",
"group": "common", "label": "", "required": false, "javaType": "String",
"deprecated": false, "deprecationNote": "", "autowired": false, "secret":
false, "description": "The commit sha. The getCommitFile producer reads the
file at this commit (a commit id of 40 hexadecimal characters).",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_SHA" },
+ "CamelGitHubFilePath": { "index": 7, "kind": "header", "displayName": "",
"group": "producer", "label": "producer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The path of the file in the repository for the
getCommitFile producer. If not set, the body is the path.", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_FILE_PATH" },
+ "CamelGitHubCommitUrl": { "index": 8, "kind": "header", "displayName": "",
"group": "consumer", "label": "consumer", "required": false, "javaType":
"String", "deprecated": false, "deprecationNote": "", "autowired": false,
"secret": false, "description": "The commit URL", "constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_COMMIT_URL" },
+ "CamelGitHubEventPayload": { "index": 9, "kind": "header", "displayName":
"", "group": "consumer", "label": "consumer", "required": false, "javaType":
"org.kohsuke.github.GHEventPayload", "deprecated": false, "deprecationNote":
"", "autowired": false, "secret": false, "description": "The event payload",
"constantName":
"org.apache.camel.component.github2.GitHub2Constants#GITHUB_EVENT_PAYLOAD" }
},
"properties": {
"type": { "index": 0, "kind": "path", "displayName": "Type", "group":
"common", "label": "", "required": true, "type": "enum", "javaType":
"org.apache.camel.component.github2.GitHub2Type", "enum": [ "CLOSEPULLREQUEST",
"PULLREQUESTCOMMENT", "COMMIT", "PULLREQUEST", "TAG", "PULLREQUESTSTATE",
"PULLREQUESTFILES", "GETCOMMITFILE", "CREATEISSUE", "EVENT" ], "deprecated":
false, "deprecationNote": "", "autowired": false, "secret": false,
"description": "What git operation to execute" },
@@ -56,7 +57,7 @@
"exceptionHandler": { "index": 9, "kind": "parameter", "displayName":
"Exception Handler", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "object", "javaType":
"org.apache.camel.spi.ExceptionHandler", "optionalPrefix": "consumer.",
"deprecated": false, "autowired": false, "secret": false, "description": "To
let the consumer use a custom ExceptionHandler. Notice if the option
bridgeErrorHandler is enabled then this option is not in use. By def [...]
"exchangePattern": { "index": 10, "kind": "parameter", "displayName":
"Exchange Pattern", "group": "consumer (advanced)", "label":
"consumer,advanced", "required": false, "type": "enum", "javaType":
"org.apache.camel.ExchangePattern", "enum": [ "InOnly", "InOut" ],
"deprecated": false, "autowired": false, "secret": false, "description": "Sets
the exchange pattern when the consumer creates an exchange." },
"pollStrategy": { "index": 11, "kind": "parameter", "displayName": "Poll
Strategy", "group": "consumer (advanced)", "label": "consumer,advanced",
"required": false, "type": "object", "javaType":
"org.apache.camel.spi.PollingConsumerPollStrategy", "deprecated": false,
"autowired": false, "secret": false, "description": "A pluggable
org.apache.camel.PollingConsumerPollingStrategy allowing you to provide your
custom implementation to control error handling usually occurred during the po
[...]
- "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding",
"group": "producer", "label": "producer", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "To use the given encoding when getting a git
commit file" },
+ "encoding": { "index": 12, "kind": "parameter", "displayName": "Encoding",
"group": "producer", "label": "producer", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The charset of the content of the file for the
getCommitFile producer (the default charset of the JVM if not set)" },
"state": { "index": 13, "kind": "parameter", "displayName": "State",
"group": "producer", "label": "producer", "required": false, "type": "enum",
"javaType": "java.lang.String", "enum": [ "error", "failure", "pending",
"success" ], "deprecated": false, "autowired": false, "secret": false,
"description": "To set git commit status state" },
"targetUrl": { "index": 14, "kind": "parameter", "displayName": "Target
Url", "group": "producer", "label": "producer", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "To set git commit status target url" },
"lazyStartProducer": { "index": 15, "kind": "parameter", "displayName":
"Lazy Start Producer", "group": "producer (advanced)", "label":
"producer,advanced", "required": false, "type": "boolean", "javaType":
"boolean", "deprecated": false, "autowired": false, "secret": false,
"defaultValue": false, "description": "Whether the producer should be started
lazy (on the first message). By starting lazy you can use this to allow
CamelContext and routes to startup in situations where a produ [...]
diff --git a/components/camel-github2/src/main/docs/github2-component.adoc
b/components/camel-github2/src/main/docs/github2-component.adoc
index 5c3af5e033cf..ba2168cc4eda 100644
--- a/components/camel-github2/src/main/docs/github2-component.adoc
+++ b/components/camel-github2/src/main/docs/github2-component.adoc
@@ -123,9 +123,9 @@ Sets the commit status on a pull request.
|pullRequestFiles |none | - `CamelGitHubPullRequest` (integer) (REQUIRED):
Pull request number.
Returns the list of files changed in a pull request.
-|getCommitFile |none | - `CamelGitHubCommitSha` (String) (REQUIRED): Commit
SHA.
-- `CamelGitHubFilePath` (String) (REQUIRED): File path.
-Returns the content of a file at a specific commit.
+|getCommitFile |String (the path of the file, if the `CamelGitHubFilePath`
header is not set) | - `CamelGitHubCommitSha` (String) (REQUIRED): The commit
id (40 hexadecimal characters; a branch or tag name is not accepted).
+- `CamelGitHubFilePath` (String): The path of the file in the repository, such
as `docs/README.md`. If not set, the body is the path.
+Returns the content of the file in the tree of that commit, as a `String`
(decoded with the `encoding` option, or the default charset of the JVM).
|=======================================================================
=== Example: Consuming commits
diff --git
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java
index 140b48118a23..7560874cd655 100644
---
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java
+++
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Constants.java
@@ -35,8 +35,15 @@ public interface GitHub2Constants {
String GITHUB_COMMIT_AUTHOR = "CamelGitHubCommitAuthor";
@Metadata(label = "consumer", description = "The committer name", javaType
= "String")
String GITHUB_COMMIT_COMMITTER = "CamelGitHubCommitCommitter";
- @Metadata(label = "consumer", description = "The commit sha", javaType =
"String")
+ @Metadata(description = "The commit sha. The getCommitFile producer reads
the file at this commit (a commit id of"
+ + " 40 hexadecimal characters).",
+ javaType = "String")
String GITHUB_COMMIT_SHA = "CamelGitHubCommitSha";
+ @Metadata(label = "producer",
+ description = "The path of the file in the repository for the
getCommitFile producer. If not set, the body"
+ + " is the path.",
+ javaType = "String")
+ String GITHUB_FILE_PATH = "CamelGitHubFilePath";
@Metadata(label = "consumer", description = "The commit URL", javaType =
"String")
String GITHUB_COMMIT_URL = "CamelGitHubCommitUrl";
@Metadata(label = "consumer", description = "The event payload",
diff --git
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java
index 0696b2001178..837a4c70383e 100644
---
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java
+++
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/GitHub2Endpoint.java
@@ -252,7 +252,7 @@ public class GitHub2Endpoint extends ScheduledPollEndpoint
implements EndpointSe
}
/**
- * To use the given encoding when getting a git commit file
+ * The charset of the content of the file for the getCommitFile producer
(the default charset of the JVM if not set)
*/
public void setEncoding(String encoding) {
this.encoding = encoding;
diff --git
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java
index 6de27c410dad..93c04b7807ac 100644
---
a/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java
+++
b/components/camel-github2/src/main/java/org/apache/camel/component/github2/producer/GetCommitFileProducer.java
@@ -16,22 +16,28 @@
*/
package org.apache.camel.component.github2.producer;
+import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.Base64;
+import java.util.regex.Pattern;
import org.apache.camel.Exchange;
+import org.apache.camel.InvalidPayloadException;
import org.apache.camel.component.github2.GitHub2Constants;
import org.apache.camel.component.github2.GitHub2Endpoint;
import org.apache.camel.util.ObjectHelper;
-import org.kohsuke.github.GHBlob;
-import org.kohsuke.github.GHCommit;
+import org.kohsuke.github.GHContent;
/**
- * Producer endpoint that gets a file from a commit. The endpoint requires the
"CamelGitHubCommitSha" header,
- * identifying the commit SHA. The message body should contain the file path
within the repository.
+ * Producer endpoint that gets the content of a file at a commit. The
"CamelGitHubCommitSha" header is the commit id (40
+ * hexadecimal characters), and the "CamelGitHubFilePath" header, or else the
message body, is the path of the file in
+ * the repository. The file is read from the tree of that commit, and the body
is set to its content as text.
*/
public class GetCommitFileProducer extends AbstractGitHub2Producer {
+ private static final Pattern COMMIT_ID =
Pattern.compile("[0-9a-fA-F]{40}");
+
private final String encoding;
public GetCommitFileProducer(GitHub2Endpoint endpoint) {
@@ -43,35 +49,57 @@ public class GetCommitFileProducer extends
AbstractGitHub2Producer {
public void process(Exchange exchange) throws Exception {
String sha =
exchange.getIn().getHeader(GitHub2Constants.GITHUB_COMMIT_SHA, String.class);
if (ObjectHelper.isEmpty(sha)) {
- sha = exchange.getIn().getBody(String.class);
+ throw new IllegalArgumentException(
+ "The getCommitFile operation needs the " +
GitHub2Constants.GITHUB_COMMIT_SHA
+ + " header with the commit id");
}
-
- if (ObjectHelper.isEmpty(sha)) {
- throw new IllegalArgumentException("Commit SHA must be specified");
+ if (!COMMIT_ID.matcher(sha).matches()) {
+ throw new IllegalArgumentException(
+ "The " + GitHub2Constants.GITHUB_COMMIT_SHA
+ + " header must be a commit id
of 40 hexadecimal characters, but was: "
+ + sha);
}
- GHCommit commit = getRepository().getCommit(sha);
- GHBlob blob = null;
+ String path =
exchange.getIn().getHeader(GitHub2Constants.GITHUB_FILE_PATH, String.class);
+ if (ObjectHelper.isEmpty(path)) {
+ path = exchange.getIn().getBody(String.class);
+ }
+ if (ObjectHelper.isEmpty(path)) {
+ throw new InvalidPayloadException(exchange, String.class,
exchange.getIn());
+ }
- // Get the tree and find the file
- // For simplicity, we get the blob SHA from the exchange body if it's
a file path
- String filePath = exchange.getIn().getBody(String.class);
- if (filePath != null && !filePath.equals(sha)) {
- // Try to get the file content from the commit
- blob = getRepository().getBlob(sha);
- } else {
- blob = getRepository().getBlob(sha);
+ for (String segment : path.split("/")) {
+ if (".".equals(segment) || "..".equals(segment)) {
+ throw new IllegalArgumentException(
+ "The path of the file must be a path in the tree of
the commit, without . or .. segments, but was: "
+ + path);
+ }
}
- String content = null;
- if (blob != null) {
- String encodedContent = blob.getContent();
- Charset charset = encoding != null ? Charset.forName(encoding) :
Charset.defaultCharset();
- content = new String(Base64.getDecoder().decode(encodedContent),
charset);
+ // the file at its path in the tree of the commit
+ GHContent file = getRepository().getFileContent(path, sha);
+ if (!file.isFile()) {
+ throw new IllegalArgumentException(
+ "The path " + path + " at commit " + sha + " is not a file
but a " + file.getType());
}
+ Charset charset = encoding != null ? Charset.forName(encoding) :
Charset.defaultCharset();
+ String content = new String(readContent(file), charset);
+
// copy the header of in message to the out message
exchange.getMessage().copyFrom(exchange.getIn());
exchange.getMessage().setBody(content);
}
+
+ @SuppressWarnings("deprecation")
+ private byte[] readContent(GHContent file) throws IOException {
+ if ("base64".equals(file.getEncoding())) {
+ // GitHub wraps the lines of the base64 content
+ return Base64.getMimeDecoder().decode(file.getEncodedContent());
+ }
+ // the contents API gives no content for a file of more than 1 MB:
read its blob
+ try (InputStream is = getRepository().getBlob(file.getSha()).read()) {
+ return is.readAllBytes();
+ }
+ }
}
diff --git
a/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java
b/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java
new file mode 100644
index 000000000000..7ca6a16e8eeb
--- /dev/null
+++
b/components/camel-github2/src/test/java/org/apache/camel/component/github2/producer/GetCommitFileProducerTest.java
@@ -0,0 +1,161 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.github2.producer;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Base64;
+import java.util.Map;
+
+import org.apache.camel.BindToRegistry;
+import org.apache.camel.CamelExecutionException;
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.component.github2.GitHub2Constants;
+import org.apache.camel.test.junit5.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+import org.kohsuke.github.GHBlob;
+import org.kohsuke.github.GHContent;
+import org.kohsuke.github.GHRepository;
+import org.kohsuke.github.GitHub;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+/**
+ * The getCommitFile producer reads the file at its path in the tree of the
commit.
+ */
+class GetCommitFileProducerTest extends CamelTestSupport {
+
+ private static final String SHA =
"0123456789abcdef0123456789abcdef01234567";
+ private static final String PATH = "docs/readme.md";
+ // longer than the 76 characters of a line of MIME base64, so the encoded
content has line breaks
+ private static final String TEXT = "The Open Source Integration Framework,
with more than 300 connectors: æøå\n";
+
+ private final GHRepository repository = mock(GHRepository.class);
+
+ @BindToRegistry(GitHub2Constants.GITHUB_CLIENT)
+ private final GitHub github = createGitHub();
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:start")
+
.to("github2:getCommitFile?repoOwner=acme&repoName=demo&oauthToken=not-used&encoding=UTF-8");
+ }
+ };
+ }
+
+ @Test
+ void fileFromPathHeader() throws Exception {
+ GHContent content
+ = fileContent("base64",
Base64.getMimeEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8)));
+ when(repository.getFileContent(PATH, SHA)).thenReturn(content);
+
+ String body = template.requestBodyAndHeaders("direct:start", "ignored",
+ Map.of(GitHub2Constants.GITHUB_COMMIT_SHA, SHA,
GitHub2Constants.GITHUB_FILE_PATH, PATH), String.class);
+
+ assertEquals(TEXT, body);
+ }
+
+ @Test
+ void fileFromPathInBody() throws Exception {
+ GHContent content
+ = fileContent("base64",
Base64.getMimeEncoder().encodeToString(TEXT.getBytes(StandardCharsets.UTF_8)));
+ when(repository.getFileContent(PATH, SHA)).thenReturn(content);
+
+ String body = template.requestBodyAndHeader("direct:start", PATH,
GitHub2Constants.GITHUB_COMMIT_SHA, SHA,
+ String.class);
+
+ assertEquals(TEXT, body);
+ }
+
+ @Test
+ void largeFileIsReadAsBlob() throws Exception {
+ // the contents API gives no content for a file of more than 1 MB
+ GHContent content = fileContent("none", "");
+
when(content.getSha()).thenReturn("fedcba9876543210fedcba9876543210fedcba98");
+ when(repository.getFileContent(PATH, SHA)).thenReturn(content);
+ GHBlob blob = mock(GHBlob.class);
+ when(blob.read()).thenReturn(new
ByteArrayInputStream(TEXT.getBytes(StandardCharsets.UTF_8)));
+
when(repository.getBlob("fedcba9876543210fedcba9876543210fedcba98")).thenReturn(blob);
+
+ String body = template.requestBodyAndHeader("direct:start", PATH,
GitHub2Constants.GITHUB_COMMIT_SHA, SHA,
+ String.class);
+
+ assertEquals(TEXT, body);
+ }
+
+ @Test
+ void shaMustBeCommitId() throws Exception {
+ CamelExecutionException e = assertThrows(CamelExecutionException.class,
+ () -> template.requestBodyAndHeader("direct:start", PATH,
GitHub2Constants.GITHUB_COMMIT_SHA, "main"));
+
+ IllegalArgumentException cause =
assertInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertTrue(cause.getMessage().contains("40 hexadecimal characters"),
cause.getMessage());
+ verify(repository, never()).getFileContent(anyString(), anyString());
+ }
+
+ @Test
+ void pathMustBeInTreeOfCommit() throws Exception {
+ CamelExecutionException e = assertThrows(CamelExecutionException.class,
+ () -> template.requestBodyAndHeader("direct:start",
"docs/../readme.md", GitHub2Constants.GITHUB_COMMIT_SHA,
+ SHA));
+
+ IllegalArgumentException cause =
assertInstanceOf(IllegalArgumentException.class, e.getCause());
+ assertTrue(cause.getMessage().contains("path in the tree of the
commit"), cause.getMessage());
+ verify(repository, never()).getFileContent(anyString(), anyString());
+ }
+
+ @Test
+ void shaHeaderIsRequired() throws Exception {
+ CamelExecutionException e = assertThrows(CamelExecutionException.class,
+ () -> template.requestBody("direct:start", SHA));
+
+ IllegalArgumentException cause =
assertInstanceOf(IllegalArgumentException.class, e.getCause());
+
assertTrue(cause.getMessage().contains(GitHub2Constants.GITHUB_COMMIT_SHA),
cause.getMessage());
+ verify(repository, never()).getFileContent(anyString(), anyString());
+ }
+
+ private static GHContent fileContent(String encoding, String
encodedContent) throws IOException {
+ GHContent content = mock(GHContent.class);
+ when(content.isFile()).thenReturn(true);
+ when(content.getType()).thenReturn("file");
+ when(content.getEncoding()).thenReturn(encoding);
+ when(content.getEncodedContent()).thenReturn(encodedContent);
+ return content;
+ }
+
+ private GitHub createGitHub() {
+ GitHub client = mock(GitHub.class);
+ try {
+ when(client.getRepository("acme/demo")).thenReturn(repository);
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ return client;
+ }
+}
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java
index b479792a5104..bf87a817fc91 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GitHub2EndpointBuilderFactory.java
@@ -880,7 +880,8 @@ public interface GitHub2EndpointBuilderFactory {
return this;
}
/**
- * To use the given encoding when getting a git commit file.
+ * The charset of the content of the file for the getCommitFile
producer
+ * (the default charset of the JVM if not set).
*
* The option is a: <code>java.lang.String</code> type.
*
@@ -1246,17 +1247,31 @@ public interface GitHub2EndpointBuilderFactory {
return "CamelGitHubCommitCommitter";
}
/**
- * The commit sha.
+ * The commit sha. The getCommitFile producer reads the file at this
+ * commit (a commit id of 40 hexadecimal characters).
*
* The option is a: {@code String} type.
*
- * Group: consumer
+ * Group: common
*
* @return the name of the header {@code GitHubCommitSha}.
*/
public String gitHubCommitSha() {
return "CamelGitHubCommitSha";
}
+ /**
+ * The path of the file in the repository for the getCommitFile
+ * producer. If not set, the body is the path.
+ *
+ * The option is a: {@code String} type.
+ *
+ * Group: producer
+ *
+ * @return the name of the header {@code GitHubFilePath}.
+ */
+ public String gitHubFilePath() {
+ return "CamelGitHubFilePath";
+ }
/**
* The commit URL.
*