This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch feature/CAMEL-24961-catalog-body-types-most-used-2 in repository https://gitbox.apache.org/repos/asf/camel.git
commit 9d5daebee61de96cb48e56894a97868b2620880f Author: Claus Ibsen <[email protected]> AuthorDate: Thu Oct 22 19:40:05 2026 +0200 CAMEL-24961: Catalog body types for spring-redis, couchbase, fhir, solr, ssh, slack and telegram Declares the body types of seven more of the most used components: spring-redis, couchbase, fhir, solr, ssh, slack and telegram. All were checked at runtime with camel run: camel infra for redis, couchbase, solr and fhir, an embedded Apache MINA SSHD server for ssh, and fake APIs served by platform-http for slack and telegram. fhir is the first API component with body types. Fixes the doc prose about the body: the result types of the spring-redis commands and that a command without a result keeps the body, the couchbase consumer body and a section on the producer, the fhir inBody option and the result of a call, the solr bodies per operation and the response, the ssh output stream, the slack message body, and the telegram media and update bodies. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../apache/camel/catalog/components/couchbase.json | 5 ++ .../org/apache/camel/catalog/components/fhir.json | 5 ++ .../org/apache/camel/catalog/components/slack.json | 5 ++ .../org/apache/camel/catalog/components/solr.json | 4 + .../camel/catalog/components/spring-redis.json | 5 ++ .../org/apache/camel/catalog/components/ssh.json | 5 ++ .../apache/camel/catalog/components/telegram.json | 5 ++ .../camel/catalog/docs/couchbase-component.adoc | 16 +++- .../apache/camel/catalog/docs/fhir-component.adoc | 8 ++ .../apache/camel/catalog/docs/slack-component.adoc | 10 ++- .../apache/camel/catalog/docs/solr-component.adoc | 12 ++- .../camel/catalog/docs/spring-redis-component.adoc | 34 ++++---- .../apache/camel/catalog/docs/ssh-component.adoc | 4 + .../camel/catalog/docs/telegram-component.adoc | 15 +++- .../camel/component/couchbase/couchbase.json | 5 ++ .../src/main/docs/couchbase-component.adoc | 16 +++- .../component/couchbase/CouchbaseEndpoint.java | 23 ++++++ .../org/apache/camel/component/fhir/fhir.json | 5 ++ .../src/main/docs/fhir-component.adoc | 8 ++ .../apache/camel/component/fhir/FhirEndpoint.java | 27 +++++++ .../org/apache/camel/component/slack/slack.json | 5 ++ .../camel-slack/src/main/docs/slack-component.adoc | 10 ++- .../camel/component/slack/SlackEndpoint.java | 14 ++++ .../org/apache/camel/component/solr/solr.json | 4 + .../camel-solr/src/main/docs/solr-component.adoc | 12 ++- .../apache/camel/component/solr/SolrEndpoint.java | 17 ++++ .../apache/camel/component/redis/spring-redis.json | 5 ++ .../src/main/docs/spring-redis-component.adoc | 34 ++++---- .../camel/component/redis/RedisEndpoint.java | 84 +++++++++++++++++++ .../org/apache/camel/component/ssh/ssh.json | 5 ++ .../camel-ssh/src/main/docs/ssh-component.adoc | 4 + .../apache/camel/component/ssh/SshEndpoint.java | 15 ++++ .../apache/camel/component/telegram/telegram.json | 5 ++ .../src/main/docs/telegram-component.adoc | 15 +++- .../camel/component/telegram/TelegramEndpoint.java | 22 +++++ .../camel/dsl/yaml/validator/TextBodyFlowTest.java | 93 ++++++++++++++++++++++ 36 files changed, 511 insertions(+), 50 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/couchbase.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/couchbase.json index 4681d0125811..20c35c68bcd8 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/couchbase.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/couchbase.json @@ -38,6 +38,11 @@ "CamelCouchbaseViewName": { "index": 4, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The view name", "constantName": "org.apache.camel.component.couchbase.CouchbaseConstants#HEADER_VIEWNAME" }, "CamelCqlResumeQuery": { "index": 5, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The resume action to execute when resuming.", "constantName": "org.apache.camel.component.couchbase.CouchbaseConstants#COUCHBASE_RESUME_ACTION" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "com.couchbase.client.java.kv.GetResult", "byOption": [ { "option": "fullDocument", "value": "false", "carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": "useView", "value": "true", "carriers": [ "pojo" ], "javaType": "com.couchbase.client.java.kv.GetResult" } ], "description": "One message per document the query finds, read by its id (the CamelCouchbaseId header) as the GetResult of the Couchbase SDK: contentAsOb [...] + "producerIn": { "carriers": [ "text", "map", "list", "pojo" ], "dependsOn": [ "operation" ], "description": "The document that CCB_PUT, the default, stores as JSON: a Map, a List or a POJO becomes JSON, and text is stored as a JSON string, so unmarshal JSON text first. Bytes, a stream or no body fail. CCB_GET and CCB_DEL do not use the body: the id is the CamelCouchbaseId header" }, + "producerOut": { "carriers": [ "value" ], "javaType": "java.lang.Boolean", "dependsOn": [ "operation" ], "description": "true for CCB_PUT, the default; the GetResult of the Couchbase SDK for CCB_GET, and the MutationResult as text for CCB_DEL. The producer removes the CamelCouchbaseId header" } + }, "properties": { "protocol": { "index": 0, "kind": "path", "displayName": "Protocol", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The protocol to use" }, "hostname": { "index": 1, "kind": "path", "displayName": "Hostname", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The hostname to use" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/fhir.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/fhir.json index e7797c75d1e1..d122ed869f39 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/fhir.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/fhir.json @@ -53,6 +53,11 @@ "password": { "index": 25, "kind": "property", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "Password to use for basic authentication" }, "username": { "index": 26, "kind": "property", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "Username to use for basic authentication" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "apiName", "value": "TRANSACTION", "carriers": [ "pojo", "text" ] } ], "description": "One message per poll with the return value of the method: a MethodOutcome for create, update, patch, delete and validate, and a HAPI FHIR model object of the fhirVersion for the others, such as the Bundle of a search. A List is split into one message per resource, and transaction withBundle with stringBundle gives the response as text" }, + "producerIn": { "carriers": [ "any" ], "dependsOn": [ "inBody" ], "description": "The body is used only when inBody names the argument of the method it is, and then passed as it is, without type conversion: resourceAsString, stringBundle and url take text (convertBodyTo String first), and resource, bundle and resources a HAPI FHIR object or a List of them (unmarshal with fhirJson or fhirXml first). Without inBody the arguments come from the options and the CamelFhir. headers" }, + "producerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "apiName", "value": "TRANSACTION", "carriers": [ "pojo", "text", "list" ] } ], "description": "The return value of the method: a MethodOutcome for create, update, patch, delete and validate, and a HAPI FHIR model object of the fhirVersion for the others, such as the Bundle of a search or the resource of a read; marshal a resource or a Bundle with fhirJson or fhirXml to get text. transaction gives a Bundle for withBund [...] + }, "properties": { "apiName": { "index": 0, "kind": "path", "displayName": "Api Name", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.fhir.internal.FhirApiName", "enum": [ "CAPABILITIES", "CREATE", "DELETE", "HISTORY", "LOAD_PAGE", "META", "OPERATION", "PATCH", "READ", "SEARCH", "TRANSACTION", "UPDATE", "VALIDATE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.componen [...] "methodName": { "index": 1, "kind": "path", "displayName": "Method Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "What sub operation to use for the selected operation" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/slack.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/slack.json index c127837baf01..8fd717ae367a 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/slack.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/slack.json @@ -32,6 +32,11 @@ "token": { "index": 5, "kind": "property", "displayName": "Token", "group": "token", "label": "token", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The token to access Slack. This app needs to have channels:history, groups:history, im:history, mpim:history, channels:read, groups:read, im:read and mpim:read permissions. The User OAuth Token is the kind of token needed." }, "webhookUrl": { "index": 6, "kind": "property", "displayName": "Webhook Url", "group": "webhook", "label": "webhook", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The incoming webhook URL" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "com.slack.api.model.Message", "description": "One message per new message of the conversation, the most recent first, or the oldest first with naturalOrder; the first poll gives the last message of the history. The text is ${body.text}" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "description": "The text of the message, or a com.slack.api.model.Message with its blocks: bytes, a stream or a file are read as text. A Map or another POJO is sent as its toString(): marshal it first. No body sends no text, which Slack rejects" }, + "producerOut": { "carriers": [ "unchanged" ], "description": "Unchanged: the answer of Slack is not kept" } + }, "properties": { "channel": { "index": 0, "kind": "path", "displayName": "Channel", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The channel name (syntax #name) or slack user (syntax userName) to send a message directly to an user." }, "token": { "index": 1, "kind": "parameter", "displayName": "Token", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "description": "The token to access Slack. This app needs to have channels:history, groups:history, im:history, mpim:history, channels:read, groups:read, im:read and mpim:read permissions. The User OAuth Token is the kind of token needed." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json index 698cf8e42fe4..c5806545bcf2 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/solr.json @@ -51,6 +51,10 @@ "CamelSolrField.": { "index": 9, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prefix of the headers that carry the fields of the document to insert: the header CamelSolrField.id holds the id field.", "constantName": "org.apache.camel.component.solr.SolrConstants#HEADER_FIELD_PREFIX" }, "CamelSolrParam.": { "index": 10, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prefix of the headers that carry extra request parameters: the header CamelSolrParam.commit holds the commit parameter.", "constantName": "org.apache.camel.component.solr.SolrConstants#HEADER_PARAM_PREFIX" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": "operation", "value": "DELETE", "carriers": [ "text", "list" ] } ], "description": "What the operation needs: INSERT a Map or a SolrInputDocument, a List of them or of @Field beans, a JSON array or XML text, or a file; DELETE the id or, with deleteByQuery=true, the query, as text or a List of text; SEARCH the query as text or a SolrQuery; PING does not use it. Convert bytes or a stream to text first. The CamelSolrField. [...] + "producerOut": { "carriers": [ "map" ], "description": "The response of Solr as a Map (a SimpleOrderedMap) with the responseHeader and, for a search, the documents under response as a SolrDocumentList. Convert it to a QueryResponse or a SolrPingResponse to read it with SolrJ" } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.solr.SolrConfiguration", "configurationField": "configuration", "description": "The solr instance host name (set to 'default' to use the host name defined on component level)" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8983, "configurationClass": "org.apache.camel.component.solr.SolrConfiguration", "configurationField": "configuration", "description": "The solr instance port number" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json index d6b8fc72d591..d10a3cef862f 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/spring-redis.json @@ -60,6 +60,11 @@ "CamelRedis.Longitude": { "index": 27, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Longitude.", "constantName": "org.apache.camel.component.redis.RedisConstants#LONGITUDE" }, "CamelRedis.Radius": { "index": 28, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Radius.", "constantName": "org.apache.camel.component.redis.RedisConstants#RADIUS" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unknown" ], "description": "The message as the serializer reads it: with the default JDK serializer the Java object the publisher serialized, such as the String a PUBLISH of this component sent, with the channel in the CamelRedis.Channel header. A message that is not Java serialized, such as one from redis-cli, is dropped with a WARN: set a StringRedisSerializer as the serializer to receive text" }, + "producerIn": { "carriers": [ "any" ], "description": "The body is not used: the key, the value and the other arguments of the command come from headers, such as CamelRedis.Key and CamelRedis.Value, or CamelRedis.Channel and CamelRedis.Message for PUBLISH. Set CamelRedis.Value from the body to store the body: with the default serializer it must be Serializable, so a stream fails" }, + "producerOut": { "carriers": [ "unchanged" ], "dependsOn": [ "command" ], "byOption": [ { "option": "command", "value": "SET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "SETEX", "carriers": [ "unchanged" ] }, { "option": "command", "value": "MSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HMSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HDEL", "car [...] + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "The host where Redis server is running." }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Redis server port number" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json index dd1c9ac5740b..6fa65e1a1bc7 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/ssh.json @@ -63,6 +63,11 @@ "CamelSshStderr": { "index": 2, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "InputStream", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header is a InputStream with the standard error stream of the executable.", "constantName": "org.apache.camel.component.ssh.SshConstants#STDERR" }, "CamelSshExitValue": { "index": 3, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The value of this header is the exit value that is returned, after the execution. By convention a non-zero status exit value indicates abnormal termination. Note that the exit value is OS dependent.", "constantName": "org.apac [...] }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream" ], "description": "The standard output of the pollCommand as a stream, with the exit value and the standard error in the CamelSshExitValue and CamelSshStderr headers" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "description": "The command to run, as text: bytes, a stream or a file are read as text. A Map or a POJO is sent as its toString(): build the command as text first. No body fails" }, + "producerOut": { "carriers": [ "stream" ], "description": "The standard output of the command as a stream, with the exit value and the standard error in the CamelSshExitValue and CamelSshStderr headers; an exit value that is not 0 does not fail the exchange. Convert it to text to read it more than once" } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.ssh.SshConfiguration", "configurationField": "configuration", "description": "Sets the hostname of the remote SSH server." }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 22, "configurationClass": "org.apache.camel.component.ssh.SshConfiguration", "configurationField": "configuration", "description": "Sets the port number for the remote SSH server." }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/telegram.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/telegram.json index a26ad023217c..2c99fd226779 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/telegram.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/telegram.json @@ -41,6 +41,11 @@ "CamelTelegramParseMode": { "index": 4, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.telegram.TelegramParseMode", "enum": [ "HTML", "MARKDOWN" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "This header is used to format text messages using HTML or Markdown", "constantName": "org.apache.camel.component.telegram.TelegramConstants#TELEGRAM_PARSE_MODE" }, "CamelMessageTimestamp": { "index": 5, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message timestamp.", "constantName": "org.apache.camel.component.telegram.TelegramConstants#MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo", "null" ], "description": "One message per update: an IncomingMessage for a message or a channel post, with the chat id in the CamelTelegramChatId header, and the IncomingCallbackQuery, IncomingInlineQuery, PreCheckoutQuery, ShippingQuery or PaidMediaPurchased of the other updates; no body for an update of another kind, such as an edited message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo", "null" ], "description": "A message of org.apache.camel.component.telegram.model, such as an OutgoingTextMessage, or text to send as a text message: a stream or a file is read as text, and a Map or another POJO is sent as its toString(), so marshal it first. A byte array is sent as the photo, audio, video or document the CamelTelegramMediaType header names, and fails without it. No body sends nothing" }, + "producerOut": { "carriers": [ "pojo", "unchanged" ], "description": "The MessageResult of the call, with the message sent, or a subclass of it such as MessageResultString for createInvoiceLink; an answer with ok=false is a MessageResult too. No body is left as it is" } + }, "properties": { "type": { "index": 0, "kind": "path", "displayName": "Type", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "bots" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.telegram.TelegramConfiguration", "configurationField": "configuration", "description": "The endpoint type. Currently, only the 'bots' type is supported." }, "limit": { "index": 1, "kind": "parameter", "displayName": "Limit", "group": "consumer", "label": "consumer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 100, "configurationClass": "org.apache.camel.component.telegram.TelegramConfiguration", "configurationField": "configuration", "description": "Limit on the number of updates that can be received in a si [...] diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/couchbase-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/couchbase-component.adoc index 8d5ad8edda5e..707109794a00 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/couchbase-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/couchbase-component.adoc @@ -116,8 +116,10 @@ from("couchbase:http://localhost?bucket=myBucket&username=user&password=pass" The query must include `META().id AS \__id` in the SELECT clause so the consumer can identify each document. -When `fullDocument` is `true` (the default), the consumer fetches the complete document from the KV store using -the document ID. When `false`, the query result row is used directly as the message body. +When `fullDocument` is `true` (the default), the consumer fetches the document from the KV store by its id, and the +message body is the `GetResult` of the Couchbase SDK, with the id in the `CamelCouchbaseId` header: `contentAsObject()` +gives the JSON document. When `false`, the message body is the query result row as JSON text, or with views the value the +view emits. NOTE: SQL++ queries require a primary index or appropriate secondary index on the bucket/collection. Create one with: `CREATE PRIMARY INDEX ON \`myBucket\`` or via the Couchbase SDK. @@ -192,6 +194,16 @@ from("couchbase:http://localhost?bucket=myBucket&username=user&password=pass" + "&statement=SELECT META().id AS __id, * FROM `myBucket` ORDER BY META().id DESC LIMIT 10") ---- +== Producing documents + +The producer runs the operation of the `operation` option: `CCB_PUT` (the default) stores the message body as the +document with the id of the `CamelCouchbaseId` header, `CCB_GET` reads the document of that id, and `CCB_DEL` deletes +it. The producer removes the `CamelCouchbaseId` header after each call, so set it again before the next one. + +`CCB_PUT` stores the body as JSON: a Map, a List or a POJO becomes a JSON document, and text is stored as a JSON string, +so unmarshal JSON text to a Map first. A byte array, a stream or no body fail. After the call the message body is `true` +for `CCB_PUT`, the `GetResult` of the Couchbase SDK for `CCB_GET`, and the `MutationResult` as text for `CCB_DEL`. + == Couchbase SDK compatibility Using collections and scopes is supported only for Couchbase Server 7.0 and later. diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fhir-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fhir-component.adoc index ad69d116e9e7..df7f1a144759 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fhir-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/fhir-component.adoc @@ -120,3 +120,11 @@ The URI names the API and its method, such as `fhir://read/resourceById`. The pa - log: message: "Found ${body.total} patients" ---- + +The `inBody` option names the parameter of the method that the message body is, such as `resourceAsString` in the +first route. The body is passed as it is, without type conversion, so a parameter of type String needs a String body: +convert a byte array or a stream with `convertBodyTo` first. Without `inBody` the body is not used. + +After the call the message body is the result of the method: a `MethodOutcome` for create, update, patch, delete and +validate, and a HAPI FHIR model object for the others, such as the `Bundle` of a search or the resource of a read. Marshal +a resource or a `Bundle` with `fhirJson` or `fhirXml` to get text. diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/slack-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/slack-component.adoc index b0708b1d502b..7f26de4031be 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/slack-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/slack-component.adoc @@ -53,7 +53,12 @@ include::partial$component-endpoint-headers.adoc[] == Usage To send a message contained in the message body, a pre-established -https://api.slack.com/incoming-webhooks[Slack incoming webhook] must be configured in Slack. +https://api.slack.com/incoming-webhooks[Slack incoming webhook] must be configured in Slack, or the producer uses a token +of a Slack app. + +The producer sends the message body as the text of the message: bytes, a stream or a file are read as text. A +`com.slack.api.model.Message` body is sent with its blocks and attachments. Marshal a Map or another object to text +first, as it is otherwise sent as its `toString()`. The message body is unchanged after the call. === Configuring in Spring XML @@ -185,7 +190,8 @@ YAML:: ---- ==== -This way you'll get the last message from `general` channel. +This way you'll get the last message from `general` channel, as a `com.slack.api.model.Message`, whose text is +`${body.text}`. The consumer will track the timestamp of the last message consumed, and in the next poll it will consume only newer messages in the channel. When more than `maxResults` new messages were posted since the previous poll, the consumer reads the following pages of diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/solr-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/solr-component.adoc index 9e574e8b94b5..5e8d8ccca062 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/solr-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/solr-component.adoc @@ -62,16 +62,22 @@ the following. Some operations also require the message body to be set. |`INSERT` | File |inserts/updates a document or documents using the given File (using ContentStreamUpdateRequest) |`INSERT` | SolrInputDocument or Collection<SolrInputDocument>|inserts/updates a document or documents based on the given (collection of) SolrInputDocument |`INSERT` | bean or Collection<bean> |inserts/updates a document or documents based on values in an http://wiki.apache.org/solr/Solrj#Directly_adding_POJOs_to_Solr[annotated bean] -|`INSERT` | String |inserts/updates a document or documents index based on the given XML or json as string (must follow SolrInputDocument format) +|`INSERT` | Map or Collection<Map> |inserts/updates a document or documents with the fields of the Map +|`INSERT` | String |inserts/updates the documents of the given JSON or XML text: a JSON array of documents, or XML documents (an `<add>` element is added when the text does not start with one) -|`DELETE` | String or Collection<String> | delete a record by ID (or collection of ids) or by a query (or a collection of queries) when header "deleteByQuery=true" +|`DELETE` | String or Collection<String> | delete a record by ID (or collection of ids) or by a query (or a collection of queries) when the `deleteByQuery` option or the `CamelSolrDeleteByQuery` header is true -|`PING` |SolrPing | Pings the solr instance +|`PING` |n/a | Pings the solr instance |`SEARCH` |SolrQuery | Performs a search request to the solr instance |`SEARCH` |QueryRequest | Performs a search request to the solr instance +|`SEARCH` |String | Performs a search with the query as text, unless the `CamelSolrQueryString` header holds the query |======================================================================= +After each operation the message body is the response of Solr as a Map (a `SimpleOrderedMap`) with the +`responseHeader`, and for a search the documents under `response` as a `SolrDocumentList`. Convert it to a +`QueryResponse` to read it with SolrJ. + == Example Below is a simple `INSERT`, `DELETE` and `SEARCH` example diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-redis-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-redis-component.adoc index 302c90ea9268..a9037b6057bb 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-redis-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/spring-redis-component.adoc @@ -53,7 +53,9 @@ at https://github.com/apache/camel/tree/main/components/camel-spring-redis/src/t The producer runs the command of the `command` option, or of the `CamelRedis.Command` header. The parameters of the command come from headers, such as `CamelRedis.Key` and `CamelRedis.Value`, not from the -message body. The tables below list the parameters of each command. The result of the command is the message body: +message body. The tables below list the parameters of each command. The result of the command becomes the message body, +and a command without a result, such as `SET`, or a result that is null, such as `GET` of a missing key, leaves the body +as it was: [source,yaml] ---- @@ -156,17 +158,19 @@ these keys: === Message headers evaluated by the Redis producer The producer issues commands to the server, and each command has -a different set of parameters with specific types. The result from the -command execution is returned in the message body. +a different set of parameters with specific types. The result of the +command becomes the message body. A command whose result is void, and a result that is null, leave the body as it was. +The value commands, such as `GET`, `HGET` and `LPOP`, return the object that was stored: with the default serializer the +Java object the `CamelRedis.Value` header held. [width="100%",cols="10%,40%,50%,10%",options="header",] |======================================================================= |Hash Commands |Description |Parameters |Result |`HSET` |Set the string value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |void -|`HGET` |Get the value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String) |String +|`HGET` |Get the value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String) |Object |`HSETNX` |Set the value of a hash field only if the field does not exist |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" -(Object) |void +(Object) |Boolean |`HMSET` |Set multiple hash fields to multiple values |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUES`/"CamelRedis.Values" (Map<String, Object>) |void |`HMGET` |Get the values of all the given hash fields |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELDS`/"CamelRedis.Fields" (Collection<String>) |Collection<Object> |`HINCRBY` |Increment the integer value of a hash field by the given number |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" @@ -195,7 +199,7 @@ command execution is returned in the message body. |`LTRIM` |Trim a list to the specified range |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.START`/"CamelRedis.Start"Long), `RedisConstants.END`/"CamelRedis.End" (Long) |void -|`LINDEX` |Get an element from a list by its index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.INDEX`/"CamelRedis.Index" (Long) |String +|`LINDEX` |Get an element from a list by its index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.INDEX`/"CamelRedis.Index" (Long) |Object |`LINSERT` |Insert an element before or after another element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.PIVOT`/"CamelRedis.Pivot" (String), `RedisConstants.POSITION`/"CamelRedis.Position" (String) |Long @@ -208,7 +212,7 @@ command execution is returned in the message body. |`LPOP` |Remove and get the first element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object -|`RPOP` |Remove and get the last element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`RPOP` |Remove and get the last element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |`RPOPLPUSH` |Remove the last element in a list, append it to another list and return it |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |Object @@ -221,20 +225,20 @@ until one is available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisCo available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |Object |`BRPOP` |Remove and get the last element in a list, or block until one is -available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |String +available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |Object |======================================================================= [width="100%",cols="10%,40%,50%,10%",options="header",] |======================================================================= |Set Commands |Description |Parameters |Result -|`SADD` |Add one or more members to a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`SADD` |Add one or more members to a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long |`SMEMBERS` |Get all the members in a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Set<Object> -|`SREM` |Remove one or more members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`SREM` |Remove one or more members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long -|`SPOP` |Remove and return a random member from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`SPOP` |Remove and return a random member from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |`SMOVE` |Move a member from one set to another |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |Boolean @@ -258,7 +262,7 @@ available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEO |`SDIFFSTORE` |Subtract multiple sets and store the resulting set in a key |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.KEYS`/"CamelRedis.Keys" (String), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |void -|`SRANDMEMBER` |Get one or multiple random members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`SRANDMEMBER` |Get one or multiple random members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |======================================================================= [width="100%",cols="10%,40%,50%,10%",options="header",] @@ -272,7 +276,7 @@ already exists |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants. |`ZRANGE` |Return a range of members in a sorted set, by index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.START`/"CamelRedis.Start"Long), `RedisConstants.END`/"CamelRedis.End" (Long), `RedisConstants.WITHSCORE`/"CamelRedis.WithScore" (Boolean) |Object -|`ZREM` |Remove one or more members from a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`ZREM` |Remove one or more members from a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long |`ZINCRBY` |Increment the score of a member in a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.INCREMENT`/"CamelRedis.Increment" (Double) |Double @@ -362,9 +366,9 @@ new key |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.KEYS`/" |`DEL` |Delete a key |`RedisConstants.KEYS`/"CamelRedis.Keys" (String) |void -|`TYPE` |Determine the type stored at key |`RedisConstants.KEY`/"CamelRedis.Key" (String) |DataType +|`TYPE` |Determine the type stored at key |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String -|`KEYS` |Find all keys matching the given pattern |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String) |Collection<String> +|`KEYS` |Find all keys matching the given pattern |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String) |Set<String> |`RANDOMKEY` |Return a random key from the keyspace |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (String) |String diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc index e1b2e38de453..79f1fabccde9 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/ssh-component.adoc @@ -50,6 +50,10 @@ When the SSH Component is used as a Producer (`.to("ssh://...")`), it will send the message body as the command to execute on the remote SSH server. +After the call, the message body is the standard output of the command as a stream, with the exit value in the +`CamelSshExitValue` header and the standard error, as a stream as well, in the `CamelSshStderr` header. An exit value +that is not 0 does not fail the exchange. The polling consumer gives the output of its `pollCommand` the same way. + Here is an example of this. Note that the command has a newline (`+ +`). diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/telegram-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/telegram-component.adoc index 1c01a0531f03..decdf4ee0d9e 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/telegram-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/telegram-component.adoc @@ -123,7 +123,7 @@ The following message bodies are allowed for a producer endpoint (messages of ty | `EditMessageMediaMessage` | To edit animation, audio, document, photo, or video messages. (editMessageMedia) | `EditMessageReplyMarkupMessage` | To edit only the reply markup of a message. (editMessageReplyMarkup) | `EditMessageDelete` | To delete a message, including service messages. (deleteMessage) -| `SendLocationMessage` | To send a location (setSendLocation) +| `SendLocationMessage` | To send a location (sendLocation) | `EditMessageLiveLocationMessage` | To send changes to a live location (editMessageLiveLocation) | `StopMessageLiveLocationMessage` | To stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires (stopMessageLiveLocation) | `SendVenueMessage` | To send information about a venue (sendVenue) @@ -136,11 +136,15 @@ The following message bodies are allowed for a producer endpoint (messages of ty | `GetStarTransactionsMessage` | To get the list of bot's Telegram Star transactions (getStarTransactions) | `RefundStarPaymentMessage` | To refund a successful payment in Telegram Stars (refundStarPayment) | `EditUserStarSubscriptionMessage` | To cancel or re-enable extension of a subscription paid in Telegram Stars (editUserStarSubscription) -| `byte[]` | To send any media type supported. It requires the `CamelTelegramMediaType` header to be set to the appropriate media type +| `byte[]` | To send a photo, an audio, a video or a document, as the `CamelTelegramMediaType` header names (`PHOTO_PNG`, `PHOTO_JPG`, `AUDIO`, `VIDEO` or `DOCUMENT`). It fails without the header. A stream or a file is sent as text, so convert it to `byte[]` to send it as media | `String` | To send a text message to a chat. It gets converted automatically into a `OutgoingTextMessage` |=================================================== +Any other body is sent as text, so marshal a Map or another object first, as it is otherwise sent as its `toString()`. +After the call the message body is the `MessageResult` of the call, with the message sent, or a subclass of it, such as +`MessageResultString` for `CreateInvoiceLinkMessage`. A message body that is null sends nothing, and stays null. + === Consumer @@ -204,11 +208,16 @@ Supported types for incoming messages are |=================================================== | Java Type | Description -| `IncomingMessage` | The full object representation of an incoming message +| `IncomingMessage` | The full object representation of an incoming message or channel post, with the chat id in the `CamelTelegramChatId` header | `String` | The content of the message, for text messages only +| `IncomingCallbackQuery` | A callback query, from a button of an inline keyboard +| `IncomingInlineQuery` | An inline query +| `PreCheckoutQuery`, `ShippingQuery`, `PaidMediaPurchased` | The payment updates, of the package `org.apache.camel.component.telegram.model.payments` |=================================================== +An update of another kind, such as an edited message, gives a message with no body. + === Reactive Chat-Bot Example diff --git a/components/camel-couchbase/src/generated/resources/META-INF/org/apache/camel/component/couchbase/couchbase.json b/components/camel-couchbase/src/generated/resources/META-INF/org/apache/camel/component/couchbase/couchbase.json index 4681d0125811..20c35c68bcd8 100644 --- a/components/camel-couchbase/src/generated/resources/META-INF/org/apache/camel/component/couchbase/couchbase.json +++ b/components/camel-couchbase/src/generated/resources/META-INF/org/apache/camel/component/couchbase/couchbase.json @@ -38,6 +38,11 @@ "CamelCouchbaseViewName": { "index": 4, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The view name", "constantName": "org.apache.camel.component.couchbase.CouchbaseConstants#HEADER_VIEWNAME" }, "CamelCqlResumeQuery": { "index": 5, "kind": "header", "displayName": "", "group": "consumer", "label": "consumer", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The resume action to execute when resuming.", "constantName": "org.apache.camel.component.couchbase.CouchbaseConstants#COUCHBASE_RESUME_ACTION" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "com.couchbase.client.java.kv.GetResult", "byOption": [ { "option": "fullDocument", "value": "false", "carriers": [ "text" ], "javaType": "java.lang.String" }, { "option": "useView", "value": "true", "carriers": [ "pojo" ], "javaType": "com.couchbase.client.java.kv.GetResult" } ], "description": "One message per document the query finds, read by its id (the CamelCouchbaseId header) as the GetResult of the Couchbase SDK: contentAsOb [...] + "producerIn": { "carriers": [ "text", "map", "list", "pojo" ], "dependsOn": [ "operation" ], "description": "The document that CCB_PUT, the default, stores as JSON: a Map, a List or a POJO becomes JSON, and text is stored as a JSON string, so unmarshal JSON text first. Bytes, a stream or no body fail. CCB_GET and CCB_DEL do not use the body: the id is the CamelCouchbaseId header" }, + "producerOut": { "carriers": [ "value" ], "javaType": "java.lang.Boolean", "dependsOn": [ "operation" ], "description": "true for CCB_PUT, the default; the GetResult of the Couchbase SDK for CCB_GET, and the MutationResult as text for CCB_DEL. The producer removes the CamelCouchbaseId header" } + }, "properties": { "protocol": { "index": 0, "kind": "path", "displayName": "Protocol", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The protocol to use" }, "hostname": { "index": 1, "kind": "path", "displayName": "Hostname", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The hostname to use" }, diff --git a/components/camel-couchbase/src/main/docs/couchbase-component.adoc b/components/camel-couchbase/src/main/docs/couchbase-component.adoc index 8d5ad8edda5e..707109794a00 100644 --- a/components/camel-couchbase/src/main/docs/couchbase-component.adoc +++ b/components/camel-couchbase/src/main/docs/couchbase-component.adoc @@ -116,8 +116,10 @@ from("couchbase:http://localhost?bucket=myBucket&username=user&password=pass" The query must include `META().id AS \__id` in the SELECT clause so the consumer can identify each document. -When `fullDocument` is `true` (the default), the consumer fetches the complete document from the KV store using -the document ID. When `false`, the query result row is used directly as the message body. +When `fullDocument` is `true` (the default), the consumer fetches the document from the KV store by its id, and the +message body is the `GetResult` of the Couchbase SDK, with the id in the `CamelCouchbaseId` header: `contentAsObject()` +gives the JSON document. When `false`, the message body is the query result row as JSON text, or with views the value the +view emits. NOTE: SQL++ queries require a primary index or appropriate secondary index on the bucket/collection. Create one with: `CREATE PRIMARY INDEX ON \`myBucket\`` or via the Couchbase SDK. @@ -192,6 +194,16 @@ from("couchbase:http://localhost?bucket=myBucket&username=user&password=pass" + "&statement=SELECT META().id AS __id, * FROM `myBucket` ORDER BY META().id DESC LIMIT 10") ---- +== Producing documents + +The producer runs the operation of the `operation` option: `CCB_PUT` (the default) stores the message body as the +document with the id of the `CamelCouchbaseId` header, `CCB_GET` reads the document of that id, and `CCB_DEL` deletes +it. The producer removes the `CamelCouchbaseId` header after each call, so set it again before the next one. + +`CCB_PUT` stores the body as JSON: a Map, a List or a POJO becomes a JSON document, and text is stored as a JSON string, +so unmarshal JSON text to a Map first. A byte array, a stream or no body fail. After the call the message body is `true` +for `CCB_PUT`, the `GetResult` of the Couchbase SDK for `CCB_GET`, and the `MutationResult` as text for `CCB_DEL`. + == Couchbase SDK compatibility Using collections and scopes is supported only for Couchbase Server 7.0 and later. diff --git a/components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseEndpoint.java b/components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseEndpoint.java index d83117e80425..8e04df6bdc2d 100644 --- a/components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseEndpoint.java +++ b/components/camel-couchbase/src/main/java/org/apache/camel/component/couchbase/CouchbaseEndpoint.java @@ -39,6 +39,9 @@ import org.apache.camel.Category; import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; @@ -63,6 +66,26 @@ import static org.apache.camel.component.couchbase.CouchbaseConstants.DEFAULT_VI */ @UriEndpoint(firstVersion = "2.19.0", scheme = "couchbase", title = "Couchbase", syntax = "couchbase:protocol://hostname:port", category = { Category.DATABASE }, headersClass = CouchbaseConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, javaType = "com.couchbase.client.java.kv.GetResult", + byOption = { + @BodyType.When(option = "fullDocument", value = "false", carriers = Carrier.TEXT, + javaType = "java.lang.String"), + @BodyType.When(option = "useView", value = "true", carriers = Carrier.POJO, + javaType = "com.couchbase.client.java.kv.GetResult") }, + description = "One message per document the query finds, read by its id (the" + + " CamelCouchbaseId header) as the GetResult of the Couchbase SDK:" + + " contentAsObject() gives the JSON document. With fullDocument=false the row" + + " of the SQL++ query as JSON text, or with useView=true the value the view" + + " emits"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.MAP, Carrier.LIST, Carrier.POJO }, dependsOn = "operation", + description = "The document that CCB_PUT, the default, stores as JSON: a Map, a List or a" + + " POJO becomes JSON, and text is stored as a JSON string, so unmarshal JSON" + + " text first. Bytes, a stream or no body fail. CCB_GET and CCB_DEL do not use" + + " the body: the id is the CamelCouchbaseId header"), + producerOut = @BodyType(value = Carrier.VALUE, javaType = "java.lang.Boolean", dependsOn = "operation", + description = "true for CCB_PUT, the default; the GetResult of the Couchbase SDK for" + + " CCB_GET, and the MutationResult as text for CCB_DEL. The producer removes" + + " the CamelCouchbaseId header")) public class CouchbaseEndpoint extends ScheduledPollEndpoint implements EndpointServiceLocation { @UriPath diff --git a/components/camel-fhir/camel-fhir-component/src/generated/resources/META-INF/org/apache/camel/component/fhir/fhir.json b/components/camel-fhir/camel-fhir-component/src/generated/resources/META-INF/org/apache/camel/component/fhir/fhir.json index e7797c75d1e1..d122ed869f39 100644 --- a/components/camel-fhir/camel-fhir-component/src/generated/resources/META-INF/org/apache/camel/component/fhir/fhir.json +++ b/components/camel-fhir/camel-fhir-component/src/generated/resources/META-INF/org/apache/camel/component/fhir/fhir.json @@ -53,6 +53,11 @@ "password": { "index": 25, "kind": "property", "displayName": "Password", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "Password to use for basic authentication" }, "username": { "index": 26, "kind": "property", "displayName": "Username", "group": "security", "label": "security", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "Username to use for basic authentication" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "apiName", "value": "TRANSACTION", "carriers": [ "pojo", "text" ] } ], "description": "One message per poll with the return value of the method: a MethodOutcome for create, update, patch, delete and validate, and a HAPI FHIR model object of the fhirVersion for the others, such as the Bundle of a search. A List is split into one message per resource, and transaction withBundle with stringBundle gives the response as text" }, + "producerIn": { "carriers": [ "any" ], "dependsOn": [ "inBody" ], "description": "The body is used only when inBody names the argument of the method it is, and then passed as it is, without type conversion: resourceAsString, stringBundle and url take text (convertBodyTo String first), and resource, bundle and resources a HAPI FHIR object or a List of them (unmarshal with fhirJson or fhirXml first). Without inBody the arguments come from the options and the CamelFhir. headers" }, + "producerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "apiName", "value": "TRANSACTION", "carriers": [ "pojo", "text", "list" ] } ], "description": "The return value of the method: a MethodOutcome for create, update, patch, delete and validate, and a HAPI FHIR model object of the fhirVersion for the others, such as the Bundle of a search or the resource of a read; marshal a resource or a Bundle with fhirJson or fhirXml to get text. transaction gives a Bundle for withBund [...] + }, "properties": { "apiName": { "index": 0, "kind": "path", "displayName": "Api Name", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "org.apache.camel.component.fhir.internal.FhirApiName", "enum": [ "CAPABILITIES", "CREATE", "DELETE", "HISTORY", "LOAD_PAGE", "META", "OPERATION", "PATCH", "READ", "SEARCH", "TRANSACTION", "UPDATE", "VALIDATE" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.componen [...] "methodName": { "index": 1, "kind": "path", "displayName": "Method Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.fhir.FhirConfiguration", "configurationField": "configuration", "description": "What sub operation to use for the selected operation" }, diff --git a/components/camel-fhir/camel-fhir-component/src/main/docs/fhir-component.adoc b/components/camel-fhir/camel-fhir-component/src/main/docs/fhir-component.adoc index ad69d116e9e7..df7f1a144759 100644 --- a/components/camel-fhir/camel-fhir-component/src/main/docs/fhir-component.adoc +++ b/components/camel-fhir/camel-fhir-component/src/main/docs/fhir-component.adoc @@ -120,3 +120,11 @@ The URI names the API and its method, such as `fhir://read/resourceById`. The pa - log: message: "Found ${body.total} patients" ---- + +The `inBody` option names the parameter of the method that the message body is, such as `resourceAsString` in the +first route. The body is passed as it is, without type conversion, so a parameter of type String needs a String body: +convert a byte array or a stream with `convertBodyTo` first. Without `inBody` the body is not used. + +After the call the message body is the result of the method: a `MethodOutcome` for create, update, patch, delete and +validate, and a HAPI FHIR model object for the others, such as the `Bundle` of a search or the resource of a read. Marshal +a resource or a `Bundle` with `fhirJson` or `fhirXml` to get text. diff --git a/components/camel-fhir/camel-fhir-component/src/main/java/org/apache/camel/component/fhir/FhirEndpoint.java b/components/camel-fhir/camel-fhir-component/src/main/java/org/apache/camel/component/fhir/FhirEndpoint.java index 63cf16681057..39eef10f8564 100644 --- a/components/camel-fhir/camel-fhir-component/src/main/java/org/apache/camel/component/fhir/FhirEndpoint.java +++ b/components/camel-fhir/camel-fhir-component/src/main/java/org/apache/camel/component/fhir/FhirEndpoint.java @@ -42,6 +42,9 @@ import org.apache.camel.component.fhir.internal.FhirApiCollection; import org.apache.camel.component.fhir.internal.FhirApiName; import org.apache.camel.component.fhir.internal.FhirConstants; import org.apache.camel.component.fhir.internal.FhirPropertiesHelper; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -55,6 +58,30 @@ import org.apache.camel.support.component.ApiMethodPropertiesHelper; @UriEndpoint(firstVersion = "2.23.0", scheme = "fhir", title = "FHIR", syntax = "fhir:apiName/methodName", apiSyntax = "apiName/methodName", category = { Category.API }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, + byOption = @BodyType.When(option = "apiName", value = "TRANSACTION", + carriers = { Carrier.POJO, Carrier.TEXT }), + description = "One message per poll with the return value of the method: a MethodOutcome" + + " for create, update, patch, delete and validate, and a HAPI FHIR model" + + " object of the fhirVersion for the others, such as the Bundle of a search." + + " A List is split into one message per resource, and transaction withBundle" + + " with stringBundle gives the response as text"), + producerIn = @BodyType(value = Carrier.ANY, dependsOn = "inBody", + description = "The body is used only when inBody names the argument of the method it is," + + " and then passed as it is, without type conversion: resourceAsString," + + " stringBundle and url take text (convertBodyTo String first), and resource," + + " bundle and resources a HAPI FHIR object or a List of them (unmarshal with" + + " fhirJson or fhirXml first). Without inBody the arguments come from the" + + " options and the CamelFhir. headers"), + producerOut = @BodyType(value = Carrier.POJO, + byOption = @BodyType.When(option = "apiName", value = "TRANSACTION", + carriers = { Carrier.POJO, Carrier.TEXT, Carrier.LIST }), + description = "The return value of the method: a MethodOutcome for create, update, patch," + + " delete and validate, and a HAPI FHIR model object of the fhirVersion for" + + " the others, such as the Bundle of a search or the resource of a read;" + + " marshal a resource or a Bundle with fhirJson or fhirXml to get text." + + " transaction gives a Bundle for withBundle with bundle, the response as" + + " text with stringBundle, and a List of the resources for withResources")) public class FhirEndpoint extends AbstractApiEndpoint<FhirApiName, FhirConfiguration> implements EndpointServiceLocation { private static final String EXTRA_PARAMETERS_PROPERTY = "extraParameters"; diff --git a/components/camel-slack/src/generated/resources/META-INF/org/apache/camel/component/slack/slack.json b/components/camel-slack/src/generated/resources/META-INF/org/apache/camel/component/slack/slack.json index c127837baf01..8fd717ae367a 100644 --- a/components/camel-slack/src/generated/resources/META-INF/org/apache/camel/component/slack/slack.json +++ b/components/camel-slack/src/generated/resources/META-INF/org/apache/camel/component/slack/slack.json @@ -32,6 +32,11 @@ "token": { "index": 5, "kind": "property", "displayName": "Token", "group": "token", "label": "token", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The token to access Slack. This app needs to have channels:history, groups:history, im:history, mpim:history, channels:read, groups:read, im:read and mpim:read permissions. The User OAuth Token is the kind of token needed." }, "webhookUrl": { "index": 6, "kind": "property", "displayName": "Webhook Url", "group": "webhook", "label": "webhook", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The incoming webhook URL" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo" ], "javaType": "com.slack.api.model.Message", "description": "One message per new message of the conversation, the most recent first, or the oldest first with naturalOrder; the first poll gives the last message of the history. The text is ${body.text}" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo" ], "description": "The text of the message, or a com.slack.api.model.Message with its blocks: bytes, a stream or a file are read as text. A Map or another POJO is sent as its toString(): marshal it first. No body sends no text, which Slack rejects" }, + "producerOut": { "carriers": [ "unchanged" ], "description": "Unchanged: the answer of Slack is not kept" } + }, "properties": { "channel": { "index": 0, "kind": "path", "displayName": "Channel", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The channel name (syntax #name) or slack user (syntax userName) to send a message directly to an user." }, "token": { "index": 1, "kind": "parameter", "displayName": "Token", "group": "common", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": true, "security": "secret", "description": "The token to access Slack. This app needs to have channels:history, groups:history, im:history, mpim:history, channels:read, groups:read, im:read and mpim:read permissions. The User OAuth Token is the kind of token needed." }, diff --git a/components/camel-slack/src/main/docs/slack-component.adoc b/components/camel-slack/src/main/docs/slack-component.adoc index b0708b1d502b..7f26de4031be 100644 --- a/components/camel-slack/src/main/docs/slack-component.adoc +++ b/components/camel-slack/src/main/docs/slack-component.adoc @@ -53,7 +53,12 @@ include::partial$component-endpoint-headers.adoc[] == Usage To send a message contained in the message body, a pre-established -https://api.slack.com/incoming-webhooks[Slack incoming webhook] must be configured in Slack. +https://api.slack.com/incoming-webhooks[Slack incoming webhook] must be configured in Slack, or the producer uses a token +of a Slack app. + +The producer sends the message body as the text of the message: bytes, a stream or a file are read as text. A +`com.slack.api.model.Message` body is sent with its blocks and attachments. Marshal a Map or another object to text +first, as it is otherwise sent as its `toString()`. The message body is unchanged after the call. === Configuring in Spring XML @@ -185,7 +190,8 @@ YAML:: ---- ==== -This way you'll get the last message from `general` channel. +This way you'll get the last message from `general` channel, as a `com.slack.api.model.Message`, whose text is +`${body.text}`. The consumer will track the timestamp of the last message consumed, and in the next poll it will consume only newer messages in the channel. When more than `maxResults` new messages were posted since the previous poll, the consumer reads the following pages of diff --git a/components/camel-slack/src/main/java/org/apache/camel/component/slack/SlackEndpoint.java b/components/camel-slack/src/main/java/org/apache/camel/component/slack/SlackEndpoint.java index fb94edc48f68..dad140f3fd68 100644 --- a/components/camel-slack/src/main/java/org/apache/camel/component/slack/SlackEndpoint.java +++ b/components/camel-slack/src/main/java/org/apache/camel/component/slack/SlackEndpoint.java @@ -22,6 +22,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.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -34,6 +37,17 @@ import org.apache.camel.util.ObjectHelper; */ @UriEndpoint(firstVersion = "2.16.0", scheme = "slack", title = "Slack", syntax = "slack:channel", category = { Category.SOCIAL }) +@BodyTypes(consumerOut = @BodyType(value = Carrier.POJO, javaType = "com.slack.api.model.Message", + description = "One message per new message of the conversation, the most recent first, or" + + " the oldest first with naturalOrder; the first poll gives the last message" + + " of the history. The text is ${body.text}"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO }, + description = "The text of the message, or a com.slack.api.model.Message with its blocks:" + + " bytes, a stream or a file are read as text. A Map or another POJO is sent" + + " as its toString(): marshal it first. No body sends no text, which Slack" + + " rejects"), + producerOut = @BodyType(value = Carrier.UNCHANGED, + description = "Unchanged: the answer of Slack is not kept")) public class SlackEndpoint extends ScheduledPollEndpoint { @UriParam(defaultValue = "" + SlackConsumer.DEFAULT_CONSUMER_DELAY, javaType = "java.time.Duration", diff --git a/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json b/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json index 698cf8e42fe4..c5806545bcf2 100644 --- a/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json +++ b/components/camel-solr/src/generated/resources/META-INF/org/apache/camel/component/solr/solr.json @@ -51,6 +51,10 @@ "CamelSolrField.": { "index": 9, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "Object", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prefix of the headers that carry the fields of the document to insert: the header CamelSolrField.id holds the id field.", "constantName": "org.apache.camel.component.solr.SolrConstants#HEADER_FIELD_PREFIX" }, "CamelSolrParam.": { "index": 10, "kind": "header", "displayName": "", "group": "producer", "label": "", "required": false, "javaType": "String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The prefix of the headers that carry extra request parameters: the header CamelSolrParam.commit holds the commit parameter.", "constantName": "org.apache.camel.component.solr.SolrConstants#HEADER_PARAM_PREFIX" } }, + "bodyTypes": { + "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": "operation", "value": "DELETE", "carriers": [ "text", "list" ] } ], "description": "What the operation needs: INSERT a Map or a SolrInputDocument, a List of them or of @Field beans, a JSON array or XML text, or a file; DELETE the id or, with deleteByQuery=true, the query, as text or a List of text; SEARCH the query as text or a SolrQuery; PING does not use it. Convert bytes or a stream to text first. The CamelSolrField. [...] + "producerOut": { "carriers": [ "map" ], "description": "The response of Solr as a Map (a SimpleOrderedMap) with the responseHeader and, for a search, the documents under response as a SolrDocumentList. Convert it to a QueryResponse or a SolrPingResponse to read it with SolrJ" } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "producer", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.solr.SolrConfiguration", "configurationField": "configuration", "description": "The solr instance host name (set to 'default' to use the host name defined on component level)" }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "producer", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 8983, "configurationClass": "org.apache.camel.component.solr.SolrConfiguration", "configurationField": "configuration", "description": "The solr instance port number" }, diff --git a/components/camel-solr/src/main/docs/solr-component.adoc b/components/camel-solr/src/main/docs/solr-component.adoc index 9e574e8b94b5..5e8d8ccca062 100644 --- a/components/camel-solr/src/main/docs/solr-component.adoc +++ b/components/camel-solr/src/main/docs/solr-component.adoc @@ -62,16 +62,22 @@ the following. Some operations also require the message body to be set. |`INSERT` | File |inserts/updates a document or documents using the given File (using ContentStreamUpdateRequest) |`INSERT` | SolrInputDocument or Collection<SolrInputDocument>|inserts/updates a document or documents based on the given (collection of) SolrInputDocument |`INSERT` | bean or Collection<bean> |inserts/updates a document or documents based on values in an http://wiki.apache.org/solr/Solrj#Directly_adding_POJOs_to_Solr[annotated bean] -|`INSERT` | String |inserts/updates a document or documents index based on the given XML or json as string (must follow SolrInputDocument format) +|`INSERT` | Map or Collection<Map> |inserts/updates a document or documents with the fields of the Map +|`INSERT` | String |inserts/updates the documents of the given JSON or XML text: a JSON array of documents, or XML documents (an `<add>` element is added when the text does not start with one) -|`DELETE` | String or Collection<String> | delete a record by ID (or collection of ids) or by a query (or a collection of queries) when header "deleteByQuery=true" +|`DELETE` | String or Collection<String> | delete a record by ID (or collection of ids) or by a query (or a collection of queries) when the `deleteByQuery` option or the `CamelSolrDeleteByQuery` header is true -|`PING` |SolrPing | Pings the solr instance +|`PING` |n/a | Pings the solr instance |`SEARCH` |SolrQuery | Performs a search request to the solr instance |`SEARCH` |QueryRequest | Performs a search request to the solr instance +|`SEARCH` |String | Performs a search with the query as text, unless the `CamelSolrQueryString` header holds the query |======================================================================= +After each operation the message body is the response of Solr as a Map (a `SimpleOrderedMap`) with the +`responseHeader`, and for a search the documents under `response` as a `SolrDocumentList`. Convert it to a +`QueryResponse` to read it with SolrJ. + == Example Below is a simple `INSERT`, `DELETE` and `SEARCH` example diff --git a/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrEndpoint.java b/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrEndpoint.java index f5f2fd66dd88..06c5ee7c0183 100644 --- a/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrEndpoint.java +++ b/components/camel-solr/src/main/java/org/apache/camel/component/solr/SolrEndpoint.java @@ -30,6 +30,9 @@ import org.apache.camel.Category; import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -50,6 +53,20 @@ import org.slf4j.LoggerFactory; */ @UriEndpoint(firstVersion = "4.8.0", scheme = "solr", title = "Solr", syntax = "solr:host:port/basePath", producerOnly = true, category = { Category.SEARCH, Category.MONITORING }, headersClass = SolrConstants.class) +@BodyTypes(producerIn = @BodyType(value = Carrier.ANY, + byOption = @BodyType.When(option = "operation", value = "DELETE", + carriers = { Carrier.TEXT, Carrier.LIST }), + description = "What the operation needs: INSERT a Map or a SolrInputDocument, a List of" + + " them or of @Field beans, a JSON array or XML text, or a file; DELETE the id" + + " or, with deleteByQuery=true, the query, as text or a List of text; SEARCH" + + " the query as text or a SolrQuery; PING does not use it. Convert bytes or a" + + " stream to text first. The CamelSolrField. and CamelSolrQueryString headers" + + " can replace the body"), + producerOut = @BodyType(value = Carrier.MAP, + description = "The response of Solr as a Map (a SimpleOrderedMap) with the responseHeader" + + " and, for a search, the documents under response as a SolrDocumentList." + + " Convert it to a QueryResponse or a SolrPingResponse to read it with" + + " SolrJ")) public class SolrEndpoint extends DefaultEndpoint implements EndpointServiceLocation { private static final Logger LOG = LoggerFactory.getLogger(SolrEndpoint.class); diff --git a/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json b/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json index d6b8fc72d591..d10a3cef862f 100644 --- a/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json +++ b/components/camel-spring-parent/camel-spring-redis/src/generated/resources/META-INF/org/apache/camel/component/redis/spring-redis.json @@ -60,6 +60,11 @@ "CamelRedis.Longitude": { "index": 27, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Longitude.", "constantName": "org.apache.camel.component.redis.RedisConstants#LONGITUDE" }, "CamelRedis.Radius": { "index": 28, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Double", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Radius.", "constantName": "org.apache.camel.component.redis.RedisConstants#RADIUS" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "unknown" ], "description": "The message as the serializer reads it: with the default JDK serializer the Java object the publisher serialized, such as the String a PUBLISH of this component sent, with the channel in the CamelRedis.Channel header. A message that is not Java serialized, such as one from redis-cli, is dropped with a WARN: set a StringRedisSerializer as the serializer to receive text" }, + "producerIn": { "carriers": [ "any" ], "description": "The body is not used: the key, the value and the other arguments of the command come from headers, such as CamelRedis.Key and CamelRedis.Value, or CamelRedis.Channel and CamelRedis.Message for PUBLISH. Set CamelRedis.Value from the body to store the body: with the default serializer it must be Serializable, so a stream fails" }, + "producerOut": { "carriers": [ "unchanged" ], "dependsOn": [ "command" ], "byOption": [ { "option": "command", "value": "SET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "SETEX", "carriers": [ "unchanged" ] }, { "option": "command", "value": "MSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HMSET", "carriers": [ "unchanged" ] }, { "option": "command", "value": "HDEL", "car [...] + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "The host where Redis server is running." }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": true, "type": "integer", "javaType": "java.lang.Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.redis.RedisConfiguration", "configurationField": "configuration", "description": "Redis server port number" }, diff --git a/components/camel-spring-parent/camel-spring-redis/src/main/docs/spring-redis-component.adoc b/components/camel-spring-parent/camel-spring-redis/src/main/docs/spring-redis-component.adoc index 302c90ea9268..a9037b6057bb 100644 --- a/components/camel-spring-parent/camel-spring-redis/src/main/docs/spring-redis-component.adoc +++ b/components/camel-spring-parent/camel-spring-redis/src/main/docs/spring-redis-component.adoc @@ -53,7 +53,9 @@ at https://github.com/apache/camel/tree/main/components/camel-spring-redis/src/t The producer runs the command of the `command` option, or of the `CamelRedis.Command` header. The parameters of the command come from headers, such as `CamelRedis.Key` and `CamelRedis.Value`, not from the -message body. The tables below list the parameters of each command. The result of the command is the message body: +message body. The tables below list the parameters of each command. The result of the command becomes the message body, +and a command without a result, such as `SET`, or a result that is null, such as `GET` of a missing key, leaves the body +as it was: [source,yaml] ---- @@ -156,17 +158,19 @@ these keys: === Message headers evaluated by the Redis producer The producer issues commands to the server, and each command has -a different set of parameters with specific types. The result from the -command execution is returned in the message body. +a different set of parameters with specific types. The result of the +command becomes the message body. A command whose result is void, and a result that is null, leave the body as it was. +The value commands, such as `GET`, `HGET` and `LPOP`, return the object that was stored: with the default serializer the +Java object the `CamelRedis.Value` header held. [width="100%",cols="10%,40%,50%,10%",options="header",] |======================================================================= |Hash Commands |Description |Parameters |Result |`HSET` |Set the string value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |void -|`HGET` |Get the value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String) |String +|`HGET` |Get the value of a hash field |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String) |Object |`HSETNX` |Set the value of a hash field only if the field does not exist |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" -(Object) |void +(Object) |Boolean |`HMSET` |Set multiple hash fields to multiple values |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUES`/"CamelRedis.Values" (Map<String, Object>) |void |`HMGET` |Get the values of all the given hash fields |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELDS`/"CamelRedis.Fields" (Collection<String>) |Collection<Object> |`HINCRBY` |Increment the integer value of a hash field by the given number |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.FIELD`/"CamelRedis.Field" (String), `RedisConstants.VALUE`/"CamelRedis.Value" @@ -195,7 +199,7 @@ command execution is returned in the message body. |`LTRIM` |Trim a list to the specified range |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.START`/"CamelRedis.Start"Long), `RedisConstants.END`/"CamelRedis.End" (Long) |void -|`LINDEX` |Get an element from a list by its index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.INDEX`/"CamelRedis.Index" (Long) |String +|`LINDEX` |Get an element from a list by its index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.INDEX`/"CamelRedis.Index" (Long) |Object |`LINSERT` |Insert an element before or after another element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.PIVOT`/"CamelRedis.Pivot" (String), `RedisConstants.POSITION`/"CamelRedis.Position" (String) |Long @@ -208,7 +212,7 @@ command execution is returned in the message body. |`LPOP` |Remove and get the first element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object -|`RPOP` |Remove and get the last element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`RPOP` |Remove and get the last element in a list |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |`RPOPLPUSH` |Remove the last element in a list, append it to another list and return it |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |Object @@ -221,20 +225,20 @@ until one is available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisCo available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |Object |`BRPOP` |Remove and get the last element in a list, or block until one is -available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |String +available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEOUT`/"CamelRedis.Timeout" (Long) |Object |======================================================================= [width="100%",cols="10%,40%,50%,10%",options="header",] |======================================================================= |Set Commands |Description |Parameters |Result -|`SADD` |Add one or more members to a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`SADD` |Add one or more members to a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long |`SMEMBERS` |Get all the members in a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Set<Object> -|`SREM` |Remove one or more members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`SREM` |Remove one or more members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long -|`SPOP` |Remove and return a random member from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`SPOP` |Remove and return a random member from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |`SMOVE` |Move a member from one set to another |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |Boolean @@ -258,7 +262,7 @@ available |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.TIMEO |`SDIFFSTORE` |Subtract multiple sets and store the resulting set in a key |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.KEYS`/"CamelRedis.Keys" (String), `RedisConstants.DESTINATION`/"CamelRedis.Destination" (String) |void -|`SRANDMEMBER` |Get one or multiple random members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String +|`SRANDMEMBER` |Get one or multiple random members from a set |`RedisConstants.KEY`/"CamelRedis.Key" (String) |Object |======================================================================= [width="100%",cols="10%,40%,50%,10%",options="header",] @@ -272,7 +276,7 @@ already exists |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants. |`ZRANGE` |Return a range of members in a sorted set, by index |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.START`/"CamelRedis.Start"Long), `RedisConstants.END`/"CamelRedis.End" (Long), `RedisConstants.WITHSCORE`/"CamelRedis.WithScore" (Boolean) |Object -|`ZREM` |Remove one or more members from a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Boolean +|`ZREM` |Remove one or more members from a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object) |Long |`ZINCRBY` |Increment the score of a member in a sorted set |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (Object), `RedisConstants.INCREMENT`/"CamelRedis.Increment" (Double) |Double @@ -362,9 +366,9 @@ new key |`RedisConstants.KEY`/"CamelRedis.Key" (String), `RedisConstants.KEYS`/" |`DEL` |Delete a key |`RedisConstants.KEYS`/"CamelRedis.Keys" (String) |void -|`TYPE` |Determine the type stored at key |`RedisConstants.KEY`/"CamelRedis.Key" (String) |DataType +|`TYPE` |Determine the type stored at key |`RedisConstants.KEY`/"CamelRedis.Key" (String) |String -|`KEYS` |Find all keys matching the given pattern |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String) |Collection<String> +|`KEYS` |Find all keys matching the given pattern |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String) |Set<String> |`RANDOMKEY` |Return a random key from the keyspace |`RedisConstants.PATERN`/"CamelRedis.Pattern" (String), `RedisConstants.VALUE`/"CamelRedis.Value" (String) |String diff --git a/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisEndpoint.java b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisEndpoint.java index fdc4049b430c..55ae4369eb32 100644 --- a/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisEndpoint.java +++ b/components/camel-spring-parent/camel-spring-redis/src/main/java/org/apache/camel/component/redis/RedisEndpoint.java @@ -20,6 +20,9 @@ import org.apache.camel.Category; import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -31,6 +34,87 @@ import org.springframework.data.redis.core.RedisTemplate; */ @UriEndpoint(firstVersion = "2.11.0", scheme = "spring-redis", title = "Spring Redis", syntax = "spring-redist:host:port", category = { Category.CACHE }, headersClass = RedisConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.UNKNOWN, + description = "The message as the serializer reads it: with the default JDK serializer the" + + " Java object the publisher serialized, such as the String a PUBLISH of this" + + " component sent, with the channel in the CamelRedis.Channel header. A" + + " message that is not Java serialized, such as one from redis-cli, is" + + " dropped with a WARN: set a StringRedisSerializer as the serializer to" + + " receive text"), + producerIn = @BodyType(value = Carrier.ANY, + description = "The body is not used: the key, the value and the other arguments of the" + + " command come from headers, such as CamelRedis.Key and CamelRedis.Value, or" + + " CamelRedis.Channel and CamelRedis.Message for PUBLISH. Set CamelRedis.Value" + + " from the body to store the body: with the default serializer it must be" + + " Serializable, so a stream fails"), + producerOut = @BodyType(value = Carrier.UNCHANGED, dependsOn = "command", + byOption = { + @BodyType.When(option = "command", value = "SET", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "SETEX", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "MSET", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "HSET", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "HMSET", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "HDEL", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "DEL", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "PUBLISH", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "LSET", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "LTRIM", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "RENAME", carriers = Carrier.UNCHANGED), + @BodyType.When(option = "command", value = "EXISTS", carriers = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "command", value = "EXPIRE", carriers = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "command", value = "SETNX", carriers = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "command", value = "HEXISTS", carriers = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "command", value = "SISMEMBER", carriers = Carrier.VALUE, + javaType = "java.lang.Boolean"), + @BodyType.When(option = "command", value = "INCR", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "INCRBY", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "DECR", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "DECRBY", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "LPUSH", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "RPUSH", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "LLEN", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "SADD", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "SREM", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "SCARD", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "HLEN", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "TTL", carriers = Carrier.VALUE, + javaType = "java.lang.Long"), + @BodyType.When(option = "command", value = "PING", carriers = Carrier.TEXT, + javaType = "java.lang.String"), + @BodyType.When(option = "command", value = "ECHO", carriers = Carrier.TEXT, + javaType = "java.lang.String"), + @BodyType.When(option = "command", value = "HGETALL", carriers = Carrier.MAP), + @BodyType.When(option = "command", value = "KEYS", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "HKEYS", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "HVALS", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "SMEMBERS", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "ZRANGE", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "ZREVRANGE", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "LRANGE", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "MGET", carriers = Carrier.LIST), + @BodyType.When(option = "command", value = "HMGET", carriers = Carrier.LIST) }, + description = "The result of the command, which the CamelRedis.Command header can pick" + + " instead of the command option: SET, the default, PUBLISH and the other" + + " commands without a result keep the body; a Boolean or a number for those" + + " that test, count or increment, a Set or a List for those that read several" + + " members, a Map for HGETALL, and the stored object for GET and the commands" + + " that read or pop one value. A null result, such as GET of a missing key," + + " keeps the body as well")) public class RedisEndpoint extends DefaultEndpoint implements EndpointServiceLocation { @UriParam diff --git a/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json b/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json index dd1c9ac5740b..6fa65e1a1bc7 100644 --- a/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json +++ b/components/camel-ssh/src/generated/resources/META-INF/org/apache/camel/component/ssh/ssh.json @@ -63,6 +63,11 @@ "CamelSshStderr": { "index": 2, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "InputStream", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The value of this header is a InputStream with the standard error stream of the executable.", "constantName": "org.apache.camel.component.ssh.SshConstants#STDERR" }, "CamelSshExitValue": { "index": 3, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "Integer", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "important": true, "description": "The value of this header is the exit value that is returned, after the execution. By convention a non-zero status exit value indicates abnormal termination. Note that the exit value is OS dependent.", "constantName": "org.apac [...] }, + "bodyTypes": { + "consumerOut": { "carriers": [ "stream" ], "description": "The standard output of the pollCommand as a stream, with the exit value and the standard error in the CamelSshExitValue and CamelSshStderr headers" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file" ], "description": "The command to run, as text: bytes, a stream or a file are read as text. A Map or a POJO is sent as its toString(): build the command as text first. No body fails" }, + "producerOut": { "carriers": [ "stream" ], "description": "The standard output of the command as a stream, with the exit value and the standard error in the CamelSshExitValue and CamelSshStderr headers; an exit value that is not 0 does not fail the exchange. Convert it to text to read it more than once" } + }, "properties": { "host": { "index": 0, "kind": "path", "displayName": "Host", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.ssh.SshConfiguration", "configurationField": "configuration", "description": "Sets the hostname of the remote SSH server." }, "port": { "index": 1, "kind": "path", "displayName": "Port", "group": "common", "label": "", "required": false, "type": "integer", "javaType": "int", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 22, "configurationClass": "org.apache.camel.component.ssh.SshConfiguration", "configurationField": "configuration", "description": "Sets the port number for the remote SSH server." }, diff --git a/components/camel-ssh/src/main/docs/ssh-component.adoc b/components/camel-ssh/src/main/docs/ssh-component.adoc index e1b2e38de453..79f1fabccde9 100644 --- a/components/camel-ssh/src/main/docs/ssh-component.adoc +++ b/components/camel-ssh/src/main/docs/ssh-component.adoc @@ -50,6 +50,10 @@ When the SSH Component is used as a Producer (`.to("ssh://...")`), it will send the message body as the command to execute on the remote SSH server. +After the call, the message body is the standard output of the command as a stream, with the exit value in the +`CamelSshExitValue` header and the standard error, as a stream as well, in the `CamelSshStderr` header. An exit value +that is not 0 does not fail the exchange. The polling consumer gives the output of its `pollCommand` the same way. + Here is an example of this. Note that the command has a newline (`+ +`). diff --git a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshEndpoint.java b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshEndpoint.java index f2f5298d821e..91874a082274 100644 --- a/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshEndpoint.java +++ b/components/camel-ssh/src/main/java/org/apache/camel/component/ssh/SshEndpoint.java @@ -22,6 +22,9 @@ import org.apache.camel.Category; import org.apache.camel.Consumer; import org.apache.camel.Processor; import org.apache.camel.Producer; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -34,6 +37,18 @@ import org.apache.sshd.common.keyprovider.KeyPairProvider; @UriEndpoint(firstVersion = "2.10.0", scheme = "ssh", title = "SSH", syntax = "ssh:host:port", alternativeSyntax = "ssh:username:password@host:port", category = { Category.FILE }, headersClass = SshConstants.class) +@BodyTypes(consumerOut = @BodyType(value = Carrier.STREAM, + description = "The standard output of the pollCommand as a stream, with the exit value and" + + " the standard error in the CamelSshExitValue and CamelSshStderr headers"), + producerIn = @BodyType(value = { Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE }, + description = "The command to run, as text: bytes, a stream or a file are read as text. A Map" + + " or a POJO is sent as its toString(): build the command as text first. No" + + " body fails"), + producerOut = @BodyType(value = Carrier.STREAM, + description = "The standard output of the command as a stream, with the exit value and the" + + " standard error in the CamelSshExitValue and CamelSshStderr headers; an" + + " exit value that is not 0 does not fail the exchange. Convert it to text to" + + " read it more than once")) public class SshEndpoint extends ScheduledPollEndpoint implements EndpointServiceLocation { @UriParam diff --git a/components/camel-telegram/src/generated/resources/META-INF/org/apache/camel/component/telegram/telegram.json b/components/camel-telegram/src/generated/resources/META-INF/org/apache/camel/component/telegram/telegram.json index a26ad023217c..2c99fd226779 100644 --- a/components/camel-telegram/src/generated/resources/META-INF/org/apache/camel/component/telegram/telegram.json +++ b/components/camel-telegram/src/generated/resources/META-INF/org/apache/camel/component/telegram/telegram.json @@ -41,6 +41,11 @@ "CamelTelegramParseMode": { "index": 4, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "org.apache.camel.component.telegram.TelegramParseMode", "enum": [ "HTML", "MARKDOWN" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "This header is used to format text messages using HTML or Markdown", "constantName": "org.apache.camel.component.telegram.TelegramConstants#TELEGRAM_PARSE_MODE" }, "CamelMessageTimestamp": { "index": 5, "kind": "header", "displayName": "", "group": "common", "label": "", "required": false, "javaType": "long", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "The message timestamp.", "constantName": "org.apache.camel.component.telegram.TelegramConstants#MESSAGE_TIMESTAMP" } }, + "bodyTypes": { + "consumerOut": { "carriers": [ "pojo", "null" ], "description": "One message per update: an IncomingMessage for a message or a channel post, with the chat id in the CamelTelegramChatId header, and the IncomingCallbackQuery, IncomingInlineQuery, PreCheckoutQuery, ShippingQuery or PaidMediaPurchased of the other updates; no body for an update of another kind, such as an edited message" }, + "producerIn": { "carriers": [ "text", "bytes", "stream", "file", "pojo", "null" ], "description": "A message of org.apache.camel.component.telegram.model, such as an OutgoingTextMessage, or text to send as a text message: a stream or a file is read as text, and a Map or another POJO is sent as its toString(), so marshal it first. A byte array is sent as the photo, audio, video or document the CamelTelegramMediaType header names, and fails without it. No body sends nothing" }, + "producerOut": { "carriers": [ "pojo", "unchanged" ], "description": "The MessageResult of the call, with the message sent, or a subclass of it such as MessageResultString for createInvoiceLink; an answer with ok=false is a MessageResult too. No body is left as it is" } + }, "properties": { "type": { "index": 0, "kind": "path", "displayName": "Type", "group": "common", "label": "", "required": true, "type": "enum", "javaType": "java.lang.String", "enum": [ "bots" ], "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "configurationClass": "org.apache.camel.component.telegram.TelegramConfiguration", "configurationField": "configuration", "description": "The endpoint type. Currently, only the 'bots' type is supported." }, "limit": { "index": 1, "kind": "parameter", "displayName": "Limit", "group": "consumer", "label": "consumer", "required": false, "type": "integer", "javaType": "java.lang.Integer", "optionalPrefix": "consumer.", "deprecated": false, "autowired": false, "secret": false, "defaultValue": 100, "configurationClass": "org.apache.camel.component.telegram.TelegramConfiguration", "configurationField": "configuration", "description": "Limit on the number of updates that can be received in a si [...] diff --git a/components/camel-telegram/src/main/docs/telegram-component.adoc b/components/camel-telegram/src/main/docs/telegram-component.adoc index 1c01a0531f03..decdf4ee0d9e 100644 --- a/components/camel-telegram/src/main/docs/telegram-component.adoc +++ b/components/camel-telegram/src/main/docs/telegram-component.adoc @@ -123,7 +123,7 @@ The following message bodies are allowed for a producer endpoint (messages of ty | `EditMessageMediaMessage` | To edit animation, audio, document, photo, or video messages. (editMessageMedia) | `EditMessageReplyMarkupMessage` | To edit only the reply markup of a message. (editMessageReplyMarkup) | `EditMessageDelete` | To delete a message, including service messages. (deleteMessage) -| `SendLocationMessage` | To send a location (setSendLocation) +| `SendLocationMessage` | To send a location (sendLocation) | `EditMessageLiveLocationMessage` | To send changes to a live location (editMessageLiveLocation) | `StopMessageLiveLocationMessage` | To stop updating a live location message sent by the bot or via the bot (for inline bots) before live_period expires (stopMessageLiveLocation) | `SendVenueMessage` | To send information about a venue (sendVenue) @@ -136,11 +136,15 @@ The following message bodies are allowed for a producer endpoint (messages of ty | `GetStarTransactionsMessage` | To get the list of bot's Telegram Star transactions (getStarTransactions) | `RefundStarPaymentMessage` | To refund a successful payment in Telegram Stars (refundStarPayment) | `EditUserStarSubscriptionMessage` | To cancel or re-enable extension of a subscription paid in Telegram Stars (editUserStarSubscription) -| `byte[]` | To send any media type supported. It requires the `CamelTelegramMediaType` header to be set to the appropriate media type +| `byte[]` | To send a photo, an audio, a video or a document, as the `CamelTelegramMediaType` header names (`PHOTO_PNG`, `PHOTO_JPG`, `AUDIO`, `VIDEO` or `DOCUMENT`). It fails without the header. A stream or a file is sent as text, so convert it to `byte[]` to send it as media | `String` | To send a text message to a chat. It gets converted automatically into a `OutgoingTextMessage` |=================================================== +Any other body is sent as text, so marshal a Map or another object first, as it is otherwise sent as its `toString()`. +After the call the message body is the `MessageResult` of the call, with the message sent, or a subclass of it, such as +`MessageResultString` for `CreateInvoiceLinkMessage`. A message body that is null sends nothing, and stays null. + === Consumer @@ -204,11 +208,16 @@ Supported types for incoming messages are |=================================================== | Java Type | Description -| `IncomingMessage` | The full object representation of an incoming message +| `IncomingMessage` | The full object representation of an incoming message or channel post, with the chat id in the `CamelTelegramChatId` header | `String` | The content of the message, for text messages only +| `IncomingCallbackQuery` | A callback query, from a button of an inline keyboard +| `IncomingInlineQuery` | An inline query +| `PreCheckoutQuery`, `ShippingQuery`, `PaidMediaPurchased` | The payment updates, of the package `org.apache.camel.component.telegram.model.payments` |=================================================== +An update of another kind, such as an edited message, gives a message with no body. + === Reactive Chat-Bot Example diff --git a/components/camel-telegram/src/main/java/org/apache/camel/component/telegram/TelegramEndpoint.java b/components/camel-telegram/src/main/java/org/apache/camel/component/telegram/TelegramEndpoint.java index f1f248bb383e..d13603036b7b 100644 --- a/components/camel-telegram/src/main/java/org/apache/camel/component/telegram/TelegramEndpoint.java +++ b/components/camel-telegram/src/main/java/org/apache/camel/component/telegram/TelegramEndpoint.java @@ -38,6 +38,9 @@ import org.apache.camel.RuntimeCamelException; import org.apache.camel.component.telegram.service.TelegramServiceRestBotAPIAdapter; import org.apache.camel.component.webhook.WebhookCapableEndpoint; import org.apache.camel.component.webhook.WebhookConfiguration; +import org.apache.camel.spi.BodyType; +import org.apache.camel.spi.BodyType.Carrier; +import org.apache.camel.spi.BodyTypes; import org.apache.camel.spi.EndpointServiceLocation; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; @@ -51,6 +54,25 @@ import org.slf4j.LoggerFactory; */ @UriEndpoint(firstVersion = "2.18.0", scheme = "telegram", title = "Telegram", syntax = "telegram:type", category = { Category.CLOUD, Category.API, Category.CHAT }, headersClass = TelegramConstants.class) +@BodyTypes(consumerOut = @BodyType(value = { Carrier.POJO, Carrier.NULL }, + description = "One message per update: an IncomingMessage for a message or a channel post," + + " with the chat id in the CamelTelegramChatId header, and the" + + " IncomingCallbackQuery, IncomingInlineQuery, PreCheckoutQuery," + + " ShippingQuery or PaidMediaPurchased of the other updates; no body for an" + + " update of another kind, such as an edited message"), + producerIn = @BodyType(value = { + Carrier.TEXT, Carrier.BYTES, Carrier.STREAM, Carrier.FILE, Carrier.POJO, + Carrier.NULL }, + description = "A message of org.apache.camel.component.telegram.model, such as an" + + " OutgoingTextMessage, or text to send as a text message: a stream or a file" + + " is read as text, and a Map or another POJO is sent as its toString(), so" + + " marshal it first. A byte array is sent as the photo, audio, video or" + + " document the CamelTelegramMediaType header names, and fails without it." + + " No body sends nothing"), + producerOut = @BodyType(value = { Carrier.POJO, Carrier.UNCHANGED }, + description = "The MessageResult of the call, with the message sent, or a subclass of it" + + " such as MessageResultString for createInvoiceLink; an answer with ok=false" + + " is a MessageResult too. No body is left as it is")) public class TelegramEndpoint extends ScheduledPollEndpoint implements WebhookCapableEndpoint, EndpointServiceLocation { private static final Logger LOG = LoggerFactory.getLogger(TelegramEndpoint.class); private static final Pattern WEBHOOK_SECRET_TOKEN_PATTERN = Pattern.compile("[A-Za-z0-9_-]{1,256}"); diff --git a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java index 24da4237401c..873a155f718b 100644 --- a/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java +++ b/dsl/camel-yaml-dsl/camel-yaml-dsl-validator/src/test/java/org/apache/camel/dsl/yaml/validator/TextBodyFlowTest.java @@ -1491,6 +1491,99 @@ public class TextBodyFlowTest { .contains("route mail: to: smtp needs the body as text, bytes, a stream or a file")); } + @Test + public void testTheBodyOfMoreComponents() { + // ssh runs the body as a command and gives its output as a stream, and so does its consumer; spring-redis keeps + // the body for SET, its default command, gives text for PING and is not known for GET; a couchbase row read with + // fullDocument=false is JSON text; the response of solr is a Map, and slack also sends an object as its text + String yaml = """ + - route: + id: shell + from: + uri: timer:tick + steps: + - setBody: + constant: '{"id": 1}' + - unmarshal: + json: {} + - to: ssh:localhost:22 + - route: + id: output + from: + uri: ssh:localhost:22?pollCommand=date + steps: + - setHeader: + name: id + simple: ${body[id]} + - route: + id: ping + from: + uri: timer:tick + steps: + - to: spring-redis:localhost:6379?command=PING + - setHeader: + name: id + simple: ${body[id]} + - route: + id: cache + from: + uri: timer:tick + steps: + - setBody: + constant: '{"id": 1}' + - unmarshal: + json: {} + - to: spring-redis:localhost:6379 + - setHeader: + name: id + simple: ${body[id]} + - route: + id: lookup + from: + uri: timer:tick + steps: + - to: spring-redis:localhost:6379?command=GET + - setHeader: + name: id + simple: ${body[id]} + - route: + id: rows + from: + uri: couchbase:http://localhost?bucket=orders&fullDocument=false + steps: + - setHeader: + name: id + simple: ${body[id]} + - route: + id: search + from: + uri: timer:tick + steps: + - setBody: + constant: "id:1" + - to: solr:localhost:8983/solr?collection=orders&operation=SEARCH + - setHeader: + name: hits + simple: ${body[response]} + - route: + id: chat + from: + uri: timer:tick + steps: + - setBody: + constant: '{"id": 1}' + - unmarshal: + json: {} + - to: slack:#orders + """; + assertThat(lines(yaml)).hasSize(4) + .anySatisfy( + m -> assertThat(m).contains("route shell: to: ssh needs the body as text, bytes, a stream or a file")) + .anySatisfy(m -> assertThat(m).contains("route output: simple reads a field of the body (${body[id]})")) + .anySatisfy(m -> assertThat(m).contains("route ping: simple reads a field of the body (${body[id]})")) + .anySatisfy(m -> assertThat(m).contains("route rows: simple reads a field of the body (${body[id]})")); + } + @Test public void testTheBodyOfCoreComponents() { // the stream consumer gives each line as text, so a field read needs an unmarshal, and stream:out writes text,
