This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new d1d2bec0a0d1 CAMEL-22561: camel-graphq - Add support for header filter
strategy to include HTTP headers like camel-http and use more of same logic as
camel-http. (#19592)
d1d2bec0a0d1 is described below
commit d1d2bec0a0d15ccbf3a3339db9ac2bc33ee87032
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Oct 16 15:04:04 2025 +0200
CAMEL-22561: camel-graphq - Add support for header filter strategy to
include HTTP headers like camel-http and use more of same logic as camel-http.
(#19592)
---
.../apache/camel/catalog/components/graphql.json | 34 ++--
components/camel-graphql/pom.xml | 4 +
.../graphql/GraphqlComponentConfigurer.java | 12 ++
.../graphql/GraphqlEndpointConfigurer.java | 12 ++
.../graphql/GraphqlEndpointUriFactory.java | 4 +-
.../apache/camel/component/graphql/graphql.json | 34 ++--
.../camel/component/graphql/GraphqlComponent.java | 16 +-
.../camel/component/graphql/GraphqlEndpoint.java | 32 ++++
.../camel/component/graphql/GraphqlProducer.java | 198 ++++++++++++++++++++-
.../component/graphql/GraphqlComponentTest.java | 36 +++-
.../component/graphql/server/GraphqlServer.java | 14 +-
.../ROOT/pages/camel-4x-upgrade-guide-4_16.adoc | 7 +
.../dsl/GraphqlComponentBuilderFactory.java | 39 ++++
.../dsl/GraphqlEndpointBuilderFactory.java | 66 +++++++
14 files changed, 468 insertions(+), 40 deletions(-)
diff --git
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json
index bf28673012e1..1070c3482236 100644
---
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json
+++
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/graphql.json
@@ -25,23 +25,27 @@
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "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 producer may otherwise fail [...]
- "autowiredEnabled": { "index": 1, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
- "httpClient": { "index": 2, "kind": "property", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." }
+ "throwExceptionOnFailure": { "index": 1, "kind": "property",
"displayName": "Throw Exception On Failure", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Option to disable throwing the HttpOperationFailedException in
case of failed responses from the remote server. This allows you to get all
responses regardless of the HTTP status code." },
+ "autowiredEnabled": { "index": 2, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ "httpClient": { "index": 3, "kind": "property", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
+ "headerFilterStrategy": { "index": 4, "kind": "property", "displayName":
"Header Filter Strategy", "group": "filter", "label": "filter", "required":
false, "type": "object", "javaType":
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired":
false, "secret": false, "description": "To use a custom
org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel
message." }
},
"properties": {
"httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.net.URI", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The GraphQL server URI." },
- "operationName": { "index": 1, "kind": "parameter", "displayName":
"Operation Name", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "The query or mutation name." },
- "proxyHost": { "index": 2, "kind": "parameter", "displayName": "Proxy
Host", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The proxy host in the format hostname:port." },
- "query": { "index": 3, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query text." },
- "queryFile": { "index": 4, "kind": "parameter", "displayName": "Query
File", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query file name located in the classpath
(or use file: to load from file system)." },
- "queryHeader": { "index": 5, "kind": "parameter", "displayName": "Query
Header", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The name of a header containing the GraphQL
query." },
- "variables": { "index": 6, "kind": "parameter", "displayName":
"Variables", "group": "producer", "label": "", "required": false, "type":
"object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated":
false, "autowired": false, "secret": false, "description": "The JsonObject
instance containing the operation variables." },
- "variablesHeader": { "index": 7, "kind": "parameter", "displayName":
"Variables Header", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The name of a header
containing a JsonObject instance containing the operation variables." },
- "lazyStartProducer": { "index": 8, "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 produc [...]
- "httpClient": { "index": 9, "kind": "parameter", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
- "accessToken": { "index": 10, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "description": "The access token sent in the
Authorization header." },
- "jwtAuthorizationType": { "index": 11, "kind": "parameter", "displayName":
"Jwt Authorization Type", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "Bearer", "description":
"The JWT Authorization type. Default is Bearer." },
- "password": { "index": 12, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The password for Basic authentication." },
- "username": { "index": 13, "kind": "parameter", "displayName": "Username",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The username for Basic authentication." }
+ "headerFilterStrategy": { "index": 1, "kind": "parameter", "displayName":
"Header Filter Strategy", "group": "common (advanced)", "label":
"common,advanced", "required": false, "type": "object", "javaType":
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired":
false, "secret": false, "description": "To use a custom HeaderFilterStrategy to
filter header to and from Camel message." },
+ "operationName": { "index": 2, "kind": "parameter", "displayName":
"Operation Name", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "The query or mutation name." },
+ "proxyHost": { "index": 3, "kind": "parameter", "displayName": "Proxy
Host", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The proxy host in the format hostname:port." },
+ "query": { "index": 4, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query text." },
+ "queryFile": { "index": 5, "kind": "parameter", "displayName": "Query
File", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query file name located in the classpath
(or use file: to load from file system)." },
+ "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query
Header", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The name of a header containing the GraphQL
query." },
+ "throwExceptionOnFailure": { "index": 7, "kind": "parameter",
"displayName": "Throw Exception On Failure", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Option to disable throwing the HttpOperationFailedException in
case of failed responses from the remote server. This allows you to get all
responses regardless of the HTTP status code." },
+ "variables": { "index": 8, "kind": "parameter", "displayName":
"Variables", "group": "producer", "label": "", "required": false, "type":
"object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated":
false, "autowired": false, "secret": false, "description": "The JsonObject
instance containing the operation variables." },
+ "variablesHeader": { "index": 9, "kind": "parameter", "displayName":
"Variables Header", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The name of a header
containing a JsonObject instance containing the operation variables." },
+ "lazyStartProducer": { "index": 10, "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 [...]
+ "httpClient": { "index": 11, "kind": "parameter", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
+ "accessToken": { "index": 12, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "description": "The access token sent in the
Authorization header." },
+ "jwtAuthorizationType": { "index": 13, "kind": "parameter", "displayName":
"Jwt Authorization Type", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "Bearer", "description":
"The JWT Authorization type. Default is Bearer." },
+ "password": { "index": 14, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The password for Basic authentication." },
+ "username": { "index": 15, "kind": "parameter", "displayName": "Username",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The username for Basic authentication." }
}
}
diff --git a/components/camel-graphql/pom.xml b/components/camel-graphql/pom.xml
index dcc0015ee79d..a8bdebabedfa 100644
--- a/components/camel-graphql/pom.xml
+++ b/components/camel-graphql/pom.xml
@@ -37,6 +37,10 @@
<groupId>org.apache.camel</groupId>
<artifactId>camel-support</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.apache.camel</groupId>
+ <artifactId>camel-http-base</artifactId>
+ </dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-util-json</artifactId>
diff --git
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlComponentConfigurer.java
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlComponentConfigurer.java
index f706783f4d41..1cdb13791e0a 100644
---
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlComponentConfigurer.java
+++
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlComponentConfigurer.java
@@ -25,10 +25,14 @@ public class GraphqlComponentConfigurer extends
PropertyConfigurerSupport implem
switch (ignoreCase ? name.toLowerCase() : name) {
case "autowiredenabled":
case "autowiredEnabled":
target.setAutowiredEnabled(property(camelContext, boolean.class, value));
return true;
+ case "headerfilterstrategy":
+ case "headerFilterStrategy":
target.setHeaderFilterStrategy(property(camelContext,
org.apache.camel.spi.HeaderFilterStrategy.class, value)); return true;
case "httpclient":
case "httpClient": target.setHttpClient(property(camelContext,
org.apache.hc.client5.http.classic.HttpClient.class, value)); return true;
case "lazystartproducer":
case "lazyStartProducer":
target.setLazyStartProducer(property(camelContext, boolean.class, value));
return true;
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure":
target.setThrowExceptionOnFailure(property(camelContext, boolean.class,
value)); return true;
default: return false;
}
}
@@ -38,10 +42,14 @@ public class GraphqlComponentConfigurer extends
PropertyConfigurerSupport implem
switch (ignoreCase ? name.toLowerCase() : name) {
case "autowiredenabled":
case "autowiredEnabled": return boolean.class;
+ case "headerfilterstrategy":
+ case "headerFilterStrategy": return
org.apache.camel.spi.HeaderFilterStrategy.class;
case "httpclient":
case "httpClient": return
org.apache.hc.client5.http.classic.HttpClient.class;
case "lazystartproducer":
case "lazyStartProducer": return boolean.class;
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure": return boolean.class;
default: return null;
}
}
@@ -52,10 +60,14 @@ public class GraphqlComponentConfigurer extends
PropertyConfigurerSupport implem
switch (ignoreCase ? name.toLowerCase() : name) {
case "autowiredenabled":
case "autowiredEnabled": return target.isAutowiredEnabled();
+ case "headerfilterstrategy":
+ case "headerFilterStrategy": return target.getHeaderFilterStrategy();
case "httpclient":
case "httpClient": return target.getHttpClient();
case "lazystartproducer":
case "lazyStartProducer": return target.isLazyStartProducer();
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure": return
target.isThrowExceptionOnFailure();
default: return null;
}
}
diff --git
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointConfigurer.java
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointConfigurer.java
index 29ac803713ac..07c0356d90e2 100644
---
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointConfigurer.java
+++
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointConfigurer.java
@@ -25,6 +25,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
switch (ignoreCase ? name.toLowerCase() : name) {
case "accesstoken":
case "accessToken": target.setAccessToken(property(camelContext,
java.lang.String.class, value)); return true;
+ case "headerfilterstrategy":
+ case "headerFilterStrategy":
target.setHeaderFilterStrategy(property(camelContext,
org.apache.camel.spi.HeaderFilterStrategy.class, value)); return true;
case "httpclient":
case "httpClient": target.setHttpClient(property(camelContext,
org.apache.hc.client5.http.classic.HttpClient.class, value)); return true;
case "jwtauthorizationtype":
@@ -41,6 +43,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
case "queryFile": target.setQueryFile(property(camelContext,
java.lang.String.class, value)); return true;
case "queryheader":
case "queryHeader": target.setQueryHeader(property(camelContext,
java.lang.String.class, value)); return true;
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure":
target.setThrowExceptionOnFailure(property(camelContext, boolean.class,
value)); return true;
case "username": target.setUsername(property(camelContext,
java.lang.String.class, value)); return true;
case "variables": target.setVariables(property(camelContext,
org.apache.camel.util.json.JsonObject.class, value)); return true;
case "variablesheader":
@@ -54,6 +58,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
switch (ignoreCase ? name.toLowerCase() : name) {
case "accesstoken":
case "accessToken": return java.lang.String.class;
+ case "headerfilterstrategy":
+ case "headerFilterStrategy": return
org.apache.camel.spi.HeaderFilterStrategy.class;
case "httpclient":
case "httpClient": return
org.apache.hc.client5.http.classic.HttpClient.class;
case "jwtauthorizationtype":
@@ -70,6 +76,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
case "queryFile": return java.lang.String.class;
case "queryheader":
case "queryHeader": return java.lang.String.class;
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure": return boolean.class;
case "username": return java.lang.String.class;
case "variables": return org.apache.camel.util.json.JsonObject.class;
case "variablesheader":
@@ -84,6 +92,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
switch (ignoreCase ? name.toLowerCase() : name) {
case "accesstoken":
case "accessToken": return target.getAccessToken();
+ case "headerfilterstrategy":
+ case "headerFilterStrategy": return target.getHeaderFilterStrategy();
case "httpclient":
case "httpClient": return target.getHttpClient();
case "jwtauthorizationtype":
@@ -100,6 +110,8 @@ public class GraphqlEndpointConfigurer extends
PropertyConfigurerSupport impleme
case "queryFile": return target.getQueryFile();
case "queryheader":
case "queryHeader": return target.getQueryHeader();
+ case "throwexceptiononfailure":
+ case "throwExceptionOnFailure": return
target.isThrowExceptionOnFailure();
case "username": return target.getUsername();
case "variables": return target.getVariables();
case "variablesheader":
diff --git
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointUriFactory.java
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointUriFactory.java
index 7e1696a1a13d..bf37617172ec 100644
---
a/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointUriFactory.java
+++
b/components/camel-graphql/src/generated/java/org/apache/camel/component/graphql/GraphqlEndpointUriFactory.java
@@ -23,8 +23,9 @@ public class GraphqlEndpointUriFactory extends
org.apache.camel.support.componen
private static final Set<String> SECRET_PROPERTY_NAMES;
private static final Map<String, String> MULTI_VALUE_PREFIXES;
static {
- Set<String> props = new HashSet<>(14);
+ Set<String> props = new HashSet<>(16);
props.add("accessToken");
+ props.add("headerFilterStrategy");
props.add("httpClient");
props.add("httpUri");
props.add("jwtAuthorizationType");
@@ -35,6 +36,7 @@ public class GraphqlEndpointUriFactory extends
org.apache.camel.support.componen
props.add("query");
props.add("queryFile");
props.add("queryHeader");
+ props.add("throwExceptionOnFailure");
props.add("username");
props.add("variables");
props.add("variablesHeader");
diff --git
a/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json
b/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json
index bf28673012e1..1070c3482236 100644
---
a/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json
+++
b/components/camel-graphql/src/generated/resources/META-INF/org/apache/camel/component/graphql/graphql.json
@@ -25,23 +25,27 @@
},
"componentProperties": {
"lazyStartProducer": { "index": 0, "kind": "property", "displayName":
"Lazy Start Producer", "group": "producer", "label": "producer", "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 producer may otherwise fail [...]
- "autowiredEnabled": { "index": 1, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
- "httpClient": { "index": 2, "kind": "property", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." }
+ "throwExceptionOnFailure": { "index": 1, "kind": "property",
"displayName": "Throw Exception On Failure", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Option to disable throwing the HttpOperationFailedException in
case of failed responses from the remote server. This allows you to get all
responses regardless of the HTTP status code." },
+ "autowiredEnabled": { "index": 2, "kind": "property", "displayName":
"Autowired Enabled", "group": "advanced", "label": "advanced", "required":
false, "type": "boolean", "javaType": "boolean", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": true, "description":
"Whether autowiring is enabled. This is used for automatic autowiring options
(the option must be marked as autowired) by looking up in the registry to find
if there is a single instance of matching t [...]
+ "httpClient": { "index": 3, "kind": "property", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
+ "headerFilterStrategy": { "index": 4, "kind": "property", "displayName":
"Header Filter Strategy", "group": "filter", "label": "filter", "required":
false, "type": "object", "javaType":
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired":
false, "secret": false, "description": "To use a custom
org.apache.camel.spi.HeaderFilterStrategy to filter header to and from Camel
message." }
},
"properties": {
"httpUri": { "index": 0, "kind": "path", "displayName": "Http Uri",
"group": "producer", "label": "", "required": true, "type": "string",
"javaType": "java.net.URI", "deprecated": false, "deprecationNote": "",
"autowired": false, "secret": false, "description": "The GraphQL server URI." },
- "operationName": { "index": 1, "kind": "parameter", "displayName":
"Operation Name", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "The query or mutation name." },
- "proxyHost": { "index": 2, "kind": "parameter", "displayName": "Proxy
Host", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The proxy host in the format hostname:port." },
- "query": { "index": 3, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query text." },
- "queryFile": { "index": 4, "kind": "parameter", "displayName": "Query
File", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query file name located in the classpath
(or use file: to load from file system)." },
- "queryHeader": { "index": 5, "kind": "parameter", "displayName": "Query
Header", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The name of a header containing the GraphQL
query." },
- "variables": { "index": 6, "kind": "parameter", "displayName":
"Variables", "group": "producer", "label": "", "required": false, "type":
"object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated":
false, "autowired": false, "secret": false, "description": "The JsonObject
instance containing the operation variables." },
- "variablesHeader": { "index": 7, "kind": "parameter", "displayName":
"Variables Header", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The name of a header
containing a JsonObject instance containing the operation variables." },
- "lazyStartProducer": { "index": 8, "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 produc [...]
- "httpClient": { "index": 9, "kind": "parameter", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
- "accessToken": { "index": 10, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "description": "The access token sent in the
Authorization header." },
- "jwtAuthorizationType": { "index": 11, "kind": "parameter", "displayName":
"Jwt Authorization Type", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "Bearer", "description":
"The JWT Authorization type. Default is Bearer." },
- "password": { "index": 12, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The password for Basic authentication." },
- "username": { "index": 13, "kind": "parameter", "displayName": "Username",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The username for Basic authentication." }
+ "headerFilterStrategy": { "index": 1, "kind": "parameter", "displayName":
"Header Filter Strategy", "group": "common (advanced)", "label":
"common,advanced", "required": false, "type": "object", "javaType":
"org.apache.camel.spi.HeaderFilterStrategy", "deprecated": false, "autowired":
false, "secret": false, "description": "To use a custom HeaderFilterStrategy to
filter header to and from Camel message." },
+ "operationName": { "index": 2, "kind": "parameter", "displayName":
"Operation Name", "group": "producer", "label": "", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": false, "description": "The query or mutation name." },
+ "proxyHost": { "index": 3, "kind": "parameter", "displayName": "Proxy
Host", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The proxy host in the format hostname:port." },
+ "query": { "index": 4, "kind": "parameter", "displayName": "Query",
"group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query text." },
+ "queryFile": { "index": 5, "kind": "parameter", "displayName": "Query
File", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The query file name located in the classpath
(or use file: to load from file system)." },
+ "queryHeader": { "index": 6, "kind": "parameter", "displayName": "Query
Header", "group": "producer", "label": "", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": false, "description": "The name of a header containing the GraphQL
query." },
+ "throwExceptionOnFailure": { "index": 7, "kind": "parameter",
"displayName": "Throw Exception On Failure", "group": "producer", "label":
"producer", "required": false, "type": "boolean", "javaType": "boolean",
"deprecated": false, "autowired": false, "secret": false, "defaultValue": true,
"description": "Option to disable throwing the HttpOperationFailedException in
case of failed responses from the remote server. This allows you to get all
responses regardless of the HTTP status code." },
+ "variables": { "index": 8, "kind": "parameter", "displayName":
"Variables", "group": "producer", "label": "", "required": false, "type":
"object", "javaType": "org.apache.camel.util.json.JsonObject", "deprecated":
false, "autowired": false, "secret": false, "description": "The JsonObject
instance containing the operation variables." },
+ "variablesHeader": { "index": 9, "kind": "parameter", "displayName":
"Variables Header", "group": "producer", "label": "", "required": false,
"type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "description": "The name of a header
containing a JsonObject instance containing the operation variables." },
+ "lazyStartProducer": { "index": 10, "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 [...]
+ "httpClient": { "index": 11, "kind": "parameter", "displayName": "Http
Client", "group": "advanced", "label": "advanced", "required": false, "type":
"object", "javaType": "org.apache.hc.client5.http.classic.HttpClient",
"deprecated": false, "autowired": false, "secret": false, "description": "To
use a custom pre-existing Http Client. Beware that when using this, then other
configurations such as proxy, access token, is not applied and all this must be
pre-configured on the Http Client." },
+ "accessToken": { "index": 12, "kind": "parameter", "displayName": "Access
Token", "group": "security", "label": "security", "required": false, "type":
"string", "javaType": "java.lang.String", "deprecated": false, "autowired":
false, "secret": true, "description": "The access token sent in the
Authorization header." },
+ "jwtAuthorizationType": { "index": 13, "kind": "parameter", "displayName":
"Jwt Authorization Type", "group": "security", "label": "security", "required":
false, "type": "string", "javaType": "java.lang.String", "deprecated": false,
"autowired": false, "secret": false, "defaultValue": "Bearer", "description":
"The JWT Authorization type. Default is Bearer." },
+ "password": { "index": 14, "kind": "parameter", "displayName": "Password",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The password for Basic authentication." },
+ "username": { "index": 15, "kind": "parameter", "displayName": "Username",
"group": "security", "label": "security", "required": false, "type": "string",
"javaType": "java.lang.String", "deprecated": false, "autowired": false,
"secret": true, "description": "The username for Basic authentication." }
}
}
diff --git
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlComponent.java
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlComponent.java
index 5fdb1e1b643b..5bfe7605e873 100644
---
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlComponent.java
+++
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlComponent.java
@@ -22,20 +22,25 @@ import java.util.Map;
import org.apache.camel.Endpoint;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.annotations.Component;
-import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.support.HeaderFilterStrategyComponent;
import org.apache.camel.util.URISupport;
import org.apache.hc.client5.http.classic.HttpClient;
@Component("graphql")
-public class GraphqlComponent extends DefaultComponent {
+public class GraphqlComponent extends HeaderFilterStrategyComponent {
@Metadata(label = "advanced")
private HttpClient httpClient;
+ @Metadata(label = "producer", defaultValue = "true",
+ description = "Option to disable throwing the
HttpOperationFailedException in case of failed responses from the remote
server. This allows you to get all responses regardless of the HTTP status
code.")
+ private boolean throwExceptionOnFailure = true;
@Override
protected Endpoint createEndpoint(String uri, String remaining,
Map<String, Object> parameters) throws Exception {
GraphqlEndpoint endpoint = new GraphqlEndpoint(uri, this);
+ endpoint.setHeaderFilterStrategy(getHeaderFilterStrategy());
endpoint.setHttpClient(httpClient);
+ endpoint.setThrowExceptionOnFailure(throwExceptionOnFailure);
endpoint.setHttpUri(new URI(remaining));
setProperties(endpoint, parameters);
return endpoint;
@@ -63,4 +68,11 @@ public class GraphqlComponent extends DefaultComponent {
this.httpClient = httpClient;
}
+ public boolean isThrowExceptionOnFailure() {
+ return throwExceptionOnFailure;
+ }
+
+ public void setThrowExceptionOnFailure(boolean throwExceptionOnFailure) {
+ this.throwExceptionOnFailure = throwExceptionOnFailure;
+ }
}
diff --git
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
index 51f7ae915233..f9e14e4513ac 100644
---
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
+++
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlEndpoint.java
@@ -28,7 +28,9 @@ import org.apache.camel.Consumer;
import org.apache.camel.Processor;
import org.apache.camel.Producer;
import org.apache.camel.RuntimeCamelException;
+import org.apache.camel.http.base.HttpHeaderFilterStrategy;
import org.apache.camel.spi.EndpointServiceLocation;
+import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.spi.Metadata;
import org.apache.camel.spi.UriEndpoint;
import org.apache.camel.spi.UriParam;
@@ -85,6 +87,12 @@ public class GraphqlEndpoint extends DefaultEndpoint
implements EndpointServiceL
private String queryHeader;
@UriParam(label = "advanced")
private HttpClient httpClient;
+ @UriParam(label = "producer", defaultValue = "true",
+ description = "Option to disable throwing the
HttpOperationFailedException in case of failed responses from the remote
server. This allows you to get all responses regardless of the HTTP status
code.")
+ private boolean throwExceptionOnFailure = true;
+ @UriParam(label = "common,advanced",
+ description = "To use a custom HeaderFilterStrategy to filter
header to and from Camel message.")
+ private HeaderFilterStrategy headerFilterStrategy;
public GraphqlEndpoint(String uri, Component component) {
super(uri, component);
@@ -106,6 +114,14 @@ public class GraphqlEndpoint extends DefaultEndpoint
implements EndpointServiceL
return null;
}
+ @Override
+ protected void doStart() throws Exception {
+ super.doStart();
+ if (headerFilterStrategy == null) {
+ headerFilterStrategy = new HttpHeaderFilterStrategy();
+ }
+ }
+
@Override
public String getServiceProtocol() {
return "rest";
@@ -302,6 +318,22 @@ public class GraphqlEndpoint extends DefaultEndpoint
implements EndpointServiceL
this.httpClient = httpClient;
}
+ public boolean isThrowExceptionOnFailure() {
+ return throwExceptionOnFailure;
+ }
+
+ public void setThrowExceptionOnFailure(boolean throwExceptionOnFailure) {
+ this.throwExceptionOnFailure = throwExceptionOnFailure;
+ }
+
+ public HeaderFilterStrategy getHeaderFilterStrategy() {
+ return headerFilterStrategy;
+ }
+
+ public void setHeaderFilterStrategy(HeaderFilterStrategy
headerFilterStrategy) {
+ this.headerFilterStrategy = headerFilterStrategy;
+ }
+
@Override
public boolean isLenientProperties() {
return true;
diff --git
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
index aa607339bab2..71134975544f 100644
---
a/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
+++
b/components/camel-graphql/src/main/java/org/apache/camel/component/graphql/GraphqlProducer.java
@@ -16,32 +16,57 @@
*/
package org.apache.camel.component.graphql;
+import java.io.IOException;
import java.net.URI;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import org.apache.camel.AsyncCallback;
import org.apache.camel.Exchange;
+import org.apache.camel.ExchangePropertyKey;
import org.apache.camel.InvalidPayloadException;
+import org.apache.camel.Message;
+import org.apache.camel.TypeConverter;
+import org.apache.camel.http.base.HttpHelper;
+import org.apache.camel.http.base.HttpOperationFailedException;
+import org.apache.camel.spi.HeaderFilterStrategy;
import org.apache.camel.support.DefaultAsyncProducer;
+import org.apache.camel.support.ObjectHelper;
+import org.apache.camel.support.http.HttpUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.json.JsonObject;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.classic.methods.HttpPost;
+import org.apache.hc.client5.http.classic.methods.HttpUriRequest;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
+import org.apache.hc.core5.http.ClassicHttpResponse;
import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
public class GraphqlProducer extends DefaultAsyncProducer {
- public GraphqlProducer(GraphqlEndpoint endpoint) {
- super(endpoint);
- }
+ private static final Logger LOG =
LoggerFactory.getLogger(GraphqlProducer.class);
+
+ private static final Integer OK_RESPONSE_CODE = 200;
+ private static final String OK_STATUS_RANGE = "200-299";
private HttpClient httpClient;
private boolean closeHttpClient;
+ public GraphqlProducer(GraphqlEndpoint endpoint) {
+ super(endpoint);
+ }
+
@Override
protected void doStart() throws Exception {
super.doStart();
@@ -74,13 +99,38 @@ public class GraphqlProducer extends DefaultAsyncProducer {
HttpEntity requestEntity = new StringEntity(requestBody,
ContentType.APPLICATION_JSON);
HttpPost httpPost = new HttpPost(httpUri);
+ httpPost.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
httpPost.setHeader(HttpHeaders.ACCEPT, "application/json");
httpPost.setHeader(HttpHeaders.ACCEPT_ENCODING, "gzip");
httpPost.setEntity(requestEntity);
+ populateRequestHeaders(exchange, httpPost);
- String responseContent = httpClient.execute(httpPost, response ->
EntityUtils.toString(response.getEntity()));
+ httpClient.execute(httpPost, httpResponse -> {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Finished executing http: {} method: {}",
httpUri, HttpPost.METHOD_NAME);
+ }
+ int responseCode = httpResponse.getCode();
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Http responseCode: {}", responseCode);
+ }
+ if (!getEndpoint().isThrowExceptionOnFailure()) {
+ // if we do not use failed exception then populate
response for all response codes
+ populateResponse(exchange, httpResponse,
getEndpoint().getHeaderFilterStrategy(), responseCode);
+ } else {
+ boolean ok = HttpHelper.isStatusCodeOk(responseCode,
OK_STATUS_RANGE);
+ if (ok) {
+ // only populate response for OK response
+ populateResponse(exchange, httpResponse,
getEndpoint().getHeaderFilterStrategy(), responseCode);
+ } else {
+ // also store response code when throwing exception
+ populateResponseCode(exchange.getMessage(),
httpResponse, responseCode);
- exchange.getMessage().setBody(responseContent);
+ // operation failed so populate exception to throw
+
exchange.setException(populateHttpOperationFailedException(exchange,
httpResponse, responseCode));
+ }
+ }
+ return null;
+ });
} catch (Exception e) {
exchange.setException(e);
@@ -90,6 +140,144 @@ public class GraphqlProducer extends DefaultAsyncProducer {
return true;
}
+ private void populateRequestHeaders(Exchange exchange, HttpPost
httpRequest) {
+ HeaderFilterStrategy strategy =
getEndpoint().getHeaderFilterStrategy();
+ final TypeConverter tc = exchange.getContext().getTypeConverter();
+ for (Map.Entry<String, Object> entry :
exchange.getMessage().getHeaders().entrySet()) {
+ String key = entry.getKey();
+ // we should not add known headers
+
+ // skip known headers from graphql
+ boolean skip = getEndpoint().getQueryHeader() != null &&
key.equalsIgnoreCase(getEndpoint().getQueryHeader())
+ || getEndpoint().getVariablesHeader() != null &&
key.equalsIgnoreCase(getEndpoint().getVariablesHeader());
+ if (skip) {
+ continue;
+ }
+
+ Object headerValue = entry.getValue();
+ if (headerValue != null) {
+ if (headerValue instanceof String || headerValue instanceof
Integer || headerValue instanceof Long
+ || headerValue instanceof Boolean || headerValue
instanceof Date) {
+ // optimise for common types
+ String value = headerValue.toString();
+ if (!strategy.applyFilterToCamelHeaders(key, value,
exchange)) {
+ httpRequest.addHeader(key, value);
+ }
+ continue;
+ }
+
+ // use an iterator as there can be multiple values. (must not
use a delimiter, and allow empty values)
+ final Iterator<?> it =
ObjectHelper.createIterator(headerValue, null, true);
+
+ HttpUtil.applyHeader(strategy, exchange, it, tc, key,
+ (multiValues, prev) -> applyHeader(httpRequest, key,
multiValues, prev));
+ }
+ }
+ }
+
+ private static void applyHeader(HttpUriRequest httpRequest, String key,
List<String> multiValues, String prev) {
+ // add the value(s) as a http request header
+ if (multiValues != null) {
+ // use the default toString of a ArrayList to create in the form
[xxx, yyy]
+ // if multi valued, for a single value, then just output the value
as is
+ String s = multiValues.size() > 1 ? multiValues.toString() :
multiValues.get(0);
+ httpRequest.addHeader(key, s);
+ } else if (prev != null) {
+ httpRequest.addHeader(key, prev);
+ }
+ }
+
+ private static void populateResponseCode(Message message,
ClassicHttpResponse httpResponse, int responseCode) {
+ // optimize for 200 response code as the boxing is outside the cached
integers
+ if (responseCode == 200) {
+ message.setHeader(Exchange.HTTP_RESPONSE_CODE, OK_RESPONSE_CODE);
+ } else {
+ message.setHeader(Exchange.HTTP_RESPONSE_CODE, responseCode);
+ }
+ if (httpResponse.getReasonPhrase() != null) {
+ message.setHeader(Exchange.HTTP_RESPONSE_TEXT,
httpResponse.getReasonPhrase());
+ }
+ }
+
+ protected Exception populateHttpOperationFailedException(
+ Exchange exchange, ClassicHttpResponse httpResponse, int
responseCode)
+ throws IOException, ParseException {
+ Exception answer;
+
+ String statusText = httpResponse.getReasonPhrase() != null ?
httpResponse.getReasonPhrase() : null;
+ Map<String, String> headers =
extractResponseHeaders(httpResponse.getHeaders());
+
+ Object responseBody = EntityUtils.toString(httpResponse.getEntity());
+
+ // make a defensive copy of the response body in the exception so its
detached from the cache
+ String copy = null;
+ if (responseBody != null) {
+ copy =
exchange.getContext().getTypeConverter().convertTo(String.class, exchange,
responseBody);
+ }
+
+ Header locationHeader = httpResponse.getFirstHeader("location");
+ String uri = getEndpoint().getHttpUri().toString();
+ if (locationHeader != null && responseCode >= 300 && responseCode <
400) {
+ answer = new HttpOperationFailedException(
+ uri, responseCode, statusText, locationHeader.getValue(),
headers, copy);
+ } else {
+ answer = new HttpOperationFailedException(uri, responseCode,
statusText, null, headers, copy);
+ }
+
+ return answer;
+ }
+
+ protected void populateResponse(
+ Exchange exchange, ClassicHttpResponse httpResponse,
+ HeaderFilterStrategy strategy, int responseCode)
+ throws IOException, ParseException {
+
+ Message answer = exchange.getMessage();
+ populateResponseCode(answer, httpResponse, responseCode);
+
+ // We just make the out message is not create when extractResponseBody
throws exception
+ Object responseBody = EntityUtils.toString(httpResponse.getEntity());
+ answer.setBody(responseBody);
+
+ // optimize to walk headers with an iterator which does not create a
new array as getAllHeaders does
+ boolean found = false;
+ Iterator<Header> it = httpResponse.headerIterator();
+ while (it.hasNext()) {
+ Header header = it.next();
+ String name = header.getName();
+ String value = header.getValue();
+ if (!found && name.equalsIgnoreCase("content-type")) {
+ name = Exchange.CONTENT_TYPE;
+ exchange.setProperty(ExchangePropertyKey.CHARSET_NAME,
IOHelper.getCharsetNameFromContentType(value));
+ found = true;
+ }
+ // use http helper to extract parameter value as it may contain
multiple values
+ Object extracted = HttpHelper.extractHttpParameterValue(value);
+ if (strategy != null &&
!strategy.applyFilterToExternalHeaders(name, extracted, exchange)) {
+ HttpHelper.appendHeader(answer.getHeaders(), name, extracted);
+ }
+ }
+ }
+
+ /**
+ * Extracts the response headers
+ *
+ * @param responseHeaders the headers
+ * @return the extracted headers or an empty map if no
headers existed
+ */
+ protected static Map<String, String> extractResponseHeaders(Header[]
responseHeaders) {
+ if (responseHeaders == null || responseHeaders.length == 0) {
+ return Map.of();
+ }
+
+ Map<String, String> answer = new HashMap<>();
+ for (Header header : responseHeaders) {
+ answer.put(header.getName(), header.getValue());
+ }
+
+ return answer;
+ }
+
protected static String buildRequestBody(String query, String
operationName, JsonObject variables) {
JsonObject jsonObject = new JsonObject();
jsonObject.put("query", query);
diff --git
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlComponentTest.java
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlComponentTest.java
index 24cff92c41ae..96459894721d 100644
---
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlComponentTest.java
+++
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/GraphqlComponentTest.java
@@ -25,6 +25,7 @@ import org.apache.camel.EndpointInject;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.graphql.server.GraphqlServer;
import org.apache.camel.component.mock.MockEndpoint;
+import org.apache.camel.http.base.HttpOperationFailedException;
import org.apache.camel.test.junit5.CamelTestSupport;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.ObjectHelper;
@@ -34,6 +35,8 @@ import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.fail;
public class GraphqlComponentTest extends CamelTestSupport {
@@ -118,6 +121,10 @@ public class GraphqlComponentTest extends CamelTestSupport
{
from("direct:start8")
.to("graphql://http://localhost:" + server.getPort() +
"/graphql?apikey=123456&query={books{id name}}")
.to("mock:result");
+ from("direct:start9")
+ .setHeader("foo", constant("cheese"))
+ .to("graphql://http://localhost:" + server.getPort() +
"/graphql?query={books{id name}}")
+ .to("mock:result");
}
};
}
@@ -208,7 +215,6 @@ public class GraphqlComponentTest extends CamelTestSupport {
@Test
public void checkApiKey() throws Exception {
-
GraphqlEndpoint graphqlEndpoint = (GraphqlEndpoint)
template.getCamelContext().getEndpoint(
"graphql://http://localhost:" + server.getPort() +
"/graphql?apikey=123456&query={books{id name}}");
URI httpUri = graphqlEndpoint.getHttpUri();
@@ -220,6 +226,34 @@ public class GraphqlComponentTest extends CamelTestSupport
{
template.sendBody("direct:start8", "");
result.assertIsSatisfied();
+ }
+
+ @Test
+ public void checkCustomHeader() throws Exception {
+ result.expectedMessageCount(1);
+ result.expectedBodiesReceived(booksQueryResult);
+ result.expectedHeaderReceived("foo", "cheese");
+ result.expectedHeaderReceived("bar", "response-cheese");
+
+ template.sendBody("direct:start9", "");
+
+ result.assertIsSatisfied();
+ }
+ @Test
+ public void checkThrowException() throws Exception {
+ result.expectedMessageCount(0);
+
+ try {
+ template.sendBodyAndHeader("direct:start9", "", "kaboom", "force
some error");
+ fail();
+ } catch (Exception e) {
+ HttpOperationFailedException he =
assertInstanceOf(HttpOperationFailedException.class, e.getCause());
+ assertEquals(500, he.getStatusCode());
+ assertEquals("Forced error due to kaboom",
he.getHttpResponseStatus());
+ }
+
+ result.assertIsSatisfied();
}
+
}
diff --git
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java
index 43edb1942bdc..65f71c622482 100644
---
a/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java
+++
b/components/camel-graphql/src/test/java/org/apache/camel/component/graphql/server/GraphqlServer.java
@@ -26,6 +26,7 @@ import graphql.ExecutionResult;
import graphql.GraphQL;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
@@ -68,8 +69,15 @@ public class GraphqlServer {
public void handle(ClassicHttpRequest request, ClassicHttpResponse
response, HttpContext context)
throws HttpException, IOException {
HttpEntity entity = request.getEntity();
- String json = EntityUtils.toString(entity);
+ Header h = request.getHeader("kaboom");
+ if (h != null) {
+ response.setCode(500);
+ response.setReasonPhrase("Forced error due to kaboom");
+ return;
+ }
+
+ String json = EntityUtils.toString(entity);
Map<String, Object> map = jsonToMap(json);
String query = (String) map.get("query");
String operationName = (String) map.get("operationName");
@@ -84,6 +92,10 @@ public class GraphqlServer {
Map<String, Object> resultMap = executionResult.toSpecification();
String result = objectMapper.writeValueAsString(resultMap);
+ h = request.getHeader("foo");
+ if (h != null) {
+ response.setHeader("bar", "response-" + h.getValue());
+ }
response.setHeader("Content-Type", "application/json;
charset=UTF-8");
response.setEntity(new StringEntity(result));
}
diff --git
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_16.adoc
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_16.adoc
index 2a081cefa188..624cb5f2e95f 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_16.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_16.adoc
@@ -18,6 +18,13 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation]
page for details.
The kamelet component is now parsing endpoint parameters using _raw mode_ to
ensure when using sensitive parameters
such as access keys, passwords etc. they are not URI encoded.
+=== camel-graphql
+
+The `camel-graphql` component now includes Camel message headers as HTTP
headers when calling the remote Graphql server.
+
+If the response is not success (2xx) then the component now throws the
`org.apache.camel.http.base.HttpOperationFailedException`.
+This can be turned off by setting `throwExceptionOnFailure=false`.
+
=== camel-infinispan
The `queryBuilder` option on `camel-infinispan` endpoint has been migrated to
no longer use the deprecated query factory
diff --git
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/GraphqlComponentBuilderFactory.java
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/GraphqlComponentBuilderFactory.java
index 6d52e8c94392..7c89c77f6bf9 100644
---
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/GraphqlComponentBuilderFactory.java
+++
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/GraphqlComponentBuilderFactory.java
@@ -76,6 +76,25 @@ public interface GraphqlComponentBuilderFactory {
}
+ /**
+ * Option to disable throwing the HttpOperationFailedException in case
+ * of failed responses from the remote server. This allows you to get
+ * all responses regardless of the HTTP status code.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param throwExceptionOnFailure the value to set
+ * @return the dsl builder
+ */
+ default GraphqlComponentBuilder throwExceptionOnFailure(boolean
throwExceptionOnFailure) {
+ doSetProperty("throwExceptionOnFailure", throwExceptionOnFailure);
+ return this;
+ }
+
+
/**
* Whether autowiring is enabled. This is used for automatic autowiring
* options (the option must be marked as autowired) by looking up in
the
@@ -114,6 +133,24 @@ public interface GraphqlComponentBuilderFactory {
doSetProperty("httpClient", httpClient);
return this;
}
+
+ /**
+ * To use a custom org.apache.camel.spi.HeaderFilterStrategy to filter
+ * header to and from Camel message.
+ *
+ * The option is a:
+ * <code>org.apache.camel.spi.HeaderFilterStrategy</code>
+ * type.
+ *
+ * Group: filter
+ *
+ * @param headerFilterStrategy the value to set
+ * @return the dsl builder
+ */
+ default GraphqlComponentBuilder
headerFilterStrategy(org.apache.camel.spi.HeaderFilterStrategy
headerFilterStrategy) {
+ doSetProperty("headerFilterStrategy", headerFilterStrategy);
+ return this;
+ }
}
class GraphqlComponentBuilderImpl
@@ -130,8 +167,10 @@ public interface GraphqlComponentBuilderFactory {
Object value) {
switch (name) {
case "lazyStartProducer": ((GraphqlComponent)
component).setLazyStartProducer((boolean) value); return true;
+ case "throwExceptionOnFailure": ((GraphqlComponent)
component).setThrowExceptionOnFailure((boolean) value); return true;
case "autowiredEnabled": ((GraphqlComponent)
component).setAutowiredEnabled((boolean) value); return true;
case "httpClient": ((GraphqlComponent)
component).setHttpClient((org.apache.hc.client5.http.classic.HttpClient)
value); return true;
+ case "headerFilterStrategy": ((GraphqlComponent)
component).setHeaderFilterStrategy((org.apache.camel.spi.HeaderFilterStrategy)
value); return true;
default: return false;
}
}
diff --git
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java
index 85147f4ee54f..055e6853409b 100644
---
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java
+++
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/GraphqlEndpointBuilderFactory.java
@@ -115,6 +115,40 @@ public interface GraphqlEndpointBuilderFactory {
doSetProperty("queryHeader", queryHeader);
return this;
}
+ /**
+ * Option to disable throwing the HttpOperationFailedException in case
+ * of failed responses from the remote server. This allows you to get
+ * all responses regardless of the HTTP status code.
+ *
+ * The option is a: <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param throwExceptionOnFailure the value to set
+ * @return the dsl builder
+ */
+ default GraphqlEndpointBuilder throwExceptionOnFailure(boolean
throwExceptionOnFailure) {
+ doSetProperty("throwExceptionOnFailure", throwExceptionOnFailure);
+ return this;
+ }
+ /**
+ * Option to disable throwing the HttpOperationFailedException in case
+ * of failed responses from the remote server. This allows you to get
+ * all responses regardless of the HTTP status code.
+ *
+ * The option will be converted to a <code>boolean</code> type.
+ *
+ * Default: true
+ * Group: producer
+ *
+ * @param throwExceptionOnFailure the value to set
+ * @return the dsl builder
+ */
+ default GraphqlEndpointBuilder throwExceptionOnFailure(String
throwExceptionOnFailure) {
+ doSetProperty("throwExceptionOnFailure", throwExceptionOnFailure);
+ return this;
+ }
/**
* The JsonObject instance containing the operation variables.
*
@@ -229,6 +263,38 @@ public interface GraphqlEndpointBuilderFactory {
return (GraphqlEndpointBuilder) this;
}
+ /**
+ * To use a custom HeaderFilterStrategy to filter header to and from
+ * Camel message.
+ *
+ * The option is a:
+ * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
+ *
+ * Group: common (advanced)
+ *
+ * @param headerFilterStrategy the value to set
+ * @return the dsl builder
+ */
+ default AdvancedGraphqlEndpointBuilder
headerFilterStrategy(org.apache.camel.spi.HeaderFilterStrategy
headerFilterStrategy) {
+ doSetProperty("headerFilterStrategy", headerFilterStrategy);
+ return this;
+ }
+ /**
+ * To use a custom HeaderFilterStrategy to filter header to and from
+ * Camel message.
+ *
+ * The option will be converted to a
+ * <code>org.apache.camel.spi.HeaderFilterStrategy</code> type.
+ *
+ * Group: common (advanced)
+ *
+ * @param headerFilterStrategy the value to set
+ * @return the dsl builder
+ */
+ default AdvancedGraphqlEndpointBuilder headerFilterStrategy(String
headerFilterStrategy) {
+ doSetProperty("headerFilterStrategy", headerFilterStrategy);
+ return this;
+ }
/**
* Whether the producer should be started lazy (on the first message).
* By starting lazy you can use this to allow CamelContext and routes
to