diff --git a/frontend/src/assets/rp-connect-schema-full.json b/frontend/src/assets/rp-connect-schema-full.json index 81850cd6f9..bd9d3588c5 100644 --- a/frontend/src/assets/rp-connect-schema-full.json +++ b/frontend/src/assets/rp-connect-schema-full.json @@ -2,13 +2,19 @@ "bloblang-functions": [ { "category": "Message Info", - "description": "Returns the index of the mapped message within a batch. This is useful for applying maps only on certain messages of a batch.", + "description": "Returns the zero-based index of the current message within its batch. Use this to conditionally process messages based on their position, or to create sequential identifiers within a batch.", "examples": [ { "mapping": "root = if batch_index() > 0 { deleted() }", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.id = \"%v-%v\".format(timestamp_unix(), batch_index())", + "results": [], + "skip_testing": false, + "summary": "Create a unique identifier combining batch position with timestamp." } ], "impure": false, @@ -18,13 +24,19 @@ }, { "category": "Message Info", - "description": "Returns the size of the message batch.", + "description": "Returns the total number of messages in the current batch. Use this to determine batch boundaries or compute relative positions.", "examples": [ { - "mapping": "root.foo = batch_size()", + "mapping": "root.total = batch_size()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.is_last = batch_index() == batch_size() - 1", + "results": [], + "skip_testing": false, + "summary": "Check if processing the last message in a batch." } ], "impure": false, @@ -32,9 +44,41 @@ "params": {}, "status": "stable" }, + { + "category": "General", + "description": "Creates a zero-initialized byte array of specified length. Use this to allocate fixed-size byte buffers for binary data manipulation or to generate padding.", + "examples": [ + { + "mapping": "root.data = bytes(5)", + "results": [], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.header = bytes(16)\nroot.payload = content()", + "results": [], + "skip_testing": false, + "summary": "Create a buffer for binary operations." + } + ], + "impure": false, + "name": "bytes", + "params": { + "named": [ + { + "description": "The size of the resulting byte array.", + "name": "length", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "integer" + } + ] + }, + "status": "stable" + }, { "category": "Message Info", - "description": "Returns the full raw contents of the mapping target message as a byte array. When mapping to a JSON field the value should be encoded using the method xref:guides:bloblang/methods.adoc#encode[`encode`], or cast to a string directly using the method xref:guides:bloblang/methods.adoc#string[`string`], otherwise it will be base64 encoded by default.", + "description": "Returns the raw message payload as bytes, regardless of the current mapping context. Use this to access the original message when working within nested contexts, or to store the entire message as a field.", "examples": [ { "mapping": "root.doc = content().string()", @@ -46,6 +90,17 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.original = content().string()\nroot.processed_by = \"ai\"", + "results": [ + [ + "{\"foo\":\"bar\"}", + "{\"original\":\"{\\\"foo\\\":\\\"bar\\\"}\",\"processed_by\":\"ai\"}" + ] + ], + "skip_testing": false, + "summary": "Preserve original message while adding metadata." } ], "impure": false, @@ -54,119 +109,99 @@ "status": "stable" }, { - "category": "Deprecated", - "description": "The `count` function is a counter starting at 1 which increments after each time it is called. Count takes an argument which is an identifier for the counter, allowing you to specify multiple unique counters in your configuration.", + "category": "General", + "description": "Generates an incrementing sequence of integers starting from a minimum value (default 1). Each counter instance maintains its own independent state across message processing. When the maximum value is reached, the counter automatically resets to the minimum.", "examples": [ { - "mapping": "root = this\nroot.id = count(\"bloblang_function_example\")", + "mapping": "root.id = counter()", "results": [ [ - "{\"message\":\"foo\"}", - "{\"id\":1,\"message\":\"foo\"}" + "{}", + "{\"id\":1}" ], [ - "{\"message\":\"bar\"}", - "{\"id\":2,\"message\":\"bar\"}" + "{}", + "{\"id\":2}" ] ], "skip_testing": false, - "summary": "" - } - ], - "impure": true, - "name": "count", - "params": { - "named": [ - { - "description": "An identifier for the counter.", - "name": "name", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "string" - } - ] - }, - "status": "deprecated" - }, - { - "category": "General", - "description": "Returns a non-negative integer that increments each time it is resolved, yielding the minimum (`1` by default) as the first value. Each instantiation of `counter` has its own independent count. Once the maximum integer (or `max` argument) is reached the counter resets back to the minimum.", - "examples": [ + "summary": "Generate sequential IDs for each message." + }, { - "mapping": "root.id = counter()", + "mapping": "root.batch_num = counter(min: 100, max: 200)", "results": [ [ "{}", - "{\"id\":1}" + "{\"batch_num\":100}" ], [ "{}", - "{\"id\":2}" + "{\"batch_num\":101}" ] ], "skip_testing": false, - "summary": "" + "summary": "Use a custom range for the counter." }, { - "mapping": "\nmap foos {\n root = counter()\n}\n\nroot.meow_id = null.apply(\"foos\")\nroot.woof_id = null.apply(\"foos\")\n", + "mapping": "\nmap increment {\n root = counter()\n}\n\nroot.first_id = null.apply(\"increment\")\nroot.second_id = null.apply(\"increment\")\n", "results": [ [ "{}", - "{\"meow_id\":1,\"woof_id\":2}" + "{\"first_id\":1,\"second_id\":2}" ], [ "{}", - "{\"meow_id\":3,\"woof_id\":4}" + "{\"first_id\":3,\"second_id\":4}" ] ], "skip_testing": false, - "summary": "It's possible to increment a counter multiple times within a single mapping invocation using a map." + "summary": "Increment a counter multiple times within a single mapping using a named map." }, { - "mapping": "root.consecutive_doggos = counter(min: 1, set: if !this.sound.lowercase().contains(\"woof\") { 0 })", + "mapping": "root.streak = counter(set: if this.status != \"success\" { 0 })", "results": [ [ - "{\"sound\":\"woof woof\"}", - "{\"consecutive_doggos\":1}" + "{\"status\":\"success\"}", + "{\"streak\":1}" ], [ - "{\"sound\":\"woofer wooooo\"}", - "{\"consecutive_doggos\":2}" + "{\"status\":\"success\"}", + "{\"streak\":2}" ], [ - "{\"sound\":\"meow\"}", - "{\"consecutive_doggos\":0}" + "{\"status\":\"failure\"}", + "{\"streak\":0}" ], [ - "{\"sound\":\"uuuuh uh uh woof uhhhhhh\"}", - "{\"consecutive_doggos\":1}" + "{\"status\":\"success\"}", + "{\"streak\":1}" ] ], "skip_testing": false, - "summary": "By specifying an optional `set` parameter it is possible to dynamically reset the counter based on input data." + "summary": "Conditionally reset a counter based on input data." }, { - "mapping": "root.things = counter(set: if this.id == null { null })", + "mapping": "root.count = counter(set: if this.peek { null })", "results": [ [ - "{\"id\":\"a\"}", - "{\"things\":1}" + "{\"peek\":false}", + "{\"count\":1}" ], [ - "{\"id\":\"b\"}", - "{\"things\":2}" + "{\"peek\":false}", + "{\"count\":2}" ], [ - "{\"what\":\"just checking\"}", - "{\"things\":2}" + "{\"peek\":true}", + "{\"count\":2}" ], [ - "{\"id\":\"c\"}", - "{\"things\":3}" + "{\"peek\":false}", + "{\"count\":3}" ] ], "skip_testing": false, - "summary": "The `set` parameter can also be utilized to peek at the counter without mutating it by returning `null`." + "summary": "Peek at the current counter value without incrementing by using null in the set parameter." } ], "impure": false, @@ -175,7 +210,7 @@ "named": [ { "default": 1, - "description": "The minimum value of the counter, this is the first value that will be yielded. If this parameter is dynamic it will be resolved only once during the lifetime of the mapping.", + "description": "The starting value of the counter. This is the first value yielded. Evaluated once when the mapping is initialized.", "name": "min", "no_dynamic": false, "scalars_to_literal": true, @@ -183,14 +218,14 @@ }, { "default": 9223372036854775807, - "description": "The maximum value of the counter, once this value is yielded the counter will reset back to the min. If this parameter is dynamic it will be resolved only once during the lifetime of the mapping.", + "description": "The maximum value before the counter resets to min. Evaluated once when the mapping is initialized.", "name": "max", "no_dynamic": false, "scalars_to_literal": true, "type": "query expression" }, { - "description": "An optional mapping that when specified will be executed each time the counter is resolved. When this mapping resolves to a non-negative integer value it will cause the counter to reset to this value and yield it. If this mapping is omitted or doesn't resolve to anything then the counter will increment and yield the value as normal. If this mapping resolves to `null` then the counter is not incremented and the current value is yielded. If this mapping resolves to a deletion then the counter is reset to the `min` value.", + "description": "An optional query that controls counter behavior: when it resolves to a non-negative integer, the counter is set to that value; when it resolves to `null`, the counter is read without incrementing; when it resolves to a deletion, the counter resets to min; otherwise the counter increments normally.", "is_optional": true, "name": "set", "no_dynamic": false, @@ -203,7 +238,7 @@ }, { "category": "General", - "description": "A function that returns a result indicating that the mapping target should be deleted. Deleting, also known as dropping, messages will result in them being acknowledged as successfully processed to inputs in a Redpanda Connect pipeline. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", + "description": "Returns a deletion marker that removes the target field or message. When applied to root, the entire message is dropped while still being acknowledged as successfully processed. Use this to filter data or conditionally remove fields.", "examples": [ { "mapping": "root = this\nroot.bar = deleted()", @@ -225,7 +260,7 @@ ] ], "skip_testing": false, - "summary": "Since the result is a value it can be used to do things like remove elements of an array within `map_each`." + "summary": "Filter array elements by returning deleted for unwanted items." } ], "impure": false, @@ -234,60 +269,20 @@ "status": "stable" }, { - "category": "Environment", - "description": "Returns the value of an environment variable, or `null` if the environment variable does not exist.", + "category": "Message Info", + "description": "Returns the error message string if the message has failed processing, otherwise `null`. Use this in error handling pipelines to log or route failed messages based on their error details.", "examples": [ { - "mapping": "root.thing.key = env(\"key\").or(\"default value\")", - "results": [], - "skip_testing": false, - "summary": "" - }, - { - "mapping": "root.thing.key = env(this.thing.key_name)", + "mapping": "root.doc.error = error()", "results": [], "skip_testing": false, "summary": "" }, { - "mapping": "root.thing.key = env(name: \"key\", no_cache: true)", - "results": [], - "skip_testing": false, - "summary": "When the name parameter is static this function will only resolve once and yield the same result for each invocation as an optimization, this means that updates to env vars during runtime will not be reflected. You can disable this cache with the optional parameter `no_cache`, which when set to `true` will cause the variable lookup to be performed for each execution of the mapping." - } - ], - "impure": true, - "name": "env", - "params": { - "named": [ - { - "description": "The name of an environment variable.", - "name": "name", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "string" - }, - { - "default": false, - "description": "Force the variable lookup to occur for each mapping invocation.", - "name": "no_cache", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "bool" - } - ] - }, - "status": "stable" - }, - { - "category": "Message Info", - "description": "If an error has occurred during the processing of a message this function returns the reported cause of the error as a string, otherwise `null`. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", - "examples": [ - { - "mapping": "root.doc.error = error()", + "mapping": "root = this\nroot.error_msg = error()\nroot.has_error = error() != null", "results": [], "skip_testing": false, - "summary": "" + "summary": "Route messages to different outputs based on error presence." } ], "impure": false, @@ -297,13 +292,19 @@ }, { "category": "Message Info", - "description": "Returns the label of the source component which raised the error during the processing of a message or an empty string if not set. `null` is returned when the error is null or no source component is associated with it. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", + "description": "Returns the user-defined label of the component that caused the error, empty string if no label is set, or `null` if the message has no error. Use this for more human-readable error tracking when components have custom labels.", "examples": [ { "mapping": "root.doc.error_source_label = error_source_label()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.error_category = error_source_label().or(\"unknown\")", + "results": [], + "skip_testing": false, + "summary": "Route errors based on component labels." } ], "impure": false, @@ -313,13 +314,19 @@ }, { "category": "Message Info", - "description": "Returns the name of the source component which raised the error during the processing of a message. `null` is returned when the error is null or no source component is associated with it. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", + "description": "Returns the component name that caused the error, or `null` if the message has no error or the error has no associated component. Use this to identify which processor or component in your pipeline caused a failure.", "examples": [ { "mapping": "root.doc.error_source_name = error_source_name()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.error_details = if errored() {\n {\n \"message\": error(),\n \"component\": error_source_name(),\n \"timestamp\": now()\n }\n}", + "results": [], + "skip_testing": false, + "summary": "Create detailed error logs with component information." } ], "impure": false, @@ -329,13 +336,19 @@ }, { "category": "Message Info", - "description": "Returns the path of the source component which raised the error during the processing of a message. `null` is returned when the error is null or no source component is associated with it. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", + "description": "Returns the dot-separated path to the component that caused the error, or `null` if the message has no error. Use this to identify the exact location of a failed component in nested pipeline configurations.", "examples": [ { "mapping": "root.doc.error_source_path = error_source_path()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.error_info = {\n \"path\": error_source_path(),\n \"component\": error_source_name(),\n \"message\": error()\n}", + "results": [], + "skip_testing": false, + "summary": "Build comprehensive error context for debugging." } ], "impure": false, @@ -345,13 +358,19 @@ }, { "category": "Message Info", - "description": "Returns a boolean value indicating whether an error has occurred during the processing of a message. For more information about error handling patterns read xref:configuration:error_handling.adoc[].", + "description": "Returns true if the message has failed processing, false otherwise. Use this for conditional logic in error handling workflows or to route failed messages to dead letter queues.", "examples": [ { "mapping": "root.doc.status = if errored() { 400 } else { 200 }", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root = if errored() { this } else { deleted() }", + "results": [], + "skip_testing": false, + "summary": "Send only failed messages to a separate stream." } ], "impure": false, @@ -361,31 +380,19 @@ }, { "category": "Fake Data Generation", - "description": "Takes in a string that maps to a https://github.com/go-faker/faker[faker^] function and returns the result from that faker function. Returns an error if the given string doesn't match a supported faker function. Supported functions: `latitude`, `longitude`, `unix_time`, `date`, `time_string`, `month_name`, `year_string`, `day_of_week`, `day_of_month`, `timestamp`, `century`, `timezone`, `time_period`, `email`, `mac_address`, `domain_name`, `url`, `username`, `ipv4`, `ipv6`, `password`, `jwt`, `word`, `sentence`, `paragraph`, `cc_type`, `cc_number`, `currency`, `amount_with_currency`, `title_male`, `title_female`, `first_name`, `first_name_male`, `first_name_female`, `last_name`, `name`, `gender`, `chinese_first_name`, `chinese_last_name`, `chinese_name`, `phone_number`, `toll_free_phone_number`, `e164_phone_number`, `uuid_hyphenated`, `uuid_digit`. Refer to the https://github.com/go-faker/faker[faker^] docs for details on these functions.", + "description": "Generates realistic fake data for testing and development purposes. Supports a wide variety of data types including personal information, network addresses, dates/times, financial data, and UUIDs. Useful for creating mock data, populating test databases, or anonymizing sensitive information.\n\nSupported functions: `latitude`, `longitude`, `unix_time`, `date`, `time_string`, `month_name`, `year_string`, `day_of_week`, `day_of_month`, `timestamp`, `century`, `timezone`, `time_period`, `email`, `mac_address`, `domain_name`, `url`, `username`, `ipv4`, `ipv6`, `password`, `jwt`, `word`, `sentence`, `paragraph`, `cc_type`, `cc_number`, `currency`, `amount_with_currency`, `title_male`, `title_female`, `first_name`, `first_name_male`, `first_name_female`, `last_name`, `name`, `gender`, `chinese_first_name`, `chinese_last_name`, `chinese_name`, `phone_number`, `toll_free_phone_number`, `e164_phone_number`, `uuid_hyphenated`, `uuid_digit`.", "examples": [ { - "mapping": "root.time = fake(\"time_string\")", - "results": [], - "skip_testing": false, - "summary": "Use `time_string` to generate a time in the format `00:00:00`:" - }, - { - "mapping": "root.email = fake(\"email\")", - "results": [], - "skip_testing": false, - "summary": "Use `email` to generate a string in email address format:" - }, - { - "mapping": "root.jwt = fake(\"jwt\")", + "mapping": "root.user = {\n \"id\": fake(\"uuid_hyphenated\"),\n \"name\": fake(\"name\"),\n \"email\": fake(\"email\"),\n \"created_at\": fake(\"timestamp\")\n}", "results": [], "skip_testing": false, - "summary": "Use `jwt` to generate a JWT token:" + "summary": "Generate fake user profile data for testing" }, { - "mapping": "root.uuid = fake(\"uuid_hyphenated\")", + "mapping": "root.event = {\n \"source_ip\": fake(\"ipv4\"),\n \"mac_address\": fake(\"mac_address\"),\n \"url\": fake(\"url\")\n}", "results": [], "skip_testing": false, - "summary": "Use `uuid_hyphenated` to generate a hyphenated UUID:" + "summary": "Create realistic test data for network monitoring" } ], "impure": false, @@ -394,7 +401,7 @@ "named": [ { "default": "", - "description": "The name of the function to use to generate the value.", + "description": "The name of the faker function to use. See description for full list of supported functions.", "name": "function", "no_dynamic": false, "scalars_to_literal": false, @@ -402,127 +409,11 @@ } ] }, - "status": "beta" - }, - { - "category": "Environment", - "description": "Reads a file and returns its contents. Relative paths are resolved from the directory of the process executing the mapping. In order to read files relative to the mapping file use the newer <>", - "examples": [ - { - "mapping": "root.doc = file(env(\"BENTHOS_TEST_BLOBLANG_FILE\")).parse_json()", - "results": [ - [ - "{}", - "{\"doc\":{\"foo\":\"bar\"}}" - ] - ], - "skip_testing": false, - "summary": "" - }, - { - "mapping": "root.doc = file(path: env(\"BENTHOS_TEST_BLOBLANG_FILE\"), no_cache: true).parse_json()", - "results": [ - [ - "{}", - "{\"doc\":{\"foo\":\"bar\"}}" - ] - ], - "skip_testing": false, - "summary": "When the path parameter is static this function will only read the specified file once and yield the same result for each invocation as an optimization, this means that updates to files during runtime will not be reflected. You can disable this cache with the optional parameter `no_cache`, which when set to `true` will cause the file to be read for each execution of the mapping." - } - ], - "impure": true, - "name": "file", - "params": { - "named": [ - { - "description": "The path of the target file.", - "name": "path", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "string" - }, - { - "default": false, - "description": "Force the file to be read for each mapping invocation.", - "name": "no_cache", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "bool" - } - ] - }, - "status": "stable" - }, - { - "category": "Environment", - "description": "Reads a file and returns its contents. Relative paths are resolved from the directory of the mapping.", - "examples": [ - { - "mapping": "root.doc = file_rel(env(\"BENTHOS_TEST_BLOBLANG_FILE\")).parse_json()", - "results": [ - [ - "{}", - "{\"doc\":{\"foo\":\"bar\"}}" - ] - ], - "skip_testing": false, - "summary": "" - }, - { - "mapping": "root.doc = file_rel(path: env(\"BENTHOS_TEST_BLOBLANG_FILE\"), no_cache: true).parse_json()", - "results": [ - [ - "{}", - "{\"doc\":{\"foo\":\"bar\"}}" - ] - ], - "skip_testing": false, - "summary": "When the path parameter is static this function will only read the specified file once and yield the same result for each invocation as an optimization, this means that updates to files during runtime will not be reflected. You can disable this cache with the optional parameter `no_cache`, which when set to `true` will cause the file to be read for each execution of the mapping." - } - ], - "impure": true, - "name": "file_rel", - "params": { - "named": [ - { - "description": "The path of the target file.", - "name": "path", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "string" - }, - { - "default": false, - "description": "Force the file to be read for each mapping invocation.", - "name": "no_cache", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "bool" - } - ] - }, - "status": "stable" - }, - { - "category": "Environment", - "description": "Returns a string matching the hostname of the machine running Benthos.", - "examples": [ - { - "mapping": "root.thing.host = hostname()", - "results": [], - "skip_testing": false, - "summary": "" - } - ], - "impure": true, - "name": "hostname", - "params": {}, "status": "stable" }, { "category": "Message Info", - "description": "Returns the value of a field within a JSON message located by a [dot path][field_paths] argument. This function always targets the entire source JSON document regardless of the mapping context.", + "description": "Returns a field from the original JSON message by dot path, always accessing the root document regardless of mapping context. Use this to reference the source message when working in nested contexts or to extract specific fields.", "examples": [ { "mapping": "root.mapped = json(\"foo.bar\")", @@ -544,7 +435,7 @@ ] ], "skip_testing": false, - "summary": "The path argument is optional and if omitted the entire JSON payload is returned." + "summary": "Access the original message from within nested mapping contexts." } ], "impure": false, @@ -565,13 +456,19 @@ }, { "category": "General", - "description": "Generates a new ksuid each time it is invoked and prints a string representation.", + "description": "Generates a K-Sortable Unique Identifier with built-in timestamp ordering. Use this for distributed unique IDs that sort chronologically and remain collision-resistant without coordination between generators.", "examples": [ { "mapping": "root.id = ksuid()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.event = {\n \"id\": ksuid(),\n \"type\": this.event_type,\n \"data\": this.payload\n}", + "results": [], + "skip_testing": false, + "summary": "Create sortable event IDs for logging." } ], "impure": false, @@ -614,7 +511,7 @@ }, { "category": "Message Info", - "description": "Returns the value of a metadata key from the input message, or `null` if the key does not exist. Since values are extracted from the read-only input message they do NOT reflect changes made from within the map, in order to query metadata mutations made within a mapping use the xref:guides:bloblang/about.adoc#metadata[`@` operator]. This function supports extracting metadata from other messages of a batch with the `from` method.", + "description": "Returns metadata from the input message by key, or `null` if the key doesn't exist. This reads the original metadata; to access modified metadata during mapping, use the `@` operator instead. Use this to extract message properties like topics, headers, or timestamps.", "examples": [ { "mapping": "root.topic = metadata(\"kafka_topic\")", @@ -626,7 +523,13 @@ "mapping": "root.all_metadata = metadata()", "results": [], "skip_testing": false, - "summary": "The key parameter is optional and if omitted the entire metadata contents are returned as an object." + "summary": "Retrieve all metadata as an object by omitting the key parameter." + }, + { + "mapping": "root.source = {\n \"topic\": metadata(\"kafka_topic\"),\n \"partition\": metadata(\"kafka_partition\"),\n \"timestamp\": metadata(\"kafka_timestamp_unix\")\n}", + "results": [], + "skip_testing": false, + "summary": "Copy specific metadata fields to the message body." } ], "impure": false, @@ -647,7 +550,7 @@ }, { "category": "General", - "description": "Generates a new nanoid each time it is invoked and prints a string representation.", + "description": "Generates a URL-safe unique identifier using Nano ID. Use this for compact, URL-friendly IDs with good collision resistance. Customize the length (default 21) or provide a custom alphabet for specific character requirements.", "examples": [ { "mapping": "root.id = nanoid()", @@ -659,13 +562,13 @@ "mapping": "root.id = nanoid(54)", "results": [], "skip_testing": false, - "summary": "It is possible to specify an optional length parameter." + "summary": "Generate a longer ID for additional uniqueness." }, { "mapping": "root.id = nanoid(54, \"abcde\")", "results": [], "skip_testing": false, - "summary": "It is also possible to specify an optional custom alphabet after the length parameter." + "summary": "Use a custom alphabet for domain-specific IDs." } ], "impure": false, @@ -701,7 +604,7 @@ }, { "category": "Environment", - "description": "Returns the current timestamp as a string in RFC 3339 format with the local timezone. Use the method `ts_format` in order to change the format and timezone.", + "description": "Returns the current timestamp as an RFC 3339 formatted string with nanosecond precision. Use this to add processing timestamps to messages or measure time between events. Chain with `ts_format` to customize the format or timezone.", "examples": [ { "mapping": "root.received_at = now()", @@ -713,7 +616,7 @@ "mapping": "root.received_at = now().ts_format(\"Mon Jan 2 15:04:05 -0700 MST 2006\", \"UTC\")", "results": [], "skip_testing": false, - "summary": "" + "summary": "Format the timestamp in a custom format and timezone." } ], "impure": false, @@ -755,7 +658,7 @@ }, { "category": "General", - "description": "\nGenerates a non-negative pseudo-random 64-bit integer. An optional integer argument can be provided in order to seed the random number generator.\n\nOptional `min` and `max` arguments can be provided in order to only generate numbers within a range. Neither of these parameters can be set via a dynamic expression (i.e. from values taken from mapped data). Instead, for dynamic ranges extract a min and max manually using a modulo operator (`random_int() % a + b`).", + "description": "\nGenerates a pseudo-random non-negative 64-bit integer. Use this for creating random IDs, sampling data, or generating test values. Provide a seed for reproducible randomness, or use a dynamic seed like `timestamp_unix_nano()` for unique values per mapping instance.\n\nOptional `min` and `max` parameters constrain the output range (both inclusive). For dynamic ranges based on message data, use the modulo operator instead: `random_int() % dynamic_max + dynamic_min`.", "examples": [ { "mapping": "root.first = random_int()\nroot.second = random_int(1)\nroot.third = random_int(max:20)\nroot.fourth = random_int(min:10, max:20)\nroot.fifth = random_int(timestamp_unix_nano(), 5, 20)\nroot.sixth = random_int(seed:timestamp_unix_nano(), max:20)\n", @@ -764,10 +667,10 @@ "summary": "" }, { - "mapping": "root.first = random_int(timestamp_unix_nano())", + "mapping": "root.random_id = random_int(timestamp_unix_nano())\nroot.sample_percent = random_int(seed: timestamp_unix_nano(), min: 0, max: 100)", "results": [], "skip_testing": false, - "summary": "It is possible to specify a dynamic seed argument, in which case the argument will only be resolved once during the lifetime of the mapping." + "summary": "Use a dynamic seed for unique random values per mapping instance." } ], "impure": false, @@ -806,7 +709,7 @@ }, { "category": "General", - "description": "The `range` function creates an array of integers following a range between a start, stop and optional step integer argument. If the step argument is omitted then it defaults to 1. A negative step can be provided as long as stop < start.", + "description": "Creates an array of integers from start (inclusive) to stop (exclusive) with an optional step. Use this to generate sequences for iteration, indexing, or creating numbered lists.", "examples": [ { "mapping": "root.a = range(0, 10)\nroot.b = range(start: 0, stop: this.max, step: 2) # Using named params\nroot.c = range(0, -this.max, -2)", @@ -818,6 +721,17 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.pages = range(0, this.total_items, 100).map_each(offset -> {\n \"offset\": offset,\n \"limit\": 100\n})", + "results": [ + [ + "{\"total_items\":250}", + "{\"pages\":[{\"limit\":100,\"offset\":0},{\"limit\":100,\"offset\":100}]}" + ] + ], + "skip_testing": false, + "summary": "Generate a sequence for batch processing." } ], "impure": false, @@ -885,19 +799,19 @@ }, { "category": "General", - "description": "Generate a new snowflake ID each time it is invoked and prints a string representation. I.e.: 1559229974454472704", + "description": "Generates a unique, time-ordered Snowflake ID. Snowflake IDs are 64-bit integers that encode timestamp, node ID, and sequence information, making them ideal for distributed systems where sortable unique identifiers are needed. Returns a string representation of the ID.", "examples": [ { - "mapping": "root.id = snowflake_id()", + "mapping": "root.id = snowflake_id()\nroot.payload = this", "results": [], "skip_testing": false, - "summary": "" + "summary": "Generate a unique Snowflake ID for each message" }, { - "mapping": "root.id = snowflake_id(2)", + "mapping": "root.id = snowflake_id(42)\nroot.data = this", "results": [], "skip_testing": false, - "summary": "It is possible to specify the node_id." + "summary": "Generate Snowflake IDs with different node IDs for multi-datacenter deployments" } ], "impure": false, @@ -906,7 +820,7 @@ "named": [ { "default": 1, - "description": "It is possible to specify the node_id.", + "description": "Optional node identifier (0-1023) to distinguish IDs generated by different machines in a distributed system. Defaults to 1.", "name": "node_id", "no_dynamic": false, "scalars_to_literal": false, @@ -918,7 +832,7 @@ }, { "category": "General", - "description": "Throws an error similar to a regular mapping error. This is useful for abandoning a mapping entirely given certain conditions.", + "description": "Immediately fails the mapping with a custom error message. Use this to halt processing when data validation fails or required fields are missing, causing the message to be routed to error handlers.", "examples": [ { "mapping": "root.doc.type = match {\n this.exists(\"header.id\") => \"foo\"\n this.exists(\"body.data\") => \"bar\"\n _ => throw(\"unknown type\")\n}\nroot.doc.contents = (this.body.content | this.thing.body)", @@ -934,6 +848,21 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root = if this.exists(\"user_id\") {\n this\n} else {\n throw(\"missing required field: user_id\")\n}", + "results": [ + [ + "{\"user_id\":123,\"name\":\"alice\"}", + "{\"name\":\"alice\",\"user_id\":123}" + ], + [ + "{\"name\":\"bob\"}", + "Error(\"failed assignment (line 1): missing required field: user_id\")" + ] + ], + "skip_testing": false, + "summary": "Validate required fields before processing." } ], "impure": false, @@ -953,13 +882,19 @@ }, { "category": "Environment", - "description": "Returns the current unix timestamp in seconds.", + "description": "Returns the current Unix timestamp in seconds since epoch. Use this for numeric timestamps compatible with most systems, or as a seed for random number generation.", "examples": [ { "mapping": "root.received_at = timestamp_unix()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.id = \"%v-%v\".format(timestamp_unix(), batch_index())", + "results": [], + "skip_testing": false, + "summary": "Create a sortable ID combining timestamp with a counter." } ], "impure": false, @@ -969,13 +904,19 @@ }, { "category": "Environment", - "description": "Returns the current unix timestamp in microseconds.", + "description": "Returns the current Unix timestamp in microseconds since epoch. Use this for high-precision timing measurements or when microsecond resolution is required.", "examples": [ { "mapping": "root.received_at = timestamp_unix_micro()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.processing_duration_us = timestamp_unix_micro() - this.start_time_us", + "results": [], + "skip_testing": false, + "summary": "Measure elapsed time between events." } ], "impure": false, @@ -985,13 +926,19 @@ }, { "category": "Environment", - "description": "Returns the current unix timestamp in milliseconds.", + "description": "Returns the current Unix timestamp in milliseconds since epoch. Use this for millisecond-precision timestamps common in web APIs and JavaScript systems.", "examples": [ { "mapping": "root.received_at = timestamp_unix_milli()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "meta processing_time_ms = timestamp_unix_milli()", + "results": [], + "skip_testing": false, + "summary": "Add processing time metadata." } ], "impure": false, @@ -1001,13 +948,19 @@ }, { "category": "Environment", - "description": "Returns the current unix timestamp in nanoseconds.", + "description": "Returns the current Unix timestamp in nanoseconds since epoch. Use this for the highest precision timing or as a unique seed value that changes on every invocation.", "examples": [ { "mapping": "root.received_at = timestamp_unix_nano()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.random_value = random_int(timestamp_unix_nano())", + "results": [], + "skip_testing": false, + "summary": "Generate unique random values on each mapping." } ], "impure": false, @@ -1017,13 +970,19 @@ }, { "category": "Message Info", - "description": "Provides the message trace id. The returned value will be zeroed if the message does not contain a span.", + "description": "Returns the OpenTelemetry trace ID for the message, or an empty string if no tracing span exists. Use this to correlate logs and events with distributed traces.", "examples": [ { "mapping": "meta trace_id = tracing_id()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.log_entry = this\nroot.log_entry.trace_id = tracing_id()", + "results": [], + "skip_testing": false, + "summary": "Add trace ID to structured logs." } ], "impure": false, @@ -1033,7 +992,7 @@ }, { "category": "Message Info", - "description": "Provides the message tracing span xref:components:tracers/about.adoc[(created via Open Telemetry APIs)] as an object serialized via text map formatting. The returned value will be `null` if the message does not have a span.", + "description": "Returns the OpenTelemetry tracing span attached to the message as a text map object, or `null` if no span exists. Use this to propagate trace context to downstream systems via headers or metadata.", "examples": [ { "mapping": "root.headers.traceparent = tracing_span().traceparent", @@ -1045,6 +1004,12 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "meta = tracing_span()", + "results": [], + "skip_testing": false, + "summary": "Forward all tracing fields to output metadata." } ], "impure": false, @@ -1054,25 +1019,19 @@ }, { "category": "General", - "description": "Generate a random ULID.", + "description": "Generates a Universally Unique Lexicographically Sortable Identifier (ULID). ULIDs are 128-bit identifiers that are sortable by creation time, URL-safe, and case-insensitive. They consist of a 48-bit timestamp (millisecond precision) and 80 bits of randomness, making them ideal for distributed systems that need time-ordered unique IDs without coordination.", "examples": [ { - "mapping": "root.id = ulid()", + "mapping": "root.message_id = ulid()\nroot.timestamp = now()\nroot.data = this", "results": [], "skip_testing": false, - "summary": "Using the defaults of Crockford Base32 encoding and secure random source" + "summary": "Generate time-sortable IDs for distributed message ordering" }, { "mapping": "root.id = ulid(\"hex\")", "results": [], "skip_testing": false, - "summary": "ULIDs can be hex-encoded too." - }, - { - "mapping": "root.id = ulid(\"crockford\", \"fast_random\")", - "results": [], - "skip_testing": false, - "summary": "They can be generated using a fast, but unsafe, random source for use cases that are not security-sensitive." + "summary": "Generate hex-encoded ULIDs for systems that prefer hexadecimal format" } ], "impure": false, @@ -1081,7 +1040,7 @@ "named": [ { "default": "crockford", - "description": "The format to encode a ULID into. Valid options are: crockford, hex", + "description": "Encoding format for the ULID. \"crockford\" produces 26-character Base32 strings (recommended). \"hex\" produces 32-character hexadecimal strings.", "name": "encoding", "no_dynamic": false, "scalars_to_literal": false, @@ -1089,7 +1048,7 @@ }, { "default": "secure_random", - "description": "The source of randomness to use for generating ULIDs. \"secure_random\" is recommended for most use cases. \"fast_random\" can be used if security is not a concern.", + "description": "Randomness source: \"secure_random\" uses cryptographically secure random (recommended for production), \"fast_random\" uses faster but non-secure random (only for non-sensitive testing).", "name": "random_source", "no_dynamic": false, "scalars_to_literal": false, @@ -1097,17 +1056,23 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "category": "General", - "description": "Generates a new RFC-4122 UUID each time it is invoked and prints a string representation.", + "description": "Generates a random RFC-4122 version 4 UUID. Use this for creating unique identifiers that don't reveal timing information or require ordering. Each invocation produces a new globally unique ID.", "examples": [ { "mapping": "root.id = uuid_v4()", "results": [], "skip_testing": false, "summary": "" + }, + { + "mapping": "root = this\nroot.request_id = uuid_v4()", + "results": [], + "skip_testing": false, + "summary": "Add unique request IDs for tracing." } ], "impure": false, @@ -1117,7 +1082,7 @@ }, { "category": "General", - "description": "Generates a new time ordered UUID each time it is invoked and prints a string representation.", + "description": "Generates a time-ordered UUID version 7 with millisecond timestamp precision. Use this for sortable unique identifiers that maintain chronological ordering, ideal for database keys or event IDs. Optionally specify a custom timestamp.", "examples": [ { "mapping": "root.id = uuid_v7()", @@ -1129,7 +1094,7 @@ "mapping": "root.id = uuid_v7(now().ts_sub_iso8601(\"PT1M\"))", "results": [], "skip_testing": false, - "summary": "It is also possible to specify the timestamp for the uuid_v7" + "summary": "Generate a UUID with a specific timestamp for backdating events." } ], "impure": false, @@ -1164,6 +1129,45 @@ ] }, "status": "hidden" + }, + { + "category": "Encoding", + "description": "Prepends a Confluent Schema Registry wire format header to message bytes. The header is 5 bytes: a magic byte (0x00) followed by a 4-byte big-endian schema ID. This format is required when producing messages to Kafka topics that use Confluent Schema Registry for schema validation and evolution.", + "examples": [ + { + "mapping": "root = with_schema_registry_header(123, content())", + "results": [], + "skip_testing": false, + "summary": "Add Schema Registry header to Avro-encoded message" + }, + { + "mapping": "root = with_schema_registry_header(meta(\"schema_id\").number(), content())", + "results": [], + "skip_testing": false, + "summary": "Use schema ID from metadata to add header dynamically" + } + ], + "impure": false, + "name": "with_schema_registry_header", + "params": { + "named": [ + { + "description": "The schema ID from your Schema Registry (0 to 4294967295). This ID references the schema version used to encode the message.", + "name": "schema_id", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" + }, + { + "description": "The serialized message bytes (e.g., Avro, Protobuf, or JSON Schema encoded data) to prepend the header to.", + "name": "message", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" + } + ] + }, + "status": "stable" } ], "bloblang-methods": [ @@ -1188,6 +1192,19 @@ } ], "description": "Returns the absolute value of an int64 or float64 number. As a special case, when an integer is provided that is the minimum value it is converted to the maximum value.", + "examples": [ + { + "mapping": "\nroot.outs = this.ins.map_each(ele -> ele.abs())\n", + "results": [ + [ + "{\"ins\":[9,-18,1.23,-4.56]}", + "{\"outs\":[9,18,1.23,4.56]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "abs", "params": {}, @@ -1213,11 +1230,58 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.all_positive = this.values.all(v -> v > 0)", + "results": [ + [ + "{\"values\":[1,2,3,4,5]}", + "{\"all_positive\":true}" + ], + [ + "{\"values\":[1,-2,3,4,5]}", + "{\"all_positive\":false}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Checks each element of an array against a query and returns true if all elements passed. An error occurs if the target is not an array, or if any element results in the provided query returning a non-boolean result. Returns false if the target array is empty.", + "description": "Tests whether all elements in an array satisfy a condition. Returns true only if the query evaluates to true for every element. Returns false for empty arrays.", + "examples": [ + { + "mapping": "root.all_over_21 = this.patrons.all(patron -> patron.age >= 21)", + "results": [ + [ + "{\"patrons\":[{\"id\":\"1\",\"age\":18},{\"id\":\"2\",\"age\":23}]}", + "{\"all_over_21\":false}" + ], + [ + "{\"patrons\":[{\"id\":\"1\",\"age\":45},{\"id\":\"2\",\"age\":23}]}", + "{\"all_over_21\":true}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.all_positive = this.values.all(v -> v > 0)", + "results": [ + [ + "{\"values\":[1,2,3,4,5]}", + "{\"all_positive\":true}" + ], + [ + "{\"values\":[1,-2,3,4,5]}", + "{\"all_positive\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "all", "params": { @@ -1253,11 +1317,58 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.has_errors = this.results.any(r -> r.status == \"error\")", + "results": [ + [ + "{\"results\":[{\"status\":\"ok\"},{\"status\":\"error\"},{\"status\":\"ok\"}]}", + "{\"has_errors\":true}" + ], + [ + "{\"results\":[{\"status\":\"ok\"},{\"status\":\"ok\"}]}", + "{\"has_errors\":false}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Checks the elements of an array against a query and returns true if any element passes. An error occurs if the target is not an array, or if an element results in the provided query returning a non-boolean result. Returns false if the target array is empty.", + "description": "Tests whether at least one element in an array satisfies a condition. Returns true if the query evaluates to true for any element. Returns false for empty arrays.", + "examples": [ + { + "mapping": "root.any_over_21 = this.patrons.any(patron -> patron.age >= 21)", + "results": [ + [ + "{\"patrons\":[{\"id\":\"1\",\"age\":18},{\"id\":\"2\",\"age\":23}]}", + "{\"any_over_21\":true}" + ], + [ + "{\"patrons\":[{\"id\":\"1\",\"age\":10},{\"id\":\"2\",\"age\":12}]}", + "{\"any_over_21\":false}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.has_errors = this.results.any(r -> r.status == \"error\")", + "results": [ + [ + "{\"results\":[{\"status\":\"ok\"},{\"status\":\"error\"},{\"status\":\"ok\"}]}", + "{\"has_errors\":true}" + ], + [ + "{\"results\":[{\"status\":\"ok\"},{\"status\":\"ok\"}]}", + "{\"has_errors\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "any", "params": { @@ -1289,11 +1400,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.combined = this.items.append(this.new_item)", + "results": [ + [ + "{\"items\":[\"apple\",\"banana\"],\"new_item\":\"orange\"}", + "{\"combined\":[\"apple\",\"banana\",\"orange\"]}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Returns an array with new elements appended to the end.", + "description": "Adds one or more elements to the end of an array and returns the new array. The original array is not modified.", + "examples": [ + { + "mapping": "root.foo = this.foo.append(\"and\", \"this\")", + "results": [ + [ + "{\"foo\":[\"bar\",\"baz\"]}", + "{\"foo\":[\"bar\",\"baz\",\"and\",\"this\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.combined = this.items.append(this.new_item)", + "results": [ + [ + "{\"items\":[\"apple\",\"banana\"],\"new_item\":\"orange\"}", + "{\"combined\":[\"apple\",\"banana\",\"orange\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "append", "params": { @@ -1302,6 +1448,36 @@ "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": [ + { + "mapping": "map thing {\n root.inner = this.first\n}\n\nroot.foo = this.doc.apply(\"thing\")", + "results": [ + [ + "{\"doc\":{\"first\":\"hello world\"}}", + "{\"foo\":{\"inner\":\"hello world\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "map create_foo {\n root.name = \"a foo\"\n root.purpose = \"to be a foo\"\n}\n\nroot = this\nroot.foo = null.apply(\"create_foo\")", + "results": [ + [ + "{\"id\":\"1234\"}", + "{\"foo\":{\"name\":\"a foo\",\"purpose\":\"to be a foo\"},\"id\":\"1234\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ] + } + ], "description": "Apply a declared mapping to a target value.", "examples": [ { @@ -1362,6 +1538,20 @@ ] } ], + "description": "Converts a value to an array", + "examples": [ + { + "mapping": "root.my_array = this.name.array()", + "results": [ + [ + "{\"name\":\"foobar bazson\"}", + "{\"my_array\":[\"foobar bazson\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "array", "params": {}, @@ -1383,11 +1573,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.config = this.defaults.assign(this.user_settings)", + "results": [ + [ + "{\"defaults\":{\"timeout\":30,\"retries\":3},\"user_settings\":{\"timeout\":60}}", + "{\"config\":{\"retries\":3,\"timeout\":60}}" + ] + ], + "skip_testing": false, + "summary": "Override defaults with user settings" } ] } ], - "description": "Merge a source object into an existing destination object. When a collision is found within the merged structures (both a source and destination object contain the same non-object keys) the value in the destination object will be overwritten by that of source object. In order to preserve both values on collision use the <> method.", + "description": "Merges two objects or arrays with override behavior. For objects, source values replace destination values on key conflicts. Arrays are concatenated. To preserve both values on conflict, use the merge method instead.", + "examples": [ + { + "mapping": "root = this.foo.assign(this.bar)", + "results": [ + [ + "{\"foo\":{\"first_name\":\"fooer\",\"likes\":\"bars\"},\"bar\":{\"second_name\":\"barer\",\"likes\":\"foos\"}}", + "{\"first_name\":\"fooer\",\"likes\":\"foos\",\"second_name\":\"barer\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.config = this.defaults.assign(this.user_settings)", + "results": [ + [ + "{\"defaults\":{\"timeout\":30,\"retries\":3},\"user_settings\":{\"timeout\":60}}", + "{\"config\":{\"retries\":3,\"timeout\":60}}" + ] + ], + "skip_testing": false, + "summary": "Override defaults with user settings" + } + ], "impure": false, "name": "assign", "params": { @@ -1415,14 +1640,17 @@ [ "{\"value\":12}", "{\"new_value\":4}" - ], - [ - "{\"value\":0}", - "{\"new_value\":0}" - ], + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.masked = this.flags.bitwise_and(15)", + "results": [ [ - "{\"value\":-4}", - "{\"new_value\":4}" + "{\"flags\":127}", + "{\"masked\":15}" ] ], "skip_testing": false, @@ -1431,7 +1659,31 @@ ] } ], - "description": "Returns the number bitwise AND-ed with the specified value.", + "description": "Performs a bitwise AND operation between the integer and the specified value.", + "examples": [ + { + "mapping": "root.new_value = this.value.bitwise_and(6)", + "results": [ + [ + "{\"value\":12}", + "{\"new_value\":4}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.masked = this.flags.bitwise_and(15)", + "results": [ + [ + "{\"flags\":127}", + "{\"masked\":15}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bitwise_and", "params": { @@ -1459,14 +1711,17 @@ [ "{\"value\":12}", "{\"new_value\":14}" - ], - [ - "{\"value\":0}", - "{\"new_value\":6}" - ], + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.combined = this.flags.bitwise_or(8)", + "results": [ [ - "{\"value\":-2}", - "{\"new_value\":-2}" + "{\"flags\":4}", + "{\"combined\":12}" ] ], "skip_testing": false, @@ -1475,7 +1730,31 @@ ] } ], - "description": "Returns the number bitwise OR-ed with the specified value.", + "description": "Performs a bitwise OR operation between the integer and the specified value.", + "examples": [ + { + "mapping": "root.new_value = this.value.bitwise_or(6)", + "results": [ + [ + "{\"value\":12}", + "{\"new_value\":14}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.combined = this.flags.bitwise_or(8)", + "results": [ + [ + "{\"flags\":4}", + "{\"combined\":12}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bitwise_or", "params": { @@ -1503,14 +1782,17 @@ [ "{\"value\":12}", "{\"new_value\":10}" - ], - [ - "{\"value\":0}", - "{\"new_value\":6}" - ], + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.toggled = this.flags.bitwise_xor(5)", + "results": [ [ - "{\"value\":-2}", - "{\"new_value\":-8}" + "{\"flags\":3}", + "{\"toggled\":6}" ] ], "skip_testing": false, @@ -1519,7 +1801,31 @@ ] } ], - "description": "Returns the number bitwise eXclusive-OR-ed with the specified value.", + "description": "Performs a bitwise XOR (exclusive OR) operation between the integer and the specified value.", + "examples": [ + { + "mapping": "root.new_value = this.value.bitwise_xor(6)", + "results": [ + [ + "{\"value\":12}", + "{\"new_value\":10}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.toggled = this.flags.bitwise_xor(5)", + "results": [ + [ + "{\"flags\":3}", + "{\"toggled\":6}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bitwise_xor", "params": { @@ -1560,6 +1866,23 @@ } ], "description": "Executes an argument Bloblang mapping on the target. This method can be used in order to execute dynamic mappings. Imports and functions that interact with the environment, such as `file` and `env`, or that access message information directly, such as `content` or `json`, are not enabled for dynamic Bloblang mappings.", + "examples": [ + { + "mapping": "root.body = this.body.bloblang(this.mapping)", + "results": [ + [ + "{\"body\":{\"foo\":\"hello world\"},\"mapping\":\"root.foo = this.foo.uppercase()\"}", + "{\"body\":{\"foo\":\"HELLO WORLD\"}}" + ], + [ + "{\"body\":{\"foo\":\"hello world 2\"},\"mapping\":\"root.foo = this.foo.capitalize()\"}", + "{\"body\":{\"foo\":\"Hello World 2\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bloblang", "params": { @@ -1590,6 +1913,15 @@ ] } ], + "description": "Converts a value to a boolean with optional fallback", + "examples": [ + { + "mapping": "root.foo = this.thing.bool()\nroot.bar = this.thing.bool(true)", + "results": [], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bool", "params": { @@ -1626,6 +1958,20 @@ ] } ], + "description": "Marshals a value into a byte array", + "examples": [ + { + "mapping": "root.first_byte = this.name.bytes().index(0)", + "results": [ + [ + "{\"name\":\"foobar bazson\"}", + "{\"first_byte\":102}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "bytes", "params": {}, @@ -1635,7 +1981,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Takes a string value and returns a copy with all Unicode letters that begin words mapped to their Unicode title case.", + "Description": "Converts the first letter of each word in a string to uppercase (title case). Useful for formatting names, titles, and headings.", "Examples": [ { "mapping": "root.title = this.title.capitalize()", @@ -1647,16 +1993,92 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.name = this.name.capitalize()", + "results": [ + [ + "{\"name\":\"alice smith\"}", + "{\"name\":\"Alice Smith\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Converts a string to title case with Unicode letter mapping", + "examples": [ + { + "mapping": "root.title = this.title.capitalize()", + "results": [ + [ + "{\"title\":\"the foo bar\"}", + "{\"title\":\"The Foo Bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.name = this.name.capitalize()", + "results": [ + [ + "{\"name\":\"alice smith\"}", + "{\"name\":\"Alice Smith\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "capitalize", "params": {}, "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": [ + { + "mapping": "root.doc.id = this.thing.id.string().catch(uuid_v4())", + "results": [], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.url = this.url.parse_url().catch(err -> {\"error\":err,\"input\":this.url})", + "results": [ + [ + "{\"url\":\"invalid %&# url\"}", + "{\"url\":{\"error\":\"field `this.url`: parse \\\"invalid %&\\\": invalid URL escape \\\"%&\\\"\",\"input\":\"invalid %&# url\"}}" + ] + ], + "skip_testing": false, + "summary": "The fallback argument can be a mapping, allowing you to capture the error string and yield structured data back." + }, + { + "mapping": "root = this.catch(deleted())", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\"doc\":{\"foo\":\"bar\"}}" + ], + [ + "not structured data", + "" + ] + ], + "skip_testing": false, + "summary": "When the input document is not structured attempting to reference structured fields with `this` will result in an error. Therefore, a convenient way to delete non-structured data is with a catch." + } + ] + } + ], "description": "If the result of a target query fails (due to incorrect types, failed parsing, etc) the argument is returned instead.", "examples": [ { @@ -1727,11 +2149,50 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.result = this.price.ceil()", + "results": [ + [ + "{\"price\":19.99}", + "{\"result\":20}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Returns the least integer value greater than or equal to a number. If the resulting value fits within a 64-bit integer then that is returned, otherwise a new floating point number is returned.", + "description": "Rounds a number up to the nearest integer. Returns an integer if the result fits in 64-bit, otherwise returns a float.", + "examples": [ + { + "mapping": "root.new_value = this.value.ceil()", + "results": [ + [ + "{\"value\":5.3}", + "{\"new_value\":6}" + ], + [ + "{\"value\":-5.9}", + "{\"new_value\":-5}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.result = this.price.ceil()", + "results": [ + [ + "{\"price\":19.99}", + "{\"result\":20}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "ceil", "params": {}, @@ -1741,7 +2202,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Collapse an array or object into an object of key/value pairs for each field, where the key is the full path of the structured field in dot path notation. Empty arrays an objects are ignored by default.", + "Description": "Flattens a nested structure into a single-level object with dot-notation keys representing the full path to each value. Empty arrays and objects are excluded by default.", "Examples": [ { "mapping": "root.result = this.collapse()", @@ -1763,11 +2224,36 @@ ] ], "skip_testing": false, - "summary": "An optional boolean parameter can be set to true in order to include empty objects and arrays." + "summary": "Set include_empty to true to preserve empty objects and arrays in the output." } ] } ], + "description": "Flattens a nested structure into a flat object with dot-notation keys", + "examples": [ + { + "mapping": "root.result = this.collapse()", + "results": [ + [ + "{\"foo\":[{\"bar\":\"1\"},{\"bar\":{}},{\"bar\":\"2\"},{\"bar\":[]}]}", + "{\"result\":{\"foo.0.bar\":\"1\",\"foo.2.bar\":\"2\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.result = this.collapse(include_empty: true)", + "results": [ + [ + "{\"foo\":[{\"bar\":\"1\"},{\"bar\":{}},{\"bar\":\"2\"},{\"bar\":[]}]}", + "{\"result\":{\"foo.0.bar\":\"1\",\"foo.1.bar\":{},\"foo.2.bar\":\"2\",\"foo.3.bar\":[]}}" + ] + ], + "skip_testing": false, + "summary": "Set include_empty to true to preserve empty objects and arrays in the output." + } + ], "impure": false, "name": "collapse", "params": { @@ -1816,6 +2302,30 @@ } ], "description": "Checks whether a string matches a hashed secret using Argon2.", + "examples": [ + { + "mapping": "root.match = this.secret.compare_argon2(\"$argon2id$v=19$m=4096,t=3,p=1$c2FsdHktbWNzYWx0ZmFjZQ$RMUMwgtS32/mbszd+ke4o4Ej1jFpYiUqY6MHWa69X7Y\")", + "results": [ + [ + "{\"secret\":\"there-are-many-blobs-in-the-sea\"}", + "{\"match\":true}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.match = this.secret.compare_argon2(\"$argon2id$v=19$m=4096,t=3,p=1$c2FsdHktbWNzYWx0ZmFjZQ$RMUMwgtS32/mbszd+ke4o4Ej1jFpYiUqY6MHWa69X7Y\")", + "results": [ + [ + "{\"secret\":\"will-i-ever-find-love\"}", + "{\"match\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "compare_argon2", "params": { @@ -1863,6 +2373,30 @@ } ], "description": "Checks whether a string matches a hashed secret using bcrypt.", + "examples": [ + { + "mapping": "root.match = this.secret.compare_bcrypt(\"$2y$10$Dtnt5NNzVtMCOZONT705tOcS8It6krJX8bEjnDJnwxiFKsz1C.3Ay\")", + "results": [ + [ + "{\"secret\":\"there-are-many-blobs-in-the-sea\"}", + "{\"match\":true}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.match = this.secret.compare_bcrypt(\"$2y$10$Dtnt5NNzVtMCOZONT705tOcS8It6krJX8bEjnDJnwxiFKsz1C.3Ay\")", + "results": [ + [ + "{\"secret\":\"will-i-ever-find-love\"}", + "{\"match\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "compare_bcrypt", "params": { @@ -1885,37 +2419,61 @@ "Description": "", "Examples": [ { - "mapping": "let long_content = range(0, 1000).map_each(content()).join(\" \")\nroot.a_len = $long_content.length()\nroot.b_len = $long_content.compress(\"gzip\").length()\n", + "mapping": "root.compressed = content().bytes().compress(\"gzip\").encode(\"base64\")", "results": [ [ - "hello world this is some content", - "{\"a_len\":32999,\"b_len\":161}" + "{\"message\":\"hello world I love space\"}", + "{\"compressed\":\"H4sIAAAJbogA/wAmANn/eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQgSSBsb3ZlIHNwYWNlIn0DAHEvdwomAAAA\"}" ] ], "skip_testing": false, - "summary": "" + "summary": "Compress and encode for safe transmission" }, { - "mapping": "root.compressed = content().compress(\"lz4\").encode(\"base64\")", + "mapping": "root.original_size = content().length()\nroot.gzip_size = content().compress(\"gzip\").length()\nroot.lz4_size = content().compress(\"lz4\").length()", "results": [ [ - "hello world I love space", - "{\"compressed\":\"BCJNGGRwuRgAAIBoZWxsbyB3b3JsZCBJIGxvdmUgc3BhY2UAAAAAGoETLg==\"}" + "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", + "{\"gzip_size\":114,\"lz4_size\":85,\"original_size\":89}" ] ], "skip_testing": false, - "summary": "" + "summary": "Compare compression ratios across algorithms" } ] } ], - "description": "Compresses a string or byte array value according to a specified algorithm.", + "description": "Compresses a string or byte array using the specified compression algorithm. Returns compressed data as bytes. Useful for reducing payload size before transmission or storage.", + "examples": [ + { + "mapping": "root.compressed = content().bytes().compress(\"gzip\").encode(\"base64\")", + "results": [ + [ + "{\"message\":\"hello world I love space\"}", + "{\"compressed\":\"H4sIAAAJbogA/wAmANn/eyJtZXNzYWdlIjoiaGVsbG8gd29ybGQgSSBsb3ZlIHNwYWNlIn0DAHEvdwomAAAA\"}" + ] + ], + "skip_testing": false, + "summary": "Compress and encode for safe transmission" + }, + { + "mapping": "root.original_size = content().length()\nroot.gzip_size = content().compress(\"gzip\").length()\nroot.lz4_size = content().compress(\"lz4\").length()", + "results": [ + [ + "The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy dog.", + "{\"gzip_size\":114,\"lz4_size\":85,\"original_size\":89}" + ] + ], + "skip_testing": false, + "summary": "Compare compression ratios across algorithms" + } + ], "impure": false, "name": "compress", "params": { "named": [ { - "description": "One of `flate`, `gzip`, `pgzip`, `lz4`, `snappy`, `zlib`, `zstd`.", + "description": "The compression algorithm: `flate`, `gzip`, `pgzip` (parallel gzip), `lz4`, `snappy`, `zlib`, or `zstd`.", "name": "algorithm", "no_dynamic": false, "scalars_to_literal": false, @@ -1923,7 +2481,7 @@ }, { "default": -1, - "description": "The level of compression to use. May not be applicable to all algorithms.", + "description": "Compression level (default: -1 for default compression). Higher values increase compression ratio but use more CPU. Range and effect varies by algorithm.", "name": "level", "no_dynamic": false, "scalars_to_literal": false, @@ -1954,6 +2512,19 @@ } ], "description": "Concatenates an array value with one or more argument arrays.", + "examples": [ + { + "mapping": "root.foo = this.foo.concat(this.bar, this.baz)", + "results": [ + [ + "{\"foo\":[\"a\",\"b\"],\"bar\":[\"c\"],\"baz\":[\"d\",\"e\",\"f\"]}", + "{\"foo\":[\"a\",\"b\",\"c\",\"d\",\"e\",\"f\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "concat", "params": { @@ -2021,6 +2592,54 @@ ] } ], + "description": "Tests if an array or object contains a value", + "examples": [ + { + "mapping": "root.has_foo = this.thing.contains(\"foo\")", + "results": [ + [ + "{\"thing\":[\"this\",\"foo\",\"that\"]}", + "{\"has_foo\":true}" + ], + [ + "{\"thing\":[\"this\",\"bar\",\"that\"]}", + "{\"has_foo\":false}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.has_bar = this.thing.contains(20)", + "results": [ + [ + "{\"thing\":[10.3,20.0,\"huh\",3]}", + "{\"has_bar\":true}" + ], + [ + "{\"thing\":[2,3,40,67]}", + "{\"has_bar\":false}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.has_foo = this.thing.contains(\"foo\")", + "results": [ + [ + "{\"thing\":\"this foo that\"}", + "{\"has_foo\":true}" + ], + [ + "{\"thing\":\"this bar that\"}", + "{\"has_foo\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "contains", "params": { @@ -2065,8 +2684,29 @@ } ], "description": "Calculates the cosine of a given angle specified in radians.", - "impure": false, - "name": "cos", + "examples": [ + { + "mapping": "root.new_value = (this.value * (pi() / 180)).cos()", + "results": [ + [ + "{\"value\":45}", + "{\"new_value\":0.7071067811865476}" + ], + [ + "{\"value\":0}", + "{\"new_value\":1}" + ], + [ + "{\"value\":180}", + "{\"new_value\":-1}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], + "impure": false, + "name": "cos", "params": {}, "status": "stable" }, @@ -2101,6 +2741,31 @@ ] } ], + "description": "Decodes an encoded string according to a chosen scheme", + "examples": [ + { + "mapping": "root.decoded = this.value.decode(\"hex\").string()", + "results": [ + [ + "{\"value\":\"68656c6c6f20776f726c64\"}", + "{\"decoded\":\"hello world\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = this.encoded.decode(\"ascii85\")", + "results": [ + [ + "{\"encoded\":\"FD,B0+DGm>FDl80Ci\\\"A>F`)8BEckl6F`M&(+Cno&@/\"}", + "this is totally unstructured data" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "decode", "params": { @@ -2123,37 +2788,61 @@ "Description": "", "Examples": [ { - "mapping": "root = this.compressed.decode(\"base64\").decompress(\"lz4\")", + "mapping": "root = this.compressed.decode(\"base64\").decompress(\"gzip\")", "results": [ [ - "{\"compressed\":\"BCJNGGRwuRgAAIBoZWxsbyB3b3JsZCBJIGxvdmUgc3BhY2UAAAAAGoETLg==\"}", + "{\"compressed\":\"H4sIAN12MWkAA8tIzcnJVyjPL8pJUfBUyMkvS1UoLkhMTgUAQpDxbxgAAAA=\"}", "hello world I love space" ] ], "skip_testing": false, - "summary": "" + "summary": "Decompress base64-encoded compressed data" }, { - "mapping": "root.result = this.compressed.decode(\"base64\").decompress(\"lz4\").string()", + "mapping": "root.message = this.compressed.decode(\"base64\").decompress(\"gzip\").string()", "results": [ [ - "{\"compressed\":\"BCJNGGRwuRgAAIBoZWxsbyB3b3JsZCBJIGxvdmUgc3BhY2UAAAAAGoETLg==\"}", - "{\"result\":\"hello world I love space\"}" + "{\"compressed\":\"H4sIAN12MWkAA8tIzcnJVyjPL8pJUfBUyMkvS1UoLkhMTgUAQpDxbxgAAAA=\"}", + "{\"message\":\"hello world I love space\"}" ] ], "skip_testing": false, - "summary": "Use the `.string()` method in order to coerce the result into a string, this makes it possible to place the data within a JSON document without automatic base64 encoding." + "summary": "Convert decompressed bytes to string for JSON output" } ] } ], - "description": "Decompresses a string or byte array value according to a specified algorithm. The result of decompression ", + "description": "Decompresses a byte array using the specified decompression algorithm. Returns decompressed data as bytes. Use with data that was previously compressed using the corresponding algorithm.", + "examples": [ + { + "mapping": "root = this.compressed.decode(\"base64\").decompress(\"gzip\")", + "results": [ + [ + "{\"compressed\":\"H4sIAN12MWkAA8tIzcnJVyjPL8pJUfBUyMkvS1UoLkhMTgUAQpDxbxgAAAA=\"}", + "hello world I love space" + ] + ], + "skip_testing": false, + "summary": "Decompress base64-encoded compressed data" + }, + { + "mapping": "root.message = this.compressed.decode(\"base64\").decompress(\"gzip\").string()", + "results": [ + [ + "{\"compressed\":\"H4sIAN12MWkAA8tIzcnJVyjPL8pJUfBUyMkvS1UoLkhMTgUAQpDxbxgAAAA=\"}", + "{\"message\":\"hello world I love space\"}" + ] + ], + "skip_testing": false, + "summary": "Convert decompressed bytes to string for JSON output" + } + ], "impure": false, "name": "decompress", "params": { "named": [ { - "description": "One of `gzip`, `pgzip`, `zlib`, `bzip2`, `flate`, `snappy`, `lz4`, `zstd`.", + "description": "The decompression algorithm: `gzip`, `pgzip` (parallel gzip), `zlib`, `bzip2`, `flate`, `snappy`, `lz4`, or `zstd`.", "name": "algorithm", "no_dynamic": false, "scalars_to_literal": false, @@ -2183,6 +2872,20 @@ ] } ], + "description": "Decrypts an AES-encrypted string or byte array", + "examples": [ + { + "mapping": "let key = \"2b7e151628aed2a6abf7158809cf4f3c\".decode(\"hex\")\nlet vector = \"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff\".decode(\"hex\")\nroot.decrypted = this.value.decode(\"hex\").decrypt_aes(\"ctr\", $key, $vector).string()", + "results": [ + [ + "{\"value\":\"84e9b31ff7400bdf80be7254\"}", + "{\"decrypted\":\"hello world!\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "decrypt_aes", "params": { @@ -2217,16 +2920,63 @@ { "Category": "Object & Array Manipulation", "Description": "", - "Examples": null + "Examples": [ + { + "mapping": "root.changes = this.before.diff(this.after)", + "results": [ + [ + "{\"before\":{\"name\":\"Alice\",\"age\":30},\"after\":{\"name\":\"Alice\",\"age\":31,\"city\":\"NYC\"}}", + "{\"changes\":[{\"From\":30,\"Path\":[\"age\"],\"To\":31,\"Type\":\"update\"},{\"From\":null,\"Path\":[\"city\"],\"To\":\"NYC\",\"Type\":\"create\"}]}" + ] + ], + "skip_testing": false, + "summary": "Compare two objects to track field changes" + }, + { + "mapping": "root.changelog = this.old_config.diff(this.new_config)", + "results": [ + [ + "{\"old_config\":{\"debug\":true,\"timeout\":30},\"new_config\":{\"timeout\":60}}", + "{\"changelog\":[{\"From\":true,\"Path\":[\"debug\"],\"To\":null,\"Type\":\"delete\"},{\"From\":30,\"Path\":[\"timeout\"],\"To\":60,\"Type\":\"update\"}]}" + ] + ], + "skip_testing": false, + "summary": "Detect deletions in configuration changes" + } + ] + } + ], + "description": "Compares the current value with another value and returns a detailed changelog describing all differences. The changelog contains operations (create, update, delete) with their paths and values, enabling you to track changes between data versions, implement audit logs, or synchronize data between systems.", + "examples": [ + { + "mapping": "root.changes = this.before.diff(this.after)", + "results": [ + [ + "{\"before\":{\"name\":\"Alice\",\"age\":30},\"after\":{\"name\":\"Alice\",\"age\":31,\"city\":\"NYC\"}}", + "{\"changes\":[{\"From\":30,\"Path\":[\"age\"],\"To\":31,\"Type\":\"update\"},{\"From\":null,\"Path\":[\"city\"],\"To\":\"NYC\",\"Type\":\"create\"}]}" + ] + ], + "skip_testing": false, + "summary": "Compare two objects to track field changes" + }, + { + "mapping": "root.changelog = this.old_config.diff(this.new_config)", + "results": [ + [ + "{\"old_config\":{\"debug\":true,\"timeout\":30},\"new_config\":{\"timeout\":60}}", + "{\"changelog\":[{\"From\":true,\"Path\":[\"debug\"],\"To\":null,\"Type\":\"delete\"},{\"From\":30,\"Path\":[\"timeout\"],\"To\":60,\"Type\":\"update\"}]}" + ] + ], + "skip_testing": false, + "summary": "Detect deletions in configuration changes" } ], - "description": "Create a diff by comparing the current value with the given one. Wraps the github.com/r3labs/diff/v3 package. See its https://pkg.go.dev/github.com/r3labs/diff/v3[docs^] for more information.", "impure": false, "name": "diff", "params": { "named": [ { - "description": "The value to compare against.", + "description": "The value to compare against the current value. Can be any structured data (object or array).", "name": "other", "no_dynamic": false, "scalars_to_literal": false, @@ -2234,7 +2984,7 @@ } ] }, - "status": "beta", + "status": "stable", "version": "4.25.0" }, { @@ -2268,6 +3018,31 @@ ] } ], + "description": "Encodes a string or byte array according to a chosen scheme", + "examples": [ + { + "mapping": "root.encoded = this.value.encode(\"hex\")", + "results": [ + [ + "{\"value\":\"hello world\"}", + "{\"encoded\":\"68656c6c6f20776f726c64\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.encoded = content().encode(\"ascii85\")", + "results": [ + [ + "this is totally unstructured data", + "{\"encoded\":\"FD,B0+DGm>FDl80Ci\\\"A>F`)8BEckl6F`M&(+Cno&@/\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "encode", "params": { @@ -2303,6 +3078,20 @@ ] } ], + "description": "Encrypts a string or byte array using AES encryption", + "examples": [ + { + "mapping": "let key = \"2b7e151628aed2a6abf7158809cf4f3c\".decode(\"hex\")\nlet vector = \"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff\".decode(\"hex\")\nroot.encrypted = this.value.encrypt_aes(\"ctr\", $key, $vector).encode(\"hex\")", + "results": [ + [ + "{\"value\":\"hello world!\"}", + "{\"encrypted\":\"84e9b31ff7400bdf80be7254\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "encrypt_aes", "params": { @@ -2348,11 +3137,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.first_two = this.items.enumerated().filter(item -> item.index < 2).map_each(item -> item.value)", + "results": [ + [ + "{\"items\":[\"a\",\"b\",\"c\",\"d\"]}", + "{\"first_two\":[\"a\",\"b\"]}" + ] + ], + "skip_testing": false, + "summary": "Useful for filtering by index position" } ] } ], - "description": "Converts an array into a new array of objects, where each object has a field index containing the `index` of the element and a field `value` containing the original value of the element.", + "description": "Transforms an array into an array of objects with index and value fields, making it easy to access both the position and content of each element.", + "examples": [ + { + "mapping": "root.foo = this.foo.enumerated()", + "results": [ + [ + "{\"foo\":[\"bar\",\"baz\"]}", + "{\"foo\":[{\"index\":0,\"value\":\"bar\"},{\"index\":1,\"value\":\"baz\"}]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.first_two = this.items.enumerated().filter(item -> item.index < 2).map_each(item -> item.value)", + "results": [ + [ + "{\"items\":[\"a\",\"b\",\"c\",\"d\"]}", + "{\"first_two\":[\"a\",\"b\"]}" + ] + ], + "skip_testing": false, + "summary": "Useful for filtering by index position" + } + ], "impure": false, "name": "enumerated", "params": {}, @@ -2362,7 +3186,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Escapes a string so that special characters like `<` to become `<`. It escapes only five such characters: `<`, `>`, `&`, `'` and `\"` so that it can be safely placed within an HTML entity.", + "Description": "Escapes special HTML characters (`<`, `>`, `&`, `'`, `\"`) to make a string safe for HTML output. Use when embedding untrusted text in HTML to prevent XSS vulnerabilities.", "Examples": [ { "mapping": "root.escaped = this.value.escape_html()", @@ -2374,10 +3198,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.safe_html = this.user_input.escape_html()", + "results": [ + [ + "{\"user_input\":\"\"}", + "{\"safe_html\":\"<script>alert('xss')</script>\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Escapes HTML special characters", + "examples": [ + { + "mapping": "root.escaped = this.value.escape_html()", + "results": [ + [ + "{\"value\":\"foo & bar\"}", + "{\"escaped\":\"foo & bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.safe_html = this.user_input.escape_html()", + "results": [ + [ + "{\"user_input\":\"\"}", + "{\"safe_html\":\"<script>alert('xss')</script>\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "escape_html", "params": {}, @@ -2387,7 +3247,68 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Escapes a string so that it can be safely placed within a URL query.", + "Description": "Encodes a string for safe use in URL path segments using percent-encoding. Unlike `escape_url_query`, spaces are encoded as `%20` instead of `+`. Use when building URL paths with dynamic segments.", + "Examples": [ + { + "mapping": "root.escaped = this.value.escape_url_path()", + "results": [ + [ + "{\"value\":\"foo & bar\"}", + "{\"escaped\":\"foo%20&%20bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.url = \"https://example.com/docs/\" + this.path.escape_url_path()", + "results": [ + [ + "{\"path\":\"my document.pdf\"}", + "{\"url\":\"https://example.com/docs/my%20document.pdf\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ] + } + ], + "description": "Escapes a string for use in URL paths", + "examples": [ + { + "mapping": "root.escaped = this.value.escape_url_path()", + "results": [ + [ + "{\"value\":\"foo & bar\"}", + "{\"escaped\":\"foo%20&%20bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.url = \"https://example.com/docs/\" + this.path.escape_url_path()", + "results": [ + [ + "{\"path\":\"my document.pdf\"}", + "{\"url\":\"https://example.com/docs/my%20document.pdf\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], + "impure": false, + "name": "escape_url_path", + "params": {}, + "status": "stable" + }, + { + "categories": [ + { + "Category": "String Manipulation", + "Description": "Encodes a string for safe use in URL query parameters. Converts spaces to `+` and special characters to percent-encoded values. Use when building URLs with dynamic query parameters.", "Examples": [ { "mapping": "root.escaped = this.value.escape_url_query()", @@ -2399,17 +3320,95 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.url = \"https://example.com?search=\" + this.query.escape_url_query()", + "results": [ + [ + "{\"query\":\"hello world!\"}", + "{\"url\":\"https://example.com?search=hello+world%21\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Escapes a string for use in URL query parameters", + "examples": [ + { + "mapping": "root.escaped = this.value.escape_url_query()", + "results": [ + [ + "{\"value\":\"foo & bar\"}", + "{\"escaped\":\"foo+%26+bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.url = \"https://example.com?search=\" + this.query.escape_url_query()", + "results": [ + [ + "{\"query\":\"hello world!\"}", + "{\"url\":\"https://example.com?search=hello+world%21\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "escape_url_query", "params": {}, "status": "stable" }, { - "description": "Checks that a field, identified via a xref:configuration:field_paths.adoc[dot path], exists in an object.", + "categories": [ + { + "Category": "Object & Array Manipulation", + "Description": "", + "Examples": [ + { + "mapping": "root.result = this.foo.exists(\"bar.baz\")", + "results": [ + [ + "{\"foo\":{\"bar\":{\"baz\":\"yep, I exist\"}}}", + "{\"result\":true}" + ], + [ + "{\"foo\":{\"bar\":{}}}", + "{\"result\":false}" + ], + [ + "{\"foo\":{}}", + "{\"result\":false}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.has_field = this.data.exists(\"optional_field\")", + "results": [ + [ + "{\"data\":{\"optional_field\":null}}", + "{\"has_field\":true}" + ], + [ + "{\"data\":{}}", + "{\"has_field\":false}" + ] + ], + "skip_testing": false, + "summary": "Also returns true for null values if the field exists" + } + ] + } + ], + "description": "Checks whether a field exists at the specified dot path within an object. Returns true if the field is present (even if null), false otherwise.", "examples": [ { "mapping": "root.result = this.foo.exists(\"bar.baz\")", @@ -2429,6 +3428,21 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.has_field = this.data.exists(\"optional_field\")", + "results": [ + [ + "{\"data\":{\"optional_field\":null}}", + "{\"has_field\":true}" + ], + [ + "{\"data\":{}}", + "{\"has_field\":false}" + ] + ], + "skip_testing": false, + "summary": "Also returns true for null values if the field exists" } ], "impure": false, @@ -2450,7 +3464,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Explodes an array or object at a xref:configuration:field_paths.adoc[field path].", + "Description": "Expands a nested array or object field into multiple documents, distributing elements while preserving the surrounding structure. Useful for denormalizing data.", "Examples": [ { "mapping": "root = this.explode(\"value\")", @@ -2461,7 +3475,7 @@ ] ], "skip_testing": false, - "summary": "##### On arrays\n\nExploding arrays results in an array containing elements matching the original document, where the target field of each element is an element of the exploded array:" + "summary": "##### On arrays\n\nWhen exploding an array, each element becomes a separate document with the array element replacing the original field:" }, { "mapping": "root = this.explode(\"value\")", @@ -2472,11 +3486,36 @@ ] ], "skip_testing": false, - "summary": "##### On objects\n\nExploding objects results in an object where the keys match the target object, and the values match the original document but with the target field replaced by the exploded value:" + "summary": "##### On objects\n\nWhen exploding an object, the output keys match the nested object's keys, with values being the full document where the target field is replaced by each nested value:" } ] } ], + "description": "Expands a nested field into multiple documents", + "examples": [ + { + "mapping": "root = this.explode(\"value\")", + "results": [ + [ + "{\"id\":1,\"value\":[\"foo\",\"bar\",\"baz\"]}", + "[{\"id\":1,\"value\":\"foo\"},{\"id\":1,\"value\":\"bar\"},{\"id\":1,\"value\":\"baz\"}]" + ] + ], + "skip_testing": false, + "summary": "##### On arrays\n\nWhen exploding an array, each element becomes a separate document with the array element replacing the original field:" + }, + { + "mapping": "root = this.explode(\"value\")", + "results": [ + [ + "{\"id\":1,\"value\":{\"foo\":2,\"bar\":[3,4],\"baz\":{\"bev\":5}}}", + "{\"bar\":{\"id\":1,\"value\":[3,4]},\"baz\":{\"id\":1,\"value\":{\"bev\":5}},\"foo\":{\"id\":1,\"value\":2}}" + ] + ], + "skip_testing": false, + "summary": "##### On objects\n\nWhen exploding an object, the output keys match the nested object's keys, with values being the full document where the target field is replaced by each nested value:" + } + ], "impure": false, "name": "explode", "params": { @@ -2496,7 +3535,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Joins an array of path elements into a single file path. The separator depends on the operating system of the machine.", + "Description": "Combines an array of path components into a single OS-specific file path using the correct separator (`/` on Unix, `\\` on Windows). Use for constructing file paths from components.", "Examples": [ { "mapping": "root.path = this.path_elements.filepath_join()", @@ -2512,6 +3551,20 @@ ] } ], + "description": "Joins filepath components into a single path", + "examples": [ + { + "mapping": "root.path = this.path_elements.filepath_join()", + "results": [ + [ + "{\"path_elements\":[\"/foo/\",\"bar.txt\"]}", + "{\"path\":\"/foo/bar.txt\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "filepath_join", "params": {}, @@ -2521,7 +3574,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Splits a file path immediately following the final Separator, separating it into a directory and file name component returned as a two element array of strings. If there is no Separator in the path, the first element will be empty and the second will contain the path. The separator depends on the operating system of the machine.", + "Description": "Separates a file path into directory and filename components, returning a two-element array `[directory, filename]`. Use for extracting the filename or directory from a full path.", "Examples": [ { "mapping": "root.path_sep = this.path.filepath_split()", @@ -2541,6 +3594,24 @@ ] } ], + "description": "Splits a filepath into directory and filename components", + "examples": [ + { + "mapping": "root.path_sep = this.path.filepath_split()", + "results": [ + [ + "{\"path\":\"/foo/bar.txt\"}", + "{\"path_sep\":[\"/foo/\",\"bar.txt\"]}" + ], + [ + "{\"path\":\"baz.txt\"}", + "{\"path_sep\":[\"\",\"baz.txt\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "filepath_split", "params": {}, @@ -2550,7 +3621,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Executes a mapping query argument for each element of an array or key/value pair of an object. If the query returns `false` the item is removed from the resulting array or object. The item will also be removed if the query returns any non-boolean value.", + "Description": "Returns a new array or object containing only elements that satisfy the provided condition. Elements for which the query returns true are kept, all others are removed.", "Examples": [ { "mapping": "root.new_nums = this.nums.filter(num -> num > 10)", @@ -2572,11 +3643,36 @@ ] ], "skip_testing": false, - "summary": "##### On objects\n\nWhen filtering objects the mapping query argument is provided a context with a field `key` containing the value key, and a field `value` containing the value." + "summary": "##### On objects\n\nWhen filtering objects, the query receives a context with `key` and `value` fields for each entry:" } ] } ], + "description": "Filters array or object elements based on a condition", + "examples": [ + { + "mapping": "root.new_nums = this.nums.filter(num -> num > 10)", + "results": [ + [ + "{\"nums\":[3,11,4,17]}", + "{\"new_nums\":[11,17]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.new_dict = this.dict.filter(item -> item.value.contains(\"foo\"))", + "results": [ + [ + "{\"dict\":{\"first\":\"hello foo\",\"second\":\"world\",\"third\":\"this foo is great\"}}", + "{\"new_dict\":{\"first\":\"hello foo\",\"third\":\"this foo is great\"}}" + ] + ], + "skip_testing": false, + "summary": "##### On objects\n\nWhen filtering objects, the query receives a context with `key` and `value` fields for each entry:" + } + ], "impure": false, "name": "filter", "params": { @@ -2623,7 +3719,31 @@ ] } ], - "description": "Returns the index of the first occurrence of a value in an array. `-1` is returned if there are no matches. Numerical comparisons are made irrespective of the representation type (float versus integer).", + "description": "Searches an array for a matching value and returns the index of the first occurrence. Returns -1 if no match is found. Numeric types are compared by value regardless of representation.", + "examples": [ + { + "mapping": "root.index = this.find(\"bar\")", + "results": [ + [ + "[\"foo\", \"bar\", \"baz\"]", + "{\"index\":1}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.index = this.things.find(this.goal)", + "results": [ + [ + "{\"goal\":\"bar\",\"things\":[\"foo\", \"bar\", \"baz\"]}", + "{\"index\":1}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "find", "params": { @@ -2670,7 +3790,31 @@ ] } ], - "description": "Returns an array containing the indexes of all occurrences of a value in an array. An empty array is returned if there are no matches. Numerical comparisons are made irrespective of the representation type (float versus integer).", + "description": "Searches an array for all occurrences of a value and returns an array of matching indexes. Returns an empty array if no matches are found. Numeric types are compared by value regardless of representation.", + "examples": [ + { + "mapping": "root.index = this.find_all(\"bar\")", + "results": [ + [ + "[\"foo\", \"bar\", \"baz\", \"bar\"]", + "{\"index\":[1,3]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.indexes = this.things.find_all(this.goal)", + "results": [ + [ + "{\"goal\":\"bar\",\"things\":[\"foo\", \"bar\", \"baz\", \"bar\", \"buz\"]}", + "{\"indexes\":[1,3]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "find_all", "params": { @@ -2702,11 +3846,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.error_indexes = this.logs.find_all_by(log -> log.level == \"error\")", + "results": [ + [ + "{\"logs\":[{\"level\":\"info\"},{\"level\":\"error\"},{\"level\":\"warn\"},{\"level\":\"error\"}]}", + "{\"error_indexes\":[1,3]}" + ] + ], + "skip_testing": false, + "summary": "Find all indexes matching criteria" } ] } ], - "description": "Returns an array containing the indexes of all occurrences of an array where the provided query resolves to a boolean `true`. An empty array is returned if there are no matches. Numerical comparisons are made irrespective of the representation type (float versus integer).", + "description": "Searches an array for all elements that satisfy a condition and returns an array of their indexes. Returns an empty array if no elements match.", + "examples": [ + { + "mapping": "root.index = this.find_all_by(v -> v != \"bar\")", + "results": [ + [ + "[\"foo\", \"bar\", \"baz\"]", + "{\"index\":[0,2]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.error_indexes = this.logs.find_all_by(log -> log.level == \"error\")", + "results": [ + [ + "{\"logs\":[{\"level\":\"info\"},{\"level\":\"error\"},{\"level\":\"warn\"},{\"level\":\"error\"}]}", + "{\"error_indexes\":[1,3]}" + ] + ], + "skip_testing": false, + "summary": "Find all indexes matching criteria" + } + ], "impure": false, "name": "find_all_by", "params": { @@ -2738,11 +3917,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.first_adult = this.users.find_by(u -> u.age >= 18)", + "results": [ + [ + "{\"users\":[{\"name\":\"Alice\",\"age\":15},{\"name\":\"Bob\",\"age\":22},{\"name\":\"Carol\",\"age\":19}]}", + "{\"first_adult\":1}" + ] + ], + "skip_testing": false, + "summary": "Find first object matching criteria" } ] } ], - "description": "Returns the index of the first occurrence of an array where the provided query resolves to a boolean `true`. `-1` is returned if there are no matches.", + "description": "Searches an array for the first element that satisfies a condition and returns its index. Returns -1 if no element matches the query.", + "examples": [ + { + "mapping": "root.index = this.find_by(v -> v != \"bar\")", + "results": [ + [ + "[\"foo\", \"bar\", \"baz\"]", + "{\"index\":0}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.first_adult = this.users.find_by(u -> u.age >= 18)", + "results": [ + [ + "{\"users\":[{\"name\":\"Alice\",\"age\":15},{\"name\":\"Bob\",\"age\":22},{\"name\":\"Carol\",\"age\":19}]}", + "{\"first_adult\":1}" + ] + ], + "skip_testing": false, + "summary": "Find first object matching criteria" + } + ], "impure": false, "name": "find_by", "params": { @@ -2774,11 +3988,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.result = this.data.flatten()", + "results": [ + [ + "{\"data\":[\"a\",[\"b\",[\"c\",\"d\"]],\"e\"]}", + "{\"result\":[\"a\",\"b\",[\"c\",\"d\"],\"e\"]}" + ] + ], + "skip_testing": false, + "summary": "Deeper nesting requires multiple flatten calls" } ] } ], - "description": "Iterates an array and any element that is itself an array is removed and has its elements inserted directly in the resulting array.", + "description": "Flattens an array by one level, expanding nested arrays into the parent array. Only the first level of nesting is removed.", + "examples": [ + { + "mapping": "root.result = this.flatten()", + "results": [ + [ + "[\"foo\",[\"bar\",\"baz\"],\"buz\"]", + "{\"result\":[\"foo\",\"bar\",\"baz\",\"buz\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.result = this.data.flatten()", + "results": [ + [ + "{\"data\":[\"a\",[\"b\",[\"c\",\"d\"]],\"e\"]}", + "{\"result\":[\"a\",\"b\",[\"c\",\"d\"],\"e\"]}" + ] + ], + "skip_testing": false, + "summary": "Deeper nesting requires multiple flatten calls" + } + ], "impure": false, "name": "flatten", "params": {}, @@ -2805,6 +4054,19 @@ } ], "description": "\nConverts a numerical type into a 32-bit floating point number, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 32-bit floating point number. Please refer to the https://pkg.go.dev/strconv#ParseFloat[`strconv.ParseFloat` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.out = this.in.float32()\n", + "results": [ + [ + "{\"in\":\"6.674282313423543523453425345e-11\"}", + "{\"out\":6.674283e-11}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "float32", "params": {}, @@ -2831,6 +4093,19 @@ } ], "description": "\nConverts a numerical type into a 64-bit floating point number, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 64-bit floating point number. Please refer to the https://pkg.go.dev/strconv#ParseFloat[`strconv.ParseFloat` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.out = this.in.float64()\n", + "results": [ + [ + "{\"in\":\"6.674282313423543523453425345e-11\"}", + "{\"out\":6.674282313423544e-11}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "float64", "params": {}, @@ -2848,6 +4123,21 @@ [ "{\"value\":5.7}", "{\"new_value\":5}" + ], + [ + "{\"value\":-3.2}", + "{\"new_value\":-4}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.whole_seconds = this.duration_seconds.floor()", + "results": [ + [ + "{\"duration_seconds\":12.345}", + "{\"whole_seconds\":12}" ] ], "skip_testing": false, @@ -2856,7 +4146,35 @@ ] } ], - "description": "Returns the greatest integer value less than or equal to the target number. If the resulting value fits within a 64-bit integer then that is returned, otherwise a new floating point number is returned.", + "description": "Rounds a number down to the nearest integer. Returns an integer if the result fits in 64-bit, otherwise returns a float.", + "examples": [ + { + "mapping": "root.new_value = this.value.floor()", + "results": [ + [ + "{\"value\":5.7}", + "{\"new_value\":5}" + ], + [ + "{\"value\":-3.2}", + "{\"new_value\":-4}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.whole_seconds = this.duration_seconds.floor()", + "results": [ + [ + "{\"duration_seconds\":12.345}", + "{\"whole_seconds\":12}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "floor", "params": {}, @@ -2877,7 +4195,7 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Sum numbers in an array" }, { "mapping": "root.result = this.foo.fold(\"\", item -> \"%v%v\".format(item.tally, item.value))", @@ -2888,7 +4206,7 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Concatenate strings" }, { "mapping": "root.smoothie = this.fruits.fold({}, item -> item.tally.merge(item.value))", @@ -2899,22 +4217,57 @@ ] ], "skip_testing": false, - "summary": "You can use fold to merge an array of objects together:" + "summary": "Merge an array of objects into a single object" } ] } ], - "description": "Takes two arguments: an initial value, and a mapping query. For each element of an array the mapping context is an object with two fields `tally` and `value`, where `tally` contains the current accumulated value and `value` is the value of the current element. The mapping must return the result of adding the value to the tally.\n\nThe first argument is the value that `tally` will have on the first call.", - "impure": false, - "name": "fold", - "params": { - "named": [ - { - "description": "The initial value to start the fold with. For example, an empty object `{}`, a zero count `0`, or an empty string `\"\"`.", - "name": "initial", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "unknown" + "description": "Reduces an array to a single value by iteratively applying a function. Also known as reduce or aggregate. The query receives an accumulator (tally) and current element (value) for each iteration.", + "examples": [ + { + "mapping": "root.sum = this.foo.fold(0, item -> item.tally + item.value)", + "results": [ + [ + "{\"foo\":[3,8,11]}", + "{\"sum\":22}" + ] + ], + "skip_testing": false, + "summary": "Sum numbers in an array" + }, + { + "mapping": "root.result = this.foo.fold(\"\", item -> \"%v%v\".format(item.tally, item.value))", + "results": [ + [ + "{\"foo\":[\"hello \", \"world\"]}", + "{\"result\":\"hello world\"}" + ] + ], + "skip_testing": false, + "summary": "Concatenate strings" + }, + { + "mapping": "root.smoothie = this.fruits.fold({}, item -> item.tally.merge(item.value))", + "results": [ + [ + "{\"fruits\":[{\"apple\":5},{\"banana\":3},{\"orange\":8}]}", + "{\"smoothie\":{\"apple\":5,\"banana\":3,\"orange\":8}}" + ] + ], + "skip_testing": false, + "summary": "Merge an array of objects into a single object" + } + ], + "impure": false, + "name": "fold", + "params": { + "named": [ + { + "description": "The initial value to start the fold with. For example, an empty object `{}`, a zero count `0`, or an empty string `\"\"`.", + "name": "initial", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" }, { "description": "A query to apply for each element. The query is provided an object with two fields; `tally` containing the current tally, and `value` containing the value of the current element. The query should result in a new tally to be passed to the next element query.", @@ -2931,7 +4284,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Use a value string as a format specifier in order to produce a new string, using any number of provided arguments. Please refer to the Go https://pkg.go.dev/fmt[`fmt` package documentation^] for the list of valid format verbs.", + "Description": "Formats a string using Go's printf-style formatting with the string as the format template. Supports all Go format verbs (`%s`, `%d`, `%v`, etc.). Use for building formatted strings from dynamic values.", "Examples": [ { "mapping": "root.foo = \"%s(%v): %v\".format(this.name, this.age, this.fingers)", @@ -2943,10 +4296,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.message = \"User %s has %v points\".format(this.username, this.score)", + "results": [ + [ + "{\"username\":\"alice\",\"score\":100}", + "{\"message\":\"User alice has 100 points\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Formats a value using a specified format string", + "examples": [ + { + "mapping": "root.foo = \"%s(%v): %v\".format(this.name, this.age, this.fingers)", + "results": [ + [ + "{\"name\":\"lance\",\"age\":37,\"fingers\":13}", + "{\"foo\":\"lance(37): 13\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.message = \"User %s has %v points\".format(this.username, this.score)", + "results": [ + [ + "{\"username\":\"alice\",\"score\":100}", + "{\"message\":\"User alice has 100 points\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "format", "params": { @@ -3029,6 +4418,75 @@ ] } ], + "description": "Formats a value as a JSON string", + "examples": [ + { + "mapping": "root = this.doc.format_json()", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\n \"foo\": \"bar\"\n}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = this.format_json(\" \")", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\n \"doc\": {\n \"foo\": \"bar\"\n }\n}" + ] + ], + "skip_testing": false, + "summary": "Pass a string to the `indent` parameter in order to customise the indentation." + }, + { + "mapping": "root.doc = this.doc.format_json().string()", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\"doc\":\"{\\n \\\"foo\\\": \\\"bar\\\"\\n}\"}" + ] + ], + "skip_testing": false, + "summary": "Use the `.string()` method in order to coerce the result into a string." + }, + { + "mapping": "root = this.doc.format_json(no_indent: true)", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\"foo\":\"bar\"}" + ] + ], + "skip_testing": false, + "summary": "Set the `no_indent` parameter to true to disable indentation. The result is equivalent to calling `bytes()`." + }, + { + "mapping": "root = this.doc.format_json()", + "results": [ + [ + "{\"doc\":{\"email\":\"foo&bar@benthos.dev\",\"name\":\"foo>bar\"}}", + "{\n \"email\": \"foo\\u0026bar@benthos.dev\",\n \"name\": \"foo\\u003ebar\"\n}" + ] + ], + "skip_testing": false, + "summary": "Escapes problematic HTML characters." + }, + { + "mapping": "root = this.doc.format_json(escape_html: false)", + "results": [ + [ + "{\"doc\":{\"email\":\"foo&bar@benthos.dev\",\"name\":\"foo>bar\"}}", + "{\n \"email\": \"foo&bar@benthos.dev\",\n \"name\": \"foo>bar\"\n}" + ] + ], + "skip_testing": false, + "summary": "Set the `escape_html` parameter to false to disable escaping of problematic HTML characters." + } + ], "impure": false, "name": "format_json", "params": { @@ -3076,23 +4534,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Serialize object to MessagePack and encode as hex for transmission" }, { - "mapping": "root.encoded = this.format_msgpack().encode(\"base64\")", + "mapping": "root.msgpack_payload = this.data.format_msgpack().encode(\"base64\")", "results": [ [ - "{\"foo\":\"bar\"}", - "{\"encoded\":\"gaNmb2+jYmFy\"}" + "{\"data\":{\"foo\":\"bar\"}}", + "{\"msgpack_payload\":\"gaNmb2+jYmFy\"}" ] ], "skip_testing": false, - "summary": "" + "summary": "Serialize data to MessagePack and base64 encode for embedding in JSON" } ] } ], - "description": "Formats data as a https://msgpack.org/[MessagePack^] message in bytes format.", + "description": "Serializes structured data into MessagePack binary format. MessagePack is a compact binary serialization that is faster and more space-efficient than JSON, making it ideal for network transmission and storage of structured data. Returns a byte array that can be further encoded as needed.", + "examples": [ + { + "mapping": "root = this.format_msgpack().encode(\"hex\")", + "results": [ + [ + "{\"foo\":\"bar\"}", + "81a3666f6fa3626172" + ] + ], + "skip_testing": false, + "summary": "Serialize object to MessagePack and encode as hex for transmission" + }, + { + "mapping": "root.msgpack_payload = this.data.format_msgpack().encode(\"base64\")", + "results": [ + [ + "{\"data\":{\"foo\":\"bar\"}}", + "{\"msgpack_payload\":\"gaNmb2+jYmFy\"}" + ] + ], + "skip_testing": false, + "summary": "Serialize data to MessagePack and base64 encode for embedding in JSON" + } + ], "impure": false, "name": "format_msgpack", "params": {}, @@ -3106,21 +4588,21 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a string according to a specified format, or RFC 3339 by default. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format.\n\nThe output format is defined by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would be displayed if it were the value. For an alternative way to specify formats check out the <> method.", + "description": "Formats a timestamp as a string using Go's reference time format. Defaults to RFC 3339 if no format specified. The format uses \"Mon Jan 2 15:04:05 -0700 MST 2006\" as a reference. Accepts unix timestamps (with decimal precision) or RFC 3339 strings. Use ts_strftime for strftime-style formats.", "impure": false, "name": "format_timestamp", "params": { "named": [ { "default": "2006-01-02T15:04:05.999999999Z07:00", - "description": "The output format to use.", + "description": "The output format using Go's reference time.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, "type": "string" }, { - "description": "An optional timezone to use, otherwise the timezone of the input string is used, or in the case of unix timestamps the local timezone is used.", + "description": "Optional timezone (e.g., 'UTC', 'America/New_York'). Defaults to input timezone or local time for unix timestamps.", "is_optional": true, "name": "tz", "no_dynamic": false, @@ -3139,20 +4621,20 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a string according to a specified strftime-compatible format. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format.", + "description": "Formats a timestamp as a string using strptime format specifiers (like %Y, %m, %d). Accepts unix timestamps (with decimal precision) or RFC 3339 strings. Supports %f for microseconds. Use ts_format for Go-style reference time formats.", "impure": false, "name": "format_timestamp_strftime", "params": { "named": [ { - "description": "The output format to use.", + "description": "The output format using strptime specifiers.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, "type": "string" }, { - "description": "An optional timezone to use, otherwise the timezone of the input string is used.", + "description": "Optional timezone. Defaults to input timezone or local time for unix timestamps.", "is_optional": true, "name": "tz", "no_dynamic": false, @@ -3171,7 +4653,7 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a unix timestamp. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp (seconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing seconds.", "impure": false, "name": "format_timestamp_unix", "params": {}, @@ -3185,7 +4667,7 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a unix timestamp with microsecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with microsecond precision (microseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing microseconds.", "impure": false, "name": "format_timestamp_unix_micro", "params": {}, @@ -3199,7 +4681,7 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a unix timestamp with millisecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with millisecond precision (milliseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing milliseconds.", "impure": false, "name": "format_timestamp_unix_milli", "params": {}, @@ -3213,7 +4695,7 @@ "Examples": null } ], - "description": "Attempts to format a timestamp value as a unix timestamp with nanosecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with nanosecond precision (nanoseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing nanoseconds.", "impure": false, "name": "format_timestamp_unix_nano", "params": {}, @@ -3231,40 +4713,10 @@ [ "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", "\n \n foo bar baz\n \n" - ], - [ - "{\"-foo\":\"bar\",\"fizz\":\"buzz\"}", - "\n buzz\n" - ], - [ - "{\"foo\":[{\"bar\":\"baz\"},{\"fizz\":\"buzz\"}]}", - "\n \n baz\n \n \n buzz\n \n" ] ], "skip_testing": false, - "summary": "Serializes a target value into a pretty-printed XML byte array (with 4 space indentation by default)." - }, - { - "mapping": "root = this.format_xml(\" \")", - "results": [ - [ - "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", - "\n \n foo bar baz\n \n" - ] - ], - "skip_testing": false, - "summary": "Pass a string to the `indent` parameter in order to customise the indentation." - }, - { - "mapping": "root.doc = this.format_xml(\"\").string()", - "results": [ - [ - "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", - "{\"doc\":\"\\n\\nfoo bar baz\\n\\n\"}" - ] - ], - "skip_testing": false, - "summary": "Use the `.string()` method in order to coerce the result into a string." + "summary": "Serialize object to pretty-printed XML with default indentation" }, { "mapping": "root = this.format_xml(no_indent: true)", @@ -3275,38 +4727,43 @@ ] ], "skip_testing": false, - "summary": "Set the `no_indent` parameter to true to disable indentation." - }, - { - "mapping": "root = this.format_xml(root_tag: \"blobfish\")", - "results": [ - [ - "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", - "\n \n \n foo bar baz\n \n \n" - ], - [ - "{\"-foo\":\"bar\",\"fizz\":\"buzz\"}", - "\n buzz\n" - ], - [ - "{\"foo\":[{\"bar\":\"baz\"},{\"fizz\":\"buzz\"}]}", - "\n \n baz\n \n \n buzz\n \n" - ] - ], - "skip_testing": false, - "summary": "Set a custom root tag." + "summary": "Create compact XML without indentation for smaller message size" } ] } ], - "description": "\nSerializes a target value into an XML byte array.\n", + "description": "Serializes an object into an XML document. Converts structured data to XML format with support for attributes (prefixed with hyphen), custom indentation, and configurable root element. Returns XML as a byte array.", + "examples": [ + { + "mapping": "root = this.format_xml()", + "results": [ + [ + "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", + "\n \n foo bar baz\n \n" + ] + ], + "skip_testing": false, + "summary": "Serialize object to pretty-printed XML with default indentation" + }, + { + "mapping": "root = this.format_xml(no_indent: true)", + "results": [ + [ + "{\"foo\":{\"bar\":{\"baz\":\"foo bar baz\"}}}", + "foo bar baz" + ] + ], + "skip_testing": false, + "summary": "Create compact XML without indentation for smaller message size" + } + ], "impure": false, "name": "format_xml", "params": { "named": [ { "default": " ", - "description": "Indentation string. Each element in an XML object or array will begin on a new, indented line followed by one or more copies of indent according to the indentation nesting.", + "description": "String to use for each level of indentation (default is 4 spaces). Each nested XML element will be indented by this string.", "name": "indent", "no_dynamic": false, "scalars_to_literal": false, @@ -3314,14 +4771,14 @@ }, { "default": false, - "description": "Disable indentation.", + "description": "Disable indentation and newlines to produce compact XML on a single line.", "name": "no_indent", "no_dynamic": false, "scalars_to_literal": false, "type": "bool" }, { - "description": "Root tag. Set this if you wish to override the root tag of the document.", + "description": "Custom name for the root XML element. By default, the root element name is derived from the first key in the object.", "is_optional": true, "name": "root_tag", "no_dynamic": false, @@ -3363,12 +4820,51 @@ ] } ], + "description": "Formats a value as a YAML string", + "examples": [ + { + "mapping": "root = this.doc.format_yaml()", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "foo: bar\n" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.doc = this.doc.format_yaml().string()", + "results": [ + [ + "{\"doc\":{\"foo\":\"bar\"}}", + "{\"doc\":\"foo: bar\\n\"}" + ] + ], + "skip_testing": false, + "summary": "Use the `.string()` method in order to coerce the result into a string." + } + ], "impure": false, "name": "format_yaml", "params": {}, "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": [ + { + "mapping": "root = this\nroot.foo = json(\"foo\").from(1)", + "results": [], + "skip_testing": false, + "summary": "For example, the following map extracts the contents of the JSON field `foo` specifically from message index `1` of a batch, effectively overriding the field `foo` for all messages of a batch to that of message 1:" + } + ] + } + ], "description": "Modifies a target query such that certain functions are executed from the perspective of another message in the batch. This allows you to mutate events based on the contents of other messages. Functions that support this behavior are `content`, `json` and `meta`.", "examples": [ { @@ -3394,6 +4890,20 @@ "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": [ + { + "mapping": "root = this\nroot.foo_summed = json(\"foo\").from_all().sum()", + "results": [], + "skip_testing": false, + "summary": "" + } + ] + } + ], "description": "Modifies a target query such that certain functions are executed from the perspective of each message in the batch, and returns the set of results as an array. Functions that support this behavior are `content`, `json` and `meta`.", "examples": [ { @@ -3430,7 +4940,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3454,7 +4964,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3478,7 +4988,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3502,7 +5012,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3526,7 +5036,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3550,7 +5060,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3574,7 +5084,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3598,7 +5108,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -3625,6 +5135,23 @@ } ], "description": "Extract a field value, identified via a xref:configuration:field_paths.adoc[dot path], from an object.", + "examples": [ + { + "mapping": "root.result = this.foo.get(this.target)", + "results": [ + [ + "{\"foo\":{\"bar\":\"from bar\",\"baz\":\"from baz\"},\"target\":\"bar\"}", + "{\"result\":\"from bar\"}" + ], + [ + "{\"foo\":{\"bar\":\"from bar\",\"baz\":\"from baz\"},\"target\":\"baz\"}", + "{\"result\":\"from baz\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "get", "params": { @@ -3644,7 +5171,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Checks whether a string has a prefix argument and returns a bool.", + "Description": "Tests if a string starts with a specified prefix. Returns `true` if the string begins with the prefix, `false` otherwise. Use for conditional logic based on string patterns.", "Examples": [ { "mapping": "root.t1 = this.v1.has_prefix(\"foo\")\nroot.t2 = this.v2.has_prefix(\"foo\")", @@ -3660,6 +5187,20 @@ ] } ], + "description": "Tests if a string starts with a specified prefix", + "examples": [ + { + "mapping": "root.t1 = this.v1.has_prefix(\"foo\")\nroot.t2 = this.v2.has_prefix(\"foo\")", + "results": [ + [ + "{\"v1\":\"foobar\",\"v2\":\"barfoo\"}", + "{\"t1\":true,\"t2\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "has_prefix", "params": { @@ -3679,7 +5220,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Checks whether a string has a suffix argument and returns a bool.", + "Description": "Tests if a string ends with a specified suffix. Returns `true` if the string ends with the suffix, `false` otherwise. Use for filtering or routing based on file extensions or string patterns.", "Examples": [ { "mapping": "root.t1 = this.v1.has_suffix(\"foo\")\nroot.t2 = this.v2.has_suffix(\"foo\")", @@ -3695,6 +5236,20 @@ ] } ], + "description": "Tests if a string ends with a specified suffix", + "examples": [ + { + "mapping": "root.t1 = this.v1.has_suffix(\"foo\")\nroot.t2 = this.v2.has_suffix(\"foo\")", + "results": [ + [ + "{\"v1\":\"foobar\",\"v2\":\"barfoo\"}", + "{\"t1\":false,\"t2\":true}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "has_suffix", "params": { @@ -3714,7 +5269,7 @@ "categories": [ { "Category": "Encoding and Encryption", - "Description": "\nHashes a string or byte array according to a chosen algorithm and returns the result as a byte array. When mapping the result to a JSON field the value should be cast to a string using the method xref:guides:bloblang/methods.adoc#string[`string`], or encoded using the method xref:guides:bloblang/methods.adoc#encode[`encode`], otherwise it will be base64 encoded by default.\n\nAvailable algorithms are: `hmac_sha1`, `hmac_sha256`, `hmac_sha512`, `md5`, `sha1`, `sha256`, `sha512`, `xxhash64`, `crc32`, `fnv32`.\n\nThe following algorithms require a key, which is specified as a second argument: `hmac_sha1`, `hmac_sha256`, `hmac_sha512`.", + "Description": "\nHashes a string or byte array according to a chosen algorithm and returns the result as a byte array. When mapping the result to a JSON field the value should be cast to a string using the method xref:guides:bloblang/methods.adoc#string[`string`], or encoded using the method xref:guides:bloblang/methods.adoc#encode[`encode`], otherwise it will be base64 encoded by default.\n\nAvailable algorithms are: `hmac_sha1`, `hmac_sha256`, `hmac_sha512`, `md5`, `sha1`, `sha256`, `sha512`, `sha3_256`, `sha3_512`, `xxhash64`, `crc32`, `fnv32`.\n\nThe following algorithms require a key, which is specified as a second argument: `hmac_sha1`, `hmac_sha256`, `hmac_sha512`.", "Examples": [ { "mapping": "root.h1 = this.value.hash(\"sha1\").encode(\"hex\")\nroot.h2 = this.value.hash(\"hmac_sha1\",\"static-key\").encode(\"hex\")", @@ -3741,12 +5296,37 @@ ] } ], + "description": "Hashes a string or byte array using a specified algorithm", + "examples": [ + { + "mapping": "root.h1 = this.value.hash(\"sha1\").encode(\"hex\")\nroot.h2 = this.value.hash(\"hmac_sha1\",\"static-key\").encode(\"hex\")", + "results": [ + [ + "{\"value\":\"hello world\"}", + "{\"h1\":\"2aae6c35c94fcfb415dbe95f408b9ce91ee846ed\",\"h2\":\"d87e5f068fa08fe90bb95bc7c8344cb809179d76\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.h1 = this.value.hash(algorithm: \"crc32\", polynomial: \"Castagnoli\").encode(\"hex\")\nroot.h2 = this.value.hash(algorithm: \"crc32\", polynomial: \"Koopman\").encode(\"hex\")", + "results": [ + [ + "{\"value\":\"hello world\"}", + "{\"h1\":\"c99465aa\",\"h2\":\"df373d3c\"}" + ] + ], + "skip_testing": false, + "summary": "The `crc32` algorithm supports options for the polynomial." + } + ], "impure": false, "name": "hash", "params": { "named": [ { - "description": "The hasing algorithm to use.", + "description": "The hashing algorithm to use.", "name": "algorithm", "no_dynamic": false, "scalars_to_literal": false, @@ -3804,6 +5384,30 @@ } ], "description": "Extract an element from an array by an index. The index can be negative, and if so the element will be selected from the end counting backwards starting from -1. E.g. an index of -1 returns the last element, an index of -2 returns the element before the last, and so on.", + "examples": [ + { + "mapping": "root.last_name = this.names.index(-1)", + "results": [ + [ + "{\"names\":[\"rachel\",\"stevens\"]}", + "{\"last_name\":\"stevens\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.last_byte = this.name.bytes().index(-1)", + "results": [ + [ + "{\"name\":\"foobar bazson\"}", + "{\"last_byte\":110}" + ] + ], + "skip_testing": false, + "summary": "It is also possible to use this method on byte arrays, in which case the selected element will be returned as an integer." + } + ], "impure": false, "name": "index", "params": { @@ -3823,7 +5427,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Returns the starting index of the argument substring in a string target, or `-1` if the target doesn't contain the argument.", + "Description": "Finds the position of a substring within a string. Returns the zero-based index of the first occurrence, or -1 if not found. Useful for searching and string manipulation.", "Examples": [ { "mapping": "root.index = this.thing.index_of(\"bar\")", @@ -3850,6 +5454,31 @@ ] } ], + "description": "Returns the index of the first occurrence of a substring", + "examples": [ + { + "mapping": "root.index = this.thing.index_of(\"bar\")", + "results": [ + [ + "{\"thing\":\"foobar\"}", + "{\"index\":3}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.index = content().index_of(\"meow\")", + "results": [ + [ + "the cat meowed, the dog woofed", + "{\"index\":8}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "index_of", "params": { @@ -3911,6 +5540,30 @@ } ], "description": "\nConverts a numerical type into a 16-bit signed integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 16-bit signed integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.int16()\nroot.b = this.b.round().int16()\nroot.c = this.c.int16()\nroot.d = this.d.int16().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":-12}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.int16()\n", + "results": [ + [ + "\"0xDE\"", + "222" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "int16", "params": {}, @@ -3948,6 +5601,30 @@ } ], "description": "\nConverts a numerical type into a 32-bit signed integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 32-bit signed integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.int32()\nroot.b = this.b.round().int32()\nroot.c = this.c.int32()\nroot.d = this.d.int32().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":-12}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.int32()\n", + "results": [ + [ + "\"0xDEAD\"", + "57005" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "int32", "params": {}, @@ -3985,6 +5662,30 @@ } ], "description": "\nConverts a numerical type into a 64-bit signed integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 64-bit signed integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.int64()\nroot.b = this.b.round().int64()\nroot.c = this.c.int64()\nroot.d = this.d.int64().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":-12}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.int64()\n", + "results": [ + [ + "\"0xDEADBEEF\"", + "3735928559" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "int64", "params": {}, @@ -4022,6 +5723,30 @@ } ], "description": "\nConverts a numerical type into a 8-bit signed integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 8-bit signed integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.int8()\nroot.b = this.b.round().int8()\nroot.c = this.c.int8()\nroot.d = this.d.int8().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":-12}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.int8()\n", + "results": [ + [ + "\"0xD\"", + "13" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "int8", "params": {}, @@ -4031,7 +5756,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Join an array of strings with an optional delimiter into a single string.", + "Description": "Concatenates an array of strings into a single string with an optional delimiter between elements. Use for building CSV strings, URLs, or combining text fragments.", "Examples": [ { "mapping": "root.joined_words = this.words.join()\nroot.joined_numbers = this.numbers.map_each(this.string()).join(\",\")", @@ -4047,6 +5772,20 @@ ] } ], + "description": "Joins an array of strings with an optional delimiter", + "examples": [ + { + "mapping": "root.joined_words = this.words.join()\nroot.joined_numbers = this.numbers.map_each(this.string()).join(\",\")", + "results": [ + [ + "{\"words\":[\"hello\",\"world\"],\"numbers\":[3,8,11]}", + "{\"joined_numbers\":\"3,8,11\",\"joined_words\":\"helloworld\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "join", "params": { @@ -4099,6 +5838,34 @@ } ], "description": "Executes the given JSONPath expression on an object or array and returns the result. The JSONPath expression syntax can be found at https://goessner.net/articles/JsonPath/. For more complex logic, you can use Gval expressions (https://github.com/PaesslerAG/gval).", + "examples": [ + { + "mapping": "root.all_names = this.json_path(\"$..name\")", + "results": [ + [ + "{\"name\":\"alice\",\"foo\":{\"name\":\"bob\"}}", + "{\"all_names\":[\"alice\",\"bob\"]}" + ], + [ + "{\"thing\":[\"this\",\"bar\",{\"name\":\"alice\"}]}", + "{\"all_names\":[\"alice\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.text_objects = this.json_path(\"$.body[?(@.type=='text')]\")", + "results": [ + [ + "{\"body\":[{\"type\":\"image\",\"id\":\"foo\"},{\"type\":\"text\",\"id\":\"bar\"}]}", + "{\"text_objects\":[{\"id\":\"bar\",\"type\":\"text\"}]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "json_path", "params": { @@ -4112,7 +5879,7 @@ } ] }, - "status": "experimental" + "status": "stable" }, { "categories": [ @@ -4145,6 +5912,29 @@ } ], "description": "Checks a https://json-schema.org/[JSON schema^] against a value and returns the value if it matches or throws and error if it does not.", + "examples": [ + { + "mapping": "root = this.json_schema(\"\"\"{\n \"type\":\"object\",\n \"properties\":{\n \"foo\":{\n \"type\":\"string\"\n }\n }\n}\"\"\")", + "results": [ + [ + "{\"foo\":\"bar\"}", + "{\"foo\":\"bar\"}" + ], + [ + "{\"foo\":5}", + "Error(\"failed assignment (line 1): field `this`: foo invalid type. expected: string, given: integer\")" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = this.json_schema(file(env(\"BENTHOS_TEST_BLOBLANG_SCHEMA_FILE\")))", + "results": [], + "skip_testing": false, + "summary": "In order to load a schema from a file use the `file` function." + } + ], "impure": false, "name": "json_schema", "params": { @@ -4176,11 +5966,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.large_items = this.items.key_values().filter(pair -> pair.value > 15).map_each(pair -> pair.key)", + "results": [ + [ + "{\"items\":{\"a\":5,\"b\":15,\"c\":20,\"d\":3}}", + "{\"large_items\":[\"c\"]}" + ] + ], + "skip_testing": false, + "summary": "Filter object entries by value" } ] } ], - "description": "Returns the key/value pairs of an object as an array, where each element is an object with a `key` field and a `value` field. The order of the resulting array will be random.", + "description": "Converts an object into an array of key-value pair objects. Each element has a 'key' field and a 'value' field. Order is not guaranteed unless sorted.", + "examples": [ + { + "mapping": "root.foo_key_values = this.foo.key_values().sort_by(pair -> pair.key)", + "results": [ + [ + "{\"foo\":{\"bar\":1,\"baz\":2}}", + "{\"foo_key_values\":[{\"key\":\"bar\",\"value\":1},{\"key\":\"baz\",\"value\":2}]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.large_items = this.items.key_values().filter(pair -> pair.value > 15).map_each(pair -> pair.key)", + "results": [ + [ + "{\"items\":{\"a\":5,\"b\":15,\"c\":20,\"d\":3}}", + "{\"large_items\":[\"c\"]}" + ] + ], + "skip_testing": false, + "summary": "Filter object entries by value" + } + ], "impure": false, "name": "key_values", "params": {}, @@ -4202,11 +6027,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.has_id = this.data.keys().contains(\"id\")", + "results": [ + [ + "{\"data\":{\"id\":123,\"name\":\"test\"}}", + "{\"has_id\":true}" + ] + ], + "skip_testing": false, + "summary": "Check if specific keys exist" } ] } ], - "description": "Returns the keys of an object as an array.", + "description": "Extracts all keys from an object and returns them as a sorted array.", + "examples": [ + { + "mapping": "root.foo_keys = this.foo.keys()", + "results": [ + [ + "{\"foo\":{\"bar\":1,\"baz\":2}}", + "{\"foo_keys\":[\"bar\",\"baz\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.has_id = this.data.keys().contains(\"id\")", + "results": [ + [ + "{\"data\":{\"id\":123,\"name\":\"test\"}}", + "{\"has_id\":true}" + ] + ], + "skip_testing": false, + "summary": "Check if specific keys exist" + } + ], "impure": false, "name": "keys", "params": {}, @@ -4216,7 +6076,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Returns the length of a string.", + "Description": "Returns the character count of a string.", "Examples": [ { "mapping": "root.foo_len = this.foo.length()", @@ -4233,7 +6093,7 @@ }, { "Category": "Object & Array Manipulation", - "Description": "Returns the length of an array or object (number of keys).", + "Description": "Returns the size of an array (element count) or object (key count).", "Examples": [ { "mapping": "root.foo_len = this.foo.length()", @@ -4253,6 +6113,35 @@ ] } ], + "description": "Returns the length of an array, object, or string", + "examples": [ + { + "mapping": "root.foo_len = this.foo.length()", + "results": [ + [ + "{\"foo\":\"hello world\"}", + "{\"foo_len\":11}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.foo_len = this.foo.length()", + "results": [ + [ + "{\"foo\":[\"first\",\"second\"]}", + "{\"foo_len\":2}" + ], + [ + "{\"foo\":{\"first\":\"bar\",\"second\":\"baz\"}}", + "{\"foo_len\":2}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "length", "params": {}, @@ -4278,11 +6167,50 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.ln_result = this.number.log()", + "results": [ + [ + "{\"number\":10}", + "{\"ln_result\":2.302585092994046}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Returns the natural logarithm of a number.", + "description": "Calculates the natural logarithm (base e) of a number.", + "examples": [ + { + "mapping": "root.new_value = this.value.log().round()", + "results": [ + [ + "{\"value\":1}", + "{\"new_value\":0}" + ], + [ + "{\"value\":2.7183}", + "{\"new_value\":1}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.ln_result = this.number.log()", + "results": [ + [ + "{\"number\":10}", + "{\"ln_result\":2.302585092994046}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "log", "params": {}, @@ -4308,11 +6236,50 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.log_value = this.magnitude.log10()", + "results": [ + [ + "{\"magnitude\":10000}", + "{\"log_value\":4}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Returns the decimal logarithm of a number.", + "description": "Calculates the base-10 logarithm of a number.", + "examples": [ + { + "mapping": "root.new_value = this.value.log10()", + "results": [ + [ + "{\"value\":100}", + "{\"new_value\":2}" + ], + [ + "{\"value\":1000}", + "{\"new_value\":3}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.log_value = this.magnitude.log10()", + "results": [ + [ + "{\"magnitude\":10000}", + "{\"log_value\":4}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "log10", "params": {}, @@ -4322,7 +6289,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Convert a string value into lowercase.", + "Description": "Converts all letters in a string to lowercase. Use for case-insensitive comparisons, normalization, or formatting output.", "Examples": [ { "mapping": "root.foo = this.foo.lowercase()", @@ -4334,16 +6301,60 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.email = this.user_email.lowercase()", + "results": [ + [ + "{\"user_email\":\"User@Example.COM\"}", + "{\"email\":\"user@example.com\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Converts all letters in a string to lowercase", + "examples": [ + { + "mapping": "root.foo = this.foo.lowercase()", + "results": [ + [ + "{\"foo\":\"HELLO WORLD\"}", + "{\"foo\":\"hello world\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.email = this.user_email.lowercase()", + "results": [ + [ + "{\"user_email\":\"User@Example.COM\"}", + "{\"email\":\"user@example.com\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "lowercase", "params": {}, "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": null + } + ], + "description": "Executes a query on the target value, allowing you to transform or extract data from the current context.", "impure": false, "name": "map", "params": { @@ -4357,13 +6368,13 @@ } ] }, - "status": "hidden" + "status": "stable" }, { "categories": [ { "Category": "Object & Array Manipulation", - "Description": "", + "Description": "Applies a mapping query to each element of an array or each value in an object. Returns a new collection with the transformed values.", "Examples": [ { "mapping": "root.new_nums = this.nums.map_each(num -> if num < 10 {\n deleted()\n} else {\n num - 10\n})", @@ -4374,7 +6385,7 @@ ] ], "skip_testing": false, - "summary": "##### On arrays\n\nApply a mapping to each element of an array and replace the element with the result. Within the argument mapping the context is the value of the element being mapped." + "summary": "##### On arrays\n\nTransforms each array element using a query. Return deleted() to remove an element, or the new value to replace it." }, { "mapping": "root.new_dict = this.dict.map_each(item -> item.value.uppercase())", @@ -4385,11 +6396,36 @@ ] ], "skip_testing": false, - "summary": "##### On objects\n\nApply a mapping to each value of an object and replace the value with the result. Within the argument mapping the context is an object with a field `key` containing the value key, and a field `value`." + "summary": "##### On objects\n\nTransforms each object value using a query. The query receives an object with 'key' and 'value' fields for each entry." } ] } ], + "description": "Applies a mapping to each element of an array or object", + "examples": [ + { + "mapping": "root.new_nums = this.nums.map_each(num -> if num < 10 {\n deleted()\n} else {\n num - 10\n})", + "results": [ + [ + "{\"nums\":[3,11,4,17]}", + "{\"new_nums\":[1,7]}" + ] + ], + "skip_testing": false, + "summary": "##### On arrays\n\nTransforms each array element using a query. Return deleted() to remove an element, or the new value to replace it." + }, + { + "mapping": "root.new_dict = this.dict.map_each(item -> item.value.uppercase())", + "results": [ + [ + "{\"dict\":{\"foo\":\"hello\",\"bar\":\"world\"}}", + "{\"new_dict\":{\"bar\":\"WORLD\",\"foo\":\"HELLO\"}}" + ] + ], + "skip_testing": false, + "summary": "##### On objects\n\nTransforms each object value using a query. The query receives an object with 'key' and 'value' fields for each entry." + } + ], "impure": false, "name": "map_each", "params": { @@ -4409,7 +6445,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Apply a mapping to each key of an object, and replace the key with the result, which must be a string.", + "Description": "Transforms object keys using a query. The query receives each key as a string and must return a new string key. Use this to rename or transform keys while preserving values.", "Examples": [ { "mapping": "root.new_dict = this.dict.map_each_key(key -> key.uppercase())", @@ -4431,11 +6467,36 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Conditionally transform keys" } ] } ], + "description": "Transforms object keys using a mapping query", + "examples": [ + { + "mapping": "root.new_dict = this.dict.map_each_key(key -> key.uppercase())", + "results": [ + [ + "{\"dict\":{\"keya\":\"hello\",\"keyb\":\"world\"}}", + "{\"new_dict\":{\"KEYA\":\"hello\",\"KEYB\":\"world\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = this.map_each_key(key -> if key.contains(\"kafka\") { \"_\" + key })", + "results": [ + [ + "{\"amqp_key\":\"foo\",\"kafka_key\":\"bar\",\"kafka_topic\":\"baz\"}", + "{\"_kafka_key\":\"bar\",\"_kafka_topic\":\"baz\",\"amqp_key\":\"foo\"}" + ] + ], + "skip_testing": false, + "summary": "Conditionally transform keys" + } + ], "impure": false, "name": "map_each_key", "params": { @@ -4469,15 +6530,11 @@ "summary": "" }, { - "mapping": "root.new_value = [0,this.value].max()", + "mapping": "root.highest_temp = this.temperatures.max()", "results": [ [ - "{\"value\":-1}", - "{\"new_value\":0}" - ], - [ - "{\"value\":7}", - "{\"new_value\":7}" + "{\"temperatures\":[20.5,22.1,19.8,23.4]}", + "{\"highest_temp\":23.4}" ] ], "skip_testing": false, @@ -4486,7 +6543,31 @@ ] } ], - "description": "Returns the largest numerical value found within an array. All values must be numerical and the array must not be empty, otherwise an error is returned.", + "description": "Returns the largest number from an array. All elements must be numbers and the array cannot be empty.", + "examples": [ + { + "mapping": "root.biggest = this.values.max()", + "results": [ + [ + "{\"values\":[0,3,2.5,7,5]}", + "{\"biggest\":7}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.highest_temp = this.temperatures.max()", + "results": [ + [ + "{\"temperatures\":[20.5,22.1,19.8,23.4]}", + "{\"highest_temp\":23.4}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "max", "params": {}, @@ -4508,11 +6589,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.combined = this.list1.merge(this.list2)", + "results": [ + [ + "{\"list1\":[\"a\",\"b\"],\"list2\":[\"c\",\"d\"]}", + "{\"combined\":[\"a\",\"b\",\"c\",\"d\"]}" + ] + ], + "skip_testing": false, + "summary": "Merge arrays" } ] } ], - "description": "Merge a source object into an existing destination object. When a collision is found within the merged structures (both a source and destination object contain the same non-object keys) the result will be an array containing both values, where values that are already arrays will be expanded into the resulting array. In order to simply override destination fields on collision use the <> method.", + "description": "Combines two objects or arrays. When merging objects, conflicting keys create arrays containing both values. Arrays are concatenated. For key override behavior instead, use the assign method.", + "examples": [ + { + "mapping": "root = this.foo.merge(this.bar)", + "results": [ + [ + "{\"foo\":{\"first_name\":\"fooer\",\"likes\":\"bars\"},\"bar\":{\"second_name\":\"barer\",\"likes\":\"foos\"}}", + "{\"first_name\":\"fooer\",\"likes\":[\"bars\",\"foos\"],\"second_name\":\"barer\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.combined = this.list1.merge(this.list2)", + "results": [ + [ + "{\"list1\":[\"a\",\"b\"],\"list2\":[\"c\",\"d\"]}", + "{\"combined\":[\"a\",\"b\",\"c\",\"d\"]}" + ] + ], + "skip_testing": false, + "summary": "Merge arrays" + } + ], "impure": false, "name": "merge", "params": { @@ -4546,15 +6662,11 @@ "summary": "" }, { - "mapping": "root.new_value = [10,this.value].min()", + "mapping": "root.lowest_temp = this.temperatures.min()", "results": [ [ - "{\"value\":2}", - "{\"new_value\":2}" - ], - [ - "{\"value\":23}", - "{\"new_value\":10}" + "{\"temperatures\":[20.5,22.1,19.8,23.4]}", + "{\"lowest_temp\":19.8}" ] ], "skip_testing": false, @@ -4563,17 +6675,49 @@ ] } ], - "description": "Returns the smallest numerical value found within an array. All values must be numerical and the array must not be empty, otherwise an error is returned.", + "description": "Returns the smallest number from an array. All elements must be numbers and the array cannot be empty.", + "examples": [ + { + "mapping": "root.smallest = this.values.min()", + "results": [ + [ + "{\"values\":[0,3,-2.5,7,5]}", + "{\"smallest\":-2.5}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.lowest_temp = this.temperatures.min()", + "results": [ + [ + "{\"temperatures\":[20.5,22.1,19.8,23.4]}", + "{\"lowest_temp\":19.8}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "min", "params": {}, "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": null + } + ], + "description": "Returns the logical NOT (negation) of a boolean value. Converts true to false and false to true.", "impure": false, "name": "not", "params": {}, - "status": "hidden" + "status": "stable" }, { "categories": [ @@ -4615,6 +6759,40 @@ ] } ], + "description": "Ensures a value is not empty", + "examples": [ + { + "mapping": "root.a = this.a.not_empty()", + "results": [ + [ + "{\"a\":\"foo\"}", + "{\"a\":\"foo\"}" + ], + [ + "{\"a\":\"\"}", + "Error(\"failed assignment (line 1): field `this.a`: string value is empty\")" + ], + [ + "{\"a\":[\"foo\",\"bar\"]}", + "{\"a\":[\"foo\",\"bar\"]}" + ], + [ + "{\"a\":[]}", + "Error(\"failed assignment (line 1): field `this.a`: array value is empty\")" + ], + [ + "{\"a\":{\"b\":\"foo\",\"c\":\"bar\"}}", + "{\"a\":{\"b\":\"foo\",\"c\":\"bar\"}}" + ], + [ + "{\"a\":{}}", + "Error(\"failed assignment (line 1): field `this.a`: object value is empty\")" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "not_empty", "params": {}, @@ -4644,6 +6822,24 @@ ] } ], + "description": "Ensures a value is not null", + "examples": [ + { + "mapping": "root.a = this.a.not_null()", + "results": [ + [ + "{\"a\":\"foobar\",\"b\":\"barbaz\"}", + "{\"a\":\"foobar\"}" + ], + [ + "{\"b\":\"barbaz\"}", + "Error(\"failed assignment (line 1): field `this.a`: value is null\")" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "not_null", "params": {}, @@ -4664,6 +6860,15 @@ ] } ], + "description": "Converts a value to a number with optional fallback", + "examples": [ + { + "mapping": "root.foo = this.thing.number() + 10\nroot.bar = this.thing.number(5) * 10", + "results": [], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "number", "params": { @@ -4681,6 +6886,20 @@ "status": "stable" }, { + "categories": [ + { + "Category": "General", + "Description": "", + "Examples": [ + { + "mapping": "root.doc.id = this.thing.id.or(uuid_v4())", + "results": [], + "skip_testing": false, + "summary": "" + } + ] + } + ], "description": "If the result of the target query fails or resolves to `null`, returns the argument instead. This is an explicit method alternative to the coalesce pipe operator `|`.", "examples": [ { @@ -4758,6 +6977,53 @@ ] } ], + "description": "Parses CSV data into an array", + "examples": [ + { + "mapping": "root.orders = this.orders.parse_csv()", + "results": [ + [ + "{\"orders\":\"foo,bar\\nfoo 1,bar 1\\nfoo 2,bar 2\"}", + "{\"orders\":[{\"bar\":\"bar 1\",\"foo\":\"foo 1\"},{\"bar\":\"bar 2\",\"foo\":\"foo 2\"}]}" + ] + ], + "skip_testing": false, + "summary": "Parses CSV data with a header row" + }, + { + "mapping": "root.orders = this.orders.parse_csv(false)", + "results": [ + [ + "{\"orders\":\"foo 1,bar 1\\nfoo 2,bar 2\"}", + "{\"orders\":[[\"foo 1\",\"bar 1\"],[\"foo 2\",\"bar 2\"]]}" + ] + ], + "skip_testing": false, + "summary": "Parses CSV data without a header row" + }, + { + "mapping": "root.orders = this.orders.parse_csv(delimiter:\".\")", + "results": [ + [ + "{\"orders\":\"foo.bar\\nfoo 1.bar 1\\nfoo 2.bar 2\"}", + "{\"orders\":[{\"bar\":\"bar 1\",\"foo\":\"foo 1\"},{\"bar\":\"bar 2\",\"foo\":\"foo 2\"}]}" + ] + ], + "skip_testing": false, + "summary": "Parses CSV data delimited by dots" + }, + { + "mapping": "root.orders = this.orders.parse_csv(lazy_quotes:true)", + "results": [ + [ + "{\"orders\":\"foo,bar\\nfoo 1,bar 1\\nfoo\\\" \\\"2,bar\\\" \\\"2\"}", + "{\"orders\":[{\"bar\":\"bar 1\",\"foo\":\"foo 1\"},{\"bar\":\"bar\\\" \\\"2\",\"foo\":\"foo\\\" \\\"2\"}]}" + ] + ], + "skip_testing": false, + "summary": "Parses CSV data containing a quote in an unquoted field" + } + ], "impure": false, "name": "parse_csv", "params": { @@ -4805,7 +7071,7 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Parse microseconds to nanoseconds." }, { "mapping": "root.delay_for_s = this.delay_for.parse_duration() / 1000000000", @@ -4816,12 +7082,36 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Parse hours to seconds." } ] } ], - "description": "Attempts to parse a string as a duration and returns an integer of nanoseconds. A duration string is a possibly signed sequence of decimal numbers, each with an optional fraction and a unit suffix, such as \"300ms\", \"-1.5h\" or \"2h45m\". Valid time units are \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".", + "description": "Parses a Go-style duration string into nanoseconds. A duration string is a signed sequence of decimal numbers with unit suffixes like \"300ms\", \"-1.5h\", or \"2h45m\". Valid units: \"ns\", \"us\" (or \"µs\"), \"ms\", \"s\", \"m\", \"h\".", + "examples": [ + { + "mapping": "root.delay_for_ns = this.delay_for.parse_duration()", + "results": [ + [ + "{\"delay_for\":\"50us\"}", + "{\"delay_for_ns\":50000}" + ] + ], + "skip_testing": false, + "summary": "Parse microseconds to nanoseconds." + }, + { + "mapping": "root.delay_for_s = this.delay_for.parse_duration() / 1000000000", + "results": [ + [ + "{\"delay_for\":\"2h\"}", + "{\"delay_for_s\":7200}" + ] + ], + "skip_testing": false, + "summary": "Parse hours to seconds." + } + ], "impure": false, "name": "parse_duration", "params": {}, @@ -4842,7 +7132,7 @@ ] ], "skip_testing": false, - "summary": "Arbitrary ISO-8601 duration string to nanoseconds:" + "summary": "Parse complex ISO 8601 duration to nanoseconds." }, { "mapping": "root.delay_for_s = this.delay_for.parse_duration_iso8601() / 1000000000", @@ -4853,23 +7143,36 @@ ] ], "skip_testing": false, - "summary": "Two hours ISO-8601 duration string to seconds:" - }, - { - "mapping": "root.delay_for_s = this.delay_for.parse_duration_iso8601() / 1000000000", - "results": [ - [ - "{\"delay_for\":\"PT2.5S\"}", - "{\"delay_for_s\":2.5}" - ] - ], - "skip_testing": false, - "summary": "Two and a half seconds ISO-8601 duration string to seconds:" + "summary": "Parse hours to seconds." } ] } ], - "description": "Attempts to parse a string using ISO-8601 rules as a duration and returns an integer of nanoseconds. A duration string is represented by the format \"P[n]Y[n]M[n]DT[n]H[n]M[n]S\" or \"P[n]W\". In these representations, the \"[n]\" is replaced by the value for each of the date and time elements that follow the \"[n]\". For example, \"P3Y6M4DT12H30M5S\" represents a duration of \"three years, six months, four days, twelve hours, thirty minutes, and five seconds\". The last field of the format allows fractions with one decimal place, so \"P3.5S\" will return 3500000000ns. Any additional decimals will be truncated.", + "description": "Parses an ISO 8601 duration string into nanoseconds. Format: \"P[n]Y[n]M[n]DT[n]H[n]M[n]S\" or \"P[n]W\". Example: \"P3Y6M4DT12H30M5S\" means 3 years, 6 months, 4 days, 12 hours, 30 minutes, 5 seconds. Supports fractional seconds with full precision (not just one decimal place).", + "examples": [ + { + "mapping": "root.delay_for_ns = this.delay_for.parse_duration_iso8601()", + "results": [ + [ + "{\"delay_for\":\"P3Y6M4DT12H30M5S\"}", + "{\"delay_for_ns\":110839937000000000}" + ] + ], + "skip_testing": false, + "summary": "Parse complex ISO 8601 duration to nanoseconds." + }, + { + "mapping": "root.delay_for_s = this.delay_for.parse_duration_iso8601() / 1000000000", + "results": [ + [ + "{\"delay_for\":\"PT2H\"}", + "{\"delay_for_s\":7200}" + ] + ], + "skip_testing": false, + "summary": "Parse hours to seconds." + } + ], "impure": false, "name": "parse_duration_iso8601", "params": {}, @@ -4896,6 +7199,19 @@ } ], "description": "Attempts to parse a url-encoded query string (from an x-www-form-urlencoded request body) and returns a structured result.", + "examples": [ + { + "mapping": "root.values = this.body.parse_form_url_encoded()", + "results": [ + [ + "{\"body\":\"noise=meow&animal=cat&fur=orange&fur=fluffy\"}", + "{\"values\":{\"animal\":\"cat\",\"fur\":[\"orange\",\"fluffy\"],\"noise\":\"meow\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_form_url_encoded", "params": {}, @@ -4932,6 +7248,31 @@ ] } ], + "description": "Parses a JSON string into a structured value", + "examples": [ + { + "mapping": "root.doc = this.doc.parse_json()", + "results": [ + [ + "{\"doc\":\"{\\\"foo\\\":\\\"bar\\\"}\"}", + "{\"doc\":{\"foo\":\"bar\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.doc = this.doc.parse_json(use_number: true)", + "results": [ + [ + "{\"doc\":\"{\\\"foo\\\":\\\"11380878173205700000000000000000000000000000000\\\"}\"}", + "{\"doc\":{\"foo\":\"11380878173205700000000000000000000000000000000\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_json", "params": { @@ -4969,6 +7310,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with ES256. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_es256(\"\"\"-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEGtLqIBePHmIhQcf0JLgc+F/4W/oI\ndp0Gta53G35VerNDgUUXmp78J2kfh4qLdh0XtmOMI587tCaqjvDAXfs//w==\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.GIRajP9JJbpTlqSCdNEz4qpQkRvzX4Q51YnTwVyxLDM9tKjR_a8ggHWn9CWj7KG0x8J56OWtmUxn112SRTZVhQ\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_es256", "params": { @@ -5006,6 +7360,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with ES384. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_es384(\"\"\"-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAERoz74/B6SwmLhs8X7CWhnrWyRrB13AuU\n8OYeqy0qHRu9JWNw8NIavqpTmu6XPT4xcFanYjq8FbeuM11eq06C52mNmS4LLwzA\n2imlFEgn85bvJoC3bnkuq4mQjwt9VxdH\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.H2HBSlrvQBaov2tdreGonbBexxtQB-xzaPL4-tNQZ6TVh7VH8VBcSwcWHYa1lBAHmdsKOFcB2Wk0SB7QWeGT3ptSgr-_EhDMaZ8bA5spgdpq5DsKfaKHrd7DbbQlmxNq\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_es384", "params": { @@ -5043,6 +7410,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with ES512. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_es512(\"\"\"-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAkHLdts9P56fFkyhpYQ31M/Stwt3w\nvpaxhlfudxnXgTO1IP4RQRgryRxZ19EUzhvWDcG3GQIckoNMY5PelsnCGnIBT2Xh\n9NQkjWF5K6xS4upFsbGSAwQ+GIyyk5IPJ2LHgOyMSCVh5gRZXV3CZLzXujx/umC9\nUeYyTt05zRRWuD+p5bY=\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.ACrpLuU7TKpAnncDCpN9m85nkL55MJ45NFOBl6-nEXmNT1eIxWjiP4pwWVbFH9et_BgN14119jbL_KqEJInPYc9nAXC6dDLq0aBU-dalvNl4-O5YWpP43-Y-TBGAsWnbMTrchILJ4-AEiICe73Ck5yWPleKg9c3LtkEFWfGs7BoPRguZ\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_es512", "params": { @@ -5080,6 +7460,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with HS256. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_hs256(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.YwXOM8v3gHVWcQRRRQc_zDlhmLnM62fwhFYGpiA0J1A\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_hs256", "params": { @@ -5117,6 +7510,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with HS384. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_hs384(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.2Y8rf_ijwN4t8hOGGViON_GrirLkCQVbCOuax6EoZ3nluX0tCGezcJxbctlIfsQ2\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_hs384", "params": { @@ -5154,6 +7560,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with HS512. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_hs512(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.utRb0urG6LGGyranZJVo5Dk0Fns1QNcSUYPN0TObQ-YzsGGB8jrxHwM5NAJccjJZzKectEUqmmKCaETZvuX4Fg\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_hs512", "params": { @@ -5191,6 +7610,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with RS256. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_rs256(\"\"\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ibN8r68pLMR6gRzg4S\n8v8l6Q7yi8qURjkEbcNeM1rkokC7xh0I4JVTwxYSVv/JIW8qJdyspl5NIfuAVi32\nWfKvSAs+NIs+DMsNPYw3yuQals4AX8hith1YDvYpr8SD44jxhz/DR9lYKZFGhXGB\n+7NqQ7vpTWp3BceLYocazWJgusZt7CgecIq57ycM5hjM93BvlrUJ8nQ1a46wfL/8\nCy4P0et70hzZrsjjN41KFhKY0iUwlyU41yEiDHvHDDsTMBxAZosWjSREGfJL6Mfp\nXOInTHs/Gg6DZMkbxjQu6L06EdJ+Q/NwglJdAXM7Zo9rNELqRig6DdvG5JesdMsO\n+QIDAQAB\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.b0lH3jEupZZ4zoaly4Y_GCvu94HH6UKdKY96zfGNsIkPZpQLHIkZ7jMWlLlNOAd8qXlsBGP_i8H2qCKI4zlWJBGyPZgxXDzNRPVrTDfFpn4t4nBcA1WK2-ntXP3ehQxsaHcQU8Z_nsogId7Pme5iJRnoHWEnWtbwz5DLSXL3ZZNnRdrHM9MdI7QSDz9mojKDCaMpGN9sG7Xl-tGdBp1XzXuUOzG8S03mtZ1IgVR1uiBL2N6oohHIAunk8DIAmNWI-zgycTgzUGU7mvPkKH43qO8Ua1-13tCUBKKa8VxcotZ67Mxm1QAvBGoDnTKwWMwghLzs6d6WViXQg6eWlJcpBA\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_rs256", "params": { @@ -5228,6 +7660,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with RS384. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_rs384(\"\"\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ibN8r68pLMR6gRzg4S\n8v8l6Q7yi8qURjkEbcNeM1rkokC7xh0I4JVTwxYSVv/JIW8qJdyspl5NIfuAVi32\nWfKvSAs+NIs+DMsNPYw3yuQals4AX8hith1YDvYpr8SD44jxhz/DR9lYKZFGhXGB\n+7NqQ7vpTWp3BceLYocazWJgusZt7CgecIq57ycM5hjM93BvlrUJ8nQ1a46wfL/8\nCy4P0et70hzZrsjjN41KFhKY0iUwlyU41yEiDHvHDDsTMBxAZosWjSREGfJL6Mfp\nXOInTHs/Gg6DZMkbxjQu6L06EdJ+Q/NwglJdAXM7Zo9rNELqRig6DdvG5JesdMsO\n+QIDAQAB\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.orcXYBcjVE5DU7mvq4KKWFfNdXR4nEY_xupzWoETRpYmQZIozlZnM_nHxEk2dySvpXlAzVm7kgOPK2RFtGlOVaNRIa3x-pMMr-bhZTno4L8Hl4sYxOks3bWtjK7wql4uqUbqThSJB12psAXw2-S-I_FMngOPGIn4jDT9b802ottJSvTpXcy0-eKTjrV2PSkRRu-EYJh0CJZW55MNhqlt6kCGhAXfbhNazN3ASX-dmpd_JixyBKphrngr_zRA-FCn_Xf3QQDA-5INopb4Yp5QiJ7UxVqQEKI80X_JvJqz9WE1qiAw8pq5-xTen1t7zTP-HT1NbbD3kltcNa3G8acmNg\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_rs384", "params": { @@ -5265,6 +7710,19 @@ } ], "description": "Parses a claims object from a JWT string encoded with RS512. This method does not validate JWT claims.", + "examples": [ + { + "mapping": "root.claims = this.signed.parse_jwt_rs512(\"\"\"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAs/ibN8r68pLMR6gRzg4S\n8v8l6Q7yi8qURjkEbcNeM1rkokC7xh0I4JVTwxYSVv/JIW8qJdyspl5NIfuAVi32\nWfKvSAs+NIs+DMsNPYw3yuQals4AX8hith1YDvYpr8SD44jxhz/DR9lYKZFGhXGB\n+7NqQ7vpTWp3BceLYocazWJgusZt7CgecIq57ycM5hjM93BvlrUJ8nQ1a46wfL/8\nCy4P0et70hzZrsjjN41KFhKY0iUwlyU41yEiDHvHDDsTMBxAZosWjSREGfJL6Mfp\nXOInTHs/Gg6DZMkbxjQu6L06EdJ+Q/NwglJdAXM7Zo9rNELqRig6DdvG5JesdMsO\n+QIDAQAB\n-----END PUBLIC KEY-----\"\"\")", + "results": [ + [ + "{\"signed\":\"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.rsMp_X5HMrUqKnZJIxo27aAoscovRA6SSQYR9rq7pifIj0YHXxMyNyOBDGnvVALHKTi25VUGHpfNUW0VVMmae0A4t_ObNU6hVZHguWvetKZZq4FZpW1lgWHCMqgPGwT5_uOqwYCH6r8tJuZT3pqXeL0CY4putb1AN2w6CVp620nh3l8d3XWb4jaifycd_4CEVCqHuWDmohfug4VhmoVKlIXZkYoAQowgHlozATDssBSWdYtv107Wd2AzEoiXPu6e3pflsuXULlyqQnS4ELEKPYThFLafh1NqvZDPddqozcPZ-iODBW-xf3A4DYDdivnMYLrh73AZOGHexxu8ay6nDA\"}", + "{\"claims\":{\"iat\":1516239022,\"mood\":\"Disdainful\",\"sub\":\"1234567890\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_jwt_rs512", "params": { @@ -5281,6 +7739,45 @@ "status": "stable", "version": "v4.20.0" }, + { + "categories": [ + { + "Category": "Parsing", + "Description": "Attempts to parse a logfmt encoded string into an object. A logfmt string contains key=value pairs separated by spaces, where values can optionally be quoted.", + "Examples": [ + { + "mapping": "root = this.msg.parse_logfmt()", + "results": [ + [ + "{\"msg\":\"level=info msg=\\\"hello world\\\" dur=1.5s\"}", + "{\"dur\":\"1.5s\",\"level\":\"info\",\"msg\":\"hello world\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ] + } + ], + "description": "Parses logfmt formatted data into an object", + "examples": [ + { + "mapping": "root = this.msg.parse_logfmt()", + "results": [ + [ + "{\"msg\":\"level=info msg=\\\"hello world\\\" dur=1.5s\"}", + "{\"dur\":\"1.5s\",\"level\":\"info\",\"msg\":\"hello world\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], + "impure": false, + "name": "parse_logfmt", + "params": {}, + "status": "stable" + }, { "categories": [ { @@ -5296,23 +7793,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Parse MessagePack data from hex-encoded content" }, { - "mapping": "root = this.encoded.decode(\"base64\").parse_msgpack()", + "mapping": "root.decoded = this.msgpack_data.decode(\"base64\").parse_msgpack()", "results": [ [ - "{\"encoded\":\"gaNmb2+jYmFy\"}", - "{\"foo\":\"bar\"}" + "{\"msgpack_data\":\"gaNmb2+jYmFy\"}", + "{\"decoded\":{\"foo\":\"bar\"}}" ] ], "skip_testing": false, - "summary": "" + "summary": "Parse MessagePack from base64-encoded field" } ] } ], - "description": "Parses a https://msgpack.org/[MessagePack^] message into a structured document.", + "description": "Parses MessagePack binary data into a structured object. MessagePack is an efficient binary serialization format that is more compact than JSON while maintaining similar data structures. Commonly used for high-performance APIs and data interchange between microservices.", + "examples": [ + { + "mapping": "root = content().decode(\"hex\").parse_msgpack()", + "results": [ + [ + "81a3666f6fa3626172", + "{\"foo\":\"bar\"}" + ] + ], + "skip_testing": false, + "summary": "Parse MessagePack data from hex-encoded content" + }, + { + "mapping": "root.decoded = this.msgpack_data.decode(\"base64\").parse_msgpack()", + "results": [ + [ + "{\"msgpack_data\":\"gaNmb2+jYmFy\"}", + "{\"decoded\":{\"foo\":\"bar\"}}" + ] + ], + "skip_testing": false, + "summary": "Parse MessagePack from base64-encoded field" + } + ], "impure": false, "name": "parse_msgpack", "params": {}, @@ -5325,15 +7846,35 @@ "Description": "", "Examples": [ { - "mapping": "root = content().parse_parquet()", + "mapping": "root.records = content().parse_parquet()", "results": [], - "skip_testing": false, - "summary": "" + "skip_testing": true, + "summary": "Parse Parquet file data into structured objects" + }, + { + "mapping": "root.users = this.parquet_data.parse_parquet().map_each(row -> {\"name\": row.name, \"email\": row.email})", + "results": [], + "skip_testing": true, + "summary": "Process Parquet data from a field and extract specific columns" } ] } ], - "description": "Decodes a https://parquet.apache.org/docs/[Parquet file^] into an array of objects, one for each row within the file.", + "description": "Parses Apache Parquet binary data into an array of objects. Parquet is a columnar storage format optimized for analytics, commonly used with big data systems like Apache Spark, Hive, and cloud data warehouses. Each row in the Parquet file becomes an object in the output array.", + "examples": [ + { + "mapping": "root.records = content().parse_parquet()", + "results": [], + "skip_testing": true, + "summary": "Parse Parquet file data into structured objects" + }, + { + "mapping": "root.users = this.parquet_data.parse_parquet().map_each(row -> {\"name\": row.name, \"email\": row.email})", + "results": [], + "skip_testing": true, + "summary": "Process Parquet data from a field and extract specific columns" + } + ], "impure": false, "name": "parse_parquet", "params": { @@ -5358,13 +7899,13 @@ "Examples": null } ], - "description": "Attempts to parse a string as a timestamp following a specified format and outputs a timestamp, which can then be fed into methods such as <>.\n\nThe input format is defined by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would be displayed if it were the value. For an alternative way to specify formats check out the <> method.", + "description": "Parses a timestamp string using Go's reference time format and outputs a timestamp object. The format uses \"Mon Jan 2 15:04:05 -0700 MST 2006\" as a reference - show how this reference time would appear in your format. Use ts_strptime for strftime-style formats instead.", "impure": false, "name": "parse_timestamp", "params": { "named": [ { - "description": "The format of the target string.", + "description": "The format of the input string using Go's reference time.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, @@ -5382,13 +7923,13 @@ "Examples": null } ], - "description": "Attempts to parse a string as a timestamp following a specified strptime-compatible format and outputs a timestamp, which can then be fed into <>.", + "description": "Parses a timestamp string using strptime format specifiers (like %Y, %m, %d) and outputs a timestamp object. Use ts_parse for Go-style reference time formats instead.", "impure": false, "name": "parse_timestamp_strptime", "params": { "named": [ { - "description": "The format of the target string.", + "description": "The format string using strptime specifiers (e.g., %Y-%m-%d).", "name": "format", "no_dynamic": false, "scalars_to_literal": false, @@ -5434,6 +7975,34 @@ } ], "description": "Attempts to parse a URL from a string value, returning a structured result that describes the various facets of the URL. The fields returned within the structured result roughly follow https://pkg.go.dev/net/url#URL, and may be expanded in future in order to present more information.", + "examples": [ + { + "mapping": "root.foo_url = this.foo_url.parse_url()", + "results": [ + [ + "{\"foo_url\":\"https://docs.redpanda.com/redpanda-connect/guides/bloblang/about/\"}", + "{\"foo_url\":{\"fragment\":\"\",\"host\":\"docs.redpanda.com\",\"opaque\":\"\",\"path\":\"/redpanda-connect/guides/bloblang/about/\",\"raw_fragment\":\"\",\"raw_path\":\"\",\"raw_query\":\"\",\"scheme\":\"https\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.username = this.url.parse_url().user.name | \"unknown\"", + "results": [ + [ + "{\"url\":\"amqp://foo:bar@127.0.0.1:5672/\"}", + "{\"username\":\"foo\"}" + ], + [ + "{\"url\":\"redis://localhost:6379\"}", + "{\"username\":\"unknown\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_url", "params": {}, @@ -5454,41 +8023,54 @@ ] ], "skip_testing": false, - "summary": "" - }, - { - "mapping": "root.doc = this.doc.parse_xml(cast: false)", - "results": [ - [ - "{\"doc\":\"This is a title123True\"}", - "{\"doc\":{\"root\":{\"bool\":\"True\",\"number\":{\"#text\":\"123\",\"-id\":\"99\"},\"title\":\"This is a title\"}}}" - ] - ], - "skip_testing": false, - "summary": "" + "summary": "Parse XML document into object structure" }, { "mapping": "root.doc = this.doc.parse_xml(cast: true)", "results": [ [ - "{\"doc\":\"This is a title123True\"}", + "{\"doc\":\"This is a title123True\"}", "{\"doc\":{\"root\":{\"bool\":true,\"number\":{\"#text\":123,\"-id\":99},\"title\":\"This is a title\"}}}" ] ], "skip_testing": false, - "summary": "" + "summary": "Parse XML with type casting enabled to convert strings to numbers and booleans" } ] } ], - "description": "\nAttempts to parse a string as an XML document and returns a structured result, where elements appear as keys of an object according to the following rules:\n\n- If an element contains attributes they are parsed by prefixing a hyphen, `-`, to the attribute label.\n- If the element is a simple element and has attributes, the element value is given the key `#text`.\n- XML comments, directives, and process instructions are ignored.\n- When elements are repeated the resulting JSON value is an array.\n- If cast is true, try to cast values to numbers and booleans instead of returning strings.\n", + "description": "Parses an XML document into a structured object. Converts XML elements to JSON-like objects following these rules:\n\n- Element attributes are prefixed with a hyphen (e.g., `-id` for an `id` attribute)\n- Elements with both attributes and text content store the text in a `#text` field\n- Repeated elements become arrays\n- XML comments, directives, and processing instructions are ignored\n- Optionally cast numeric and boolean strings to their proper types", + "examples": [ + { + "mapping": "root.doc = this.doc.parse_xml()", + "results": [ + [ + "{\"doc\":\"This is a titleThis is some content\"}", + "{\"doc\":{\"root\":{\"content\":\"This is some content\",\"title\":\"This is a title\"}}}" + ] + ], + "skip_testing": false, + "summary": "Parse XML document into object structure" + }, + { + "mapping": "root.doc = this.doc.parse_xml(cast: true)", + "results": [ + [ + "{\"doc\":\"This is a title123True\"}", + "{\"doc\":{\"root\":{\"bool\":true,\"number\":{\"#text\":123,\"-id\":99},\"title\":\"This is a title\"}}}" + ] + ], + "skip_testing": false, + "summary": "Parse XML with type casting enabled to convert strings to numbers and booleans" + } + ], "impure": false, "name": "parse_xml", "params": { "named": [ { "default": false, - "description": "whether to try to cast values that are numbers and booleans to the right type.", + "description": "Whether to automatically cast numeric and boolean string values to their proper types. When false, all values remain as strings.", "is_optional": true, "name": "cast", "no_dynamic": false, @@ -5519,6 +8101,20 @@ ] } ], + "description": "Parses a YAML string into a structured value", + "examples": [ + { + "mapping": "root.doc = this.doc.parse_yaml()", + "results": [ + [ + "{\"doc\":\"foo: bar\"}", + "{\"doc\":{\"foo\":\"bar\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "parse_yaml", "params": {}, @@ -5529,16 +8125,63 @@ { "Category": "Object & Array Manipulation", "Description": "", - "Examples": null + "Examples": [ + { + "mapping": "root.updated = this.current.patch(this.changelog)", + "results": [ + [ + "{\"current\":{\"name\":\"Alice\",\"age\":30},\"changelog\":[{\"Type\":\"update\",\"Path\":[\"age\"],\"From\":30,\"To\":31},{\"Type\":\"create\",\"Path\":[\"city\"],\"From\":null,\"To\":\"NYC\"}]}", + "{\"updated\":{\"age\":31,\"city\":\"NYC\",\"name\":\"Alice\"}}" + ] + ], + "skip_testing": false, + "summary": "Apply recorded changes to update an object" + }, + { + "mapping": "root.restored = this.modified.patch(this.reverse_changelog)", + "results": [ + [ + "{\"modified\":{\"timeout\":60},\"reverse_changelog\":[{\"Type\":\"create\",\"Path\":[\"debug\"],\"From\":null,\"To\":true},{\"Type\":\"update\",\"Path\":[\"timeout\"],\"From\":60,\"To\":30}]}", + "{\"restored\":{\"debug\":true,\"timeout\":30}}" + ] + ], + "skip_testing": false, + "summary": "Restore previous state by applying inverse changes" + } + ] + } + ], + "description": "Applies a changelog (created by the diff method) to the current value, transforming it according to the specified operations. This enables you to synchronize data, replay changes, or implement event sourcing patterns by applying recorded changes to reconstruct state.", + "examples": [ + { + "mapping": "root.updated = this.current.patch(this.changelog)", + "results": [ + [ + "{\"current\":{\"name\":\"Alice\",\"age\":30},\"changelog\":[{\"Type\":\"update\",\"Path\":[\"age\"],\"From\":30,\"To\":31},{\"Type\":\"create\",\"Path\":[\"city\"],\"From\":null,\"To\":\"NYC\"}]}", + "{\"updated\":{\"age\":31,\"city\":\"NYC\",\"name\":\"Alice\"}}" + ] + ], + "skip_testing": false, + "summary": "Apply recorded changes to update an object" + }, + { + "mapping": "root.restored = this.modified.patch(this.reverse_changelog)", + "results": [ + [ + "{\"modified\":{\"timeout\":60},\"reverse_changelog\":[{\"Type\":\"create\",\"Path\":[\"debug\"],\"From\":null,\"To\":true},{\"Type\":\"update\",\"Path\":[\"timeout\"],\"From\":60,\"To\":30}]}", + "{\"restored\":{\"debug\":true,\"timeout\":30}}" + ] + ], + "skip_testing": false, + "summary": "Restore previous state by applying inverse changes" } ], - "description": "Create a diff by comparing the current value with the given one. Wraps the github.com/r3labs/diff/v3 package. See its https://pkg.go.dev/github.com/r3labs/diff/v3[docs^] for more information.", "impure": false, "name": "patch", "params": { "named": [ { - "description": "The changelog to apply.", + "description": "The changelog array to apply. Should be in the format returned by the diff method, containing Type, Path, From, and To fields for each change.", "name": "changelog", "no_dynamic": false, "scalars_to_literal": false, @@ -5546,7 +8189,7 @@ } ] }, - "status": "beta", + "status": "stable", "version": "4.25.0" }, { @@ -5581,6 +8224,30 @@ } ], "description": "Returns the number raised to the specified exponent.", + "examples": [ + { + "mapping": "root.new_value = this.value * 10.pow(-2)", + "results": [ + [ + "{\"value\":2}", + "{\"new_value\":0.02}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.new_value = this.value.pow(-2)", + "results": [ + [ + "{\"value\":2}", + "{\"new_value\":0.25}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "pow", "params": { @@ -5600,7 +8267,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Quotes a target string using escape sequences (`\\t`, `\\n`, `\\xFF`, `\\u0100`) for control characters and non-printable characters.", + "Description": "Wraps a string in double quotes and escapes special characters (newlines, tabs, etc.) using Go escape sequences. Use for generating string literals or preparing strings for JSON-like formats.", "Examples": [ { "mapping": "root.quoted = this.thing.quote()", @@ -5612,10 +8279,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.literal = this.text.quote()", + "results": [ + [ + "{\"text\":\"hello\\tworld\"}", + "{\"literal\":\"\\\"hello\\\\tworld\\\"\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Wraps a string in double quotes and escapes special characters", + "examples": [ + { + "mapping": "root.quoted = this.thing.quote()", + "results": [ + [ + "{\"thing\":\"foo\\nbar\"}", + "{\"quoted\":\"\\\"foo\\\\nbar\\\"\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.literal = this.text.quote()", + "results": [ + [ + "{\"text\":\"hello\\tworld\"}", + "{\"literal\":\"\\\"hello\\\\tworld\\\"\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "quote", "params": {}, @@ -5625,7 +8328,7 @@ "categories": [ { "Category": "Regular Expressions", - "Description": "Returns an array containing all successive matches of a regular expression in a string.", + "Description": "Finds all matches of a regular expression in a string and returns them as an array. Use for extracting multiple patterns or validating repeating structures.", "Examples": [ { "mapping": "root.matches = this.value.re_find_all(\"a.\")", @@ -5637,10 +8340,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.numbers = this.text.re_find_all(\"[0-9]+\")", + "results": [ + [ + "{\"text\":\"I have 2 apples and 15 oranges\"}", + "{\"numbers\":[\"2\",\"15\"]}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Finds all matches of a regular expression in a string", + "examples": [ + { + "mapping": "root.matches = this.value.re_find_all(\"a.\")", + "results": [ + [ + "{\"value\":\"paranormal\"}", + "{\"matches\":[\"ar\",\"an\",\"al\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.numbers = this.text.re_find_all(\"[0-9]+\")", + "results": [ + [ + "{\"text\":\"I have 2 apples and 15 oranges\"}", + "{\"numbers\":[\"2\",\"15\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_find_all", "params": { @@ -5660,7 +8399,7 @@ "categories": [ { "Category": "Regular Expressions", - "Description": "Returns an array of objects containing all matches of the regular expression and the matches of its subexpressions. The key of each match value is the name of the group when specified, otherwise it is the index of the matching group, starting with the expression as a whole at 0.", + "Description": "Finds all regex matches and returns an array of objects with named capture groups as keys. Each object represents one match with its captured groups. Use for parsing multiple structured records from text.", "Examples": [ { "mapping": "root.matches = this.value.re_find_all_object(\"a(?Px*)b\")", @@ -5687,6 +8426,31 @@ ] } ], + "description": "Finds all regex matches as objects with named groups", + "examples": [ + { + "mapping": "root.matches = this.value.re_find_all_object(\"a(?Px*)b\")", + "results": [ + [ + "{\"value\":\"-axxb-ab-\"}", + "{\"matches\":[{\"0\":\"axxb\",\"foo\":\"xx\"},{\"0\":\"ab\",\"foo\":\"\"}]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.matches = this.value.re_find_all_object(\"(?m)(?P\\\\w+):\\\\s+(?P\\\\w+)$\")", + "results": [ + [ + "{\"value\":\"option1: value1\\noption2: value2\\noption3: value3\"}", + "{\"matches\":[{\"0\":\"option1: value1\",\"key\":\"option1\",\"value\":\"value1\"},{\"0\":\"option2: value2\",\"key\":\"option2\",\"value\":\"value2\"},{\"0\":\"option3: value3\",\"key\":\"option3\",\"value\":\"value3\"}]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_find_all_object", "params": { @@ -5706,7 +8470,7 @@ "categories": [ { "Category": "Regular Expressions", - "Description": "Returns an array of arrays containing all successive matches of the regular expression in a string and the matches, if any, of its subexpressions.", + "Description": "Finds all regex matches and their capture groups, returning an array of arrays where each inner array contains the full match and captured subgroups. Use for extracting structured data with capture groups.", "Examples": [ { "mapping": "root.matches = this.value.re_find_all_submatch(\"a(x*)b\")", @@ -5718,10 +8482,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.emails = this.text.re_find_all_submatch(\"(\\\\w+)@(\\\\w+\\\\.\\\\w+)\")", + "results": [ + [ + "{\"text\":\"Contact: alice@example.com or bob@test.org\"}", + "{\"emails\":[[\"alice@example.com\",\"alice\",\"example.com\"],[\"bob@test.org\",\"bob\",\"test.org\"]]}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Finds all regex matches with capture groups", + "examples": [ + { + "mapping": "root.matches = this.value.re_find_all_submatch(\"a(x*)b\")", + "results": [ + [ + "{\"value\":\"-axxb-ab-\"}", + "{\"matches\":[[\"axxb\",\"xx\"],[\"ab\",\"\"]]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.emails = this.text.re_find_all_submatch(\"(\\\\w+)@(\\\\w+\\\\.\\\\w+)\")", + "results": [ + [ + "{\"text\":\"Contact: alice@example.com or bob@test.org\"}", + "{\"emails\":[[\"alice@example.com\",\"alice\",\"example.com\"],[\"bob@test.org\",\"bob\",\"test.org\"]]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_find_all_submatch", "params": { @@ -5741,7 +8541,7 @@ "categories": [ { "Category": "Regular Expressions", - "Description": "Returns an object containing the first match of the regular expression and the matches of its subexpressions. The key of each match value is the name of the group when specified, otherwise it is the index of the matching group, starting with the expression as a whole at 0.", + "Description": "Finds the first regex match and returns an object with named capture groups as keys (or numeric indices for unnamed groups). The key \"0\" contains the full match. Use for parsing structured text into fields.", "Examples": [ { "mapping": "root.matches = this.value.re_find_object(\"a(?Px*)b\")", @@ -5768,6 +8568,31 @@ ] } ], + "description": "Finds the first regex match as an object with named groups", + "examples": [ + { + "mapping": "root.matches = this.value.re_find_object(\"a(?Px*)b\")", + "results": [ + [ + "{\"value\":\"-axxb-ab-\"}", + "{\"matches\":{\"0\":\"axxb\",\"foo\":\"xx\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.matches = this.value.re_find_object(\"(?P\\\\w+):\\\\s+(?P\\\\w+)\")", + "results": [ + [ + "{\"value\":\"option1: value1\"}", + "{\"matches\":{\"0\":\"option1: value1\",\"key\":\"option1\",\"value\":\"value1\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_find_object", "params": { @@ -5787,7 +8612,7 @@ "categories": [ { "Category": "Regular Expressions", - "Description": "Checks whether a regular expression matches against any part of a string and returns a boolean.", + "Description": "Tests if a regular expression matches anywhere in a string, returning `true` or `false`. Use for validation or conditional routing based on patterns.", "Examples": [ { "mapping": "root.matches = this.value.re_match(\"[0-9]\")", @@ -5807,6 +8632,24 @@ ] } ], + "description": "Tests if a string matches a regular expression", + "examples": [ + { + "mapping": "root.matches = this.value.re_match(\"[0-9]\")", + "results": [ + [ + "{\"value\":\"there are 10 puppies\"}", + "{\"matches\":true}" + ], + [ + "{\"value\":\"there are ten puppies\"}", + "{\"matches\":false}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_match", "params": { @@ -5823,6 +8666,14 @@ "status": "stable" }, { + "categories": [ + { + "Category": "Regular Expressions", + "Description": "", + "Examples": null + } + ], + "description": "Replaces all regex matches with a replacement string that can reference capture groups using `$1`, `$2`, etc. Use for pattern-based transformations or data reformatting.", "impure": false, "name": "re_replace", "params": { @@ -5843,13 +8694,13 @@ } ] }, - "status": "hidden" + "status": "stable" }, { "categories": [ { "Category": "Regular Expressions", - "Description": "Replaces all occurrences of the argument regular expression in a string with a value. Inside the value $ signs are interpreted as submatch expansions, e.g. `$1` represents the text of the first submatch.", + "Description": "Replaces all regex matches with a replacement string that can reference capture groups using `$1`, `$2`, etc. Use for pattern-based transformations or data reformatting.", "Examples": [ { "mapping": "root.new_value = this.value.re_replace_all(\"ADD ([0-9]+)\",\"+($1)\")", @@ -5861,10 +8712,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.masked = this.email.re_replace_all(\"(\\\\w{2})\\\\w+@\", \"$1***@\")", + "results": [ + [ + "{\"email\":\"alice@example.com\"}", + "{\"masked\":\"al***@example.com\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Replaces all regex matches with a replacement string", + "examples": [ + { + "mapping": "root.new_value = this.value.re_replace_all(\"ADD ([0-9]+)\",\"+($1)\")", + "results": [ + [ + "{\"value\":\"foo ADD 70\"}", + "{\"new_value\":\"foo +(70)\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.masked = this.email.re_replace_all(\"(\\\\w{2})\\\\w+@\", \"$1***@\")", + "results": [ + [ + "{\"email\":\"alice@example.com\"}", + "{\"masked\":\"al***@example.com\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "re_replace_all", "params": { @@ -5888,6 +8775,85 @@ "status": "stable" }, { + "categories": [ + { + "Category": "String Manipulation", + "Description": "Creates a new string by repeating the input string a specified number of times. Use for generating padding, separators, or test data.", + "Examples": [ + { + "mapping": "root.repeated = this.name.repeat(3)\nroot.not_repeated = this.name.repeat(0)", + "results": [ + [ + "{\"name\":\"bob\"}", + "{\"not_repeated\":\"\",\"repeated\":\"bobbobbob\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.separator = \"-\".repeat(10)", + "results": [ + [ + "{}", + "{\"separator\":\"----------\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ] + } + ], + "description": "Creates a string by repeating the input a specified number of times", + "examples": [ + { + "mapping": "root.repeated = this.name.repeat(3)\nroot.not_repeated = this.name.repeat(0)", + "results": [ + [ + "{\"name\":\"bob\"}", + "{\"not_repeated\":\"\",\"repeated\":\"bobbobbob\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.separator = \"-\".repeat(10)", + "results": [ + [ + "{}", + "{\"separator\":\"----------\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], + "impure": false, + "name": "repeat", + "params": { + "named": [ + { + "description": "The number of times to repeat the string.", + "name": "count", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "integer" + } + ] + }, + "status": "stable" + }, + { + "categories": [ + { + "Category": "String Manipulation", + "Description": "", + "Examples": null + } + ], + "description": "Replaces all occurrences of a substring with another string. Use for text transformation, cleaning data, or normalizing strings.", "impure": false, "name": "replace", "params": { @@ -5908,13 +8874,13 @@ } ] }, - "status": "hidden" + "status": "stable" }, { "categories": [ { "Category": "String Manipulation", - "Description": "Replaces all occurrences of the first argument in a target string with the second argument.", + "Description": "Replaces all occurrences of a substring with another string. Use for text transformation, cleaning data, or normalizing strings.", "Examples": [ { "mapping": "root.new_value = this.value.replace_all(\"foo\",\"dog\")", @@ -5926,10 +8892,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.clean = this.text.replace_all(\" \", \" \")", + "results": [ + [ + "{\"text\":\"hello world foo\"}", + "{\"clean\":\"hello world foo\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Replaces all occurrences of a substring with another", + "examples": [ + { + "mapping": "root.new_value = this.value.replace_all(\"foo\",\"dog\")", + "results": [ + [ + "{\"value\":\"The foo ate my homework\"}", + "{\"new_value\":\"The dog ate my homework\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.clean = this.text.replace_all(\" \", \" \")", + "results": [ + [ + "{\"text\":\"hello world foo\"}", + "{\"clean\":\"hello world foo\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "replace_all", "params": { @@ -5956,7 +8958,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "For each pair of strings in an argument array, replaces all occurrences of the first item of the pair with the second. This is a more compact way of chaining a series of `replace_all` methods.", + "Description": "Performs multiple find-and-replace operations in sequence using an array of `[old, new]` pairs. More efficient than chaining multiple `replace_all` calls. Use for bulk text transformations.", "Examples": [ { "mapping": "root.new_value = this.value.replace_all_many([\n \"\", \"<b>\",\n \"\", \"</b>\",\n \"\", \"<i>\",\n \"\", \"</i>\",\n])", @@ -5972,6 +8974,20 @@ ] } ], + "description": "Performs multiple find-and-replace operations in sequence", + "examples": [ + { + "mapping": "root.new_value = this.value.replace_all_many([\n \"\", \"<b>\",\n \"\", \"</b>\",\n \"\", \"<i>\",\n \"\", \"</i>\",\n])", + "results": [ + [ + "{\"value\":\"Hello World\"}", + "{\"new_value\":\"<i>Hello</i> <b>World</b>\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "replace_all_many", "params": { @@ -5988,6 +9004,14 @@ "status": "stable" }, { + "categories": [ + { + "Category": "String Manipulation", + "Description": "", + "Examples": null + } + ], + "description": "Performs multiple find-and-replace operations in sequence using an array of `[old, new]` pairs. More efficient than chaining multiple `replace_all` calls. Use for bulk text transformations.", "impure": false, "name": "replace_many", "params": { @@ -6001,13 +9025,13 @@ } ] }, - "status": "hidden" + "status": "stable" }, { "categories": [ { "Category": "String Manipulation", - "Description": "Returns the target string in reverse order.", + "Description": "Reverses the order of characters in a string. Unicode-aware for proper handling of multi-byte characters. Use for creating palindrome checks or reversing text data.", "Examples": [ { "mapping": "root.reversed = this.thing.reverse()", @@ -6034,6 +9058,31 @@ ] } ], + "description": "Reverses the order of characters in a string", + "examples": [ + { + "mapping": "root.reversed = this.thing.reverse()", + "results": [ + [ + "{\"thing\":\"backwards\"}", + "{\"reversed\":\"sdrawkcab\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = content().reverse()", + "results": [ + [ + "{\"thing\":\"backwards\"}", + "}\"sdrawkcab\":\"gniht\"{" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "reverse", "params": {}, @@ -6059,11 +9108,50 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.rounded = this.score.round()", + "results": [ + [ + "{\"score\":87.5}", + "{\"rounded\":88}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], - "description": "Rounds numbers to the nearest integer, rounding half away from zero. If the resulting value fits within a 64-bit integer then that is returned, otherwise a new floating point number is returned.", + "description": "Rounds a number to the nearest integer. Values at .5 round away from zero. Returns an integer if the result fits in 64-bit, otherwise returns a float.", + "examples": [ + { + "mapping": "root.new_value = this.value.round()", + "results": [ + [ + "{\"value\":5.3}", + "{\"new_value\":5}" + ], + [ + "{\"value\":5.9}", + "{\"new_value\":6}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.rounded = this.score.round()", + "results": [ + [ + "{\"score\":87.5}", + "{\"rounded\":88}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "round", "params": {}, @@ -6085,11 +9173,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es256(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using ES256.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_es256(\"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.-8LrOdkEiv_44ADWW08lpbq41ZmHCel58NMORPq1q4Dyw0zFhqDVLrRoSvCvuyyvgXAFb9IHfR-9MlJ_2ShA9A\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es256(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_es256", "params": { @@ -6100,6 +9223,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6122,11 +9253,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es384(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using ES384.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_es384(\"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.8FmTKH08dl7dyxrNu0rmvhegiIBCy-O9cddGco2e9lpZtgv5mS5qHgPkgBC5eRw1d7SRJsHwHZeehzdqT5Ba7aZJIhz9ds0sn37YQ60L7jT0j2gxCzccrt4kECHnUnLw\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es384(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_es384", "params": { @@ -6137,6 +9303,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6159,11 +9333,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es512(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using ES512.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_es512(\"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.AQbEWymoRZxDJEJtKSFFG2k2VbDCTYSuBwAZyMqexCspr3If8aERTVGif8HXG3S7TzMBCCzxkcKr3eIU441l3DlpAMNfQbkcOlBqMvNBn-CX481WyKf3K5rFHQ-6wRonz05aIsWAxCDvAozI_9J0OWllxdQ2MBAuTPbPJ38OqXsYkCQs\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_es512(signing_secret: \"\"\"-----BEGIN EC PRIVATE KEY-----\n... signature data ...\n-----END EC PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_es512", "params": { @@ -6174,6 +9383,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6196,11 +9413,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs256(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using HS256.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_hs256(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.hUl-nngPMY_3h9vveWJUPsCcO5PeL6k9hWLnMYeFbFQ\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs256(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_hs256", "params": { @@ -6211,6 +9463,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6233,11 +9493,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs384(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using HS384.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_hs384(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJIUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.zGYLr83aToon1efUNq-hw7XgT20lPvZb8sYei8x6S6mpHwb433SJdXJXx0Oio8AZ\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs384(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_hs384", "params": { @@ -6248,6 +9543,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6270,11 +9573,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs512(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using HS512.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_hs512(\"\"\"dont-tell-anyone\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyMTIzIn0.zBNR9o_6EDwXXKkpKLNJhG26j8Dc-mV-YahBwmEdCrmiWt5les8I9rgmNlWIowpq6Yxs4kLNAdFhqoRz3NXT3w\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_hs512(signing_secret: \"\"\"dont-tell-anyone\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_hs512", "params": { @@ -6285,6 +9623,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6307,11 +9653,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs256(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using RS256.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_rs256(\"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.b0lH3jEupZZ4zoaly4Y_GCvu94HH6UKdKY96zfGNsIkPZpQLHIkZ7jMWlLlNOAd8qXlsBGP_i8H2qCKI4zlWJBGyPZgxXDzNRPVrTDfFpn4t4nBcA1WK2-ntXP3ehQxsaHcQU8Z_nsogId7Pme5iJRnoHWEnWtbwz5DLSXL3ZZNnRdrHM9MdI7QSDz9mojKDCaMpGN9sG7Xl-tGdBp1XzXuUOzG8S03mtZ1IgVR1uiBL2N6oohHIAunk8DIAmNWI-zgycTgzUGU7mvPkKH43qO8Ua1-13tCUBKKa8VxcotZ67Mxm1QAvBGoDnTKwWMwghLzs6d6WViXQg6eWlJcpBA\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs256(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_rs256", "params": { @@ -6322,6 +9703,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6344,25 +9733,68 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs384(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using RS384.", - "impure": false, - "name": "sign_jwt_rs384", - "params": { - "named": [ - { - "description": "The secret to use for signing the token.", - "name": "signing_secret", - "no_dynamic": false, - "scalars_to_literal": false, - "type": "string" - } - ] - }, - "status": "stable", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_rs384(\"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJSUzM4NCIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.orcXYBcjVE5DU7mvq4KKWFfNdXR4nEY_xupzWoETRpYmQZIozlZnM_nHxEk2dySvpXlAzVm7kgOPK2RFtGlOVaNRIa3x-pMMr-bhZTno4L8Hl4sYxOks3bWtjK7wql4uqUbqThSJB12psAXw2-S-I_FMngOPGIn4jDT9b802ottJSvTpXcy0-eKTjrV2PSkRRu-EYJh0CJZW55MNhqlt6kCGhAXfbhNazN3ASX-dmpd_JixyBKphrngr_zRA-FCn_Xf3QQDA-5INopb4Yp5QiJ7UxVqQEKI80X_JvJqz9WE1qiAw8pq5-xTen1t7zTP-HT1NbbD3kltcNa3G8acmNg\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs384(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], + "impure": false, + "name": "sign_jwt_rs384", + "params": { + "named": [ + { + "description": "The secret to use for signing the token.", + "name": "signing_secret", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" + } + ] + }, + "status": "stable", "version": "v4.18.0" }, { @@ -6381,11 +9813,46 @@ ], "skip_testing": true, "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs512(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" } ] } ], "description": "Hash and sign an object representing JSON Web Token (JWT) claims using RS512.", + "examples": [ + { + "mapping": "root.signed = this.claims.sign_jwt_rs512(\"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\")", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE1MTYyMzkwMjIsIm1vb2QiOiJEaXNkYWluZnVsIiwic3ViIjoiMTIzNDU2Nzg5MCJ9.rsMp_X5HMrUqKnZJIxo27aAoscovRA6SSQYR9rq7pifIj0YHXxMyNyOBDGnvVALHKTi25VUGHpfNUW0VVMmae0A4t_ObNU6hVZHguWvetKZZq4FZpW1lgWHCMqgPGwT5_uOqwYCH6r8tJuZT3pqXeL0CY4putb1AN2w6CVp620nh3l8d3XWb4jaifycd_4CEVCqHuWDmohfug4VhmoVKlIXZkYoAQowgHlozATDssBSWdYtv107Wd2AzEoiXPu6e3pflsuXULlyqQnS4ELEKPYThFLafh1NqvZDPddqozcPZ-iODBW-xf3A4DYDdivnMYLrh73AZOGHexxu8ay6nDA\"}" + ] + ], + "skip_testing": true, + "summary": "" + }, + { + "mapping": "root.signed = this.claims.sign_jwt_rs512(signing_secret: \"\"\"-----BEGIN RSA PRIVATE KEY-----\n... signature data ...\n-----END RSA PRIVATE KEY-----\"\"\", headers: {\"kid\": \"my-key\", \"x\": \"y\"})", + "results": [ + [ + "{\"claims\":{\"sub\":\"user123\"}}", + "{\"signed\":\"\"}" + ] + ], + "skip_testing": true, + "summary": "" + } + ], "impure": false, "name": "sign_jwt_rs512", "params": { @@ -6396,6 +9863,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "description": "Optional object of JWT header fields to include in the token. Keys \"alg\", \"typ\", \"jku\", \"jwk\", \"x5u\", \"x5c\", \"x5t\",\"x5t#S256\" and \"crit\" will be ignored if provided.", + "is_optional": true, + "name": "headers", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "unknown" } ] }, @@ -6431,6 +9906,27 @@ } ], "description": "Calculates the sine of a given angle specified in radians.", + "examples": [ + { + "mapping": "root.new_value = (this.value * (pi() / 180)).sin()", + "results": [ + [ + "{\"value\":45}", + "{\"new_value\":0.7071067811865475}" + ], + [ + "{\"value\":0}", + "{\"new_value\":0}" + ], + [ + "{\"value\":90}", + "{\"new_value\":1}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "sin", "params": {}, @@ -6495,6 +9991,53 @@ ] } ], + "description": "Extracts a portion of an array or string", + "examples": [ + { + "mapping": "root.beginning = this.value.slice(0, 2)\nroot.end = this.value.slice(4)", + "results": [ + [ + "{\"value\":\"foo bar\"}", + "{\"beginning\":\"fo\",\"end\":\"bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.last_chunk = this.value.slice(-4)\nroot.the_rest = this.value.slice(0, -4)", + "results": [ + [ + "{\"value\":\"foo bar\"}", + "{\"last_chunk\":\" bar\",\"the_rest\":\"foo\"}" + ] + ], + "skip_testing": false, + "summary": "A negative low index can be used, indicating an offset from the end of the sequence. If the low index is greater than the length of the sequence then an empty result is returned." + }, + { + "mapping": "root.beginning = this.value.slice(0, 2)\nroot.end = this.value.slice(4)", + "results": [ + [ + "{\"value\":[\"foo\",\"bar\",\"baz\",\"buz\",\"bev\"]}", + "{\"beginning\":[\"foo\",\"bar\"],\"end\":[\"bev\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.last_chunk = this.value.slice(-2)\nroot.the_rest = this.value.slice(0, -2)", + "results": [ + [ + "{\"value\":[\"foo\",\"bar\",\"baz\",\"buz\",\"bev\"]}", + "{\"last_chunk\":[\"buz\",\"bev\"],\"the_rest\":[\"foo\",\"bar\",\"baz\"]}" + ] + ], + "skip_testing": false, + "summary": "A negative low index can be used, indicating an offset from the end of the sequence. If the low index is greater than the length of the sequence then an empty result is returned." + } + ], "impure": false, "name": "slice", "params": { @@ -6525,31 +10068,55 @@ "Description": "", "Examples": [ { - "mapping": "root.slug = this.value.slug()", + "mapping": "root.slug = this.title.slug()", "results": [ [ - "{\"value\":\"Gopher & Benthos\"}", - "{\"slug\":\"gopher-and-benthos\"}" + "{\"title\":\"Hello World! Welcome to Redpanda Connect\"}", + "{\"slug\":\"hello-world-welcome-to-redpanda-connect\"}" ] ], "skip_testing": false, - "summary": "Creates a slug from an English string" + "summary": "Create a URL-friendly slug from a string with special characters" }, { - "mapping": "root.slug = this.value.slug(\"fr\")", + "mapping": "root.slug = this.title.slug(\"fr\")", "results": [ [ - "{\"value\":\"Gaufre & Poisson d'Eau Profonde\"}", - "{\"slug\":\"gaufre-et-poisson-deau-profonde\"}" + "{\"title\":\"Café & Restaurant\"}", + "{\"slug\":\"cafe-et-restaurant\"}" ] ], "skip_testing": false, - "summary": "Creates a slug from a French string" + "summary": "Create a slug preserving French language rules" } ] } ], - "description": "Creates a \"slug\" from a given string. Wraps the github.com/gosimple/slug package. See its https://pkg.go.dev/github.com/gosimple/slug[docs^] for more information.", + "description": "Converts a string into a URL-friendly slug by replacing spaces with hyphens, removing special characters, and converting to lowercase. Supports multiple languages for proper transliteration of non-ASCII characters.", + "examples": [ + { + "mapping": "root.slug = this.title.slug()", + "results": [ + [ + "{\"title\":\"Hello World! Welcome to Redpanda Connect\"}", + "{\"slug\":\"hello-world-welcome-to-redpanda-connect\"}" + ] + ], + "skip_testing": false, + "summary": "Create a URL-friendly slug from a string with special characters" + }, + { + "mapping": "root.slug = this.title.slug(\"fr\")", + "results": [ + [ + "{\"title\":\"Café & Restaurant\"}", + "{\"slug\":\"cafe-et-restaurant\"}" + ] + ], + "skip_testing": false, + "summary": "Create a slug preserving French language rules" + } + ], "impure": false, "name": "slug", "params": { @@ -6564,14 +10131,14 @@ } ] }, - "status": "beta", + "status": "stable", "version": "4.2.0" }, { "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Attempts to sort the values of an array in increasing order. The type of all values must match in order for the ordering to succeed. Supports string and number values.", + "Description": "Sorts an array in ascending order. Works with strings and numbers. For custom sorting logic, provide a comparison query that receives 'left' and 'right' elements.", "Examples": [ { "mapping": "root.sorted = this.foo.sort()", @@ -6593,11 +10160,36 @@ ] ], "skip_testing": false, - "summary": "It's also possible to specify a mapping argument, which is provided an object context with fields `left` and `right`, the mapping must return a boolean indicating whether the `left` value is less than `right`. This allows you to sort arrays containing non-string or non-number values." + "summary": "Custom comparison for complex objects - return true if left < right" } ] } ], + "description": "Sorts array elements in ascending order", + "examples": [ + { + "mapping": "root.sorted = this.foo.sort()", + "results": [ + [ + "{\"foo\":[\"bbb\",\"ccc\",\"aaa\"]}", + "{\"sorted\":[\"aaa\",\"bbb\",\"ccc\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.sorted = this.foo.sort(item -> item.left.v < item.right.v)", + "results": [ + [ + "{\"foo\":[{\"id\":\"foo\",\"v\":\"bbb\"},{\"id\":\"bar\",\"v\":\"ccc\"},{\"id\":\"baz\",\"v\":\"aaa\"}]}", + "{\"sorted\":[{\"id\":\"baz\",\"v\":\"aaa\"},{\"id\":\"foo\",\"v\":\"bbb\"},{\"id\":\"bar\",\"v\":\"ccc\"}]}" + ] + ], + "skip_testing": false, + "summary": "Custom comparison for complex objects - return true if left < right" + } + ], "impure": false, "name": "sort", "params": { @@ -6618,7 +10210,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Attempts to sort the elements of an array, in increasing order, by a value emitted by an argument query applied to each element. The type of all values must match in order for the ordering to succeed. Supports string and number values.", + "Description": "Sorts an array by a value extracted from each element using a query. The extracted values determine sort order and must all be strings or numbers.", "Examples": [ { "mapping": "root.sorted = this.foo.sort_by(ele -> ele.id)", @@ -6630,10 +10222,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.sorted = this.items.sort_by(item -> item.priority)", + "results": [ + [ + "{\"items\":[{\"name\":\"low\",\"priority\":3},{\"name\":\"high\",\"priority\":1},{\"name\":\"med\",\"priority\":2}]}", + "{\"sorted\":[{\"name\":\"high\",\"priority\":1},{\"name\":\"med\",\"priority\":2},{\"name\":\"low\",\"priority\":3}]}" + ] + ], + "skip_testing": false, + "summary": "Sort by numeric field" } ] } ], + "description": "Sorts array elements by a specified field or expression", + "examples": [ + { + "mapping": "root.sorted = this.foo.sort_by(ele -> ele.id)", + "results": [ + [ + "{\"foo\":[{\"id\":\"bbb\",\"message\":\"bar\"},{\"id\":\"aaa\",\"message\":\"foo\"},{\"id\":\"ccc\",\"message\":\"baz\"}]}", + "{\"sorted\":[{\"id\":\"aaa\",\"message\":\"foo\"},{\"id\":\"bbb\",\"message\":\"bar\"},{\"id\":\"ccc\",\"message\":\"baz\"}]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.sorted = this.items.sort_by(item -> item.priority)", + "results": [ + [ + "{\"items\":[{\"name\":\"low\",\"priority\":3},{\"name\":\"high\",\"priority\":1},{\"name\":\"med\",\"priority\":2}]}", + "{\"sorted\":[{\"name\":\"high\",\"priority\":1},{\"name\":\"med\",\"priority\":2},{\"name\":\"low\",\"priority\":3}]}" + ] + ], + "skip_testing": false, + "summary": "Sort by numeric field" + } + ], "impure": false, "name": "sort_by", "params": { @@ -6653,7 +10281,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Split a string value into an array of strings by splitting it on a string separator.", + "Description": "Splits a string into an array of substrings using a delimiter. Use for parsing CSV-like data, splitting paths, or breaking text into tokens.", "Examples": [ { "mapping": "root.new_value = this.value.split(\",\")", @@ -6665,10 +10293,68 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.new_value = this.value.split(\",\", true)", + "results": [ + [ + "{\"value\":\"foo,,qux\"}", + "{\"new_value\":[\"foo\",null,\"qux\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.words = this.sentence.split(\" \")", + "results": [ + [ + "{\"sentence\":\"hello world from bloblang\"}", + "{\"words\":[\"hello\",\"world\",\"from\",\"bloblang\"]}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Splits a string into an array of substrings", + "examples": [ + { + "mapping": "root.new_value = this.value.split(\",\")", + "results": [ + [ + "{\"value\":\"foo,bar,baz\"}", + "{\"new_value\":[\"foo\",\"bar\",\"baz\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.new_value = this.value.split(\",\", true)", + "results": [ + [ + "{\"value\":\"foo,,qux\"}", + "{\"new_value\":[\"foo\",null,\"qux\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.words = this.sentence.split(\" \")", + "results": [ + [ + "{\"sentence\":\"hello world from bloblang\"}", + "{\"words\":[\"hello\",\"world\",\"from\",\"bloblang\"]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "split", "params": { @@ -6679,6 +10365,14 @@ "no_dynamic": false, "scalars_to_literal": false, "type": "string" + }, + { + "default": false, + "description": "To treat empty substrings as null values", + "name": "empty_as_null", + "no_dynamic": false, + "scalars_to_literal": false, + "type": "bool" } ] }, @@ -6705,6 +10399,19 @@ } ], "description": "Squashes an array of objects into a single object, where key collisions result in the values being merged (following similar rules as the `.merge()` method)", + "examples": [ + { + "mapping": "root.locations = this.locations.map_each(loc -> {loc.state: [loc.name]}).squash()", + "results": [ + [ + "{\"locations\":[{\"name\":\"Seattle\",\"state\":\"WA\"},{\"name\":\"New York\",\"state\":\"NY\"},{\"name\":\"Bellevue\",\"state\":\"WA\"},{\"name\":\"Olympia\",\"state\":\"WA\"}]}", + "{\"locations\":{\"NY\":[\"New York\"],\"WA\":[\"Seattle\",\"Bellevue\",\"Olympia\"]}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "squash", "params": {}, @@ -6714,7 +10421,7 @@ "categories": [ { "Category": "Type Coercion", - "Description": "Marshal a value into a string. If the value is already a string it is unchanged.", + "Description": "Converts any value to its string representation. Numbers, booleans, and objects are converted to strings; existing strings are unchanged. Use for type coercion or creating string representations.", "Examples": [ { "mapping": "root.nested_json = this.string()", @@ -6741,6 +10448,31 @@ ] } ], + "description": "Converts a value to a string representation", + "examples": [ + { + "mapping": "root.nested_json = this.string()", + "results": [ + [ + "{\"foo\":\"bar\"}", + "{\"nested_json\":\"{\\\"foo\\\":\\\"bar\\\"}\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.id = this.id.string()", + "results": [ + [ + "{\"id\":228930314431312345}", + "{\"id\":\"228930314431312345\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "string", "params": {}, @@ -6753,37 +10485,61 @@ "Description": "", "Examples": [ { - "mapping": "root.stripped = this.value.strip_html()", + "mapping": "root.plain_text = this.html_content.strip_html()", "results": [ [ - "{\"value\":\"

the plain old text

\"}", - "{\"stripped\":\"the plain old text\"}" + "{\"html_content\":\"

Welcome to Redpanda Connect!

\"}", + "{\"plain_text\":\"Welcome to Redpanda Connect!\"}" ] ], "skip_testing": false, - "summary": "" + "summary": "Extract plain text from HTML content" }, { - "mapping": "root.stripped = this.value.strip_html([\"article\"])", + "mapping": "root.sanitized = this.html.strip_html([\"strong\", \"em\"])", "results": [ [ - "{\"value\":\"

the plain old text

\"}", - "{\"stripped\":\"
the plain old text
\"}" + "{\"html\":\"

Some bold and italic text with a

\"}", + "{\"sanitized\":\"Some bold and italic text with a \"}" ] ], "skip_testing": false, - "summary": "It's also possible to provide an explicit list of element types to preserve in the output." + "summary": "Preserve specific HTML elements while removing others" } ] } ], - "description": "Attempts to remove all HTML tags from a target string.", + "description": "Removes HTML tags from a string, returning only the text content. Useful for extracting plain text from HTML documents, sanitizing user input, or preparing content for text analysis. Optionally preserves specific HTML elements while stripping all others.", + "examples": [ + { + "mapping": "root.plain_text = this.html_content.strip_html()", + "results": [ + [ + "{\"html_content\":\"

Welcome to Redpanda Connect!

\"}", + "{\"plain_text\":\"Welcome to Redpanda Connect!\"}" + ] + ], + "skip_testing": false, + "summary": "Extract plain text from HTML content" + }, + { + "mapping": "root.sanitized = this.html.strip_html([\"strong\", \"em\"])", + "results": [ + [ + "{\"html\":\"

Some bold and italic text with a

\"}", + "{\"sanitized\":\"Some bold and italic text with a \"}" + ] + ], + "skip_testing": false, + "summary": "Preserve specific HTML elements while removing others" + } + ], "impure": false, "name": "strip_html", "params": { "named": [ { - "description": "An optional array of element types to preserve in the output.", + "description": "Optional array of HTML element names to preserve (e.g., [\"strong\", \"em\", \"a\"]). All other HTML tags will be removed.", "is_optional": true, "name": "preserve", "no_dynamic": false, @@ -6798,7 +10554,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Sum the numerical values of an array.", + "Description": "Calculates the sum of all numeric values in an array. Non-numeric values cause an error.", "Examples": [ { "mapping": "root.sum = this.foo.sum()", @@ -6810,10 +10566,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.total = this.prices.sum()", + "results": [ + [ + "{\"prices\":[10.5,20.25,5.00]}", + "{\"total\":35.75}" + ] + ], + "skip_testing": false, + "summary": "Works with decimals" } ] } ], + "description": "Returns the sum of numeric values in an array", + "examples": [ + { + "mapping": "root.sum = this.foo.sum()", + "results": [ + [ + "{\"foo\":[3,8,4]}", + "{\"sum\":15}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.total = this.prices.sum()", + "results": [ + [ + "{\"prices\":[10.5,20.25,5.00]}", + "{\"total\":35.75}" + ] + ], + "skip_testing": false, + "summary": "Works with decimals" + } + ], "impure": false, "name": "sum", "params": {}, @@ -6848,6 +10640,27 @@ } ], "description": "Calculates the tangent of a given angle specified in radians.", + "examples": [ + { + "mapping": "root.new_value = \"%f\".format((this.value * (pi() / 180)).tan())", + "results": [ + [ + "{\"value\":0}", + "{\"new_value\":\"0.000000\"}" + ], + [ + "{\"value\":45}", + "{\"new_value\":\"1.000000\"}" + ], + [ + "{\"value\":180}", + "{\"new_value\":\"-0.000000\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "tan", "params": {}, @@ -6868,6 +10681,15 @@ ] } ], + "description": "Converts a value to a timestamp with optional fallback", + "examples": [ + { + "mapping": "root.foo = this.ts.timestamp()\nroot.bar = this.none.timestamp(1234567890.timestamp())", + "results": [], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "timestamp", "params": { @@ -6888,7 +10710,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Remove all leading and trailing characters from a string that are contained within an argument cutset. If no arguments are provided then whitespace is removed.", + "Description": "Removes leading and trailing characters from a string. Without arguments, removes whitespace. With a cutset argument, removes any characters in the cutset. Use for cleaning user input or normalizing strings.", "Examples": [ { "mapping": "root.title = this.title.trim(\"!?\")\nroot.description = this.description.trim()", @@ -6904,6 +10726,20 @@ ] } ], + "description": "Removes leading and trailing characters from a string", + "examples": [ + { + "mapping": "root.title = this.title.trim(\"!?\")\nroot.description = this.description.trim()", + "results": [ + [ + "{\"description\":\" something happened and its amazing! \",\"title\":\"!!!watch out!?\"}", + "{\"description\":\"something happened and its amazing!\",\"title\":\"watch out\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "trim", "params": { @@ -6924,7 +10760,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Remove the provided leading prefix substring from a string. If the string does not have the prefix substring, it is returned unchanged.", + "Description": "Removes a specified prefix from the beginning of a string if present. If the string doesn't start with the prefix, returns the string unchanged. Use for stripping known prefixes from identifiers or paths.", "Examples": [ { "mapping": "root.name = this.name.trim_prefix(\"foobar_\")\nroot.description = this.description.trim_prefix(\"foobar_\")", @@ -6940,6 +10776,20 @@ ] } ], + "description": "Removes a specified prefix from the beginning of a string", + "examples": [ + { + "mapping": "root.name = this.name.trim_prefix(\"foobar_\")\nroot.description = this.description.trim_prefix(\"foobar_\")", + "results": [ + [ + "{\"description\":\"unchanged\",\"name\":\"foobar_blobton\"}", + "{\"description\":\"unchanged\",\"name\":\"blobton\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "trim_prefix", "params": { @@ -6960,7 +10810,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Remove the provided trailing suffix substring from a string. If the string does not have the suffix substring, it is returned unchanged.", + "Description": "Removes a specified suffix from the end of a string if present. If the string doesn't end with the suffix, returns the string unchanged. Use for stripping file extensions or known suffixes.", "Examples": [ { "mapping": "root.name = this.name.trim_suffix(\"_foobar\")\nroot.description = this.description.trim_suffix(\"_foobar\")", @@ -6976,6 +10826,20 @@ ] } ], + "description": "Removes a specified suffix from the end of a string", + "examples": [ + { + "mapping": "root.name = this.name.trim_suffix(\"_foobar\")\nroot.description = this.description.trim_suffix(\"_foobar\")", + "results": [ + [ + "{\"description\":\"unchanged\",\"name\":\"blobton_foobar\"}", + "{\"description\":\"unchanged\",\"name\":\"blobton\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "trim_suffix", "params": { @@ -6997,16 +10861,63 @@ { "Category": "Timestamp Manipulation", "Description": "", - "Examples": null + "Examples": [ + { + "mapping": "root.next_year = this.created_at.ts_add_iso8601(\"P1Y\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"next_year\":\"2021-08-14T05:54:23Z\"}" + ] + ], + "skip_testing": false, + "summary": "Add one year to a timestamp." + }, + { + "mapping": "root.future_date = this.created_at.ts_add_iso8601(\"P1Y2M3DT4H5M6S\")", + "results": [ + [ + "{\"created_at\":\"2020-01-01T00:00:00Z\"}", + "{\"future_date\":\"2021-03-04T04:05:06Z\"}" + ] + ], + "skip_testing": false, + "summary": "Add a complex duration with multiple units." + } + ] + } + ], + "description": "Adds an ISO 8601 duration to a timestamp with calendar-aware precision for years, months, and days. Useful when you need to add durations that account for variable month lengths or leap years.", + "examples": [ + { + "mapping": "root.next_year = this.created_at.ts_add_iso8601(\"P1Y\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"next_year\":\"2021-08-14T05:54:23Z\"}" + ] + ], + "skip_testing": false, + "summary": "Add one year to a timestamp." + }, + { + "mapping": "root.future_date = this.created_at.ts_add_iso8601(\"P1Y2M3DT4H5M6S\")", + "results": [ + [ + "{\"created_at\":\"2020-01-01T00:00:00Z\"}", + "{\"future_date\":\"2021-03-04T04:05:06Z\"}" + ] + ], + "skip_testing": false, + "summary": "Add a complex duration with multiple units." } ], - "description": "Parse parameter string as ISO 8601 period and add it to value with high precision for units larger than an hour.", "impure": false, "name": "ts_add_iso8601", "params": { "named": [ { - "description": "Duration in ISO 8601 format", + "description": "Duration in ISO 8601 format (e.g., \"P1Y2M3D\" for 1 year, 2 months, 3 days)", "name": "duration", "no_dynamic": false, "scalars_to_literal": false, @@ -7023,65 +10934,69 @@ "Description": "", "Examples": [ { - "mapping": "root.something_at = (this.created_at + 300).ts_format()", - "results": [], - "skip_testing": false, - "summary": "" - }, - { - "mapping": "root.something_at = (this.created_at + 300).ts_format(\"2006-Jan-02 15:04:05\")", - "results": [], - "skip_testing": false, - "summary": "An optional string argument can be used in order to specify the output format of the timestamp. The format is defined by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would be displayed if it were the value." - }, - { - "mapping": "root.something_at = this.created_at.ts_format(format: \"2006-Jan-02 15:04:05\", tz: \"UTC\")", + "mapping": "root.something_at = this.created_at.ts_format(\"2006-Jan-02 15:04:05\")", "results": [ - [ - "{\"created_at\":1597405526}", - "{\"something_at\":\"2020-Aug-14 11:45:26\"}" - ], [ "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", "{\"something_at\":\"2020-Aug-14 11:50:26\"}" ] ], "skip_testing": false, - "summary": "A second optional string argument can also be used in order to specify a timezone, otherwise the timezone of the input string is used, or in the case of unix timestamps the local timezone is used." + "summary": "Format timestamp with custom format." }, { - "mapping": "root.something_at = this.created_at.ts_format(\"2006-Jan-02 15:04:05.999999\", \"UTC\")", + "mapping": "root.something_at = this.created_at.ts_format(format: \"2006-Jan-02 15:04:05\", tz: \"UTC\")", "results": [ [ - "{\"created_at\":1597405526.123456}", - "{\"something_at\":\"2020-Aug-14 11:45:26.123456\"}" - ], - [ - "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", - "{\"something_at\":\"2020-Aug-14 11:50:26.371\"}" + "{\"created_at\":1597405526}", + "{\"something_at\":\"2020-Aug-14 11:45:26\"}" ] ], "skip_testing": false, - "summary": "And `ts_format` supports up to nanosecond precision with floating point timestamp values." + "summary": "Format unix timestamp with timezone specification." } ] } ], - "description": "Attempts to format a timestamp value as a string according to a specified format, or RFC 3339 by default. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format.\n\nThe output format is defined by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would be displayed if it were the value. For an alternative way to specify formats check out the <> method.", + "description": "Formats a timestamp as a string using Go's reference time format. Defaults to RFC 3339 if no format specified. The format uses \"Mon Jan 2 15:04:05 -0700 MST 2006\" as a reference. Accepts unix timestamps (with decimal precision) or RFC 3339 strings. Use ts_strftime for strftime-style formats.", + "examples": [ + { + "mapping": "root.something_at = this.created_at.ts_format(\"2006-Jan-02 15:04:05\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", + "{\"something_at\":\"2020-Aug-14 11:50:26\"}" + ] + ], + "skip_testing": false, + "summary": "Format timestamp with custom format." + }, + { + "mapping": "root.something_at = this.created_at.ts_format(format: \"2006-Jan-02 15:04:05\", tz: \"UTC\")", + "results": [ + [ + "{\"created_at\":1597405526}", + "{\"something_at\":\"2020-Aug-14 11:45:26\"}" + ] + ], + "skip_testing": false, + "summary": "Format unix timestamp with timezone specification." + } + ], "impure": false, "name": "ts_format", "params": { "named": [ { "default": "2006-01-02T15:04:05.999999999Z07:00", - "description": "The output format to use.", + "description": "The output format using Go's reference time.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, "type": "string" }, { - "description": "An optional timezone to use, otherwise the timezone of the input string is used, or in the case of unix timestamps the local timezone is used.", + "description": "Optional timezone (e.g., 'UTC', 'America/New_York'). Defaults to input timezone or local time for unix timestamps.", "is_optional": true, "name": "tz", "no_dynamic": false, @@ -7107,18 +11022,53 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Parse a date with abbreviated month name." + }, + { + "mapping": "root.parsed = this.timestamp.ts_parse(\"Jan 2, 2006 at 3:04pm (MST)\")", + "results": [ + [ + "{\"timestamp\":\"Aug 14, 2020 at 5:54am (UTC)\"}", + "{\"parsed\":\"2020-08-14T05:54:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Parse a custom datetime format." } ] } ], - "description": "Attempts to parse a string as a timestamp following a specified format and outputs a timestamp, which can then be fed into methods such as <>.\n\nThe input format is defined by showing how the reference time, defined to be Mon Jan 2 15:04:05 -0700 MST 2006, would be displayed if it were the value. For an alternative way to specify formats check out the <> method.", + "description": "Parses a timestamp string using Go's reference time format and outputs a timestamp object. The format uses \"Mon Jan 2 15:04:05 -0700 MST 2006\" as a reference - show how this reference time would appear in your format. Use ts_strptime for strftime-style formats instead.", + "examples": [ + { + "mapping": "root.doc.timestamp = this.doc.timestamp.ts_parse(\"2006-Jan-02\")", + "results": [ + [ + "{\"doc\":{\"timestamp\":\"2020-Aug-14\"}}", + "{\"doc\":{\"timestamp\":\"2020-08-14T00:00:00Z\"}}" + ] + ], + "skip_testing": false, + "summary": "Parse a date with abbreviated month name." + }, + { + "mapping": "root.parsed = this.timestamp.ts_parse(\"Jan 2, 2006 at 3:04pm (MST)\")", + "results": [ + [ + "{\"timestamp\":\"Aug 14, 2020 at 5:54am (UTC)\"}", + "{\"parsed\":\"2020-08-14T05:54:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Parse a custom datetime format." + } + ], "impure": false, "name": "ts_parse", "params": { "named": [ { - "description": "The format of the target string.", + "description": "The format of the input string using Go's reference time.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, @@ -7143,12 +11093,47 @@ ] ], "skip_testing": false, - "summary": "Use the method `parse_duration` to convert a duration string into an integer argument." + "summary": "Round timestamp to the nearest hour." + }, + { + "mapping": "root.created_at_minute = this.created_at.ts_round(\"1m\".parse_duration())", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"created_at_minute\":\"2020-08-14T05:54:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Round timestamp to the nearest minute." } ] } ], - "description": "Returns the result of rounding a timestamp to the nearest multiple of the argument duration (nanoseconds). The rounding behavior for halfway values is to round up. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Rounds a timestamp to the nearest multiple of the specified duration. Halfway values round up. Accepts unix timestamps (seconds with optional decimal precision) or RFC 3339 formatted strings.", + "examples": [ + { + "mapping": "root.created_at_hour = this.created_at.ts_round(\"1h\".parse_duration())", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"created_at_hour\":\"2020-08-14T06:00:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Round timestamp to the nearest hour." + }, + { + "mapping": "root.created_at_minute = this.created_at.ts_round(\"1m\".parse_duration())", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"created_at_minute\":\"2020-08-14T05:54:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Round timestamp to the nearest minute." + } + ], "impure": false, "name": "ts_round", "params": { @@ -7172,58 +11157,68 @@ "Description": "", "Examples": [ { - "mapping": "root.something_at = (this.created_at + 300).ts_strftime(\"%Y-%b-%d %H:%M:%S\")", - "results": [], - "skip_testing": false, - "summary": "The format consists of zero or more conversion specifiers and ordinary characters (except `%`). All ordinary characters are copied to the output string without modification. Each conversion specification begins with `%` character followed by the character that determines the behavior of the specifier. Please refer to https://linux.die.net/man/3/strftime[man 3 strftime] for the list of format specifiers." - }, - { - "mapping": "root.something_at = this.created_at.ts_strftime(\"%Y-%b-%d %H:%M:%S\", \"UTC\")", + "mapping": "root.something_at = this.created_at.ts_strftime(\"%Y-%b-%d %H:%M:%S\")", "results": [ - [ - "{\"created_at\":1597405526}", - "{\"something_at\":\"2020-Aug-14 11:45:26\"}" - ], [ "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", "{\"something_at\":\"2020-Aug-14 11:50:26\"}" ] ], "skip_testing": false, - "summary": "A second optional string argument can also be used in order to specify a timezone, otherwise the timezone of the input string is used, or in the case of unix timestamps the local timezone is used." + "summary": "Format timestamp with strftime specifiers." }, { "mapping": "root.something_at = this.created_at.ts_strftime(\"%Y-%b-%d %H:%M:%S.%f\", \"UTC\")", "results": [ - [ - "{\"created_at\":1597405526}", - "{\"something_at\":\"2020-Aug-14 11:45:26.000000\"}" - ], [ "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", "{\"something_at\":\"2020-Aug-14 11:50:26.371000\"}" ] ], "skip_testing": false, - "summary": "As an extension provided by the underlying formatting library, https://github.com/itchyny/timefmt-go[itchyny/timefmt-go], the `%f` directive is supported for zero-padded microseconds, which originates from Python. Note that E and O modifier characters are not supported." + "summary": "Format with microseconds using %f directive." } ] } ], - "description": "Attempts to format a timestamp value as a string according to a specified strftime-compatible format. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format.", + "description": "Formats a timestamp as a string using strptime format specifiers (like %Y, %m, %d). Accepts unix timestamps (with decimal precision) or RFC 3339 strings. Supports %f for microseconds. Use ts_format for Go-style reference time formats.", + "examples": [ + { + "mapping": "root.something_at = this.created_at.ts_strftime(\"%Y-%b-%d %H:%M:%S\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", + "{\"something_at\":\"2020-Aug-14 11:50:26\"}" + ] + ], + "skip_testing": false, + "summary": "Format timestamp with strftime specifiers." + }, + { + "mapping": "root.something_at = this.created_at.ts_strftime(\"%Y-%b-%d %H:%M:%S.%f\", \"UTC\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T11:50:26.371Z\"}", + "{\"something_at\":\"2020-Aug-14 11:50:26.371000\"}" + ] + ], + "skip_testing": false, + "summary": "Format with microseconds using %f directive." + } + ], "impure": false, "name": "ts_strftime", "params": { "named": [ { - "description": "The output format to use.", + "description": "The output format using strptime specifiers.", "name": "format", "no_dynamic": false, "scalars_to_literal": false, "type": "string" }, { - "description": "An optional timezone to use, otherwise the timezone of the input string is used.", + "description": "Optional timezone. Defaults to input timezone or local time for unix timestamps.", "is_optional": true, "name": "tz", "no_dynamic": false, @@ -7249,7 +11244,7 @@ ] ], "skip_testing": false, - "summary": "The format consists of zero or more conversion specifiers and ordinary characters (except `%`). All ordinary characters are copied to the output string without modification. Each conversion specification begins with a `%` character followed by the character that determines the behavior of the specifier. Please refer to https://linux.die.net/man/3/strptime[man 3 strptime] for the list of format specifiers." + "summary": "Parse date with abbreviated month using strptime format." }, { "mapping": "root.doc.timestamp = this.doc.timestamp.ts_strptime(\"%Y-%b-%d %H:%M:%S.%f\")", @@ -7260,18 +11255,42 @@ ] ], "skip_testing": false, - "summary": "As an extension provided by the underlying formatting library, https://github.com/itchyny/timefmt-go[itchyny/timefmt-go], the `%f` directive is supported for zero-padded microseconds, which originates from Python. Note that E and O modifier characters are not supported." + "summary": "Parse datetime with microseconds using %f directive." } ] } ], - "description": "Attempts to parse a string as a timestamp following a specified strptime-compatible format and outputs a timestamp, which can then be fed into <>.", + "description": "Parses a timestamp string using strptime format specifiers (like %Y, %m, %d) and outputs a timestamp object. Use ts_parse for Go-style reference time formats instead.", + "examples": [ + { + "mapping": "root.doc.timestamp = this.doc.timestamp.ts_strptime(\"%Y-%b-%d\")", + "results": [ + [ + "{\"doc\":{\"timestamp\":\"2020-Aug-14\"}}", + "{\"doc\":{\"timestamp\":\"2020-08-14T00:00:00Z\"}}" + ] + ], + "skip_testing": false, + "summary": "Parse date with abbreviated month using strptime format." + }, + { + "mapping": "root.doc.timestamp = this.doc.timestamp.ts_strptime(\"%Y-%b-%d %H:%M:%S.%f\")", + "results": [ + [ + "{\"doc\":{\"timestamp\":\"2020-Aug-14 11:50:26.371000\"}}", + "{\"doc\":{\"timestamp\":\"2020-08-14T11:50:26.371Z\"}}" + ] + ], + "skip_testing": false, + "summary": "Parse datetime with microseconds using %f directive." + } + ], "impure": false, "name": "ts_strptime", "params": { "named": [ { - "description": "The format of the target string.", + "description": "The format string using strptime specifiers (e.g., %Y-%m-%d).", "name": "format", "no_dynamic": false, "scalars_to_literal": false, @@ -7296,18 +11315,53 @@ ] ], "skip_testing": false, - "summary": "Use the `.abs()` method in order to calculate an absolute duration between two timestamps." + "summary": "Calculate absolute duration between two timestamps." + }, + { + "mapping": "root.duration_ns = this.end_time.ts_sub(this.start_time)", + "results": [ + [ + "{\"start_time\":\"2020-08-14T10:00:00Z\",\"end_time\":\"2020-08-14T11:30:00Z\"}", + "{\"duration_ns\":5400000000000}" + ] + ], + "skip_testing": false, + "summary": "Calculate signed duration (can be negative)." } ] } ], - "description": "Returns the difference in nanoseconds between the target timestamp (t1) and the timestamp provided as a parameter (t2). The <> method can be used in order to parse different timestamp formats.", - "impure": false, + "description": "Calculates the duration in nanoseconds between two timestamps (t1 - t2). Returns a signed integer: positive if t1 is after t2, negative if t1 is before t2. Use .abs() for absolute duration.", + "examples": [ + { + "mapping": "root.between = this.started_at.ts_sub(\"2020-08-14T05:54:23Z\").abs()", + "results": [ + [ + "{\"started_at\":\"2020-08-13T05:54:23Z\"}", + "{\"between\":86400000000000}" + ] + ], + "skip_testing": false, + "summary": "Calculate absolute duration between two timestamps." + }, + { + "mapping": "root.duration_ns = this.end_time.ts_sub(this.start_time)", + "results": [ + [ + "{\"start_time\":\"2020-08-14T10:00:00Z\",\"end_time\":\"2020-08-14T11:30:00Z\"}", + "{\"duration_ns\":5400000000000}" + ] + ], + "skip_testing": false, + "summary": "Calculate signed duration (can be negative)." + } + ], + "impure": false, "name": "ts_sub", "params": { "named": [ { - "description": "The second timestamp to be subtracted from the method target.", + "description": "The timestamp to subtract from the target timestamp.", "name": "t2", "no_dynamic": false, "scalars_to_literal": false, @@ -7323,16 +11377,63 @@ { "Category": "Timestamp Manipulation", "Description": "", - "Examples": null + "Examples": [ + { + "mapping": "root.last_year = this.created_at.ts_sub_iso8601(\"P1Y\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"last_year\":\"2019-08-14T05:54:23Z\"}" + ] + ], + "skip_testing": false, + "summary": "Subtract one year from a timestamp." + }, + { + "mapping": "root.past_date = this.created_at.ts_sub_iso8601(\"P1Y2M3DT4H5M6S\")", + "results": [ + [ + "{\"created_at\":\"2021-03-04T04:05:06Z\"}", + "{\"past_date\":\"2020-01-01T00:00:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Subtract a complex duration with multiple units." + } + ] + } + ], + "description": "Subtracts an ISO 8601 duration from a timestamp with calendar-aware precision for years, months, and days. Useful when you need to subtract durations that account for variable month lengths or leap years.", + "examples": [ + { + "mapping": "root.last_year = this.created_at.ts_sub_iso8601(\"P1Y\")", + "results": [ + [ + "{\"created_at\":\"2020-08-14T05:54:23Z\"}", + "{\"last_year\":\"2019-08-14T05:54:23Z\"}" + ] + ], + "skip_testing": false, + "summary": "Subtract one year from a timestamp." + }, + { + "mapping": "root.past_date = this.created_at.ts_sub_iso8601(\"P1Y2M3DT4H5M6S\")", + "results": [ + [ + "{\"created_at\":\"2021-03-04T04:05:06Z\"}", + "{\"past_date\":\"2020-01-01T00:00:00Z\"}" + ] + ], + "skip_testing": false, + "summary": "Subtract a complex duration with multiple units." } ], - "description": "Parse parameter string as ISO 8601 period and subtract it from value with high precision for units larger than an hour.", "impure": false, "name": "ts_sub_iso8601", "params": { "named": [ { - "description": "Duration in ISO 8601 format", + "description": "Duration in ISO 8601 format (e.g., \"P1Y2M3D\" for 1 year, 2 months, 3 days)", "name": "duration", "no_dynamic": false, "scalars_to_literal": false, @@ -7357,18 +11458,53 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Convert timestamp to UTC timezone." + }, + { + "mapping": "root.created_at_ny = this.created_at.ts_tz(\"America/New_York\")", + "results": [ + [ + "{\"created_at\":\"2021-02-03T16:05:06Z\"}", + "{\"created_at_ny\":\"2021-02-03T11:05:06-05:00\"}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to a specific timezone." } ] } ], - "description": "Returns the result of converting a timestamp to a specified timezone. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a different timezone while preserving the moment in time. Accepts unix timestamps (seconds with optional decimal precision) or RFC 3339 formatted strings.", + "examples": [ + { + "mapping": "root.created_at_utc = this.created_at.ts_tz(\"UTC\")", + "results": [ + [ + "{\"created_at\":\"2021-02-03T17:05:06+01:00\"}", + "{\"created_at_utc\":\"2021-02-03T16:05:06Z\"}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to UTC timezone." + }, + { + "mapping": "root.created_at_ny = this.created_at.ts_tz(\"America/New_York\")", + "results": [ + [ + "{\"created_at\":\"2021-02-03T16:05:06Z\"}", + "{\"created_at_ny\":\"2021-02-03T11:05:06-05:00\"}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to a specific timezone." + } + ], "impure": false, "name": "ts_tz", "params": { "named": [ { - "description": "The timezone to change to. If set to \"UTC\" then the timezone will be UTC. If set to \"Local\" then the local timezone will be used. Otherwise, the argument is taken to be a location name corresponding to a file in the IANA Time Zone database, such as \"America/New_York\".", + "description": "The timezone to change to. Use \"UTC\" for UTC, \"Local\" for local timezone, or an IANA Time Zone database location name like \"America/New_York\".", "name": "tz", "no_dynamic": false, "scalars_to_literal": false, @@ -7394,12 +11530,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Convert RFC 3339 timestamp to unix seconds." + }, + { + "mapping": "root.timestamp = this.ts.ts_unix()", + "results": [ + [ + "{\"ts\":1257894000}", + "{\"timestamp\":1257894000}" + ] + ], + "skip_testing": false, + "summary": "Unix timestamp passthrough returns same value." } ] } ], - "description": "Attempts to format a timestamp value as a unix timestamp. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp (seconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing seconds.", + "examples": [ + { + "mapping": "root.created_at_unix = this.created_at.ts_unix()", + "results": [ + [ + "{\"created_at\":\"2009-11-10T23:00:00Z\"}", + "{\"created_at_unix\":1257894000}" + ] + ], + "skip_testing": false, + "summary": "Convert RFC 3339 timestamp to unix seconds." + }, + { + "mapping": "root.timestamp = this.ts.ts_unix()", + "results": [ + [ + "{\"ts\":1257894000}", + "{\"timestamp\":1257894000}" + ] + ], + "skip_testing": false, + "summary": "Unix timestamp passthrough returns same value." + } + ], "impure": false, "name": "ts_unix", "params": {}, @@ -7420,12 +11591,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Convert timestamp to microseconds since epoch." + }, + { + "mapping": "root.precise_time = this.timestamp.ts_unix_micro()", + "results": [ + [ + "{\"timestamp\":\"2020-08-14T11:45:26.123456Z\"}", + "{\"precise_time\":1597405526123456}" + ] + ], + "skip_testing": false, + "summary": "Preserve microsecond precision from timestamp." } ] } ], - "description": "Attempts to format a timestamp value as a unix timestamp with microsecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with microsecond precision (microseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing microseconds.", + "examples": [ + { + "mapping": "root.created_at_unix = this.created_at.ts_unix_micro()", + "results": [ + [ + "{\"created_at\":\"2009-11-10T23:00:00Z\"}", + "{\"created_at_unix\":1257894000000000}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to microseconds since epoch." + }, + { + "mapping": "root.precise_time = this.timestamp.ts_unix_micro()", + "results": [ + [ + "{\"timestamp\":\"2020-08-14T11:45:26.123456Z\"}", + "{\"precise_time\":1597405526123456}" + ] + ], + "skip_testing": false, + "summary": "Preserve microsecond precision from timestamp." + } + ], "impure": false, "name": "ts_unix_micro", "params": {}, @@ -7446,12 +11652,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Convert timestamp to milliseconds since epoch." + }, + { + "mapping": "root.js_timestamp = this.event_time.ts_unix_milli()", + "results": [ + [ + "{\"event_time\":\"2020-08-14T11:45:26.123Z\"}", + "{\"js_timestamp\":1597405526123}" + ] + ], + "skip_testing": false, + "summary": "Useful for JavaScript timestamp compatibility." } ] } ], - "description": "Attempts to format a timestamp value as a unix timestamp with millisecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with millisecond precision (milliseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing milliseconds.", + "examples": [ + { + "mapping": "root.created_at_unix = this.created_at.ts_unix_milli()", + "results": [ + [ + "{\"created_at\":\"2009-11-10T23:00:00Z\"}", + "{\"created_at_unix\":1257894000000}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to milliseconds since epoch." + }, + { + "mapping": "root.js_timestamp = this.event_time.ts_unix_milli()", + "results": [ + [ + "{\"event_time\":\"2020-08-14T11:45:26.123Z\"}", + "{\"js_timestamp\":1597405526123}" + ] + ], + "skip_testing": false, + "summary": "Useful for JavaScript timestamp compatibility." + } + ], "impure": false, "name": "ts_unix_milli", "params": {}, @@ -7472,12 +11713,47 @@ ] ], "skip_testing": false, - "summary": "" + "summary": "Convert timestamp to nanoseconds since epoch." + }, + { + "mapping": "root.precise_time = this.timestamp.ts_unix_nano()", + "results": [ + [ + "{\"timestamp\":\"2020-08-14T11:45:26.123456789Z\"}", + "{\"precise_time\":1597405526123456789}" + ] + ], + "skip_testing": false, + "summary": "Preserve full nanosecond precision." } ] } ], - "description": "Attempts to format a timestamp value as a unix timestamp with nanosecond precision. Timestamp values can either be a numerical unix time in seconds (with up to nanosecond precision via decimals), or a string in RFC 3339 format. The <> method can be used in order to parse different timestamp formats.", + "description": "Converts a timestamp to a unix timestamp with nanosecond precision (nanoseconds since epoch). Accepts unix timestamps or RFC 3339 strings. Returns an integer representing nanoseconds.", + "examples": [ + { + "mapping": "root.created_at_unix = this.created_at.ts_unix_nano()", + "results": [ + [ + "{\"created_at\":\"2009-11-10T23:00:00Z\"}", + "{\"created_at_unix\":1257894000000000000}" + ] + ], + "skip_testing": false, + "summary": "Convert timestamp to nanoseconds since epoch." + }, + { + "mapping": "root.precise_time = this.timestamp.ts_unix_nano()", + "results": [ + [ + "{\"timestamp\":\"2020-08-14T11:45:26.123456789Z\"}", + "{\"precise_time\":1597405526123456789}" + ] + ], + "skip_testing": false, + "summary": "Preserve full nanosecond precision." + } + ], "impure": false, "name": "ts_unix_nano", "params": {}, @@ -7556,6 +11832,73 @@ ] } ], + "description": "Returns the type of a value as a string", + "examples": [ + { + "mapping": "root.bar_type = this.bar.type()\nroot.foo_type = this.foo.type()", + "results": [ + [ + "{\"bar\":10,\"foo\":\"is a string\"}", + "{\"bar_type\":\"number\",\"foo_type\":\"string\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.type = this.type()", + "results": [ + [ + "\"foobar\"", + "{\"type\":\"string\"}" + ], + [ + "666", + "{\"type\":\"number\"}" + ], + [ + "false", + "{\"type\":\"bool\"}" + ], + [ + "[\"foo\", \"bar\"]", + "{\"type\":\"array\"}" + ], + [ + "{\"foo\": \"bar\"}", + "{\"type\":\"object\"}" + ], + [ + "null", + "{\"type\":\"null\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.type = content().type()", + "results": [ + [ + "foobar", + "{\"type\":\"bytes\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.type = this.ts_parse(\"2006-01-02\").type()", + "results": [ + [ + "\"2022-06-06\"", + "{\"type\":\"timestamp\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "type", "params": {}, @@ -7593,6 +11936,30 @@ } ], "description": "\nConverts a numerical type into a 16-bit unsigned integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 16-bit unsigned integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.uint16()\nroot.b = this.b.round().uint16()\nroot.c = this.c.uint16()\nroot.d = this.d.uint16().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":0}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.uint16()\n", + "results": [ + [ + "\"0xDE\"", + "222" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uint16", "params": {}, @@ -7630,6 +11997,30 @@ } ], "description": "\nConverts a numerical type into a 32-bit unsigned integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 32-bit unsigned integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.uint32()\nroot.b = this.b.round().uint32()\nroot.c = this.c.uint32()\nroot.d = this.d.uint32().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":0}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.uint32()\n", + "results": [ + [ + "\"0xDEAD\"", + "57005" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uint32", "params": {}, @@ -7667,6 +12058,30 @@ } ], "description": "\nConverts a numerical type into a 64-bit unsigned integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 64-bit unsigned integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.uint64()\nroot.b = this.b.round().uint64()\nroot.c = this.c.uint64()\nroot.d = this.d.uint64().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":0}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.uint64()\n", + "results": [ + [ + "\"0xDEADBEEF\"", + "3735928559" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uint64", "params": {}, @@ -7704,6 +12119,30 @@ } ], "description": "\nConverts a numerical type into a 8-bit unsigned integer, this is for advanced use cases where a specific data type is needed for a given component (such as the ClickHouse SQL driver).\n\nIf the value is a string then an attempt will be made to parse it as a 8-bit unsigned integer. If the target value exceeds the capacity of an integer or contains decimal values then this method will throw an error. In order to convert a floating point number containing decimals first use <> on the value. Please refer to the https://pkg.go.dev/strconv#ParseInt[`strconv.ParseInt` documentation] for details regarding the supported formats.", + "examples": [ + { + "mapping": "\nroot.a = this.a.uint8()\nroot.b = this.b.round().uint8()\nroot.c = this.c.uint8()\nroot.d = this.d.uint8().catch(0)\n", + "results": [ + [ + "{\"a\":12,\"b\":12.34,\"c\":\"12\",\"d\":-12}", + "{\"a\":12,\"b\":12,\"c\":12,\"d\":0}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "\nroot = this.uint8()\n", + "results": [ + [ + "\"0xD\"", + "13" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uint8", "params": {}, @@ -7713,7 +12152,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Unescapes a string so that entities like `<` become `<`. It unescapes a larger range of entities than `escape_html` escapes. For example, `á` unescapes to `á`, as does `á` and `&xE1;`.", + "Description": "Converts HTML entities back to their original characters. Handles named entities (`&`, `<`), decimal (`á`), and hexadecimal (`&xE1;`) formats. Use for processing HTML content or decoding HTML-escaped data.", "Examples": [ { "mapping": "root.unescaped = this.value.unescape_html()", @@ -7725,10 +12164,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.text = this.html.unescape_html()", + "results": [ + [ + "{\"html\":\"<p>Hello & goodbye</p>\"}", + "{\"text\":\"

Hello & goodbye

\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Converts HTML entities back to their original characters", + "examples": [ + { + "mapping": "root.unescaped = this.value.unescape_html()", + "results": [ + [ + "{\"value\":\"foo & bar\"}", + "{\"unescaped\":\"foo & bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.text = this.html.unescape_html()", + "results": [ + [ + "{\"html\":\"<p>Hello & goodbye</p>\"}", + "{\"text\":\"

Hello & goodbye

\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "unescape_html", "params": {}, @@ -7738,24 +12213,60 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Expands escape sequences from a URL query string.", + "Description": "Decodes URL path percent-encoding, converting `%20` to spaces and other percent-encoded characters to their original values. Use for parsing URL path segments.", "Examples": [ { - "mapping": "root.unescaped = this.value.unescape_url_query()", + "mapping": "root.unescaped = this.value.unescape_url_path()", "results": [ [ - "{\"value\":\"foo+%26+bar\"}", + "{\"value\":\"foo%20&%20bar\"}", "{\"unescaped\":\"foo & bar\"}" ] ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.filename = this.path.unescape_url_path()", + "results": [ + [ + "{\"path\":\"my%20document.pdf\"}", + "{\"filename\":\"my document.pdf\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Unescapes URL path encoding", + "examples": [ + { + "mapping": "root.unescaped = this.value.unescape_url_path()", + "results": [ + [ + "{\"value\":\"foo%20&%20bar\"}", + "{\"unescaped\":\"foo & bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.filename = this.path.unescape_url_path()", + "results": [ + [ + "{\"path\":\"my%20document.pdf\"}", + "{\"filename\":\"my document.pdf\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, - "name": "unescape_url_query", + "name": "unescape_url_path", "params": {}, "status": "stable" }, @@ -7763,50 +12274,125 @@ "categories": [ { "Category": "String Manipulation", - "Description": "", + "Description": "Decodes URL query parameter encoding, converting `+` to spaces and percent-encoded characters to their original values. Use for parsing URL query parameters.", "Examples": [ { - "mapping": "root.sentences = this.value.unicode_segments(\"sentence\")", + "mapping": "root.unescaped = this.value.unescape_url_query()", "results": [ [ - "{\"value\":\"This is sentence 1.0. And this is sentence two.\"}", - "{\"sentences\":[\"This is sentence 1.0. \",\"And this is sentence two.\"]}" + "{\"value\":\"foo+%26+bar\"}", + "{\"unescaped\":\"foo & bar\"}" ] ], "skip_testing": false, - "summary": "Splits a string into different sentences" + "summary": "" }, { - "mapping": "root.graphemes = this.value.unicode_segments(\"grapheme\")", + "mapping": "root.search = this.param.unescape_url_query()", + "results": [ + [ + "{\"param\":\"hello+world%21\"}", + "{\"search\":\"hello world!\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ] + } + ], + "description": "Unescapes URL query parameter encoding", + "examples": [ + { + "mapping": "root.unescaped = this.value.unescape_url_query()", + "results": [ + [ + "{\"value\":\"foo+%26+bar\"}", + "{\"unescaped\":\"foo & bar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.search = this.param.unescape_url_query()", + "results": [ + [ + "{\"param\":\"hello+world%21\"}", + "{\"search\":\"hello world!\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], + "impure": false, + "name": "unescape_url_query", + "params": {}, + "status": "stable" + }, + { + "categories": [ + { + "Category": "String Manipulation", + "Description": "", + "Examples": [ + { + "mapping": "root.sentences = this.text.unicode_segments(\"sentence\")", "results": [ [ - "{\"value\":\"🐕‍🦺 🫠\"}", - "{\"graphemes\":[\"🐕‍🦺\",\" \",\"🫠\"]}" + "{\"text\":\"Hello world. How are you?\"}", + "{\"sentences\":[\"Hello world. \",\"How are you?\"]}" ] ], "skip_testing": false, - "summary": "Splits a string into different graphemes" + "summary": "Split text into sentences (preserves trailing spaces)" }, { - "mapping": "root.words = this.value.unicode_segments(\"word\")", + "mapping": "root.graphemes = this.emoji.unicode_segments(\"grapheme\")", "results": [ [ - "{\"value\":\"Hello, world!\"}", - "{\"words\":[\"Hello\",\",\",\" \",\"world\",\"!\"]}" + "{\"emoji\":\"👨‍👩‍👧‍👦❤️\"}", + "{\"graphemes\":[\"👨‍👩‍👧‍👦\",\"❤️\"]}" ] ], "skip_testing": false, - "summary": "Splits text into words" + "summary": "Split text into grapheme clusters (handles complex emoji correctly)" } ] } ], - "description": "Splits text into segments from a given string based on the unicode text segmentation rules.", + "description": "Splits text into segments based on Unicode text segmentation rules. Returns an array of strings representing individual graphemes (visual characters), words (including punctuation and whitespace), or sentences. Handles complex Unicode correctly, including emoji with skin tone modifiers and zero-width joiners.", + "examples": [ + { + "mapping": "root.sentences = this.text.unicode_segments(\"sentence\")", + "results": [ + [ + "{\"text\":\"Hello world. How are you?\"}", + "{\"sentences\":[\"Hello world. \",\"How are you?\"]}" + ] + ], + "skip_testing": false, + "summary": "Split text into sentences (preserves trailing spaces)" + }, + { + "mapping": "root.graphemes = this.emoji.unicode_segments(\"grapheme\")", + "results": [ + [ + "{\"emoji\":\"👨‍👩‍👧‍👦❤️\"}", + "{\"graphemes\":[\"👨‍👩‍👧‍👦\",\"❤️\"]}" + ] + ], + "skip_testing": false, + "summary": "Split text into grapheme clusters (handles complex emoji correctly)" + } + ], "impure": false, "name": "unicode_segments", "params": { "named": [ { + "description": "Type of segmentation: \"grapheme\", \"word\", or \"sentence\"", "name": "segmentation_type", "no_dynamic": false, "scalars_to_literal": false, @@ -7814,13 +12400,13 @@ } ] }, - "status": "beta" + "status": "stable" }, { "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Attempts to remove duplicate values from an array. The array may contain a combination of different value types, but numbers and strings are checked separately (`\"5\"` is a different element to `5`).", + "Description": "Removes duplicate values from an array, keeping the first occurrence of each unique value. Strings and numbers are treated as distinct types (\"5\" differs from 5).", "Examples": [ { "mapping": "root.uniques = this.foo.unique()", @@ -7832,10 +12418,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.unique_users = this.users.unique(u -> u.id)", + "results": [ + [ + "{\"users\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"},{\"id\":1,\"name\":\"Alice Duplicate\"}]}", + "{\"unique_users\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]}" + ] + ], + "skip_testing": false, + "summary": "Use a query to determine uniqueness by a field" } ] } ], + "description": "Returns an array with duplicate elements removed", + "examples": [ + { + "mapping": "root.uniques = this.foo.unique()", + "results": [ + [ + "{\"foo\":[\"a\",\"b\",\"a\",\"c\"]}", + "{\"uniques\":[\"a\",\"b\",\"c\"]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.unique_users = this.users.unique(u -> u.id)", + "results": [ + [ + "{\"users\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"},{\"id\":1,\"name\":\"Alice Duplicate\"}]}", + "{\"unique_users\":[{\"id\":1,\"name\":\"Alice\"},{\"id\":2,\"name\":\"Bob\"}]}" + ] + ], + "skip_testing": false, + "summary": "Use a query to determine uniqueness by a field" + } + ], "impure": false, "name": "unique", "params": { @@ -7856,7 +12478,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Unquotes a target string, expanding any escape sequences (`\\t`, `\\n`, `\\xFF`, `\\u0100`) for control characters and non-printable characters.", + "Description": "Removes surrounding quotes and interprets escape sequences (`\\n`, `\\t`, etc.) to their literal characters. Use for parsing quoted string literals.", "Examples": [ { "mapping": "root.unquoted = this.thing.unquote()", @@ -7868,10 +12490,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.text = this.literal.unquote()", + "results": [ + [ + "{\"literal\":\"\\\"hello\\\\tworld\\\"\"}", + "{\"text\":\"hello\\tworld\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Removes surrounding quotes and interprets escape sequences", + "examples": [ + { + "mapping": "root.unquoted = this.thing.unquote()", + "results": [ + [ + "{\"thing\":\"\\\"foo\\\\nbar\\\"\"}", + "{\"unquoted\":\"foo\\nbar\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.text = this.literal.unquote()", + "results": [ + [ + "{\"literal\":\"\\\"hello\\\\tworld\\\"\"}", + "{\"text\":\"hello\\tworld\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "unquote", "params": {}, @@ -7881,7 +12539,7 @@ "categories": [ { "Category": "String Manipulation", - "Description": "Convert a string value into uppercase.", + "Description": "Converts all letters in a string to uppercase. Use for case-insensitive comparisons or formatting output.", "Examples": [ { "mapping": "root.foo = this.foo.uppercase()", @@ -7893,10 +12551,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.code = this.product_code.uppercase()", + "results": [ + [ + "{\"product_code\":\"abc-123\"}", + "{\"code\":\"ABC-123\"}" + ] + ], + "skip_testing": false, + "summary": "" } ] } ], + "description": "Converts all letters in a string to uppercase", + "examples": [ + { + "mapping": "root.foo = this.foo.uppercase()", + "results": [ + [ + "{\"foo\":\"hello world\"}", + "{\"foo\":\"HELLO WORLD\"}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.code = this.product_code.uppercase()", + "results": [ + [ + "{\"product_code\":\"abc-123\"}", + "{\"code\":\"ABC-123\"}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uppercase", "params": {}, @@ -7929,6 +12623,27 @@ ] } ], + "description": "Generates a version 5 UUID from a namespace and name", + "examples": [ + { + "mapping": "root.id = \"example\".uuid_v5()", + "results": [], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.id = \"example\".uuid_v5(\"x500\")", + "results": [], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.id = \"example\".uuid_v5(\"77f836b7-9f61-46c0-851e-9b6ca3535e69\")", + "results": [], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "uuid_v5", "params": { @@ -7949,7 +12664,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Returns the values of an object as an array. The order of the resulting array will be random.", + "Description": "Extracts all values from an object and returns them as an array. Order is not guaranteed unless the result is sorted.", "Examples": [ { "mapping": "root.foo_vals = this.foo.values().sort()", @@ -7961,41 +12676,91 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root.max = this.scores.values().sort().index(-1)", + "results": [ + [ + "{\"scores\":{\"player1\":85,\"player2\":92,\"player3\":78}}", + "{\"max\":92}" + ] + ], + "skip_testing": false, + "summary": "Find max value in object" } ] } ], - "impure": false, - "name": "values", - "params": {}, - "status": "stable" - }, - { - "categories": [ + "description": "Returns an array of all values from an object", + "examples": [ + { + "mapping": "root.foo_vals = this.foo.values().sort()", + "results": [ + [ + "{\"foo\":{\"bar\":1,\"baz\":2}}", + "{\"foo_vals\":[1,2]}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root.max = this.scores.values().sort().index(-1)", + "results": [ + [ + "{\"scores\":{\"player1\":85,\"player2\":92,\"player3\":78}}", + "{\"max\":92}" + ] + ], + "skip_testing": false, + "summary": "Find max value in object" + } + ], + "impure": false, + "name": "values", + "params": {}, + "status": "stable" + }, + { + "categories": [ { "Category": "SQL", "Description": "", "Examples": [ { - "mapping": "root.embeddings = [1.2, 0.6, 0.9].vector()", + "mapping": "root.embedding = this.embeddings.vector()\nroot.text = this.text", "results": [], - "skip_testing": false, - "summary": "Create a vector from an array literal" + "skip_testing": true, + "summary": "Convert embeddings array to vector for pgvector storage" }, { - "mapping": "root.embedding_vector = this.embedding_array.vector()", + "mapping": "root.doc_id = this.id\nroot.vector_embedding = this.model_output.map_each(num -> num.number()).vector()", "results": [], - "skip_testing": false, - "summary": "Create a vector from an array" + "skip_testing": true, + "summary": "Process ML model output into database-ready vector format" } ] } ], - "description": "Creates a vector from a given array of floating point numbers.\n\nThis vector can be inserted into various SQL databases if they have support for embeddings vectors (for example `pgvector`).", + "description": "Converts an array of numbers into a vector type suitable for insertion into SQL databases with vector/embedding support. This is commonly used with PostgreSQL's pgvector extension for storing and querying machine learning embeddings, enabling similarity search and vector operations in your database.", + "examples": [ + { + "mapping": "root.embedding = this.embeddings.vector()\nroot.text = this.text", + "results": [], + "skip_testing": true, + "summary": "Convert embeddings array to vector for pgvector storage" + }, + { + "mapping": "root.doc_id = this.id\nroot.vector_embedding = this.model_output.map_each(num -> num.number()).vector()", + "results": [], + "skip_testing": true, + "summary": "Process ML model output into database-ready vector format" + } + ], "impure": false, "name": "vector", "params": {}, - "status": "beta", + "status": "stable", "version": "4.33.0" }, { @@ -8019,6 +12784,19 @@ } ], "description": "Returns an object where all but one or more xref:configuration:field_paths.adoc[field path] arguments are removed. Each path specifies a specific field to be retained from the input object, allowing for nested fields.\n\nIf a key within a nested path does not exist then it is ignored.", + "examples": [ + { + "mapping": "root = this.with(\"inner.a\",\"inner.c\",\"d\")", + "results": [ + [ + "{\"inner\":{\"a\":\"first\",\"b\":\"second\",\"c\":\"third\"},\"d\":\"fourth\",\"e\":\"fifth\"}", + "{\"d\":\"fourth\",\"inner\":{\"a\":\"first\",\"c\":\"third\"}}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "with", "params": { @@ -8030,7 +12808,7 @@ "categories": [ { "Category": "Object & Array Manipulation", - "Description": "Returns an object where one or more xref:configuration:field_paths.adoc[field path] arguments are removed. Each path specifies a specific field to be deleted from the input object, allowing for nested fields.\n\nIf a key within a nested path does not exist or is not an object then it is not removed.", + "Description": "Removes specified fields from an object using dot-notation paths. Returns a new object with the fields removed. Non-existent paths are safely ignored.", "Examples": [ { "mapping": "root = this.without(\"inner.a\",\"inner.c\",\"d\")", @@ -8042,10 +12820,46 @@ ], "skip_testing": false, "summary": "" + }, + { + "mapping": "root = this.without(\"password\",\"ssn\",\"creditCard\")", + "results": [ + [ + "{\"username\":\"alice\",\"password\":\"secret\",\"email\":\"alice@example.com\",\"ssn\":\"123-45-6789\"}", + "{\"email\":\"alice@example.com\",\"username\":\"alice\"}" + ] + ], + "skip_testing": false, + "summary": "Remove sensitive fields" } ] } ], + "description": "Returns an object with specified keys removed", + "examples": [ + { + "mapping": "root = this.without(\"inner.a\",\"inner.c\",\"d\")", + "results": [ + [ + "{\"inner\":{\"a\":\"first\",\"b\":\"second\",\"c\":\"third\"},\"d\":\"fourth\",\"e\":\"fifth\"}", + "{\"e\":\"fifth\",\"inner\":{\"b\":\"second\"}}" + ] + ], + "skip_testing": false, + "summary": "" + }, + { + "mapping": "root = this.without(\"password\",\"ssn\",\"creditCard\")", + "results": [ + [ + "{\"username\":\"alice\",\"password\":\"secret\",\"email\":\"alice@example.com\",\"ssn\":\"123-45-6789\"}", + "{\"email\":\"alice@example.com\",\"username\":\"alice\"}" + ] + ], + "skip_testing": false, + "summary": "Remove sensitive fields" + } + ], "impure": false, "name": "without", "params": { @@ -8074,6 +12888,19 @@ } ], "description": "Zip an array value with one or more argument arrays. Each array must match in length.", + "examples": [ + { + "mapping": "root.foo = this.foo.zip(this.bar, this.baz)", + "results": [ + [ + "{\"foo\":[\"a\",\"b\",\"c\"],\"bar\":[1,2,3],\"baz\":[4,5,6]}", + "{\"foo\":[[\"a\",1,4],[\"b\",2,5],[\"c\",3,6]]}" + ] + ], + "skip_testing": false, + "summary": "" + } + ], "impure": false, "name": "zip", "params": { @@ -8206,51 +13033,6 @@ "summary": "Do not buffer messages. This is the default and most resilient configuration.", "type": "buffer" }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "description": "The path of the database file, which will be created if it does not already exist.", - "kind": "scalar", - "name": "path", - "type": "string" - }, - { - "description": "An optional list of processors to apply to messages before they are stored within the buffer. These processors are useful for compressing, archiving or otherwise reducing the data in size before it's stored on disk.", - "is_optional": true, - "kind": "array", - "name": "pre_processors", - "type": "processor" - }, - { - "description": "An optional list of processors to apply to messages after they are consumed from the buffer. These processors are useful for undoing any compression, archiving, etc that may have been done by your `pre_processors`.", - "is_optional": true, - "kind": "array", - "name": "post_processors", - "type": "processor" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nStored messages are then consumed as a stream from the database and deleted only once they are successfully sent at the output level. If the service is restarted Redpanda Connect will make a best attempt to finish delivering messages that are already read from the database, and when it starts again it will consume from the oldest message that has not yet been delivered.\n\n== Delivery guarantees\n\nMessages are not acknowledged at the input level until they have been added to the SQLite database, and they are not removed from the SQLite database until they have been successfully delivered. This means at-least-once delivery guarantees are preserved in cases where the service is shut down unexpectedly. However, since this process relies on interaction with the disk (wherever the SQLite DB is stored) these delivery guarantees are not resilient to disk corruption or loss.\n\n== Batching\n\nMessages that are logically batched at the point where they are added to the buffer will continue to be associated with that batch when they are consumed. This buffer is also more efficient when storing messages within batches, and therefore it is recommended to use batching at the input level in high-throughput use cases even if they are not required for processing.\n", - "examples": [ - { - "config": "\ninput:\n batched:\n child:\n sql_select:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n table: footable\n columns: [ '*' ]\n policy:\n count: 100\n period: 500ms\n\nbuffer:\n sqlite:\n path: ./foo.db\n post_processors:\n - split: {}\n", - "summary": "Batching at the input level greatly increases the throughput of this buffer. If logical batches aren't needed for processing add a xref:components:processors/split.adoc[`split` processor] to the `post_processors`.", - "title": "Batching for optimization" - } - ], - "name": "sqlite", - "plugin": true, - "status": "stable", - "summary": "Stores messages in an SQLite database and acknowledges them at the input level.", - "type": "buffer" - }, { "categories": [ "Windowing" @@ -8444,170 +13226,62 @@ { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "profile", - "type": "string" - }, - { - "description": "The ID of credentials to use.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "id", - "type": "string" - }, - { - "description": "The secret for the credentials being used.", - "is_advanced": true, - "is_optional": true, - "is_secret": true, - "kind": "scalar", - "name": "secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "token", + "name": "connect_timeout", "type": "string" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" - }, - { - "description": "A role ARN to assume.", + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "role", - "type": "string" + "name": "keep_alive", + "type": "object" }, { - "description": "An external ID to provide when assuming a role.", + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role_external_id", + "name": "tcp_user_timeout", "type": "string" } ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "description": "TCP socket configuration.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "credentials", + "name": "tcp", "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "A prefix can be specified to allow multiple cache types to share a single DynamoDB table. An optional TTL duration (`ttl`) and field\n(`ttl_key`) can be specified if the backing table has TTL enabled.\n\nStrong read consistency can be enabled using the `consistent_read` configuration field.", - "name": "aws_dynamodb", - "plugin": true, - "status": "stable", - "summary": "Stores key/value pairs as a single document in a DynamoDB table. The key is stored as a string value and used as the table hash key. The value is stored as\na binary value using the `data_key` field name.", - "type": "cache", - "version": "3.36.0" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The S3 bucket to store items in.", - "kind": "scalar", - "name": "bucket", - "type": "string" - }, - { - "default": "application/octet-stream", - "description": "The content type to set for each item.", - "kind": "scalar", - "name": "content_type", - "type": "string" - }, - { - "default": false, - "description": "Forces the client API to use path style URLs, which helps when connecting to custom endpoints.", - "is_advanced": true, - "kind": "scalar", - "name": "force_path_style_urls", - "type": "bool" - }, - { - "children": [ - { - "default": "1s", - "description": "The initial period to wait between retry attempts.", - "examples": [ - "50ms", - "1s" - ], - "is_advanced": true, - "kind": "scalar", - "name": "initial_interval", - "type": "string" - }, - { - "default": "5s", - "description": "The maximum period to wait between retry attempts", - "examples": [ - "5s", - "1m" - ], - "is_advanced": true, - "kind": "scalar", - "name": "max_interval", - "type": "string" - }, - { - "default": "30s", - "description": "The maximum overall period of time to spend on retry attempts before the request is aborted.", - "examples": [ - "1m", - "1h" - ], - "is_advanced": true, - "kind": "scalar", - "name": "max_elapsed_time", - "type": "string" - } - ], - "description": "Determine time intervals and cut offs for retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "retries", - "type": "object" - }, - { - "description": "The AWS region to target.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "region", - "type": "string" - }, - { - "description": "Allows you to specify a custom endpoint for the AWS API.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "endpoint", - "type": "string" }, { "children": [ @@ -8683,11 +13357,11 @@ "name": "", "type": "object" }, - "description": "It is not possible to atomically upload S3 objects exclusively when the target does not already exist, therefore this cache is not suitable for deduplication.", - "name": "aws_s3", + "description": "A prefix can be specified to allow multiple cache types to share a single DynamoDB table. An optional TTL duration (`ttl`) and field\n(`ttl_key`) can be specified if the backing table has TTL enabled.\n\nStrong read consistency can be enabled using the `consistent_read` configuration field.", + "name": "aws_dynamodb", "plugin": true, "status": "stable", - "summary": "Stores each item in an S3 bucket as a file, where an item ID is the path of the item within the bucket.", + "summary": "Stores key/value pairs as a single document in a DynamoDB table. The key is stored as a string value and used as the table hash key. The value is stored as\na binary value using the `data_key` field name.", "type": "cache", "version": "3.36.0" }, @@ -8696,133 +13370,228 @@ "config": { "children": [ { - "description": "Couchbase connection string.", - "examples": [ - "couchbase://localhost:11210" - ], + "description": "The S3 bucket to store items in.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "bucket", "type": "string" }, { - "description": "Username to connect to the cluster.", - "is_optional": true, + "default": "application/octet-stream", + "description": "The content type to set for each item.", "kind": "scalar", - "name": "username", + "name": "content_type", "type": "string" }, { - "description": "Password to connect to the cluster.", - "is_optional": true, - "is_secret": true, + "default": false, + "description": "Forces the client API to use path style URLs, which helps when connecting to custom endpoints.", + "is_advanced": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "force_path_style_urls", + "type": "bool" }, { - "description": "Couchbase bucket.", + "children": [ + { + "default": "1s", + "description": "The initial period to wait between retry attempts.", + "examples": [ + "50ms", + "1s" + ], + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "5s", + "description": "The maximum period to wait between retry attempts", + "examples": [ + "5s", + "1m" + ], + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": "30s", + "description": "The maximum overall period of time to spend on retry attempts before the request is aborted.", + "examples": [ + "1m", + "1h" + ], + "is_advanced": true, + "kind": "scalar", + "name": "max_elapsed_time", + "type": "string" + } + ], + "description": "Determine time intervals and cut offs for retry attempts.", + "is_advanced": true, "kind": "scalar", - "name": "bucket", - "type": "string" + "name": "retries", + "type": "object" }, { - "description": "Bucket collection.", + "description": "The AWS region to target.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "collection", + "name": "region", "type": "string" }, { - "description": "Bucket scope.", + "description": "Allows you to specify a custom endpoint for the AWS API.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "scope", + "name": "endpoint", "type": "string" }, { - "annotated_options": [ - [ - "json", - "JSONTranscoder implements the default transcoding behavior and applies JSON transcoding to all values. This will apply the following behavior to the value: binary ([]byte) -> error. default -> JSON value, JSON Flags." - ], - [ - "legacy", - "LegacyTranscoder implements the behavior for a backward-compatible transcoder. This transcoder implements behavior matching that of gocb v1.This will apply the following behavior to the value: binary ([]byte) -> binary bytes, Binary expectedFlags. string -> string bytes, String expectedFlags. default -> JSON value, JSON expectedFlags." - ], - [ - "raw", - "RawBinaryTranscoder implements passthrough behavior of raw binary data. This transcoder does not apply any serialization. This will apply the following behavior to the value: binary ([]byte) -> binary bytes, binary expectedFlags. default -> error." - ], - [ - "rawjson", - "RawJSONTranscoder implements passthrough behavior of JSON data. This transcoder does not apply any serialization. It will forward data across the network without incurring unnecessary parsing costs. This will apply the following behavior to the value: binary ([]byte) -> JSON bytes, JSON expectedFlags. string -> JSON bytes, JSON expectedFlags. default -> error." - ], - [ - "rawstring", - "RawStringTranscoder implements passthrough behavior of raw string data. This transcoder does not apply any serialization. This will apply the following behavior to the value: string -> string bytes, string expectedFlags. default -> error." - ] + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } ], - "default": "legacy", - "description": "Couchbase transcoder to use.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"json\": true,\n \"legacy\": true,\n \"raw\": true,\n \"rawjson\": true,\n \"rawstring\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "transcoder", - "type": "string" - }, - { - "default": "15s", - "description": "Operation timeout.", + "description": "TCP socket configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "timeout", - "type": "string" + "name": "tcp", + "type": "object" }, { - "description": "An optional default TTL to set for items, calculated from the moment the item is cached.", + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "default_ttl", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "couchbase", - "plugin": true, - "status": "experimental", - "summary": "Use a Couchbase instance as a cache.", - "type": "cache", - "version": "4.12.0" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The directory within which to store items.", - "kind": "scalar", - "name": "directory", - "type": "string" + "name": "credentials", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "This type currently offers no form of item expiry or garbage collection, and is intended to be used for development and debugging purposes only.", - "name": "file", + "description": "It is not possible to atomically upload S3 objects exclusively when the target does not already exist, therefore this cache is not suitable for deduplication.", + "name": "aws_s3", "plugin": true, "status": "stable", - "summary": "Stores each item in a directory as a file, where an item ID is the path relative to the configured directory.", - "type": "cache" + "summary": "Stores each item in an S3 bucket as a file, where an item ID is the path of the item within the bucket.", + "type": "cache", + "version": "3.36.0" }, { "categories": null, @@ -8858,7 +13627,7 @@ "description": "It is not possible to atomically upload cloud storage objects exclusively when the target does not already exist, therefore this cache is not suitable for deduplication.", "name": "gcp_cloud_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Use a Google Cloud Storage bucket as a cache.", "type": "cache" }, @@ -9150,7 +13919,7 @@ }, "name": "mongodb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Use a MongoDB instance as a cache.", "type": "cache", "version": "3.43.0" @@ -9407,6 +14176,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -9420,10 +14217,10 @@ "name": "", "type": "object" }, - "description": "== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_kv", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Cache key/values in a NATS key-value bucket.", "type": "cache", "version": "4.27.0" @@ -9487,6 +14284,15 @@ "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { @@ -9713,7 +14519,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -9866,6 +14672,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -9882,7 +14692,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -9938,6 +14748,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -10033,7 +14903,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -10056,6 +14926,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "description": "The topic to store data in.", "kind": "scalar", @@ -10075,10 +15005,10 @@ "name": "", "type": "object" }, - "description": "\nA cache that stores data in a Kafka topic.\n\nThis cache is useful for data that is written frequently and queried infreqently.\nReads of the cache require reading the entire topic partition, so if there is a need for frequent reads, it's recommended to put an in memory caching layer infront of this cache.\n\nTopics that are used as caches should be compacted so that reads are less expensive when they rescan the topic, as only the latest value is needed.\n\nThis cache does not support any special TTL mechanism, any TTL should be handled by the Kafka topic itself using data retention policies.\n", + "description": "\nA cache that stores data in a Kafka topic.\n\nThis cache is useful for data that is written frequently and queried infrequently.\nReads of the cache require reading the entire topic partition, so if there is a need for frequent reads, it's recommended to put an in memory caching layer in front of this cache.\n\nTopics that are used as caches should be compacted so that reads are less expensive when they rescan the topic, as only the latest value is needed.\n\nThis cache does not support any special TTL mechanism, any TTL should be handled by the Kafka topic itself using data retention policies.\n", "name": "redpanda", "plugin": true, - "status": "beta", + "status": "stable", "summary": "A Kafka cache using the https://github.com/twmb/franz-go[Franz Kafka client library^].", "type": "cache" }, @@ -10171,11 +15101,12 @@ { "description": "A database <> to use.", "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "driver", "options": [ "mysql", "postgres", + "pgx", "clickhouse", "mssql", "sqlite", @@ -10183,17 +15114,19 @@ "snowflake", "trino", "gocosmos", - "spanner" + "spanner", + "databricks" ], "type": "string" }, { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", "examples": [ "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", "foouser:foopassword@tcp(localhost:3306)/foodb", "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" ], "kind": "scalar", "name": "dsn", @@ -10309,7 +15242,7 @@ "description": "\nEach cache key/value pair will exist as a row within the specified table. Currently only the key and value columns are set, and therefore any other columns present within the target table must allow NULL values if this cache is going to be used for set and add operations.\n\nCache operations are translated into SQL statements as follows:\n\n== Get\n\nAll `get` operations are performed with a traditional `select` statement.\n\n== Delete\n\nAll `delete` operations are performed with a traditional `delete` statement.\n\n== Set\n\nThe `set` operation is performed with a traditional `insert` statement.\n\nThis will behave as an `add` operation by default, and so ideally needs to be adapted in order to provide updates instead of failing on collision\ts. Since different SQL engines implement upserts differently it is necessary to specify a `set_suffix` that modifies an `insert` statement in order to perform updates on conflict.\n\n== Add\n\nThe `add` operation is performed with a traditional `insert` statement.\n", "name": "sql", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Uses an SQL database table as a destination for storing cache key/value items.", "type": "cache", "version": "4.26.0" @@ -10519,9 +15452,7 @@ "type": "object" }, { - "default": { - "stdin": {} - }, + "default": {}, "description": "An input to source messages from.", "kind": "scalar", "name": "input", @@ -10559,9 +15490,7 @@ "type": "object" }, { - "default": { - "stdout": {} - }, + "default": {}, "description": "An output to sink messages to.", "kind": "scalar", "name": "output", @@ -10751,6 +15680,23 @@ "name": "shutdown_timeout", "type": "string" }, + { + "children": [ + { + "default": false, + "description": "When enabled, a processing error is terminal for the affected message: it skips the remaining processors in its pipeline and is rejected (nacked) at the output rather than written. To recover from an expected error, wrap the fallible step within a `try_catch` processor (or a `retry` processor for transient failures); a standalone `catch` does not recover messages under this mode, as the failed message short-circuits past it. This setting will become the default and only behaviour in the next major version.", + "is_advanced": true, + "kind": "scalar", + "name": "strict", + "type": "bool" + } + ], + "description": "Configures engine-wide error handling behaviour.", + "is_advanced": true, + "kind": "scalar", + "name": "error_handling", + "type": "object" + }, { "children": [ { @@ -11039,6 +15985,16 @@ "name": "tests", "type": "object" }, + { + "default": true, + "description": "Whether test cases should be executed in parallel.", + "is_advanced": true, + "is_deprecated": true, + "is_optional": true, + "kind": "scalar", + "name": "parallel", + "type": "bool" + }, { "children": [ { @@ -11060,7 +16016,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -11213,6 +16169,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -11229,7 +16189,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -11285,6 +16245,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -11380,7 +16400,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -11403,6 +16423,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "default": "", "description": "An optional identifier for the pipeline, this will be present in logs and status updates sent to topics.", @@ -11479,12 +16559,35 @@ }, { "default": true, - "description": "Enable the idempotent write producer option. This requires the `IDEMPOTENT_WRITE` permission on `CLUSTER` and can be disabled if this permission is not available.", + "description": "Enable the idempotent write producer option. When enabled, the producer initializes a producer ID and uses it to guarantee exactly-once semantics per partition (no duplicates on retries). This requires the `IDEMPOTENT_WRITE` permission on the `CLUSTER` resource. If your cluster does not grant this permission or uses ACLs restrictively, disable this option. Note: Idempotent writes are strictly a win for data integrity but may be unavailable in restricted environments (e.g., some managed Kafka services, Redpanda with strict ACLs). Disabling this option is safe and only affects retry behavior—duplicates may occur on producer retries, but the pipeline will continue to function normally.", "is_advanced": true, "kind": "scalar", "name": "idempotent_write", "type": "bool" }, + { + "annotated_options": [ + [ + "all", + "Wait for all in-sync replicas to acknowledge (acks=-1). Required when idempotent_write is enabled." + ], + [ + "leader", + "Wait for the leader broker to acknowledge (acks=1). Messages are lost if the leader fails before replication." + ], + [ + "none", + "Do not wait for any acknowledgement (acks=0). Highest throughput but messages may be lost." + ] + ], + "default": "all", + "description": "The number of acknowledgements the leader broker must receive from ISR brokers before responding to the produce request. When `idempotent_write` is enabled this must be set to `all`.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"all\": true,\n \"leader\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "acks", + "type": "string" + }, { "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", "is_advanced": true, @@ -11519,7 +16622,7 @@ }, { "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", + "description": "The maximum size of a produced record batch in bytes. A `MESSAGE_TOO_LARGE` error is returned if a batch exceeds this limit. This field maps to the `max.message.bytes` Kafka property. Ensure the Redpanda broker's `kafka_batch_max_bytes` property is at least as large as this value, see https://docs.redpanda.com/current/reference/properties/cluster-properties/#kafka_batch_max_bytes.", "examples": [ "100MB", "50mib" @@ -11540,6 +16643,50 @@ "kind": "scalar", "name": "broker_write_max_bytes", "type": "string" + }, + { + "default": 10000, + "description": "The maximum number of records the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered and space frees up. Increase this value for high-throughput pipelines to avoid back-pressure stalls.", + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_records", + "type": "int" + }, + { + "default": "0", + "description": "The maximum number of bytes the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered. Set to `0` to disable the byte-level limit (only `max_buffered_records` applies). This limit is checked after `max_buffered_records`.", + "examples": [ + "256MB", + "50mib" + ], + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_bytes", + "type": "string" + }, + { + "default": 1, + "description": "The maximum number of produce requests in flight per broker connection. When `idempotent_write` is enabled, this is capped at 5 by the Kafka protocol (and at 1 for Kafka < v1.0.0). When `idempotent_write` is disabled, higher values improve throughput by pipelining requests but may cause out-of-order delivery.", + "is_advanced": true, + "kind": "scalar", + "name": "max_in_flight_requests", + "type": "int" + }, + { + "default": 0, + "description": "The maximum number of times a record produce is retried on failure before the record is failed. When a record fails, all records buffered in the same partition are also failed to preserve gapless ordering. Set to `0` for unlimited retries (the default). With `idempotent_write` enabled, retries are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_retries", + "type": "int" + }, + { + "default": "0s", + "description": "The maximum time a record can sit in the producer buffer before it is failed, roughly equivalent to Kafka's `delivery.timeout.ms`. This is evaluated before writing a request or after a produce response. When a record times out, all records in the same partition are also failed. Set to `0s` for no timeout (the default). With `idempotent_write` enabled, timeouts are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_delivery_timeout", + "type": "string" } ], "kind": "scalar", @@ -11607,7 +16754,7 @@ "type": "bool" }, { - "description": "\nOptional arguments specific to the server's implementation of the queue that can be sent for queue types which require extra parameters.\n\n== Arguments\n\n- x-queue-type\n\nIs used to declare quorum and stream queues. Accepted values are: 'classic' (default), 'quorum', 'stream', 'drop-head', 'reject-publish' and 'reject-publish-dlx'.\n\n- x-max-length\n\nMaximum number of messages, is a non-negative integer value.\n\n- x-max-length-bytes\n\nMaximum number of messages, is a non-negative integer value.\n\n- x-overflow\n\nSets overflow behaviour. Possible values are: 'drop-head' (default), 'reject-publish', 'reject-publish-dlx'.\n\n- x-message-ttl\n\nTTL period in milliseconds. Must be a string representation of the number.\n\n- x-expires\n\nExpiration policy, describes the expiration period in milliseconds. Must be a positive integer.\n\n- x-max-age\n\nControls the retention of a stream. Must be a strin, valid units: (Y, M, D, h, m, s) e.g. '7D' for a week.\n\n- x-stream-max-segment-size-bytes\n\nControls the size of the segment files on disk (default 500000000). Must be a positive integer.\n\n- x-queue-version\n\ndeclares the Classic Queue version to use. Expects an integer, either 1 or 2.\n\n- x-consumer-timeout\n\nInteger specified in milliseconds.\n\n- x-single-active-consumer\n\nEnables Single Active Consumer, Expects a Boolean.\n\nSee https://github.com/rabbitmq/amqp091-go/blob/b3d409fe92c34bea04d8123a136384c85e8dc431/types.go#L282-L362 for more information on available arguments.", + "description": "\nOptional arguments specific to the server's implementation of the queue that can be sent for queue types which require extra parameters.\n\n== Arguments\n\n- x-queue-type\n\nIs used to declare quorum and stream queues. Accepted values are: 'classic' (default), 'quorum', 'stream', 'drop-head', 'reject-publish' and 'reject-publish-dlx'.\n\n- x-max-length\n\nMaximum number of messages, is a non-negative integer value.\n\n- x-max-length-bytes\n\nMaximum number of messages, is a non-negative integer value.\n\n- x-overflow\n\nSets overflow behaviour. Possible values are: 'drop-head' (default), 'reject-publish', 'reject-publish-dlx'.\n\n- x-message-ttl\n\nTTL period in milliseconds. Must be a string representation of the number.\n\n- x-expires\n\nExpiration policy, describes the expiration period in milliseconds. Must be a positive integer.\n\n- x-max-age\n\nControls the retention of a stream. Must be a string, valid units: (Y, M, D, h, m, s) e.g. '7D' for a week.\n\n- x-stream-max-segment-size-bytes\n\nControls the size of the segment files on disk (default 500000000). Must be a positive integer.\n\n- x-queue-version\n\ndeclares the Classic Queue version to use. Expects an integer, either 1 or 2.\n\n- x-consumer-timeout\n\nInteger specified in milliseconds.\n\n- x-single-active-consumer\n\nEnables Single Active Consumer, Expects a Boolean.\n\nSee https://github.com/rabbitmq/amqp091-go/blob/b3d409fe92c34bea04d8123a136384c85e8dc431/types.go#L282-L362 for more information on available arguments.", "examples": [ { "x-max-length": 1000, @@ -11851,282 +16998,261 @@ }, { "categories": [ - "Services" + "Services", + "AWS" ], "config": { "children": [ { - "description": "A URL to connect to.", + "description": "The name of the CloudWatch Log Group to consume from.", "examples": [ - "amqp://localhost:5672/", - "amqps://guest:guest@localhost:5672/" + "my-app-logs" ], - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "log_group_name", "type": "string" }, { - "description": "A list of URLs to connect to. The first URL to successfully establish a connection will be used until the connection is closed. If an item of the list contains commas it will be expanded into multiple URLs.", + "description": "An optional list of log stream names to consume from. If not set, events from all streams in the log group will be consumed.", "examples": [ [ - "amqp://guest:guest@127.0.0.1:5672/" - ], - [ - "amqp://127.0.0.1:5672/,amqp://127.0.0.2:5672/" - ], - [ - "amqp://127.0.0.1:5672/", - "amqp://127.0.0.2:5672/" + "stream-1", + "stream-2" ] ], "is_optional": true, "kind": "array", - "name": "urls", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string", - "version": "4.23.0" + "name": "log_stream_names", + "type": "string" }, { - "description": "The source address to consume from.", + "description": "An optional log stream name prefix to filter streams. Only streams starting with this prefix will be consumed.", "examples": [ - "/foo", - "queue:/bar", - "topic:/baz" + "prod-" ], + "is_optional": true, "kind": "scalar", - "name": "source_address", + "name": "log_stream_prefix", "type": "string" }, { - "default": false, - "description": "Experimental: Azure service bus specific option to renew lock if processing takes more then configured lock time", + "description": "An optional CloudWatch Logs filter pattern to apply when querying log events. See AWS documentation for filter pattern syntax.", + "examples": [ + "[ERROR]" + ], + "is_optional": true, + "kind": "scalar", + "name": "filter_pattern", + "type": "string" + }, + { + "description": "The time to start consuming log events from. Can be an RFC3339 timestamp (e.g., `2024-01-01T00:00:00Z`) or the string `now` to start consuming from the current time. If not set, starts from the beginning of available logs.", + "examples": [ + "2024-01-01T00:00:00Z", + "now" + ], + "is_optional": true, + "kind": "scalar", + "name": "start_time", + "type": "string" + }, + { + "default": "5s", + "description": "The interval at which to poll for new log events.", + "kind": "scalar", + "name": "poll_interval", + "type": "string" + }, + { + "default": 1000, + "description": "The maximum number of log events to return in a single API call. Valid range: 1-10000.", "is_advanced": true, "kind": "scalar", - "name": "azure_renew_lock", - "type": "bool", - "version": "3.45.0" + "linter": "root = if this < 1 || this > 10000 { [\"limit must be between 1 and 10000\"] }", + "name": "limit", + "type": "int" }, { - "default": false, - "description": "Read additional message header fields into `amqp_*` metadata properties.", + "default": true, + "description": "Whether to output log events as structured JSON objects with all metadata fields, or as plain text messages with metadata in message metadata.", "is_advanced": true, "kind": "scalar", - "name": "read_header", - "type": "bool", - "version": "4.25.0" + "name": "structured_log", + "type": "bool" }, { - "default": 64, - "description": "Specifies the maximum number of unacknowledged messages the sender can transmit. Once this limit is reached, no more messages will arrive until messages are acknowledged and settled.", + "default": "30s", + "description": "The maximum time to wait for an API request to complete.", "is_advanced": true, "kind": "scalar", - "linter": "root = if this < 1 { [ \"credit must be at least 1\" ] }", - "name": "credit", - "type": "int", - "version": "4.26.0" + "name": "api_timeout", + "type": "string" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", + "type": "bool" + }, + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Allows you to specify a custom endpoint for the AWS API.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", + "name": "idle", "type": "string" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "interval", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "count", + "type": "int" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], + "description": "TCP keep-alive probe configuration.", "is_advanced": true, - "kind": "array", - "name": "client_certs", + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "TCP socket configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "tcp", "type": "object" }, { "children": [ { - "annotated_options": [ - [ - "anonymous", - "Anonymous SASL authentication." - ], - [ - "none", - "No SASL based authentication." - ], - [ - "plain", - "Plain text SASL authentication." - ] - ], - "default": "none", - "description": "The SASL authentication mechanism to use.", + "description": "A profile from `~/.aws/credentials` to use.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"anonymous\": true,\n \"none\": true,\n \"plain\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "mechanism", + "name": "profile", "type": "string" }, { - "default": "", - "description": "A SASL plain text username. It is recommended that you use environment variables to populate this field.", - "examples": [ - "${USER}" - ], + "description": "The ID of credentials to use.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "user", + "name": "id", "type": "string" }, { - "default": "", - "description": "A SASL plain text password. It is recommended that you use environment variables to populate this field.", - "examples": [ - "${PASSWORD}" - ], + "description": "The secret for the credentials being used.", "is_advanced": true, + "is_optional": true, "is_secret": true, "kind": "scalar", - "name": "password", + "name": "secret", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" } ], - "description": "Enables SASL authentication.", + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "sasl", + "name": "credentials", "type": "object" } ], "kind": "scalar", - "linter": "\nroot = if this.url.or(\"\") == \"\" && this.urls.or([]).length() == 0 {\n \"field 'urls' must be set\"\n}\n", + "linter": "\nroot = if this.log_stream_names.or([]).length() > 0 && this.exists(\"log_stream_prefix\") {\n \"cannot specify both log_stream_names and log_stream_prefix\"\n}\n", "name": "", "type": "object" }, - "description": "== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- amqp_content_type\n- amqp_content_encoding\n- amqp_creation_time\n- All string typed message annotations\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\nBy setting `read_header` to `true`, additional message header properties will be added to each message:\n\n```text\n- amqp_durable\n- amqp_priority\n- amqp_ttl\n- amqp_first_acquirer\n- amqp_delivery_count\n```\n\n== Performance\n\nThis input benefits from receiving multiple messages in flight in parallel for improved performance.\nYou can tune the max number of in flight messages with the field `credit`.\n", - "name": "amqp_1", + "description": "\nPolls CloudWatch Log Groups for log events. Supports filtering by log streams, CloudWatch filter patterns, and configurable start times.\n\nEach log event becomes a separate message with metadata including the log group name, log stream name, timestamp, and ingestion time.\n\nIMPORTANT: This input tracks its position in memory only. If the process restarts, it will resume from the configured start_time (or the beginning if not set). For exactly-once processing, you should configure an appropriate start_time or implement idempotent downstream processing.\n\n## Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to AWS services. It's also possible to set them explicitly at the component level, allowing you to transfer data across accounts. You can find out more in xref:guides:cloud/aws.adoc[].\n\n## Metadata\n\nThis input adds the following metadata fields to each message:\n\n- `cloudwatch_log_group` - The name of the log group\n- `cloudwatch_log_stream` - The name of the log stream\n- `cloudwatch_timestamp` - The timestamp of the log event (Unix milliseconds)\n- `cloudwatch_ingestion_time` - The ingestion timestamp (Unix milliseconds)\n- `cloudwatch_event_id` - The unique event ID\n\nYou can access these metadata fields using xref:guides:bloblang/about.adoc[Bloblang].\n", + "name": "aws_cloudwatch_logs", "plugin": true, "status": "stable", - "summary": "Reads messages from an AMQP (1.0) server.", - "type": "input" + "summary": "Consumes log events from AWS CloudWatch Logs.", + "type": "input", + "version": "4.81.0" }, { "categories": [ @@ -12136,48 +17262,413 @@ "config": { "children": [ { - "description": "One or more Kinesis data streams to consume from. Streams can either be specified by their name or full ARN. Shards of a stream are automatically balanced across consumers by coordinating through the provided DynamoDB table. Multiple comma separated streams can be listed in a single element. Shards are automatically distributed across consumers of a stream by coordinating through the provided DynamoDB table. Alternatively, it's possible to specify an explicit shard to consume from with a colon after the stream name, e.g. `foo:0` would consume the shard `0` of the stream `foo`.", - "examples": [ - [ - "foo", - "arn:aws:kinesis:*:111122223333:stream/my-stream" - ] - ], + "default": [], + "description": "List of table names to stream from. For single table mode, provide one table. For multi-table mode, provide multiple tables.", "kind": "array", - "name": "streams", + "name": "tables", "type": "string" }, { - "children": [ - { - "default": "", - "description": "The name of the table to access.", - "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "default": false, - "description": "Whether, if the table does not exist, it should be created.", - "kind": "scalar", - "name": "create", - "type": "bool" - }, - { - "default": "PAY_PER_REQUEST", - "description": "When creating the table determines the billing mode.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"provisioned\": true,\n \"pay_per_request\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "billing_mode", - "options": [ - "PROVISIONED", - "PAY_PER_REQUEST" - ], - "type": "string" - }, - { - "default": 0, + "default": "single", + "description": "Table discovery mode. `single`: stream from tables specified in `tables` list. `tag`: auto-discover tables by tags (ignores `tables` field). `includelist`: stream from tables in `tables` list (alias for `single`, kept for compatibility).", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"single\": true,\n \"tag\": true,\n \"includelist\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "table_discovery_mode", + "options": [ + "single", + "tag", + "includelist" + ], + "type": "string" + }, + { + "default": "", + "description": "Multi-tag filter: 'key1:v1,v2;key2:v3,v4'. Matches tables with (key1=v1 OR key1=v2) AND (key2=v3 OR key2=v4). Required when `table_discovery_mode` is `tag`.", + "is_advanced": true, + "kind": "scalar", + "name": "table_tag_filter", + "type": "string" + }, + { + "default": "5m", + "description": "Interval for rescanning and discovering new tables when using `tag` or `includelist` mode. Set to 0 to disable periodic rescanning.", + "is_advanced": true, + "kind": "scalar", + "name": "table_discovery_interval", + "type": "string" + }, + { + "default": "redpanda_dynamodb_checkpoints", + "description": "DynamoDB table name for storing checkpoints. Will be created if it doesn't exist.", + "kind": "scalar", + "name": "checkpoint_table", + "type": "string" + }, + { + "default": false, + "description": "Provision the checkpoint table as a DynamoDB Global Table (v2) so checkpoints replicate across regions. Requires `global_table_replicas`. When the table is auto-created it is created as a global table; when it already exists, its replicas are reconciled (missing regions are added via `UpdateTable`). The existing table must have been created in global mode (`TableId` hash key) — enabling this against a pre-existing non-global checkpoint table fails fast with a clear error.", + "is_advanced": true, + "kind": "scalar", + "name": "global_table", + "type": "bool" + }, + { + "default": [], + "description": "Regions other than this pipeline's own region to replicate the checkpoint table to. The pipeline's own region is always included. Required when `global_table` is true. Applied both when the checkpoint table is created and, for an existing global table, when reconciling replicas (missing regions are added; this list is not used to remove regions).", + "is_advanced": true, + "kind": "array", + "name": "global_table_replicas", + "type": "string" + }, + { + "default": 1000, + "description": "Maximum number of records to read per shard in a single request. Valid range: 1-1000.", + "is_advanced": true, + "kind": "scalar", + "name": "batch_size", + "type": "int" + }, + { + "default": "1s", + "description": "Time to wait between polling attempts when no records are available.", + "is_advanced": true, + "kind": "scalar", + "name": "poll_interval", + "type": "string" + }, + { + "default": "trim_horizon", + "description": "Where to start reading when no checkpoint exists. `trim_horizon` starts from the oldest available record, `latest` starts from new records.", + "kind": "scalar", + "linter": "\nlet options = {\n \"trim_horizon\": true,\n \"latest\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "start_from", + "options": [ + "trim_horizon", + "latest" + ], + "type": "string" + }, + { + "default": 1000, + "description": "Maximum number of unacknowledged messages before forcing a checkpoint update. Lower values provide better recovery guarantees but increase write overhead.", + "is_advanced": true, + "kind": "scalar", + "name": "checkpoint_limit", + "type": "int" + }, + { + "default": 10000, + "description": "Maximum number of shards to track simultaneously. Prevents memory issues with extremely large tables.", + "is_advanced": true, + "kind": "scalar", + "name": "max_tracked_shards", + "type": "int" + }, + { + "default": "100ms", + "description": "Time to wait when applying backpressure due to too many in-flight messages.", + "is_advanced": true, + "kind": "scalar", + "name": "throttle_backoff", + "type": "string" + }, + { + "default": "none", + "description": "Snapshot behavior. `none`: CDC only (default). `snapshot_only`: one-time table scan, no streaming. `snapshot_and_cdc`: scan entire table then stream changes.", + "kind": "scalar", + "linter": "\nlet options = {\n \"none\": true,\n \"snapshot_only\": true,\n \"snapshot_and_cdc\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "snapshot_mode", + "options": [ + "none", + "snapshot_only", + "snapshot_and_cdc" + ], + "type": "string" + }, + { + "default": 1, + "description": "Number of parallel scan segments (1-10). Higher parallelism scans faster but consumes more RCUs. Start with 1 for safety.", + "is_advanced": true, + "kind": "scalar", + "linter": "root = if this < 1 || this > 10 { [\"snapshot_segments must be between 1 and 10\"] }", + "name": "snapshot_segments", + "type": "int" + }, + { + "default": 100, + "description": "Records per scan request during snapshot. Maximum 1000. Lower values provide better backpressure control but require more API calls.", + "is_advanced": true, + "kind": "scalar", + "linter": "root = if this < 1 || this > 1000 { [\"snapshot_batch_size must be between 1 and 1000\"] }", + "name": "snapshot_batch_size", + "type": "int" + }, + { + "default": "100ms", + "description": "Minimum time between scan requests per segment. Use this to limit RCU consumption during snapshot.", + "is_advanced": true, + "kind": "scalar", + "linter": "root = if this <= 0 { [\"snapshot_throttle must be greater than 0\"] }", + "name": "snapshot_throttle", + "type": "string" + }, + { + "default": true, + "description": "Deduplicate records that appear in both snapshot and CDC stream. Requires buffering CDC events during snapshot. If buffer is exceeded, deduplication is disabled to prevent data loss.", + "is_advanced": true, + "kind": "scalar", + "name": "snapshot_deduplicate", + "type": "bool" + }, + { + "default": 100000, + "description": "Maximum CDC events to buffer for deduplication (approximately 100 bytes per entry). If exceeded, deduplication is disabled and duplicates may be emitted.", + "is_advanced": true, + "kind": "scalar", + "name": "snapshot_buffer_size", + "type": "int" + }, + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Allows you to specify a custom endpoint for the AWS API.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nConsumes records from DynamoDB Streams with automatic checkpointing and shard management.\n\nDynamoDB Streams capture item-level changes in DynamoDB tables. This input supports:\n\n- Automatic shard discovery and management\n- Checkpoint-based resumption after restarts\n- Concurrent processing of multiple shards\n- Optional initial snapshot of existing table data\n- Multi-table streaming with auto-discovery by tags or explicit table lists\n\n### Table Discovery Modes\n\nThis input supports three table discovery modes:\n\n- `single` (default) - Stream from a single table specified in the `tables` field\n- `tag` - Auto-discover and stream from multiple tables based on DynamoDB table tags. Use `table_tag_filter` to filter tables (e.g. `key:value`)\n- `includelist` - Stream from an explicit list of tables specified in the `tables` field\n\nWhen using `tag` or `includelist` mode, the connector will stream from all matching tables simultaneously. Each table maintains its own checkpoint state. Use `table_discovery_interval` to periodically rescan for new tables (useful for dynamically tagged tables).\n\n### Prerequisites\n\nThe source DynamoDB table(s) must have streams enabled. You can enable streams with one of these view types:\n\n- `KEYS_ONLY` - Only the key attributes of the modified item\n- `NEW_IMAGE` - The entire item as it appears after the modification\n- `OLD_IMAGE` - The entire item as it appeared before the modification\n- `NEW_AND_OLD_IMAGES` - Both the new and old item images\n\n### Snapshots\n\nWhen `snapshot_mode` is set to `snapshot_only` or `snapshot_and_cdc`, the input will first scan the entire table before (or instead of) streaming changes. This is useful for:\n\n- Building a replica or cache with all existing data\n- Syncing historical data to a data warehouse\n- Populating a search index with existing records\n\nWARNING: Snapshots use the DynamoDB Scan API which consumes read capacity units (RCUs). For large tables, this can be expensive and take considerable time. Use `snapshot_segments` and `snapshot_throttle` to control RCU consumption.\n\nNOTE: Snapshots use eventually consistent reads and do not provide point-in-time consistency. Records modified during the snapshot may appear in both the snapshot and CDC stream (with different values). Use `snapshot_deduplicate` to minimize duplicates.\n\n### Checkpointing\n\nCheckpoints are stored in a separate DynamoDB table (configured via `checkpoint_table`). This table is created automatically if it does not exist. On restart, the input resumes from the last checkpointed position for each shard. Snapshot progress is also checkpointed, allowing resumption mid-snapshot after failures.\n\n### Alternative\n\nFor better performance and longer retention (up to 1 year vs 24 hours), consider using Kinesis Data Streams for DynamoDB with the `aws_kinesis` input instead.\n\n### Metadata\n\nThis input adds the following metadata fields to each message:\n\n- `dynamodb_shard_id` - The shard ID from which the record was read (empty for snapshot records)\n- `dynamodb_sequence_number` - The sequence number of the record in the stream (empty for snapshot records)\n- `dynamodb_approximate_creation_time` - RFC3339 approximate creation time of the stream record (empty for snapshot records)\n- `dynamodb_event_name` - The type of change: INSERT, MODIFY, REMOVE, or READ (for snapshot records)\n- `dynamodb_table` - The name of the DynamoDB table\n\n### Metrics\n\nThis input emits the following metrics:\n\n- `dynamodb_cdc_shards_tracked` - Total number of shards being tracked (gauge)\n- `dynamodb_cdc_shards_active` - Number of shards currently being read from (gauge)\n- `dynamodb_cdc_snapshot_state` - Snapshot state: 0=not_started, 1=in_progress, 2=complete (gauge)\n- `dynamodb_cdc_snapshot_records_read` - Total records read during snapshot (counter)\n- `dynamodb_cdc_snapshot_segments_active` - Number of active snapshot scan segments (gauge)\n- `dynamodb_cdc_snapshot_buffer_overflow` - Incremented when the deduplication buffer exceeds its size limit, disabling dedup (counter)\n- `dynamodb_cdc_snapshot_segment_duration` - Time taken by each snapshot scan segment to complete (timer)\n- `dynamodb_cdc_checkpoint_failures` - Number of failed checkpoint writes to the checkpoint table (counter)\n- `dynamodb_cdc_failover_skipped` - Records skipped during global-table failover replay because they predate the resumed cutoff (counter)\n\n### Global Table Checkpoints (multi-region failover)\n\nIn active/active or active/passive multi-region deployments, set `global_table: true` and list the other regions in `global_table_replicas` so the auto-created checkpoint table is provisioned as a DynamoDB Global Table (v2). Checkpoints then replicate across regions: a failed-over pipeline resumes near the last committed position instead of replaying the whole stream. Because each region's stream has its own sequence numbers, cross-region resume is time-based (at-least-once, replaying from the trim horizon up to the last replicated record time); same-region restarts still resume exactly. If the checkpoint table already exists, enabling `global_table` reconciles it towards the desired configuration: any missing replica regions are added via `UpdateTable`. The existing table must have been created in global mode (it must use a `TableId` hash key); pointing `global_table` at a pre-existing non-global checkpoint table fails fast with a clear error rather than mutating it.\n\nWhen `global_table` is enabled the principal additionally needs `dynamodb:CreateTable`, `dynamodb:UpdateTable`, `dynamodb:DescribeTable`, `dynamodb:DescribeLimits`, `iam:CreateServiceLinkedRole`, and create/describe permissions in each replica region.\n", + "examples": [ + { + "config": "\ninput:\n aws_dynamodb_cdc:\n tables: [my-table]\n region: us-east-1\n", + "summary": "Read change events from a DynamoDB table with streams enabled.", + "title": "Consume CDC events" + }, + { + "config": "\ninput:\n aws_dynamodb_cdc:\n tables: [orders]\n start_from: latest\n region: us-west-2\n", + "summary": "Only process new changes, ignoring existing stream data.", + "title": "Start from latest" + }, + { + "config": "\ninput:\n aws_dynamodb_cdc:\n tables: [products]\n snapshot_mode: snapshot_and_cdc\n snapshot_segments: 5\n region: us-east-1\n", + "summary": "Scan all existing records, then stream ongoing changes.", + "title": "Snapshot and CDC" + }, + { + "config": "\ninput:\n aws_dynamodb_cdc:\n table_discovery_mode: tag\n table_tag_filter: \"stream-enabled:true\"\n table_discovery_interval: 5m\n region: us-east-1\n", + "summary": "Automatically discover and stream from all tables with a specific tag.", + "title": "Auto-discover tables by tag" + }, + { + "config": "\ninput:\n aws_dynamodb_cdc:\n table_discovery_mode: tag\n table_tag_filter: \"environment:prod,staging;team:data,analytics\"\n table_discovery_interval: 5m\n region: us-east-1\n # Matches tables with: (environment=prod OR environment=staging) AND (team=data OR team=analytics)\n", + "summary": "Discover tables matching multiple tag criteria with OR logic per key, AND logic across keys.", + "title": "Auto-discover tables by multiple tags" + }, + { + "config": "\ninput:\n aws_dynamodb_cdc:\n table_discovery_mode: includelist\n tables:\n - orders\n - customers\n - products\n region: us-west-2\n", + "summary": "Stream from an explicit list of tables simultaneously.", + "title": "Stream from multiple specific tables" + } + ], + "name": "aws_dynamodb_cdc", + "plugin": true, + "status": "stable", + "summary": "Reads change data capture (CDC) events from DynamoDB Streams.", + "type": "input", + "version": "4.79.0" + }, + { + "categories": [ + "Services", + "AWS" + ], + "config": { + "children": [ + { + "description": "One or more Kinesis data streams to consume from. Streams can either be specified by their name or full ARN. Shards of a stream are automatically balanced across consumers by coordinating through the provided DynamoDB table. Multiple comma separated streams can be listed in a single element. Shards are automatically distributed across consumers of a stream by coordinating through the provided DynamoDB table. Alternatively, it's possible to specify an explicit shard to consume from with a colon after the stream name, e.g. `foo:0` would consume the shard `0` of the stream `foo`.", + "examples": [ + [ + "foo", + "arn:aws:kinesis:*:111122223333:stream/my-stream" + ] + ], + "kind": "array", + "name": "streams", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "The name of the table to access.", + "kind": "scalar", + "name": "table", + "type": "string" + }, + { + "default": false, + "description": "Whether, if the table does not exist, it should be created.", + "kind": "scalar", + "name": "create", + "type": "bool" + }, + { + "default": "PAY_PER_REQUEST", + "description": "When creating the table determines the billing mode.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"provisioned\": true,\n \"pay_per_request\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "billing_mode", + "options": [ + "PROVISIONED", + "PAY_PER_REQUEST" + ], + "type": "string" + }, + { + "default": 0, "description": "Set the provisioned read capacity when creating the table with a `billing_mode` of `PROVISIONED`.", "is_advanced": true, "kind": "scalar", @@ -12208,6 +17699,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -12350,6 +17901,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -12561,6 +18172,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -12812,6 +18483,14 @@ "kind": "scalar", "name": "wait_time_seconds", "type": "int" + }, + { + "default": 0, + "description": "Custom SQS Nack Visibility timeout in seconds. Default is 0", + "is_optional": true, + "kind": "scalar", + "name": "nack_visibility_timeout", + "type": "int" } ], "description": "Consume SQS messages in order to trigger key downloads.", @@ -12910,6 +18589,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -13185,7 +18924,7 @@ "description": "\nSupports multiple authentication methods but only one of the following is required:\n\n- `storage_connection_string`\n- `storage_account` and `storage_access_key`\n- `storage_account` and `storage_sas_token`\n- `storage_account` to access via https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#DefaultAzureCredential[DefaultAzureCredential^]\n\nIf multiple are set then the `storage_connection_string` is given priority.\n\nIf the `storage_connection_string` does not contain the `AccountName` parameter, please specify it in the\n`storage_account` field.\n\n== Download large files\n\nWhen downloading large files it's often necessary to process it in streamed parts in order to avoid loading the entire file in memory at a given time. In order to do this a <> can be specified that determines how to break the input into smaller individual messages.\n\n== Stream new files\n\nBy default this input will consume all files found within the target container and will then gracefully terminate. This is referred to as a \"batch\" mode of operation. However, it's possible to instead configure a container as https://learn.microsoft.com/en-gb/azure/event-grid/event-schema-blob-storage[an Event Grid source^] and then use this as a <>, in which case new files are consumed as they're uploaded and Redpanda Connect will continue listening for and downloading files as they arrive. This is referred to as a \"streamed\" mode of operation.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- blob_storage_key\n- blob_storage_container\n- blob_storage_last_modified\n- blob_storage_last_modified_unix\n- blob_storage_content_type\n- blob_storage_content_encoding\n- All user defined metadata\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", "name": "azure_blob_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Downloads objects within an Azure Blob Storage container, optionally filtered by a prefix.", "type": "input", "version": "3.36.0" @@ -13315,7 +19054,7 @@ "footnotes": "\n\n== CosmosDB emulator\n\nIf you wish to run the CosmosDB emulator that is referenced in the documentation https://learn.microsoft.com/en-us/azure/cosmos-db/linux-emulator[here^], the following Docker command should do the trick:\n\n```bash\n> docker run --rm -it -p 8081:8081 --name=cosmosdb -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10 -e AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=false mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator\n```\n\nNote: `AZURE_COSMOS_EMULATOR_PARTITION_COUNT` controls the number of partitions that will be supported by the emulator. The bigger the value, the longer it takes for the container to start up.\n\nAdditionally, instead of installing the container self-signed certificate which is exposed via `https://localhost:8081/_explorer/emulator.pem`, you can run https://mitmproxy.org/[mitmproxy^] like so:\n\n```bash\n> mitmproxy -k --mode \"reverse:https://localhost:8081\"\n```\n\nThen you can access the CosmosDB UI via `http://localhost:8080/_explorer/index.html` and use `http://localhost:8080` as the CosmosDB endpoint.\n", "name": "azure_cosmosdb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Executes a SQL query against https://learn.microsoft.com/en-us/azure/cosmos-db/introduction[Azure CosmosDB^] and creates a batch of messages from each page of items.", "type": "input", "version": "v4.25.0" @@ -13400,7 +19139,7 @@ "description": "\nThis input adds the following metadata fields to each message:\n\n```\n- queue_storage_insertion_time\n- queue_storage_queue_name\n- queue_storage_message_lag (if 'track_properties' set to true)\n- All user defined queue metadata\n```\n\nOnly one authentication method is required, `storage_connection_string` or `storage_account` and `storage_access_key`. If both are set then the `storage_connection_string` is given priority.", "name": "azure_queue_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Dequeue objects from an Azure Storage Queue.", "type": "input", "version": "3.42.0" @@ -13488,7 +19227,7 @@ "description": "\nQueries an Azure Storage Account Table, optionally with multiple filters.\n== Metadata\nThis input adds the following metadata fields to each message:\n\n- table_storage_name\n- row_num\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", "name": "azure_table_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Queries an Azure Storage Account Table, optionally with multiple filters.", "type": "input", "version": "4.10.0" @@ -13610,33 +19349,6 @@ "type": "input", "version": "4.11.0" }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "An address to connect to.", - "examples": [ - "127.0.0.1:11300" - ], - "kind": "scalar", - "name": "address", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "beanstalkd", - "plugin": true, - "status": "experimental", - "summary": "Reads messages from a Beanstalkd queue.", - "type": "input", - "version": "4.7.0" - }, { "categories": [ "Utility" @@ -13763,271 +19475,179 @@ }, { "categories": [ - "Services" + "Network" ], "config": { "children": [ { - "description": "A list of Cassandra nodes to connect to. Multiple comma separated addresses can be specified on a single line.", - "examples": [ - [ - "localhost:9042" - ], - [ - "foo:9042", - "bar:9042" - ], - [ - "foo:9042,bar:9042" - ] - ], - "kind": "array", - "name": "addresses", + "default": "/", + "description": "The endpoint path to listen for data delivery requests.", + "kind": "scalar", + "name": "path", + "type": "string" + }, + { + "default": "", + "description": "An optional xref:components:rate_limits/about.adoc[rate limit] to throttle requests by.", + "kind": "scalar", + "name": "rate_limit", "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "default": "200", + "description": "Specify the status code to return with synchronous responses. This is a string value, which allows you to customize it based on resulting payloads and their metadata.", "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "${! json(\"status\") }", + "${! meta(\"status\") }" ], - "is_advanced": true, - "is_secret": true, + "interpolated": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "status", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", + "default": { + "Content-Type": "application/octet-stream" + }, + "description": "Specify headers to return with synchronous responses.", + "interpolated": true, + "kind": "map", + "name": "headers", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", + "default": [], + "description": "Provide a list of explicit metadata key prefixes to match against.", + "examples": [ + [ + "foo_", + "bar_" + ], + [ + "kafka_" + ], + [ + "content-" + ] + ], + "kind": "array", + "name": "include_prefixes", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "default": [], + "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", "examples": [ - "foo", - "${KEY_PASSWORD}" + [ + ".*" + ], + [ + "_timestamp_unix$" + ] ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "kind": "array", + "name": "include_patterns", "type": "string" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", + "description": "Specify criteria for which metadata values are added to the response as headers.", + "kind": "scalar", + "name": "metadata_headers", "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, "kind": "scalar", - "name": "tls", + "name": "sync_response", "type": "object" }, { "children": [ { "default": false, - "description": "Whether to use password authentication", + "description": "Enable SO_REUSEADDR, allowing binding to ports in TIME_WAIT state. Useful for graceful restarts and config reloads where the server needs to rebind to the same port immediately after shutdown.", "is_advanced": true, "kind": "scalar", - "name": "enabled", + "name": "reuse_addr", "type": "bool" }, { - "default": "", - "description": "The username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "The password to authenticate with.", + "default": false, + "description": "Enable SO_REUSEPORT, allowing multiple sockets to bind to the same port for load balancing across multiple processes/threads.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "reuse_port", + "type": "bool" } ], - "description": "Optional configuration of Cassandra authentication parameters.", + "description": "Customize messages returned via xref:guides:sync_responses.adoc[synchronous responses].", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "password_authenticator", + "name": "tcp", "type": "object" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThe field `rate_limit` allows you to specify an optional xref:components:rate_limits/about.adoc[`rate_limit` resource], which will be applied to each HTTP request made and each websocket payload received.\n\nWhen the rate limit is breached HTTP requests will have a 429 response returned with a Retry-After header.\n\n== Responses\n\nIt's possible to return a response for each message received using xref:guides:sync_responses.adoc[synchronous responses]. When doing so you can customize headers with the `sync_response` field `headers`, which can also use xref:configuration:interpolation.adoc#bloblang-queries[function interpolation] in the value based on the response message contents.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- http_server_user_agent\n- http_server_request_path\n- http_server_verb\n- http_server_remote_ip\n- All headers (only first values are taken)\n- All query parameters\n- All path parameters\n- All cookies\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", + "name": "gateway", + "plugin": true, + "status": "stable", + "summary": "Receive messages delivered over HTTP.", + "type": "input" + }, + { + "categories": [ + "Services", + "GCP" + ], + "config": { + "children": [ { - "default": false, - "description": "If enabled the driver will not attempt to get host info from the system.peers table. This can speed up queries but will mean that data_centre, rack and token information will not be available.", - "is_advanced": true, + "description": "GCP project where the query job will execute.", "kind": "scalar", - "name": "disable_initial_host_lookup", - "type": "bool" + "name": "project", + "type": "string" }, { - "default": 3, - "description": "The maximum number of retries before giving up on a request.", - "is_advanced": true, + "default": "", + "description": "An optional field to set Google Service Account Credentials json.", + "is_secret": true, "kind": "scalar", - "name": "max_retries", - "type": "int" + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" }, { - "children": [ - { - "default": "1s", - "description": "The initial period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "initial_interval", - "type": "string" - }, - { - "default": "5s", - "description": "The maximum period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "max_interval", - "type": "string" - } + "description": "Fully-qualified BigQuery table name to query.", + "examples": [ + "bigquery-public-data.samples.shakespeare" ], - "description": "Control time intervals between retry attempts.", - "is_advanced": true, "kind": "scalar", - "name": "backoff", - "type": "object" + "name": "table", + "type": "string" }, { - "default": "600ms", - "description": "The client connection timeout.", - "kind": "scalar", - "name": "timeout", + "description": "A list of columns to query.", + "kind": "array", + "name": "columns", "type": "string" }, { - "children": [ - { - "description": "The local DC to use, enables DC aware policy.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "local_dc", - "type": "string" - }, - { - "description": "The local rack to use, requires local_dc to be set, enables rack aware policy.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "local_rack", - "type": "string" - } + "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks (`?`).", + "examples": [ + "type = ? and created_at > ?", + "user_id = ?" ], - "description": "Optional host selection policy configurations. Highly recommended in deployments with multiple DCs. Host selection is always token aware if the token can be calculated from query. By default the underlying policy is round robin over all nodes. Users can specify a local DC and rack to use for the DC Aware & Rack Aware policies. ", - "is_advanced": true, - "kind": "scalar", - "linter": "root = if this.local_rack != \"\" && (!this.exists(\"local_dc\") || this.local_dc == \"\") { \"local_dc must be set if local_rack is set\" }", - "name": "host_selection_policy", - "type": "object" - }, - { - "description": "A query to execute.", + "is_optional": true, "kind": "scalar", - "name": "query", + "name": "where", "type": "string" }, { @@ -14036,407 +19656,93 @@ "kind": "scalar", "name": "auto_replay_nacks", "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "examples": [ - { - "config": "\ninput:\n cassandra:\n addresses:\n - 172.17.0.2\n query:\n 'SELECT * FROM learn_cassandra.users_by_country'\n", - "summary": "\nLet's presume that we have 3 Cassandra nodes, like in this tutorial by Sebastian Sigl from freeCodeCamp:\n\nhttps://www.freecodecamp.org/news/the-apache-cassandra-beginner-tutorial/\n\nThen if we want to select everything from the table users_by_country, we should use the configuration below.\nIf we specify the stdin output, the result will look like:\n\n```json\n{\"age\":23,\"country\":\"UK\",\"first_name\":\"Bob\",\"last_name\":\"Sandler\",\"user_email\":\"bob@email.com\"}\n```\n\nThis configuration also works for Scylla.\n", - "title": "Minimal Select (Cassandra/Scylla)" - } - ], - "name": "cassandra", - "plugin": true, - "status": "experimental", - "summary": "Executes a find query and creates a message for each row received.", - "type": "input" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ + }, { - "description": "A Data Source Name to identify the target database.", - "examples": [ - "postgres://user:password@example.com:26257/defaultdb?sslmode=require" - ], - "kind": "scalar", - "name": "dsn", + "default": {}, + "description": "A list of labels to add to the query job.", + "kind": "map", + "name": "job_labels", "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, + "default": "", + "description": "The priority with which to schedule the query.", "kind": "scalar", - "name": "tls", - "type": "object" + "name": "priority", + "type": "string" }, { - "description": "CSV of tables to be included in the changefeed", + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", "examples": [ - [ - "table1", - "table2" - ] + "root = [ \"article\", now().ts_format(\"2006-01-02\") ]" ], - "kind": "array", - "name": "tables", - "type": "string" - }, - { - "description": "A https://docs.redpanda.com/redpanda-connect/components/caches/about[cache resource^] to use for storing the current latest cursor that has been successfully delivered, this allows Redpanda Connect to continue from that cursor upon restart, rather than consume the entire state of the table.", "is_optional": true, "kind": "scalar", - "name": "cursor_cache", + "name": "args_mapping", "type": "string" }, { - "description": "A list of options to be included in the changefeed (WITH X, Y...).\n\nNOTE: Both the CURSOR option and UPDATED will be ignored from these options when a `cursor_cache` is specified, as they are set explicitly by Redpanda Connect in this case.", - "examples": [ - [ - "virtual_columns=\"omitted\"" - ] - ], - "is_advanced": true, + "description": "An optional prefix to prepend to the select query (before SELECT).", "is_optional": true, - "kind": "array", - "name": "options", + "kind": "scalar", + "name": "prefix", "type": "string" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "description": "An optional suffix to append to the select query.", + "is_optional": true, "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" + "name": "suffix", + "type": "string" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "This input will continue to listen to the changefeed until shutdown. A backfill of the full current state of the table will be delivered upon each run unless a cache is configured for storing cursor timestamps, as this is how Redpanda Connect keeps track as to which changes have been successfully delivered.\n\nNote: You must have `SET CLUSTER SETTING kv.rangefeed.enabled = true;` on your CRDB cluster, for more information refer to https://www.cockroachlabs.com/docs/stable/changefeed-examples?filters=core[the official CockroachDB documentation^].", - "name": "cockroachdb_changefeed", + "description": "Once the rows from the query are exhausted, this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", + "examples": [ + { + "config": "\ninput:\n gcp_bigquery_select:\n project: sample-project\n table: bigquery-public-data.samples.shakespeare\n columns:\n - word\n - sum(word_count) as total_count\n where: length(word) >= ?\n suffix: |\n GROUP BY word\n ORDER BY total_count DESC\n LIMIT 10\n args_mapping: |\n root = [ 3 ]\n", + "summary": "\nHere we query the public corpus of Shakespeare's works to generate a stream of the top 10 words that are 3 or more characters long:", + "title": "Word counts" + } + ], + "name": "gcp_bigquery_select", "plugin": true, - "status": "experimental", - "summary": "Listens to a https://www.cockroachlabs.com/docs/stable/changefeed-examples[CockroachDB Core Changefeed^] and creates a message for each row received. Each message is a json object looking like: \n```json\n{\n\t\"primary_key\": \"[\\\"1a7ff641-3e3b-47ee-94fe-a0cadb56cd8f\\\", 2]\", // stringifed JSON array\n\t\"row\": \"{\\\"after\\\": {\\\"k\\\": \\\"1a7ff641-3e3b-47ee-94fe-a0cadb56cd8f\\\", \\\"v\\\": 2}, \\\"updated\\\": \\\"1637953249519902405.0000000000\\\"}\", // stringified JSON object\n\t\"table\": \"strm_2\"\n}\n```", - "type": "input" + "status": "stable", + "summary": "Executes a `SELECT` query against BigQuery and creates a message for each row received.", + "type": "input", + "version": "3.63.0" }, { "categories": [ - "Local" + "Services", + "GCP" ], "config": { "children": [ { - "description": "A list of file paths to read from. Each file will be read sequentially until the list is exhausted, at which point the input will close. Glob patterns are supported, including super globs (double star).", - "examples": [ - [ - "/tmp/foo.csv", - "/tmp/bar/*.csv", - "/tmp/data/**/*.csv" - ] - ], - "kind": "array", - "name": "paths", - "type": "string" - }, - { - "default": true, - "description": "Whether to reference the first row as a header row. If set to true the output structure for messages will be an object where field keys are determined by the header row. Otherwise, each message will consist of an array of values from the corresponding CSV row.", - "kind": "scalar", - "name": "parse_header_row", - "type": "bool" - }, - { - "default": ",", - "description": "The delimiter to use for splitting values in each record. It must be a single character.", - "kind": "scalar", - "name": "delimiter", - "type": "string" - }, - { - "default": false, - "description": "If set to `true`, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.", - "kind": "scalar", - "name": "lazy_quotes", - "type": "bool", - "version": "4.1.0" - }, - { - "default": false, - "description": "Whether to delete input files from the disk once they are fully consumed.", - "is_advanced": true, - "kind": "scalar", - "name": "delete_on_finish", - "type": "bool" - }, - { - "default": 1, - "description": "Optionally process records in batches. This can help to speed up the consumption of exceptionally large CSV files. When the end of the file is reached the remaining records are processed as a (potentially smaller) batch.", - "is_advanced": true, - "kind": "scalar", - "name": "batch_count", - "type": "int" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis input offers more control over CSV parsing than the xref:components:inputs/file.adoc[`file` input].\n\nWhen parsing with a header row each line of the file will be consumed as a structured object, where the key names are determined from the header now. For example, the following CSV file:\n\n```csv\nfoo,bar,baz\nfirst foo,first bar,first baz\nsecond foo,second bar,second baz\n```\n\nWould produce the following messages:\n\n```json\n{\"foo\":\"first foo\",\"bar\":\"first bar\",\"baz\":\"first baz\"}\n{\"foo\":\"second foo\",\"bar\":\"second bar\",\"baz\":\"second baz\"}\n```\n\nIf, however, the field `parse_header_row` is set to `false` then arrays are produced instead, like follows:\n\n```json\n[\"first foo\",\"first bar\",\"first baz\"]\n[\"second foo\",\"second bar\",\"second baz\"]\n```\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- header\n- path\n- mod_time_unix\n- mod_time (RFC3339)\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\nNote: The `header` field is only set when `parse_header_row` is `true`.\n\n=== Output CSV column order\n\nWhen xref:guides:bloblang/advanced.adoc#creating-csv[creating CSV] from Redpanda Connect messages, the columns must be sorted lexicographically to make the output deterministic. Alternatively, when using the `csv` input, one can leverage the `header` metadata field to retrieve the column order:\n\n```yaml\ninput:\n csv:\n paths:\n - ./foo.csv\n - ./bar.csv\n parse_header_row: true\n\n processors:\n - mapping: |\n map escape_csv {\n root = if this.re_match(\"[\\\"\\n,]+\") {\n \"\\\"\" + this.replace_all(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n } else {\n this\n }\n }\n\n let header = if count(@path) == 1 {\n @header.map_each(c -> c.apply(\"escape_csv\")).join(\",\") + \"\\n\"\n } else { \"\" }\n\n root = $header + @header.map_each(c -> this.get(c).string().apply(\"escape_csv\")).join(\",\")\n\noutput:\n file:\n path: ./output/${! @path.filepath_split().index(-1) }\n```\n", - "footnotes": "This input is particularly useful when consuming CSV from files too large to parse entirely within memory. However, in cases where CSV is consumed from other input types it's also possible to parse them using the xref:guides:bloblang/methods.adoc#parse_csv[Bloblang `parse_csv` method].", - "name": "csv", - "plugin": true, - "status": "stable", - "summary": "Reads one or more CSV files as structured records following the format described in RFC 4180.", - "type": "input" - }, - { - "categories": [ - "Services", - "Social" - ], - "config": { - "children": [ - { - "description": "A discord channel ID to consume messages from.", - "kind": "scalar", - "name": "channel_id", - "type": "string" - }, - { - "description": "A bot token used for authentication.", - "kind": "scalar", - "name": "bot_token", - "type": "string" - }, - { - "description": "A cache resource to use for performing unread message backfills, the ID of the last message received will be stored in this cache and used for subsequent requests.", - "kind": "scalar", - "name": "cache", - "type": "string" - }, - { - "default": "last_message_id", - "description": "The key identifier used when storing the ID of the last message received.", - "is_advanced": true, - "kind": "scalar", - "name": "cache_key", - "type": "string" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "default": "1m", - "description": "The length of time (as a duration string) to wait between each poll for backlogged messages. This field can be set empty, in which case requests are made at the limit set by the rate limit. This field also supports cron expressions.", - "is_deprecated": true, + "description": "The name of the bucket from which to download objects.", "kind": "scalar", - "name": "poll_period", + "name": "bucket", "type": "string" }, { - "default": 100, - "description": "The maximum number of messages to receive in a single request.", - "is_deprecated": true, - "kind": "scalar", - "name": "limit", - "type": "int" - }, - { - "default": "An optional rate limit resource to restrict API requests with.", - "is_deprecated": true, + "default": "", + "description": "An optional path prefix, if set only objects with the prefix are consumed.", "kind": "scalar", - "name": "rate_limit", + "name": "prefix", "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "This input works by authenticating as a bot using token based authentication. The ID of the newest message consumed and acked is stored in a cache in order to perform a backfill of unread messages each time the input is initialised. Ideally this cache should be persisted across restarts.", - "name": "discord", - "plugin": true, - "status": "experimental", - "summary": "Consumes messages posted in a Discord channel.", - "type": "input" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "default": {}, - "description": "A map of inputs to statically create.", - "kind": "map", - "name": "inputs", - "type": "input" }, { "default": "", - "description": "A path prefix for HTTP endpoints that are registered.", + "description": "An optional field to set Google Service Account Credentials json.", + "is_secret": true, "kind": "scalar", - "name": "prefix", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "footnotes": "\n== Endpoints\n\n=== GET `/inputs`\n\nReturns a JSON object detailing all dynamic inputs, providing information such as their current uptime and configuration.\n\n=== GET `/inputs/\\{id}`\n\nReturns the configuration of an input.\n\n=== POST `/inputs/\\{id}`\n\nCreates or updates an input with a configuration provided in the request body (in YAML or JSON format).\n\n=== DELETE `/inputs/\\{id}`\n\nStops and removes an input.\n\n=== GET `/inputs/\\{id}/uptime`\n\nReturns the uptime of an input as a duration string (of the form \"72h3m0.5s\"), or \"stopped\" in the case where the input has gracefully terminated.", - "name": "dynamic", - "plugin": true, - "status": "stable", - "summary": "A special broker type where the inputs are identified by unique labels and can be created, changed and removed during runtime via a REST HTTP interface.", - "type": "input" - }, - { - "categories": [ - "Local" - ], - "config": { - "children": [ - { - "description": "A list of paths to consume sequentially. Glob patterns are supported, including super globs (double star).", - "kind": "array", - "name": "paths", + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { @@ -14528,7 +19834,7 @@ }, { "default": { - "lines": {} + "to_the_end": {} }, "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", "is_optional": true, @@ -14539,17 +19845,10 @@ }, { "default": false, - "description": "Whether to delete input files from the disk once they are fully consumed.", + "description": "Whether to delete downloaded objects from the bucket once they are processed.", "is_advanced": true, "kind": "scalar", - "name": "delete_on_finish", - "type": "bool" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", + "name": "delete_objects", "type": "bool" } ], @@ -14557,116 +19856,97 @@ "name": "", "type": "object" }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- path\n- mod_time_unix\n- mod_time (RFC3339)\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", - "examples": [ - { - "config": "\ninput:\n file:\n paths: [ ./data/*.csv ]\n scanner:\n csv: {}\n", - "summary": "If we wished to consume a directory of CSV files as structured documents we can use a glob pattern and the `csv` scanner:", - "title": "Read a Bunch of CSVs" - } - ], - "name": "file", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```\n- gcs_key\n- gcs_bucket\n- gcs_last_modified\n- gcs_last_modified_unix\n- gcs_content_type\n- gcs_content_encoding\n- All user defined metadata\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n=== Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to GCP services. You can find out more in xref:guides:cloud/gcp.adoc[].", + "name": "gcp_cloud_storage", "plugin": true, "status": "stable", - "summary": "Consumes data from files on disk, emitting messages according to a chosen codec.", - "type": "input" + "summary": "Downloads objects within a Google Cloud Storage bucket, optionally filtered by a prefix.", + "type": "input", + "version": "3.43.0" }, { "categories": [ - "Network" + "Services", + "GCP" ], "config": { "children": [ { - "default": "/", - "description": "The endpoint path to listen for data delivery requests.", + "description": "The project ID of the target subscription.", "kind": "scalar", - "name": "path", + "name": "project", "type": "string" }, { "default": "", - "description": "An optional xref:components:rate_limits/about.adoc[rate limit] to throttle requests by.", + "description": "An optional field to set Google Service Account Credentials json.", + "is_secret": true, "kind": "scalar", - "name": "rate_limit", + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The target subscription ID.", + "kind": "scalar", + "name": "subscription", + "type": "string" + }, + { + "default": "", + "description": "An optional endpoint to override the default of `pubsub.googleapis.com:443`. This can be used to connect to a region specific pubsub endpoint. For a list of valid values, see https://cloud.google.com/pubsub/docs/reference/service_apis_overview#list_of_regional_endpoints[this document^].", + "examples": [ + "us-central1-pubsub.googleapis.com:443", + "us-west3-pubsub.googleapis.com:443" + ], + "kind": "scalar", + "name": "endpoint", "type": "string" }, + { + "default": false, + "description": "Enable synchronous pull mode.", + "kind": "scalar", + "name": "sync", + "type": "bool" + }, + { + "default": 1000, + "description": "The maximum number of outstanding pending messages to be consumed at a given time.", + "kind": "scalar", + "name": "max_outstanding_messages", + "type": "int" + }, + { + "default": 1000000000, + "description": "The maximum number of outstanding pending messages to be consumed measured in bytes.", + "kind": "scalar", + "name": "max_outstanding_bytes", + "type": "int" + }, { "children": [ { - "default": "200", - "description": "Specify the status code to return with synchronous responses. This is a string value, which allows you to customize it based on resulting payloads and their metadata.", - "examples": [ - "${! json(\"status\") }", - "${! meta(\"status\") }" - ], - "interpolated": true, + "default": false, + "description": "Whether to configure subscription or not.", "is_advanced": true, "kind": "scalar", - "name": "status", - "type": "string" - }, - { - "default": { - "Content-Type": "application/octet-stream" - }, - "description": "Specify headers to return with synchronous responses.", - "interpolated": true, - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "children": [ - { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "is_advanced": true, - "kind": "array", - "name": "include_prefixes", - "type": "string" - }, - { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", - "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] - ], - "is_advanced": true, - "kind": "array", - "name": "include_patterns", - "type": "string" - } - ], - "description": "Specify criteria for which metadata values are added to the response as headers.", + "default": "", + "description": "Defines the topic that the subscription should be vinculated to.", "is_advanced": true, "kind": "scalar", - "name": "metadata_headers", - "type": "object" + "name": "topic", + "type": "string" } ], - "description": "Customize messages returned via xref:guides:sync_responses.adoc[synchronous responses].", + "description": "Allows you to configure the input subscription and creates if it doesn't exist.", "is_advanced": true, "kind": "scalar", - "name": "sync_response", + "name": "create_subscription", "type": "object" } ], @@ -14674,11 +19954,11 @@ "name": "", "type": "object" }, - "description": "\nThe field `rate_limit` allows you to specify an optional xref:components:rate_limits/about.adoc[`rate_limit` resource], which will be applied to each HTTP request made and each websocket payload received.\n\nWhen the rate limit is breached HTTP requests will have a 429 response returned with a Retry-After header.\n\n== Responses\n\nIt's possible to return a response for each message received using xref:guides:sync_responses.adoc[synchronous responses]. When doing so you can customize headers with the `sync_response` field `headers`, which can also use xref:configuration:interpolation.adoc#bloblang-queries[function interpolation] in the value based on the response message contents.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- http_server_user_agent\n- http_server_request_path\n- http_server_verb\n- http_server_remote_ip\n- All headers (only first values are taken)\n- All query parameters\n- All path parameters\n- All cookies\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", - "name": "gateway", + "description": "\nFor information on how to set up credentials see https://cloud.google.com/docs/authentication/production[this guide^].\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- gcp_pubsub_publish_time_unix - The time at which the message was published to the topic.\n- gcp_pubsub_delivery_attempt - When dead lettering is enabled, this is set to the number of times PubSub has attempted to deliver a message.\n- gcp_pubsub_message_id - The unique identifier of the message.\n- gcp_pubsub_ordering_key - The ordering key of the message.\n- All message attributes\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n", + "name": "gcp_pubsub", "plugin": true, "status": "stable", - "summary": "Receive messages delivered over HTTP.", + "summary": "Consumes messages from a GCP Cloud Pub/Sub subscription.", "type": "input" }, { @@ -14688,457 +19968,98 @@ ], "config": { "children": [ - { - "description": "GCP project where the query job will execute.", - "kind": "scalar", - "name": "project", - "type": "string" - }, { "default": "", - "description": "An optional field to set Google Service Account Credentials json.", - "is_secret": true, + "description": "Base64 encoded GCP service account JSON credentials file for authentication. If not provided, Application Default Credentials (ADC) will be used.", + "is_optional": true, "kind": "scalar", "name": "credentials_json", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Fully-qualified BigQuery table name to query.", - "examples": [ - "bigquery-public-data.samples.shakespeare" - ], + "description": "GCP project ID containing the Spanner instance", "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "description": "A list of columns to query.", - "kind": "array", - "name": "columns", + "name": "project_id", "type": "string" }, { - "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks (`?`).", - "examples": [ - "type = ? and created_at > ?", - "user_id = ?" - ], - "is_optional": true, + "description": "Spanner instance ID", "kind": "scalar", - "name": "where", + "name": "instance_id", "type": "string" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "description": "Spanner database ID", "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "default": {}, - "description": "A list of labels to add to the query job.", - "kind": "map", - "name": "job_labels", + "name": "database_id", "type": "string" }, { - "default": "", - "description": "The priority with which to schedule the query.", + "description": "The name of the change stream to track, the stream must exist in the database. To create a change stream, see https://cloud.google.com/spanner/docs/change-streams/manage.", "kind": "scalar", - "name": "priority", + "name": "stream_id", "type": "string" }, { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", + "default": "", + "description": "RFC3339 formatted inclusive timestamp to start reading from the change stream (default: current time)", "examples": [ - "root = [ \"article\", now().ts_format(\"2006-01-02\") ]" + "2022-01-01T00:00:00Z" ], "is_optional": true, "kind": "scalar", - "name": "args_mapping", + "name": "start_timestamp", "type": "string" }, { - "description": "An optional prefix to prepend to the select query (before SELECT).", + "default": "", + "description": "RFC3339 formatted exclusive timestamp to stop reading at (default: no end time)", + "examples": [ + "2022-01-01T00:00:00Z" + ], "is_optional": true, "kind": "scalar", - "name": "prefix", + "name": "end_timestamp", "type": "string" }, { - "description": "An optional suffix to append to the select query.", - "is_optional": true, - "kind": "scalar", - "name": "suffix", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Once the rows from the query are exhausted, this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", - "examples": [ - { - "config": "\ninput:\n gcp_bigquery_select:\n project: sample-project\n table: bigquery-public-data.samples.shakespeare\n columns:\n - word\n - sum(word_count) as total_count\n where: length(word) >= ?\n suffix: |\n GROUP BY word\n ORDER BY total_count DESC\n LIMIT 10\n args_mapping: |\n root = [ 3 ]\n", - "summary": "\nHere we query the public corpus of Shakespeare's works to generate a stream of the top 10 words that are 3 or more characters long:", - "title": "Word counts" - } - ], - "name": "gcp_bigquery_select", - "plugin": true, - "status": "beta", - "summary": "Executes a `SELECT` query against BigQuery and creates a message for each row received.", - "type": "input", - "version": "3.63.0" - }, - { - "categories": [ - "Services", - "GCP" - ], - "config": { - "children": [ - { - "description": "The name of the bucket from which to download objects.", + "default": "10s", + "description": "Duration string for heartbeat interval", + "is_advanced": true, "kind": "scalar", - "name": "bucket", + "name": "heartbeat_interval", "type": "string" }, { "default": "", - "description": "An optional path prefix, if set only objects with the prefix are consumed.", + "description": "The table to store metadata in (default: cdc_metadata_)", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "prefix", + "name": "metadata_table", "type": "string" }, { - "default": "", - "description": "An optional field to set Google Service Account Credentials json.", - "is_secret": true, + "default": "5s", + "description": "Duration string for frequency of querying Spanner for minimum watermark.", + "is_advanced": true, "kind": "scalar", - "name": "credentials_json", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "min_watermark_cache_ttl", "type": "string" }, { - "annotated_options": [ - [ - "auto", - "EXPERIMENTAL: Attempts to derive a codec for each file based on information such as the extension. For example, a .tar.gz file would be consumed with the `gzip/tar` codec. Defaults to all-bytes." - ], - [ - "all-bytes", - "Consume the entire file as a single binary message." - ], - [ - "avro-ocf:marshaler=x", - "EXPERIMENTAL: Consume a stream of Avro OCF datum. The `marshaler` parameter is optional and has the options: `goavro` (default), `json`. Use `goavro` if OCF contains logical types." - ], - [ - "chunker:x", - "Consume the file in chunks of a given number of bytes." - ], - [ - "csv", - "Consume structured rows as comma separated values, the first row must be a header row." - ], - [ - "csv:x", - "Consume structured rows as values separated by a custom delimiter, the first row must be a header row. The custom delimiter must be a single character, e.g. the codec `\"csv:\\t\"` would consume a tab delimited file." - ], - [ - "csv-safe", - "Consume structured rows like `csv`, but sends messages with empty maps on failure to parse. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "csv-safe:x", - "Consume structured rows like `csv:x` as values separated by a custom delimiter, but sends messages with empty maps on failure to parse. The custom delimiter must be a single character, e.g. the codec `\"csv-safe:\\t\"` would consume a tab delimited file. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "delim:x", - "Consume the file in segments divided by a custom delimiter." - ], - [ - "gzip", - "Decompress a gzip file, this codec should precede another codec, e.g. `gzip/all-bytes`, `gzip/tar`, `gzip/csv`, etc." - ], - [ - "pgzip", - "Decompress a gzip file in parallel, this codec should precede another codec, e.g. `pgzip/all-bytes`, `pgzip/tar`, `pgzip/csv`, etc." - ], - [ - "lines", - "Consume the file in segments divided by linebreaks." - ], - [ - "multipart", - "Consumes the output of another codec and batches messages together. A batch ends when an empty message is consumed. For example, the codec `lines/multipart` could be used to consume multipart messages where an empty line indicates the end of each batch." - ], - [ - "regex:(?m)^\\d\\d:\\d\\d:\\d\\d", - "Consume the file in segments divided by regular expression." - ], - [ - "skipbom", - "Skip one or more byte order marks for each opened reader, this codec should precede another codec, e.g. `skipbom/csv`, etc." - ], + "description": "List of modification types to process. If not specified, all modification types are processed. Allowed values: INSERT, UPDATE, DELETE", + "examples": [ [ - "tar", - "Parse the file as a tar archive, and consume each file of the archive as a message." + "INSERT", + "UPDATE", + "DELETE" ] ], - "description": "The way in which the bytes of a data source should be converted into discrete messages, codecs are useful for specifying how large files or continuous streams of data might be processed in small chunks rather than loading it all in memory. It's possible to consume lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter. Codecs can be chained with `/`, for example a gzip compressed CSV file can be consumed with the codec `gzip/csv`.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar", - "gzip/csv" - ], - "is_deprecated": true, + "is_advanced": true, "is_optional": true, - "kind": "scalar", - "name": "codec", - "type": "string" - }, - { - "default": 1000000, - "is_deprecated": true, - "kind": "scalar", - "name": "max_buffer", - "type": "int" - }, - { - "default": { - "to_the_end": {} - }, - "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", - "is_optional": true, - "kind": "scalar", - "name": "scanner", - "type": "scanner", - "version": "4.25.0" - }, - { - "default": false, - "description": "Whether to delete downloaded objects from the bucket once they are processed.", - "is_advanced": true, - "kind": "scalar", - "name": "delete_objects", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```\n- gcs_key\n- gcs_bucket\n- gcs_last_modified\n- gcs_last_modified_unix\n- gcs_content_type\n- gcs_content_encoding\n- All user defined metadata\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n=== Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to GCP services. You can find out more in xref:guides:cloud/gcp.adoc[].", - "name": "gcp_cloud_storage", - "plugin": true, - "status": "beta", - "summary": "Downloads objects within a Google Cloud Storage bucket, optionally filtered by a prefix.", - "type": "input", - "version": "3.43.0" - }, - { - "categories": [ - "Services", - "GCP" - ], - "config": { - "children": [ - { - "description": "The project ID of the target subscription.", - "kind": "scalar", - "name": "project", - "type": "string" - }, - { - "default": "", - "description": "An optional field to set Google Service Account Credentials json.", - "is_secret": true, - "kind": "scalar", - "name": "credentials_json", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "The target subscription ID.", - "kind": "scalar", - "name": "subscription", - "type": "string" - }, - { - "default": "", - "description": "An optional endpoint to override the default of `pubsub.googleapis.com:443`. This can be used to connect to a region specific pubsub endpoint. For a list of valid values, see https://cloud.google.com/pubsub/docs/reference/service_apis_overview#list_of_regional_endpoints[this document^].", - "examples": [ - "us-central1-pubsub.googleapis.com:443", - "us-west3-pubsub.googleapis.com:443" - ], - "kind": "scalar", - "name": "endpoint", - "type": "string" - }, - { - "default": false, - "description": "Enable synchronous pull mode.", - "kind": "scalar", - "name": "sync", - "type": "bool" - }, - { - "default": 1000, - "description": "The maximum number of outstanding pending messages to be consumed at a given time.", - "kind": "scalar", - "name": "max_outstanding_messages", - "type": "int" - }, - { - "default": 1000000000, - "description": "The maximum number of outstanding pending messages to be consumed measured in bytes.", - "kind": "scalar", - "name": "max_outstanding_bytes", - "type": "int" - }, - { - "children": [ - { - "default": false, - "description": "Whether to configure subscription or not.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "Defines the topic that the subscription should be vinculated to.", - "is_advanced": true, - "kind": "scalar", - "name": "topic", - "type": "string" - } - ], - "description": "Allows you to configure the input subscription and creates if it doesn't exist.", - "is_advanced": true, - "kind": "scalar", - "name": "create_subscription", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nFor information on how to set up credentials see https://cloud.google.com/docs/authentication/production[this guide^].\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- gcp_pubsub_publish_time_unix - The time at which the message was published to the topic.\n- gcp_pubsub_delivery_attempt - When dead lettering is enabled, this is set to the number of times PubSub has attempted to deliver a message.\n- gcp_pubsub_message_id - The unique identifier of the message.\n- gcp_pubsub_ordering_key - The ordering key of the message.\n- All message attributes\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n", - "name": "gcp_pubsub", - "plugin": true, - "status": "stable", - "summary": "Consumes messages from a GCP Cloud Pub/Sub subscription.", - "type": "input" - }, - { - "categories": [ - "Services", - "GCP" - ], - "config": { - "children": [ - { - "default": "", - "description": "Base64 encoded GCP service account JSON credentials file for authentication. If not provided, Application Default Credentials (ADC) will be used.", - "is_optional": true, - "kind": "scalar", - "name": "credentials_json", - "type": "string" - }, - { - "description": "GCP project ID containing the Spanner instance", - "kind": "scalar", - "name": "project_id", - "type": "string" - }, - { - "description": "Spanner instance ID", - "kind": "scalar", - "name": "instance_id", - "type": "string" - }, - { - "description": "Spanner database ID", - "kind": "scalar", - "name": "database_id", - "type": "string" - }, - { - "description": "The name of the change stream to track, the stream must exist in the database. To create a change stream, see https://cloud.google.com/spanner/docs/change-streams/manage.", - "kind": "scalar", - "name": "stream_id", - "type": "string" - }, - { - "default": "", - "description": "RFC3339 formatted inclusive timestamp to start reading from the change stream (default: current time)", - "examples": [ - "2022-01-01T00:00:00Z" - ], - "is_optional": true, - "kind": "scalar", - "name": "start_timestamp", - "type": "string" - }, - { - "default": "", - "description": "RFC3339 formatted exclusive timestamp to stop reading at (default: no end time)", - "examples": [ - "2022-01-01T00:00:00Z" - ], - "is_optional": true, - "kind": "scalar", - "name": "end_timestamp", - "type": "string" - }, - { - "default": "10s", - "description": "Duration string for heartbeat interval", - "is_advanced": true, - "kind": "scalar", - "name": "heartbeat_interval", - "type": "string" - }, - { - "default": "", - "description": "The table to store metadata in (default: cdc_metadata_)", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "metadata_table", - "type": "string" - }, - { - "default": "5s", - "description": "Duration string for frequency of querying Spanner for minimum watermark.", - "is_advanced": true, - "kind": "scalar", - "name": "min_watermark_cache_ttl", - "type": "string" - }, - { - "description": "List of modification types to process. If not specified, all modification types are processed. Allowed values: INSERT, UPDATE, DELETE", - "examples": [ - [ - "INSERT", - "UPDATE", - "DELETE" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "allowed_mod_types", + "kind": "array", + "name": "allowed_mod_types", "type": "string" }, { @@ -15248,7 +20169,7 @@ "description": "\nConsumes change records from a Google Cloud Spanner change stream. This input allows\nyou to track and process database changes in real-time, making it useful for data\nreplication, event-driven architectures, and maintaining derived data stores.\n\nThe input reads from a specified change stream within a Spanner database and converts\neach change record into a message. The message payload contains the change records in\nJSON format, and metadata is added with details about the Spanner instance, database,\nand stream.\n\nChange streams provide a way to track mutations to your Spanner database tables. For\nmore information about Spanner change streams, refer to the Google Cloud documentation:\nhttps://cloud.google.com/spanner/docs/change-streams\n", "name": "gcp_spanner_cdc", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Creates an input that consumes from a spanner change stream.", "type": "input", "version": "4.56.0" @@ -15507,51 +20428,11 @@ "description": "\nThe git input clones the specified repository (or pulls updates if already cloned) and reads \nthe content of the specified file. It periodically polls the repository for new commits and emits \na message when changes are detected.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- git_file_path\n- git_file_size\n- git_file_mode\n- git_file_modified\n- git_commit\n- git_mime_type\n- git_is_binary\n- git_encoding (present if the file was base64 encoded)\n- git_deleted (only present if the file was deleted)\n\nYou can access these metadata fields using function interpolation.", "name": "git", "plugin": true, - "status": "beta", + "status": "stable", "summary": "A Git input that clones (or pulls) a repository and reads the repository contents.", "type": "input", "version": "4.51.0" }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A list of target host addresses to connect to.", - "examples": [ - "localhost:9000" - ], - "kind": "array", - "name": "hosts", - "type": "string" - }, - { - "default": "", - "description": "A user ID to connect as.", - "kind": "scalar", - "name": "user", - "type": "string" - }, - { - "description": "The directory to consume from.", - "kind": "scalar", - "name": "directory", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- hdfs_name\n- hdfs_path\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", - "name": "hdfs", - "plugin": true, - "status": "stable", - "summary": "Reads files from a HDFS directory, where each discrete file will be consumed as a single message payload.", - "type": "input" - }, { "categories": [ "Network" @@ -16484,6 +21365,32 @@ "kind": "scalar", "name": "sync_response", "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Enable SO_REUSEADDR, allowing binding to ports in TIME_WAIT state. Useful for graceful restarts and config reloads where the server needs to rebind to the same port immediately after shutdown.", + "is_advanced": true, + "kind": "scalar", + "name": "reuse_addr", + "type": "bool" + }, + { + "default": false, + "description": "Enable SO_REUSEPORT, allowing multiple sockets to bind to the same port for load balancing across multiple processes/threads.", + "is_advanced": true, + "kind": "scalar", + "name": "reuse_port", + "type": "bool" + } + ], + "description": "TCP listener configuration for the HTTP server. Only valid with a custom `address`.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" } ], "kind": "scalar", @@ -16525,6 +21432,619 @@ "status": "stable", "type": "input" }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "children": [ + { + "description": "Email or username of the Jira account.", + "kind": "scalar", + "name": "email", + "type": "string" + }, + { + "description": "Jira API token.", + "is_secret": true, + "kind": "scalar", + "name": "api_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "API token authentication.", + "kind": "scalar", + "name": "auth", + "type": "object" + }, + { + "default": "issues", + "description": "Which Jira resource to emit.", + "kind": "scalar", + "linter": "\nlet options = {\n \"issues\": true,\n \"comments\": true,\n \"changelog\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "resource", + "options": [ + "issues", + "comments", + "changelog" + ], + "type": "string" + }, + { + "default": "", + "description": "Jira JQL filter. The input appends an `updated >= cursor` predicate and `ORDER BY updated ASC, key ASC`. Empty means all issues visible to the principal.", + "kind": "scalar", + "name": "jql", + "type": "string" + }, + { + "default": [ + "*all" + ], + "description": "Jira `fields` query parameter - narrow this for throughput.", + "kind": "array", + "name": "fields", + "type": "string" + }, + { + "default": [], + "description": "Jira `expand` query parameter. The input automatically adds `changelog` when resource=changelog.", + "kind": "array", + "name": "expand", + "type": "string" + }, + { + "default": 50, + "description": "Issues per Jira page (Jira max 100).", + "kind": "scalar", + "name": "page_size", + "type": "int" + }, + { + "default": "60s", + "description": "Time to wait between polls once the input has caught up. Minimum 10s.", + "kind": "scalar", + "name": "poll_interval", + "type": "string" + }, + { + "children": [ + { + "description": "Name of a cache resource used to persist the cursor.", + "kind": "scalar", + "name": "cache", + "type": "string" + }, + { + "default": "", + "description": "Cache key. Defaults to `redpanda_connect_jira_input_`.", + "kind": "scalar", + "name": "key", + "type": "string" + }, + { + "default": "60s", + "description": "Widens `updated >= cursor - overlap` to absorb minute-boundary effects. Jira JQL's `updated` operator has minute precision, so this should be set to at least 1m to have an effect.", + "kind": "scalar", + "name": "overlap", + "type": "string" + } + ], + "description": "Cursor checkpoint storage.", + "kind": "scalar", + "name": "cursor", + "type": "object" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", + "type": "bool" + }, + { + "description": "Base URL of the target service (e.g., https://api.example.com). TLS is enabled automatically for https URLs.", + "kind": "scalar", + "name": "base_url", + "type": "string" + }, + { + "default": "5s", + "description": "HTTP request timeout.", + "kind": "scalar", + "name": "timeout", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "default": "", + "description": "HTTP proxy URL. Empty string disables proxying.", + "is_advanced": true, + "kind": "scalar", + "name": "proxy_url", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_http2", + "type": "bool" + }, + { + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_limit", + "type": "float" + }, + { + "default": 1, + "description": "Maximum burst size for rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_burst", + "type": "int" + }, + { + "children": [ + { + "default": "1s", + "description": "Initial interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", + "is_advanced": true, + "kind": "scalar", + "name": "backoff", + "type": "object" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } + ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "h2", + "type": "object" + } + ], + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "Periodically queries Jira's REST API using a JQL filter and emits one message per resource. The cursor (max issue `updated` timestamp, plus the set of issue versions already emitted at the boundary) is persisted via the configured cache resource after every fully-acknowledged page, so progress survives restarts — including mid-backfill — and boundary issues are not re-emitted on every poll.\n\nAuthentication uses API token (email + token) basic auth. The `backoff` settings govern the adaptive backoff applied to 429 responses; retries of 502/503/504 responses use a fixed three-attempt policy.\n\nEach message body is the raw JSON of the resource. Metadata fields:\n\n- `jira_id` - issue key (issues) / comment ID / changelog history ID\n- `jira_issue_key` - parent issue key (omitted for resource=issues)\n- `jira_project` - project key\n- `jira_updated` - RFC 3339 timestamp of the resource\n- `jira_event_type` - \"issue\" / \"comment\" / \"changelog\"\n- `jira_self` - Jira API URL of the resource\n\nLimitations (v1): OAuth and the worklogs resource are not yet supported. For resource=comments and resource=changelog, only the first page of child resources (up to ~50 comments or ~100 changelog entries per issue update) is emitted; a WARN is logged when truncation is detected. Use a downstream Jira processor to fetch the full child set if your issues exceed this limit.", + "name": "jira", + "plugin": true, + "status": "stable", + "summary": "Streams Jira issues, comments, or changelog entries via JQL with incremental polling.", + "type": "input", + "version": "4.96.0" + }, { "categories": [ "Services" @@ -17052,7 +22572,7 @@ "description": "\nOffsets are managed within Kafka under the specified consumer group, and partitions for each topic are automatically balanced across members of the consumer group.\n\nThe Kafka input allows parallel processing of messages from different topic partitions, and messages of the same topic partition are processed with a maximum parallelism determined by the field <>.\n\nIn order to enforce ordered processing of partition messages set the > to `1` and this will force partitions to be processed in lock-step, where a message will only be processed once the prior message is delivered.\n\nBatching messages before processing can be enabled using the <> field, and this batching is performed per-partition such that messages of a batch will always originate from the same partition. This batching mechanism is capable of creating batches of greater size than the <>, in which case the next batch will only be created upon delivery of the current one.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All existing message headers (version 0.11+)\n\nThe field `kafka_lag` is the calculated difference between the high water mark offset of the partition at the time of ingestion and the current message offset.\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Ordering\n\nBy default messages of a topic partition can be processed in parallel, up to a limit determined by the field `checkpoint_limit`. However, if strict ordered processing is required then this value must be set to 1 in order to process shard messages in lock-step. When doing so it is recommended that you perform batching at this component for performance as it will not be possible to batch lock-stepped messages at the output level.\n\n== Troubleshooting\n\nIf you're seeing issues writing to or reading from Kafka with this component then it's worth trying out the newer xref:components:inputs/kafka_franz.adoc[`kafka_franz` input].\n\n- I'm seeing logs that report `Failed to connect to kafka: kafka: client has run out of available brokers to talk to (Is your cluster reachable?)`, but the brokers are definitely reachable.\n\nUnfortunately this error message will appear for a wide range of connection problems even when the broker endpoint can be reached. Double check your authentication configuration and also ensure that you have <> if applicable.", "name": "kafka", "plugin": true, - "status": "stable", + "status": "deprecated", "summary": "Connects to Kafka brokers and consumes one or more topics.", "type": "input" }, @@ -17081,7 +22601,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -17234,6 +22754,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -17250,7 +22774,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -17309,9 +22833,69 @@ { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, - "is_optional": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", "name": "profile", "type": "string" @@ -17401,7 +22985,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -17424,6 +23008,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "description": "\nA list of topics to consume from. Multiple comma separated topics can be listed in a single element. When a `consumer_group` is specified partitions are automatically distributed across consumers of a topic, otherwise all partitions are consumed.\n\nAlternatively, it's possible to specify explicit partitions to consume from with a colon after the topic name, e.g. `foo:0` would consume the partition 0 of the topic foo. This syntax supports ranges, e.g. `foo:0-10` would consume partitions 0 through to 10 inclusive.\n\nFinally, it's also possible to specify an explicit offset to consume from by adding another colon after the partition, e.g. `foo:0:10` would consume the partition 0 of the topic foo starting from the offset 10. If the offset is not present (or remains unspecified) then the field `start_from_oldest` determines which offset to start from.", "examples": [ @@ -17449,17 +23093,42 @@ "foo:0-5" ] ], + "is_optional": true, "kind": "array", "name": "topics", "type": "string" }, { "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.", + "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.\n\nThis field is deprecated, use `regexp_topics_include` instead.", + "is_deprecated": true, "kind": "scalar", "name": "regexp_topics", "type": "bool" }, + { + "description": "A list of regular expression patterns for matching topics to consume from. When specified, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. This enables regex mode and cannot be used together with the `topics` field. Use `regexp_topics_exclude` to exclude specific patterns.", + "examples": [ + [ + "logs_.*", + "metrics_.*" + ], + [ + "events_[0-9]+" + ] + ], + "is_optional": true, + "kind": "array", + "name": "regexp_topics_include", + "type": "string" + }, + { + "description": "A list of regular expression patterns for excluding topics when regex mode is enabled (via `regexp_topics` or `regexp_topics_include`). Topics matching any of these patterns will be excluded from consumption, even if they match include patterns.", + "is_optional": true, + "kind": "array", + "name": "regexp_topics_exclude", + "type": "string" + }, { "default": "", "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", @@ -17486,7 +23155,7 @@ }, { "default": "1m", - "description": "When using a consumer group, `session_timeout` sets how long a member in hte group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", + "description": "When using a consumer group, `session_timeout` sets how long a member in the group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", "is_advanced": true, "kind": "scalar", "name": "session_timeout", @@ -17494,7 +23163,7 @@ }, { "default": "3s", - "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's sesion stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", + "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's session stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", "is_advanced": true, "kind": "scalar", "name": "heartbeat_interval", @@ -17735,18 +23404,242 @@ } ], "kind": "scalar", - "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", + "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\nlet has_topics = this.topics.length() > 0\nlet has_regexp_topics_include = this.regexp_topics_include.length() > 0 \nlet is_regex_mode = this.regexp_topics || $has_regexp_topics_include\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n if !$has_topics && !$has_regexp_topics_include {\n \"either topics or regexp_topics_include must be specified\"\n },\n if $has_topics && $has_regexp_topics_include {\n \"cannot specify both topics and regexp_topics_include, use one or the other\"\n },\n if this.regexp_topics_exclude.length() > 0 && !$is_regex_mode {\n \"regexp_topics_exclude can only be used when regexp_topics is set to true or regexp_topics_include is specified\"\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", "name": "", "type": "object" }, "description": "\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\nThis input often out-performs the traditional `kafka` input as well as providing more useful logs and error messages.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", "name": "kafka_franz", "plugin": true, - "status": "beta", + "status": "deprecated", "summary": "A Kafka input using the https://github.com/twmb/franz-go[Franz Kafka client library^].", "type": "input", "version": "3.61.0" }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "The connection string of the Microsoft SQL Server database to connect to.", + "examples": [ + "sqlserver://username:password@host/instance?param1=value¶m2=value" + ], + "kind": "scalar", + "name": "connection_string", + "type": "string" + }, + { + "default": false, + "description": "If set to true, the connector will query all the existing data as a part of snapshot process. Otherwise, it will start from the current Log Sequence Number position.", + "examples": [ + true + ], + "kind": "scalar", + "name": "stream_snapshot", + "type": "bool" + }, + { + "default": 1, + "description": "Specifies a number of tables that will be processed in parallel during the snapshot processing stage.", + "kind": "scalar", + "name": "max_parallel_snapshot_tables", + "type": "int" + }, + { + "default": 1000, + "description": "The maximum number of rows to be streamed in a single batch when taking a snapshot.", + "kind": "scalar", + "name": "snapshot_max_batch_size", + "type": "int" + }, + { + "description": "Regular expressions for tables to include.", + "examples": [ + "dbo.products" + ], + "kind": "array", + "name": "include", + "type": "string" + }, + { + "description": "Regular expressions for tables to exclude.", + "examples": [ + "dbo.privatetable" + ], + "is_optional": true, + "kind": "array", + "name": "exclude", + "type": "string" + }, + { + "description": "A https://www.docs.redpanda.com/redpanda-connect/components/caches/about[cache resource^] to use for storing the current Log Sequence Number (LSN) that has been successfully delivered, this allows Redpanda Connect to continue from that Log Sequence Number (LSN) upon restart, rather than consume the entire state of the change table. If not set the default Microsoft SQL Server based cache will be used, see `checkpoint_cache_table_name` for more information.", + "is_optional": true, + "kind": "scalar", + "name": "checkpoint_cache", + "type": "string" + }, + { + "default": "rpcn.CdcCheckpointCache", + "description": "The multipart identifier for the checkpoint cache table name. If no `checkpoint_cache` field is specified, this input will automatically create a table and stored procedure under the `rpcn` schema to act as a checkpoint cache. This table stores the latest processed Log Sequence Number (LSN) that has been successfully delivered, allowing Redpanda Connect to resume from that point upon restart rather than reconsume the entire change table.", + "examples": [ + "dbo.checkpoint_cache" + ], + "is_optional": true, + "kind": "scalar", + "name": "checkpoint_cache_table_name", + "type": "string" + }, + { + "description": "An optional connection string for a remote Microsoft SQL Server to use for the checkpoint cache. When set, the checkpoint cache table is created on this remote server instead of the source database. If `checkpoint_cache` is also set, that takes precedence.", + "examples": [ + "sqlserver://username:password@remotehost/instance?param1=value¶m2=value" + ], + "is_optional": true, + "kind": "scalar", + "name": "checkpoint_cache_connection_string", + "type": "string" + }, + { + "default": "microsoft_sql_server_cdc", + "description": "The key to use to store the snapshot position in `checkpoint_cache`. An alternative key can be provided if multiple CDC inputs share the same cache.", + "is_optional": true, + "kind": "scalar", + "name": "checkpoint_cache_key", + "type": "string" + }, + { + "default": 1024, + "description": "The maximum number of messages that can be processed at a given time. Increasing this limit enables parallel processing and batching at the output level. Any given Log Sequence Number (LSN) will not be acknowledged unless all messages under that offset are delivered in order to preserve at least once delivery guarantees.", + "kind": "scalar", + "name": "checkpoint_limit", + "type": "int" + }, + { + "default": "5s", + "description": "The interval between attempts to check for new changes once all data is processed. For low traffic tables increasing this value can reduce network traffic to the server.", + "examples": [ + "5s", + "1m" + ], + "kind": "scalar", + "name": "stream_backoff_interval", + "type": "string" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", + "type": "bool" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "Streams changes from a Microsoft SQL Server database for Change Data Capture (CDC).\nAdditionally, if `stream_snapshot` is set to true, then the existing data in the database is also streamed too.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n- database_schema (The database schema for the table where the message originates from)\n- schema (The table schema in benthos common schema format, compatible with processors like parquet_encode)\n- table (Name of the table that the message originated from)\n- operation (Type of operation that generated the message: \"read\", \"delete\", \"insert\", or \"update_before\" and \"update_after\". \"read\" is from messages that are read in the initial snapshot phase.)\n- lsn (the Log Sequence Number in Microsoft SQL Server)\n\n== Permissions\n\nWhen using the default Microsoft SQL Server based cache, the Connect user requires permission to create tables and stored procedures, and the rpcn schema must already exist. Refer to `checkpoint_cache_table_name` for more information.\n\t\t", + "name": "microsoft_sql_server_cdc", + "plugin": true, + "status": "stable", + "summary": "Enables Change Data Capture by consuming from Microsoft SQL Server's change tables.", + "type": "input", + "version": "0.0.1" + }, { "categories": [ "Services" @@ -17894,7 +23787,7 @@ "description": "Once the documents from the query are exhausted, this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", "name": "mongodb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Executes a query and creates a message for each document received.", "type": "input", "version": "3.64.0" @@ -17997,7 +23890,7 @@ }, { "default": false, - "description": "If true, determine parallel snapshot chunks using `$bucketAuto` instead of the `splitVector` command. This allows parallel collection reading in environments where privledged access to the MongoDB cluster is not allowed such as MongoDB Atlas.", + "description": "If true, determine parallel snapshot chunks using `$bucketAuto` instead of the `splitVector` command. This allows parallel collection reading in environments where privileged access to the MongoDB cluster is not allowed such as MongoDB Atlas.", "is_advanced": true, "kind": "scalar", "name": "snapshot_auto_bucket_sharding", @@ -18065,10 +23958,10 @@ "name": "", "type": "object" }, - "description": "Read from a MongoDB replica set using https://www.mongodb.com/docs/manual/changeStreams/[^Change Streams]. It's only possible to watch for changes when using a sharded MongoDB or a MongoDB cluster running as a replica set.\n\nBy default MongoDB does not propagate changes in all cases. In order to capture all changes (including deletes) in a MongoDB cluster one needs to enable pre and post image saving and the collection needs to also enable saving these pre and post images. For more information see https://www.mongodb.com/docs/manual/changeStreams/#change-streams-with-document-pre--and-post-images[^MongoDB documentation].\n\n== Metadata\n\nEach message omitted by this plugin has the following metadata:\n\n- operation: either \"create\", \"replace\", \"delete\" or \"update\" for changes streamed. Documents from the initial snapshot have the operation set to \"read\".\n- collection: the collection the document was written to.\n- operation_time: the oplog time for when this operation occurred.\n ", + "description": "Read from a MongoDB replica set using https://www.mongodb.com/docs/manual/changeStreams/[^Change Streams]. It's only possible to watch for changes when using a sharded MongoDB or a MongoDB cluster running as a replica set.\n\nBy default MongoDB does not propagate changes in all cases. In order to capture all changes (including deletes) in a MongoDB cluster one needs to enable pre and post image saving and the collection needs to also enable saving these pre and post images. For more information see https://www.mongodb.com/docs/manual/changeStreams/#change-streams-with-document-pre--and-post-images[^MongoDB documentation].\n\n== Metadata\n\nEach message emitted by this plugin has the following metadata:\n\n- operation: either \"insert\", \"replace\", \"delete\" or \"update\" for changes streamed. Documents from the initial snapshot have the operation set to \"read\".\n- collection: the collection the document was written to.\n- operation_time: the oplog time for when this operation occurred.\n- schema: the collection schema in benthos common schema format (set as immutable metadata). Extracted from the collection's `$jsonSchema` validator if available, otherwise inferred from the first document seen. Not present on messages where no schema could be determined (e.g. deletes without pre-images when no prior schema is cached).\n\n== Schema Detection\n\nSchema metadata is discovered using a two-tier strategy:\n\n1. *$jsonSchema validators* are preferred and queried at startup for each watched collection. When a validator exists, the schema provides accurate type information and required/optional field classification.\n2. When no validator exists, schema is *inferred from the first document* received per collection. All fields are marked optional.\n\n*Change detection:* when a document's top-level field set differs from the cached schema, the schema is re-inferred from that document. This applies to both validator-sourced and inference-sourced schemas.\n\n*Limitations:* type changes within existing fields and structural changes inside nested subdocuments are not detected automatically. Restart the input to force a full schema refresh.\n\n*Fields with null values, unknown BSON types, or mixed-type arrays* are mapped to the `Any` schema type. The `parquet_encode` processor does not support `Any` and will error if it encounters one. Add an upstream processor (e.g. `mapping`) to convert or remove these fields before `parquet_encode`.\n\n*Schema stability:* MongoDB collections may contain documents with varying field sets. When this occurs, the schema updates on each structural change, which can cause frequent schema version bumps in schema registries with compatibility modes. For schema registry targets, configuring a `$jsonSchema` validator on the collection is strongly recommended.\n ", "name": "mongodb_cdc", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Streams changes from a MongoDB replica set.", "type": "input" }, @@ -18436,12 +24329,28 @@ "name": "snapshot_max_batch_size", "type": "int" }, + { + "default": 10, + "description": "The maximum number of attempts the MySQL driver will try to re-establish a broken connection before Connect attempts reconnection. A zero or negative number means infinite retry attempts.", + "is_advanced": true, + "kind": "scalar", + "name": "max_reconnect_attempts", + "type": "int" + }, { "description": "If set to true, the connector will query all the existing data as a part of snapshot process. Otherwise, it will start from the current binlog position.", "kind": "scalar", "name": "stream_snapshot", "type": "bool" }, + { + "default": 1, + "description": "Specifies the number of tables that will be snapshotted in parallel.", + "kind": "scalar", + "linter": "root = if this < 1 { [ \"max_parallel_snapshot_tables must be at least 1\" ] }", + "name": "max_parallel_snapshot_tables", + "type": "int" + }, { "default": true, "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", @@ -18456,6 +24365,231 @@ "name": "checkpoint_limit", "type": "int" }, + { + "children": [ + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Using this field overrides the SSL/TLS settings in the environment and DSN.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Enable AWS IAM authentication for MySQL. When enabled, an IAM authentication token is generated and used as the password. When using IAM authentication ensure `max_reconnect_attempts` is set to a low value to ensure it can refresh credentials.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "description": "The AWS region where the MySQL instance is located. If no region is specified then the environment default will be used.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "The MySQL endpoint hostname (e.g., mydb.abc123.us-east-1.rds.amazonaws.com).", + "is_advanced": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Optional AWS IAM role ARN to assume for authentication. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "Optional external ID for the role assumption. Only used with the `role` field. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "AWS IAM role ARN to assume.", + "is_advanced": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "default": "", + "description": "Optional external ID for the role assumption.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional array of AWS IAM roles to assume for authentication. Roles can be assumed in sequence, enabling chaining for purposes such as cross-account access. Each role can optionally specify an external ID.", + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "roles", + "type": "object" + } + ], + "description": "AWS IAM authentication configuration for MySQL instances. When enabled, IAM credentials are used to generate temporary authentication tokens instead of a static password.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "aws", + "type": "object" + }, { "children": [ { @@ -18553,80 +24687,14 @@ "name": "", "type": "object" }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- operation\n- table\n- binlog_position\n", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- operation: The type of operation (insert, update, delete, or read for snapshot messages)\n- table: The name of the table\n- binlog_position: The binlog position (for CDC messages only, not set for snapshot messages)\n- schema: The table schema in benthos common schema format, compatible with processors like parquet_encode\n", "name": "mysql_cdc", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Enables MySQL streaming for RedPanda Connect.", "type": "input", "version": "4.45.0" }, - { - "categories": [ - "Network" - ], - "config": { - "children": [ - { - "description": "A list of URLs to connect to (or as). If an item of the list contains commas it will be expanded into multiple URLs.", - "kind": "array", - "name": "urls", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": true, - "description": "Whether the URLs provided should be connected to, or bound as.", - "kind": "scalar", - "name": "bind", - "type": "bool" - }, - { - "default": "PULL", - "description": "The socket type to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"pull\": true,\n \"sub\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "socket_type", - "options": [ - "PULL", - "SUB" - ], - "type": "string" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "default": [], - "description": "A list of subscription topic filters to use when consuming from a SUB socket. Specifying a single sub_filter of `''` will subscribe to everything.", - "kind": "array", - "name": "sub_filters", - "type": "string" - }, - { - "default": "5s", - "description": "The period to wait until a poll is abandoned and reattempted.", - "is_advanced": true, - "kind": "scalar", - "name": "poll_timeout", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Currently only PULL and SUB sockets are supported.", - "name": "nanomsg", - "plugin": true, - "status": "stable", - "summary": "Consumes messages via Nanomsg sockets (scalability protocols).", - "type": "input" - }, { "categories": [ "Services" @@ -18903,6 +24971,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -18930,7 +25026,7 @@ "name": "", "type": "object" }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_reply_subject\n- All message headers (when supported by the connection)\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_reply_subject\n- All message headers (when supported by the connection)\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats", "plugin": true, "status": "stable", @@ -19006,6 +25102,14 @@ "name": "bind", "type": "bool" }, + { + "default": false, + "description": "Whether to automatically create the stream if it doesn't exist (requires the stream field to be set).", + "is_advanced": true, + "kind": "scalar", + "name": "create_stream", + "type": "bool" + }, { "annotated_options": [ [ @@ -19247,6 +25351,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -19275,7 +25407,7 @@ "name": "", "type": "object" }, - "description": "\n== Consume mirrored streams\n\nIn the case where a stream being consumed is mirrored from a different JetStream domain the stream cannot be resolved from the subject name alone, and so the stream name as well as the subject (if applicable) must both be specified.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_sequence_stream\n- nats_sequence_consumer\n- nats_num_delivered\n- nats_num_pending\n- nats_domain\n- nats_timestamp_unix_nano\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\n== Consume mirrored streams\n\nIn the case where a stream being consumed is mirrored from a different JetStream domain the stream cannot be resolved from the subject name alone, and so the stream name as well as the subject (if applicable) must both be specified.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_sequence_stream\n- nats_sequence_consumer\n- nats_num_delivered\n- nats_num_pending\n- nats_domain\n- nats_timestamp_unix_nano\n- nats_consumer\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_jetstream", "plugin": true, "status": "stable", @@ -19559,6 +25691,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -19572,10 +25732,10 @@ "name": "", "type": "object" }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_delta\n- nats_kv_operation\n- nats_kv_created\n```\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_delta\n- nats_kv_operation\n- nats_kv_created\n```\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_kv", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Watches for updates in a NATS key-value bucket.", "type": "input", "version": "4.12.0" @@ -19587,539 +25747,460 @@ "config": { "children": [ { - "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", + "description": "The connection string of the Oracle database to connect to. Additional connection options can be supplied as URL query parameters, for example: `oracle://user:password@host:1522/service?WALLET=/opt/oracle/wallet&SSL=true`.", "examples": [ - [ - "nats://127.0.0.1:4222" - ], - [ - "nats://username:password@127.0.0.1:4222" - ] + "oracle://username:password@host:port/service_name", + "oracle://user:password@host:1522/service?WALLET=/opt/oracle/wallet&SSL=true" ], - "kind": "array", - "name": "urls", - "type": "string" - }, - { - "description": "The maximum number of times to attempt to reconnect to the server. If negative, it will never stop trying to reconnect.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "max_reconnects", - "type": "int" - }, - { - "description": "The ID of the cluster to consume from.", - "kind": "scalar", - "name": "cluster_id", - "type": "string" - }, - { - "default": "", - "description": "A client ID to connect as.", - "kind": "scalar", - "name": "client_id", - "type": "string" - }, - { - "default": "", - "description": "The queue to consume from.", "kind": "scalar", - "name": "queue", + "name": "connection_string", "type": "string" }, { - "default": "", - "description": "A subject to consume from.", + "description": "Path to the Oracle Wallet directory. When set, SSL is enabled automatically. The directory must contain either `cwallet.sso` (auto-login, no password required) or `ewallet.p12` (requires `wallet_password`).", + "examples": [ + "/opt/oracle/wallet" + ], + "is_optional": true, "kind": "scalar", - "name": "subject", + "name": "wallet_path", "type": "string" }, { - "default": "", - "description": "Preserve the state of your consumer under a durable name.", + "description": "Password for the `ewallet.p12` PKCS#12 wallet file. Only required when the wallet directory contains `ewallet.p12` rather than `cwallet.sso`.", + "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "durable_name", + "name": "wallet_password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": false, - "description": "Whether the subscription should be destroyed when this client disconnects.", + "description": "If set to true, the connector will query all the existing data as a part of snapshot process. Otherwise, it will start from the current System Change Number position.", + "examples": [ + true + ], + "is_deprecated": true, "kind": "scalar", - "name": "unsubscribe_on_close", + "name": "stream_snapshot", "type": "bool" }, { - "default": true, - "description": "If a position is not found for a queue, determines whether to consume from the oldest available message, otherwise messages are consumed from the latest.", - "is_advanced": true, + "description": "Controls snapshot behaviour. `none` (default) skips snapshotting and starts streaming from the current SCN. `snapshot_only` performs a full snapshot, persists the SCN checkpoint, then stops without streaming. `snapshot_and_stream` performs a full snapshot then transitions to streaming.", + "is_optional": true, "kind": "scalar", - "name": "start_from_oldest", - "type": "bool" + "linter": "\nlet options = {\n \"none\": true,\n \"snapshot_only\": true,\n \"snapshot_and_stream\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "snapshot_mode", + "options": [ + "none", + "snapshot_only", + "snapshot_and_stream" + ], + "type": "string", + "version": "4.99.0" }, { - "default": 1024, - "description": "The maximum number of unprocessed messages to fetch at a given time.", - "is_advanced": true, + "default": 1, + "description": "Specifies a number of tables that will be processed in parallel during the snapshot processing stage.", "kind": "scalar", - "name": "max_inflight", + "name": "max_parallel_snapshot_tables", "type": "int" }, { - "default": "30s", - "description": "An optional duration to specify at which a message that is yet to be acked will be automatically retried.", - "is_advanced": true, + "default": 1000, + "description": "The maximum number of rows to be streamed in a single batch when taking a snapshot.", "kind": "scalar", - "name": "ack_wait", - "type": "string" + "name": "snapshot_max_batch_size", + "type": "int" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, + "default": 20000, + "description": "The SCN range to mine per cycle. Each cycle reads changes between the current SCN and current SCN + scn_window_size. Smaller values mean more frequent queries with lower memory usage but higher overhead; larger values reduce query frequency and improve throughput at the cost of higher memory usage per cycle.", "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "scn_window_size", + "type": "int" }, { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, + "default": 1000, + "description": "The minimum SCN gap required before starting a new LogMiner session. When the gap between the connector's current position and the database's current SCN is smaller than this value, the mining cycle is skipped and the connector backs off instead. This prevents excessive LogMiner start/stop cycles on low-traffic databases where Oracle background activity advances the SCN without producing relevant events. Set to 0 to disable.", "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" + "name": "min_scn_window_size", + "type": "int" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, + "default": 100000, + "description": "The maximum SCN range that can be mined in a single cycle. The window starts at scn_window_size and grows by scn_window_size each cycle that ends at the cap (backlog present), up to this limit. It shrinks by the same step each cycle that catches up to the database. This allows the connector to automatically mine larger windows during heavy backlog and smaller windows during steady state.", "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "max_scn_window_size", + "type": "int" }, { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "default": "5s", + "description": "The interval between attempts to check for new changes once all data is processed. For low traffic tables increasing this value can reduce network traffic to the server.", "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "5s", + "1m" ], - "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "backoff_interval", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "default": "300ms", + "description": "The interval between mining cycles during normal operation. Controls how frequently LogMiner polls for new changes when not caught up.", "examples": [ - "./root_cas.pem" + "100ms", + "1s" ], - "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "mining_interval", "type": "string" }, { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" + "default": "online_catalog", + "description": "Controls how LogMiner retrieves data dictionary information. `online_catalog` (default) uses the current data dictionary for best performance but cannot capture DDL changes. `online_catalog` currently only supported.", + "kind": "scalar", + "name": "strategy", + "type": "string" + }, + { + "default": 0, + "description": "The maximum number of events that can be buffered for a single transaction. If a transaction exceeds this limit it is discarded and its events will not be emitted. Set to 0 to disable the limit.", + "kind": "scalar", + "name": "max_transaction_events", + "type": "int" + }, + { + "default": true, + "description": "When enabled, large object (CLOB, BLOB) columns are included in both snapshot and streaming change events. When disabled, these columns are still present but contain no values. Enabling this option introduces additional performance overhead and increases memory requirements.", + "kind": "scalar", + "name": "lob_enabled", + "type": "bool" + }, + { + "description": "A https://www.docs.redpanda.com/redpanda-connect/components/caches/about[cache resource^] to use for buffering in-flight transactions. When set, DML events are serialized and stored in the named cache rather than held in memory, reducing connector memory usage for workloads with large or long-running transactions. If not set, an in-memory buffer is used.\n\nEach in-flight transaction is stored as N+1 cache entries: one metadata key holding the transaction ID, start SCN, and event count; and one event key per DML event. A transaction with 1000 events occupies 1001 cache entries. Each AddEvent call writes exactly two keys regardless of how many events the transaction has already accumulated.\n\nThis cache is designed for low-latency stores with cheap per-operation cost. Redis and Memcached are the recommended backends. The built-in `memory:{}` cache works but provides no durability across restarts. High-latency or per-request-cost stores such as S3 or DynamoDB are not recommended - a transaction with 1000 events generates approximately 3000 cache operations across its lifetime, and because LogMiner processes events on a single goroutine, per-call latency directly reduces throughput. A backend that causes timeouts or errors will also cause the mining cycle to restart from an earlier checkpoint SCN, which can result in duplicate event delivery.", + "is_optional": true, + "kind": "scalar", + "name": "transaction_cache", + "type": "string" + }, + { + "default": "oracledb_cdc", + "description": "The key prefix used when storing transactions in `transaction_cache`. An alternative prefix must be set if multiple `oracledb_cdc` inputs share the same cache resource, since Oracle transaction IDs (USN.SLOT.SEQ) are only unique within a single Oracle instance and would otherwise collide.", + "is_optional": true, + "kind": "scalar", + "name": "transaction_cache_key", + "type": "string" } ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, + "description": "LogMiner configuration settings.", "kind": "scalar", - "name": "tls", + "name": "logminer", "type": "object" }, { - "default": false, - "description": "Perform a TLS handshake before sending the INFO protocol message.", - "is_advanced": true, + "description": "Regular expressions for tables to include.", + "examples": [ + "SCHEMA.PRODUCTS" + ], + "kind": "array", + "name": "include", + "type": "string" + }, + { + "description": "Regular expressions for tables to exclude.", + "examples": [ + "SCHEMA.PRIVATETABLE" + ], + "is_optional": true, + "kind": "array", + "name": "exclude", + "type": "string" + }, + { + "description": "A https://www.docs.redpanda.com/redpanda-connect/components/caches/about[cache resource^] to use for storing the current System Change Number (SCN) that has been successfully delivered, this allows Redpanda Connect to continue from that System Change Number (SCN) upon restart, rather than consume the entire state of OracleDB's redo logs. If not set the default Oracle based cache will be used, see `checkpoint_cache_table_name` for more information.", + "is_optional": true, "kind": "scalar", - "name": "tls_handshake_first", + "name": "checkpoint_cache", + "type": "string" + }, + { + "default": "RPCN.CDC_CHECKPOINT_CACHE", + "description": "The identifier for the checkpoint cache table name. If no `checkpoint_cache` field is specified, this input will automatically create a table and stored procedure under the `rpcn` schema to act as a checkpoint cache. This table stores the latest processed System Change Number (SCN) that has been successfully delivered, allowing Redpanda Connect to resume from that point upon restart rather than reconsume the entire redo log. When `pdb_name` is set and this field is left at its default value, the table name is automatically derived per PDB (e.g. `RPCN.CDC_CHECKPOINT_MYPDB`) to avoid SCN collisions between pipelines monitoring different PDBs. Set this field explicitly to opt out of that auto-derivation.", + "examples": [ + "RPCN.CHECKPOINT_CACHE" + ], + "is_optional": true, + "kind": "scalar", + "name": "checkpoint_cache_table_name", + "type": "string" + }, + { + "default": "oracledb_cdc", + "description": "The key to use to store the snapshot position in `checkpoint_cache`. An alternative key can be provided if multiple CDC inputs share the same cache.", + "is_optional": true, + "kind": "scalar", + "linter": "root = if this == \"\" { [ \"must not be empty\" ] } else if this.length() > 128 { [ \"must not exceed 128 characters\" ] }", + "name": "checkpoint_cache_key", + "type": "string" + }, + { + "default": 1024, + "description": "The maximum number of messages that can be processed at a given time. Increasing this limit enables parallel processing and batching at the output level. Any given System Change Number (SCN) will not be acknowledged unless all messages under that offset are delivered in order to preserve at least once delivery guarantees.", + "kind": "scalar", + "name": "checkpoint_limit", + "type": "int" + }, + { + "description": "The name of the pluggable database (PDB) to monitor. When connecting to a CDB root, LogMiner output is scoped to this PDB via SRC_CON_NAME filtering and catalog queries use ALTER SESSION SET CONTAINER to switch context. Requires GRANT SET CONTAINER TO CONTAINER=ALL.", + "is_optional": true, + "kind": "scalar", + "name": "pdb_name", + "type": "string" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", "type": "bool" }, { "children": [ { - "description": "An optional file containing a NKey seed.", - "examples": [ - "./seed.nk" - ], - "is_advanced": true, - "is_optional": true, + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", "kind": "scalar", - "name": "nkey_file", - "type": "string" + "name": "count", + "type": "int" }, { - "description": "The NKey seed.", - "examples": [ - "UDXU4RCSJNZOIQHZNWXHXORDPRTGNJAHAHFRGZNEEJCPQTT2M7NLCNF4" - ], - "is_advanced": true, - "is_optional": true, - "is_secret": true, + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", "kind": "scalar", - "name": "nkey", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string", - "version": "4.38.0" + "name": "byte_size", + "type": "int" }, { - "description": "An optional file containing user credentials which consist of an user JWT and corresponding NKey seed.", + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", "examples": [ - "./user.creds" + "1s", + "1m", + "500ms" ], - "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "user_credentials_file", + "name": "period", "type": "string" }, { - "description": "An optional plain text user JWT (given along with the corresponding user NKey Seed).", - "is_advanced": true, - "is_optional": true, - "is_secret": true, + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], "kind": "scalar", - "name": "user_jwt", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "check", "type": "string" }, { - "description": "An optional plain text user NKey Seed (given along with the corresponding user JWT).", + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], "is_advanced": true, "is_optional": true, - "is_secret": true, - "kind": "scalar", - "name": "user_nkey_seed", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "kind": "array", + "name": "processors", + "type": "processor" } ], - "description": "Optional configuration of NATS authentication parameters.", - "is_advanced": true, - "kind": "scalar", - "name": "auth", - "type": "object" - }, - { - "bloblang": true, - "description": "EXPERIMENTAL: A xref:guides:bloblang/about.adoc[Bloblang mapping] that attempts to extract an object containing tracing propagation information, which will then be used as the root tracing span for the message. The specification of the extracted fields must match the format used by the service wide tracer.", + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", "examples": [ - "root = @", - "root = this.meta.span" + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "extract_tracing_map", - "type": "string", - "version": "4.23.0" + "kind": "", + "name": "batching", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\n[CAUTION]\n.Deprecation notice\n====\nThe NATS Streaming Server is being deprecated. Critical bug fixes and security fixes will be applied until June of 2023. NATS-enabled applications requiring persistence should use https://docs.nats.io/nats-concepts/jetstream[JetStream^].\n====\n\nTracking and persisting offsets through a durable name is also optional and works with or without a queue. If a durable name is not provided then subjects are consumed from the most recently published message.\n\nWhen a consumer closes its connection it unsubscribes, when all consumers of a durable queue do this the offsets are deleted. In order to avoid this you can stop the consumers from unsubscribing by setting the field `unsubscribe_on_close` to `false`.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- nats_stream_subject\n- nats_stream_sequence\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", - "name": "nats_stream", + "description": "Streams changes from an Oracle database for Change Data Capture (CDC).\nAdditionally, if `stream_snapshot` is set to true, then the existing data in the database is also streamed too.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- database_schema: The database schema for the table where the message originates from.\n- table_name: Name of the table that the message originated from.\n- operation: Type of operation that generated the message: \"read\", \"delete\", \"insert\", or \"update\". \"read\" is from messages that are read in the initial snapshot phase.\n- scn: The System Change Number in Oracle. Messages published as part of a snapshot will contain Oracle's current SCN captured at time of snapshot.\n- transaction_id: The Oracle transaction ID in `USN.SLOT.SEQ` format, identifying the transaction that produced the change. Not present on snapshot (`read`) messages.\n- source_ts_ms: The timestamp of when Oracle wrote the change record into the redo log, expressed as milliseconds since the Unix epoch. This reflects the database server's wall-clock time at the moment the DML executed, not the transaction commit time.\n- commit_ts_ms: The timestamp of the transaction commit, expressed as milliseconds since the Unix epoch. Sourced from `V$LOGMNR_CONTENTS.TIMESTAMP` on the COMMIT redo record — this is Oracle's wall-clock time when the commit was written to the redo log, not a dedicated commit-timestamp column. For snapshot (`read`) messages, this reflects Oracle's `SYSTIMESTAMP` at the moment the snapshot SCN was captured, so all snapshot messages share the same value.\n- schema: The table schema, for use with schema-aware downstream processors such as `schema_registry_encode`. When new columns are detected in CDC events, the schema is automatically refreshed from the Oracle catalog. Dropped columns are reflected after a connector restart.\n\n== Permissions\n\nWhen using the default Oracle based cache, the Connect user requires permission to create tables and stored procedures, and the rpcn schema must already exist. Refer to `checkpoint_cache_table_name` for more information.\n\t\t", + "name": "oracledb_cdc", "plugin": true, "status": "stable", - "summary": "Subscribe to a NATS Stream subject. Joining a queue is optional and allows multiple clients of a subject to consume using queue semantics.", - "type": "input" + "summary": "Enables Change Data Capture by consuming from OracleDB.", + "type": "input", + "version": "4.83.0" }, { "categories": [ + "Network", "Services" ], "config": { "children": [ { - "description": "A list of nsqd addresses to connect to.", - "kind": "array", - "name": "nsqd_tcp_addresses", + "default": "json", + "description": "Encoding format for messages in the batch. Options: 'protobuf' or 'json'.", + "kind": "scalar", + "linter": "\nlet options = {\n \"protobuf\": true,\n \"json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "encoding", + "options": [ + "protobuf", + "json" + ], "type": "string" }, { - "description": "A list of nsqlookupd addresses to connect to.", - "kind": "array", - "name": "lookupd_http_addresses", + "default": "0.0.0.0:4317", + "description": "The address to listen on for gRPC connections.", + "kind": "scalar", + "name": "address", "type": "string" }, { "children": [ { "default": false, - "description": "Whether custom TLS settings are enabled.", + "description": "Enable TLS connections.", "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, { "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], + "description": "Path to the TLS certificate file.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "cert_file", "type": "string" }, { "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "description": "Path to the TLS key file.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "key_file", "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "TLS configuration for gRPC.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" }, { - "description": "The topic to consume from.", + "default": "", + "description": "Optional bearer token for authentication. When set, requests must include 'authorization: Bearer ' metadata.", + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "topic", + "name": "auth_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The channel to consume from.", + "default": 4194304, + "description": "Maximum size of gRPC messages to receive in bytes.", + "is_advanced": true, "kind": "scalar", - "name": "channel", - "type": "string" + "name": "max_recv_msg_size", + "type": "int" }, { - "description": "A user agent to assume when connecting.", - "is_optional": true, + "default": "", + "description": "An optional rate limit resource to throttle requests.", "kind": "scalar", - "name": "user_agent", + "name": "rate_limit", "type": "string" }, { - "default": 100, - "description": "The maximum number of pending messages to consume at any given time.", + "children": [ + { + "default": false, + "description": "Enable SO_REUSEADDR, allowing binding to ports in TIME_WAIT state. Useful for graceful restarts and config reloads where the server needs to rebind to the same port immediately after shutdown.", + "is_advanced": true, + "kind": "scalar", + "name": "reuse_addr", + "type": "bool" + }, + { + "default": false, + "description": "Enable SO_REUSEPORT, allowing multiple sockets to bind to the same port for load balancing across multiple processes/threads.", + "is_advanced": true, + "kind": "scalar", + "name": "reuse_port", + "type": "bool" + } + ], + "description": "TCP listener socket configuration.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "tcp", + "type": "object" }, - { - "default": 5, - "description": "The maximum number of attempts to successfully consume a messages.", - "kind": "scalar", - "name": "max_attempts", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- nsq_attempts\n- nsq_id\n- nsq_nsqd_address\n- nsq_timestamp\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n", - "name": "nsq", - "plugin": true, - "status": "stable", - "summary": "Subscribe to an NSQ instance topic and channel.", - "type": "input" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ { "children": [ { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", + "description": "Schema Registry URL for schema operations.", "examples": [ - [ - "localhost:9092" - ], - [ - "foo:9092", - "bar:9092" - ], - [ - "foo:9092,bar:9092" - ] + "http://localhost:8081" ], - "is_optional": true, - "kind": "array", - "name": "seed_brokers", + "is_advanced": true, + "kind": "scalar", + "name": "url", + "type": "string" + }, + { + "default": "5s", + "description": "HTTP client timeout for Schema Registry requests.", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", "type": "string" }, { @@ -20253,415 +26334,810 @@ "type": "object" }, { - "description": "\nA list of topics to consume from. Multiple comma separated topics can be listed in a single element. When a `consumer_group` is specified partitions are automatically distributed across consumers of a topic, otherwise all partitions are consumed.\n\nAlternatively, it's possible to specify explicit partitions to consume from with a colon after the topic name, e.g. `foo:0` would consume the partition 0 of the topic foo. This syntax supports ranges, e.g. `foo:0-10` would consume partitions 0 through to 10 inclusive.\n\nFinally, it's also possible to specify an explicit offset to consume from by adding another colon after the partition, e.g. `foo:0:10` would consume the partition 0 of the topic foo starting from the offset 10. If the offset is not present (or remains unspecified) then the field `start_from_oldest` determines which offset to start from.", - "examples": [ - [ - "foo", - "bar" - ], - [ - "things.*" - ], - [ - "foo,bar" - ], - [ - "foo:0", - "bar:1", - "bar:3" - ], - [ - "foo:0,bar:1,bar:3" - ], - [ - "foo:0-5" - ] + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 2 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "client_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the client key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The URL of the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "token_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": [], + "description": "A list of optional requested permissions.", + "is_advanced": true, + "kind": "array", + "name": "scopes", + "type": "string" + }, + { + "default": {}, + "description": "A list of optional endpoint parameters, values should be arrays of strings.", + "examples": [ + { + "audience": [ + "https://example.com" + ], + "resource": [ + "https://api.example.com" + ] + } + ], + "is_advanced": true, + "is_optional": true, + "kind": "map", + "linter": "\nroot = if this.type() == \"object\" {\n this.values().map_each(ele -> if ele.type() != \"array\" {\n \"field must be an object containing arrays of strings, got %s (%v)\".format(ele.format_json(no_indent: true), ele.type())\n } else {\n ele.map_each(str -> if str.type() != \"string\" {\n \"field values must be strings, got %s (%v)\".format(str.format_json(no_indent: true), str.type())\n } else { deleted() })\n }).\n flatten()\n}\n", + "name": "endpoint_params", + "type": "unknown" + } ], - "kind": "array", - "name": "topics", - "type": "string" - }, - { - "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.", - "kind": "scalar", - "name": "regexp_topics", - "type": "bool" - }, - { - "default": "", - "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", + "description": "Allows you to specify open authentication via OAuth version 2 using the client credentials token flow.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "rack_id", - "type": "string" + "name": "oauth2", + "type": "object" }, { - "default": "", - "description": "When using a consumer group, an instance ID specifies the groups static membership, which can prevent rebalances during reconnects. When using a instance ID the client does NOT leave the group when closing. To actually leave the group one must use an external admin command to leave the group on behalf of this instance ID. This ID must be unique per consumer within the group.", + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "instance_id", - "type": "string" + "name": "oauth", + "type": "object" }, { - "default": "45s", - "description": "When using a consumer group, `rebalance_timeout` sets how long group members are allowed to take when a rebalance has begun. This timeout is how long all members are allowed to complete work and commit offsets, minus the time it took to detect the rebalance (from a heartbeat).", + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "rebalance_timeout", - "type": "string" + "name": "basic_auth", + "type": "object" }, { - "default": "1m", - "description": "When using a consumer group, `session_timeout` sets how long a member in hte group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", "is_advanced": true, "kind": "scalar", - "name": "session_timeout", - "type": "string" + "name": "jwt", + "type": "object" }, { - "default": "3s", - "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's sesion stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", + "default": "", + "description": "Schema subject name for the common protobuf schema. Only used when encoding is 'protobuf'. Defaults to 'redpanda-otel-common' for protobuf encoding or 'redpanda-otel-common-json' for JSON encoding.", "is_advanced": true, "kind": "scalar", - "name": "heartbeat_interval", + "name": "common_subject", "type": "string" }, { - "default": true, - "description": "Determines whether to consume from the oldest available offset, otherwise messages are consumed from the latest offset. The setting is applied when creating a new consumer group or the saved offset no longer exists.", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "start_from_oldest", - "type": "bool" - }, - { - "annotated_options": [ - [ - "committed", - "Prevents consuming a partition in a group if the partition has no prior commits. Corresponds to Kafka's `auto.offset.reset=none` option" - ], - [ - "earliest", - "Start from the earliest offset. Corresponds to Kafka's `auto.offset.reset=earliest` option." - ], - [ - "latest", - "Start from the latest offset. Corresponds to Kafka's `auto.offset.reset=latest` option." - ] - ], - "default": "earliest", - "description": "Sets the offset to start consuming from, or if OffsetOutOfRange is seen while fetching, to restart consuming from.", + "default": "", + "description": "Schema subject name for trace data. Defaults to 'redpanda-otel-traces' for protobuf encoding or 'redpanda-otel-traces-json' for JSON encoding.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"committed\": true,\n \"earliest\": true,\n \"latest\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "start_offset", + "name": "trace_subject", "type": "string" }, { - "default": "50MiB", - "description": "Sets the maximum amount of bytes a broker will try to send during a fetch. Note that brokers may not obey this limit if it has records larger than this limit. This is the equivalent to the Java fetch.max.bytes setting.", + "default": "", + "description": "Schema subject name for log data. Defaults to 'redpanda-otel-logs' for protobuf encoding or 'redpanda-otel-logs-json' for JSON encoding.", "is_advanced": true, "kind": "scalar", - "name": "fetch_max_bytes", + "name": "log_subject", "type": "string" }, { - "default": "5s", - "description": "Sets the maximum amount of time a broker will wait for a fetch response to hit the minimum number of required bytes. This is the equivalent to the Java fetch.max.wait.ms setting.", + "default": "", + "description": "Schema subject name for metric data. Defaults to 'redpanda-otel-metrics' for protobuf encoding or 'redpanda-otel-metrics-json' for JSON encoding.", "is_advanced": true, "kind": "scalar", - "name": "fetch_max_wait", + "name": "metric_subject", "type": "string" - }, + } + ], + "description": "Optional Schema Registry configuration for adding Schema Registry wire format headers to messages.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "schema_registry", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nExposes an OpenTelemetry Collector gRPC receiver that accepts traces, logs, and metrics via gRPC.\n\nTelemetry data is received in OTLP protobuf format and converted to individual Redpanda OTEL v1 messages.\nEach signal (span, log record, or metric) becomes a separate message with embedded Resource and Scope metadata.\n\n## Protocols\n\nThis input supports OTLP/gRPC on the default port 4317 using the standard OTLP protobuf format for all signal types (traces, logs, metrics).\n\n## Output Format\n\nEach OTLP export request is unbatched into individual messages:\n- **Traces**: One message per span\n- **Logs**: One message per log record\n- **Metrics**: One message per metric\n\nMessages are encoded in Redpanda OTEL v1 format (protobuf or JSON, configurable via `encoding` field).\n\nEach message includes the following metadata:\n- `otel_signal_type`: The signal type - \"trace\", \"log\", or \"metric\"\n- `otel_encoding` : The message encoding - \"json\" or \"protobuf\"\n\n## Authentication\n\nWhen `auth_token` is configured, clients must include the token in the gRPC metadata:\n\n**Go Client Example:**\n```go\nimport (\n \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc\"\n)\n\nexporter, err := otlptracegrpc.New(ctx,\n otlptracegrpc.WithEndpoint(\"localhost:4317\"),\n otlptracegrpc.WithInsecure(), // or WithTLSCredentials() for TLS\n otlptracegrpc.WithHeaders(map[string]string{\n \"authorization\": \"Bearer your-token-here\",\n }),\n)\n```\n\n**Environment Variable:**\n```bash\nexport OTEL_EXPORTER_OTLP_HEADERS=\"authorization=Bearer your-token-here\"\n```\n\n## Rate Limiting\n\nAn optional rate limit resource can be specified to throttle incoming requests. When the rate limit is breached, requests will receive a ResourceExhausted gRPC status code.\n", + "name": "otlp_grpc", + "plugin": true, + "status": "stable", + "summary": "Receive OpenTelemetry traces, logs, and metrics via OTLP/gRPC protocol.", + "type": "input", + "version": "4.78.0" + }, + { + "categories": [ + "Network", + "Services" + ], + "config": { + "children": [ + { + "default": "json", + "description": "Encoding format for messages in the batch. Options: 'protobuf' or 'json'.", + "kind": "scalar", + "linter": "\nlet options = {\n \"protobuf\": true,\n \"json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "encoding", + "options": [ + "protobuf", + "json" + ], + "type": "string" + }, + { + "default": "0.0.0.0:4318", + "description": "The address to listen on for HTTP connections.", + "kind": "scalar", + "name": "address", + "type": "string" + }, + { + "children": [ { - "default": "1B", - "description": "Sets the minimum amount of bytes a broker will try to send during a fetch. This is the equivalent to the Java fetch.min.bytes setting.", + "default": false, + "description": "Enable TLS connections.", "is_advanced": true, "kind": "scalar", - "name": "fetch_min_bytes", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "1MiB", - "description": "Sets the maximum amount of bytes that will be consumed for a single partition in a fetch request. Note that if a single batch is larger than this number, that batch will still be returned so the client can make progress. This is the equivalent to the Java fetch.max.partition.bytes setting.", + "default": "", + "description": "Path to the TLS certificate file.", "is_advanced": true, "kind": "scalar", - "name": "fetch_max_partition_bytes", + "name": "cert_file", "type": "string" }, { - "annotated_options": [ - [ - "read_committed", - "If set, only committed transactional records are processed." - ], - [ - "read_uncommitted", - "If set, then uncommitted records are processed." - ] - ], - "default": "read_uncommitted", - "description": "The transaction isolation level", + "default": "", + "description": "Path to the TLS key file.", + "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"read_committed\": true,\n \"read_uncommitted\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "transaction_isolation_level", + "name": "key_file", "type": "string" - }, + } + ], + "description": "TLS configuration for HTTP.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "default": "", + "description": "Optional bearer token for authentication. When set, requests must include 'Authorization: Bearer ' header.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "auth_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum duration for reading the entire request.", + "is_advanced": true, + "kind": "scalar", + "name": "read_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum duration for writing the response.", + "is_advanced": true, + "kind": "scalar", + "name": "write_timeout", + "type": "string" + }, + { + "default": 4194304, + "description": "Maximum size of HTTP request body in bytes.", + "is_advanced": true, + "kind": "scalar", + "name": "max_body_size", + "type": "int" + }, + { + "default": "", + "description": "An optional rate limit resource to throttle requests.", + "kind": "scalar", + "name": "rate_limit", + "type": "string" + }, + { + "children": [ { - "description": "An optional consumer group to consume as. When specified the partitions of specified topics are automatically distributed across consumers sharing a consumer group, and partition offsets are automatically committed and resumed under this name. Consumer groups are not supported when specifying explicit partitions to consume from in the `topics` field.", - "is_optional": true, + "default": false, + "description": "Enable SO_REUSEADDR, allowing binding to ports in TIME_WAIT state. Useful for graceful restarts and config reloads where the server needs to rebind to the same port immediately after shutdown.", + "is_advanced": true, "kind": "scalar", - "name": "consumer_group", - "type": "string" + "name": "reuse_addr", + "type": "bool" }, { - "default": 1024, - "description": "Determines how many messages of the same partition can be processed in parallel before applying back pressure. When a message of a given offset is delivered to the output the offset is only allowed to be committed when all messages of prior offsets have also been delivered, this ensures at-least-once delivery guarantees. However, this mechanism also increases the likelihood of duplicates in the event of crashes or server faults, reducing the checkpoint limit will mitigate this.", + "default": false, + "description": "Enable SO_REUSEPORT, allowing multiple sockets to bind to the same port for load balancing across multiple processes/threads.", "is_advanced": true, "kind": "scalar", - "name": "checkpoint_limit", - "type": "int" - }, + "name": "reuse_port", + "type": "bool" + } + ], + "description": "TCP listener socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ { - "default": "5s", - "description": "The period of time between each commit of the current partition offsets. Offsets are always committed during shutdown.", + "description": "Schema Registry URL for schema operations.", + "examples": [ + "http://localhost:8081" + ], "is_advanced": true, "kind": "scalar", - "name": "commit_period", + "name": "url", "type": "string" }, { - "default": false, - "description": "Decode headers into lists to allow handling of multiple values with the same key", + "default": "5s", + "description": "HTTP client timeout for Schema Registry requests.", "is_advanced": true, "kind": "scalar", - "name": "multi_header", - "type": "bool" + "name": "timeout", + "type": "string" }, { "children": [ { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "count", - "type": "int" + "name": "enabled", + "type": "bool" }, { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, "kind": "scalar", - "name": "byte_size", - "type": "int" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "period", - "type": "string" - }, + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, { - "bloblang": true, "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "this.type == \"end_of_transaction\"" + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "check", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", "examples": [ [ { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } + "cert": "foo", + "key": "bar" } ], [ { - "archive": { - "format": "json_array" - } + "cert_file": "./example.pem", + "key_file": "./example.key" } ] ], "is_advanced": true, - "is_optional": true, "kind": "array", - "name": "processors", - "type": "processor" + "name": "client_certs", + "type": "object" } ], - "description": "Allows you to configure a xref:configuration:batching.adoc[batching policy] that applies to individual topic partitions in order to batch messages together before flushing them for processing. Batching can be beneficial for performance as well as useful for windowed processing, and doing so this way preserves the ordering of topic partitions.", - "examples": [ + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ { - "byte_size": 5000, - "count": 0, - "period": "1s" + "default": false, + "description": "Whether to use OAuth version 2 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" }, { - "count": 10, - "period": "1s" + "default": "", + "description": "A value used to identify the client to the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "client_key", + "type": "string" }, { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" + "default": "", + "description": "A secret used to establish ownership of the client key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The URL of the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "token_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": [], + "description": "A list of optional requested permissions.", + "is_advanced": true, + "kind": "array", + "name": "scopes", + "type": "string" + }, + { + "default": {}, + "description": "A list of optional endpoint parameters, values should be arrays of strings.", + "examples": [ + { + "audience": [ + "https://example.com" + ], + "resource": [ + "https://api.example.com" + ] + } + ], + "is_advanced": true, + "is_optional": true, + "kind": "map", + "linter": "\nroot = if this.type() == \"object\" {\n this.values().map_each(ele -> if ele.type() != \"array\" {\n \"field must be an object containing arrays of strings, got %s (%v)\".format(ele.format_json(no_indent: true), ele.type())\n } else {\n ele.map_each(str -> if str.type() != \"string\" {\n \"field values must be strings, got %s (%v)\".format(str.format_json(no_indent: true), str.type())\n } else { deleted() })\n }).\n flatten()\n}\n", + "name": "endpoint_params", + "type": "unknown" } ], + "description": "Allows you to specify open authentication via OAuth version 2 using the client credentials token flow.", "is_advanced": true, - "kind": "", - "name": "batching", + "is_optional": true, + "kind": "scalar", + "name": "oauth2", "type": "object" }, { - "default": "5s", - "description": "The period of time between each topic lag refresh cycle.", + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "oauth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object" + }, + { + "default": "", + "description": "Schema subject name for the common protobuf schema. Only used when encoding is 'protobuf'. Defaults to 'redpanda-otel-common' for protobuf encoding or 'redpanda-otel-common-json' for JSON encoding.", + "is_advanced": true, + "kind": "scalar", + "name": "common_subject", + "type": "string" + }, + { + "default": "", + "description": "Schema subject name for trace data. Defaults to 'redpanda-otel-traces' for protobuf encoding or 'redpanda-otel-traces-json' for JSON encoding.", + "is_advanced": true, + "kind": "scalar", + "name": "trace_subject", + "type": "string" + }, + { + "default": "", + "description": "Schema subject name for log data. Defaults to 'redpanda-otel-logs' for protobuf encoding or 'redpanda-otel-logs-json' for JSON encoding.", + "is_advanced": true, + "kind": "scalar", + "name": "log_subject", + "type": "string" + }, + { + "default": "", + "description": "Schema subject name for metric data. Defaults to 'redpanda-otel-metrics' for protobuf encoding or 'redpanda-otel-metrics-json' for JSON encoding.", "is_advanced": true, "kind": "scalar", - "name": "topic_lag_refresh_period", + "name": "metric_subject", "type": "string" } ], - "kind": "scalar", - "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", - "name": "kafka", - "type": "object" - }, - { - "default": false, - "kind": "scalar", - "name": "disable_content_encryption", - "type": "bool" - }, - { - "is_optional": true, - "kind": "scalar", - "name": "enrollment_ticket", - "type": "string" - }, - { - "is_optional": true, - "kind": "scalar", - "name": "identity_name", - "type": "string" - }, - { - "default": "self", - "kind": "scalar", - "name": "allow", - "type": "string" - }, - { - "default": "self", - "kind": "scalar", - "name": "route_to_kafka_outlet", - "type": "string" - }, - { - "default": "self", - "kind": "scalar", - "name": "allow_producer", - "type": "string" - }, - { - "is_optional": true, - "kind": "scalar", - "name": "relay", - "type": "string" - }, - { - "default": "127.0.0.1:6262", - "kind": "scalar", - "name": "node_address", - "type": "string" - }, - { - "default": [], - "description": "The fields to encrypt in the kafka messages, assuming the record is a valid JSON map. By default, the whole record is encrypted.", - "kind": "array", - "name": "encrypted_fields", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "ockam_kafka", - "plugin": true, - "status": "experimental", - "summary": "Ockam", - "type": "input" - }, - { - "categories": [ - "Local" - ], - "config": { - "children": [ - { - "description": "A list of file paths to read from. Each file will be read sequentially until the list is exhausted, at which point the input will close. Glob patterns are supported, including super globs (double star).", - "examples": [ - "/tmp/foo.parquet", - "/tmp/bar/*.parquet", - "/tmp/data/**/*.parquet" - ], - "kind": "array", - "name": "paths", - "type": "string" - }, - { - "default": 1, - "description": "Optionally process records in batches. This can help to speed up the consumption of exceptionally large files. When the end of the file is reached the remaining records are processed as a (potentially smaller) batch.", + "description": "Optional Schema Registry configuration for adding Schema Registry wire format headers to messages.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "batch_count", - "type": "int" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" + "name": "schema_registry", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nThis input uses https://github.com/parquet-go/parquet-go[https://github.com/parquet-go/parquet-go^], which is itself experimental. Therefore changes could be made into how this processor functions outside of major version releases.\n\nBy default any BYTE_ARRAY or FIXED_LEN_BYTE_ARRAY value will be extracted as a byte slice (`[]byte`) unless the logical type is UTF8, in which case they are extracted as a string (`string`).\n\nWhen a value extracted as a byte slice exists within a document which is later JSON serialized by default it will be base 64 encoded into strings, which is the default for arbitrary data fields. It is possible to convert these binary values to strings (or other data types) using Bloblang transformations such as `root.foo = this.foo.string()` or `root.foo = this.foo.encode(\"hex\")`, etc.", - "name": "parquet", + "description": "\nExposes an OpenTelemetry Collector HTTP receiver that accepts traces, logs, and metrics via HTTP.\n\nTelemetry data is received in OTLP format (protobuf or JSON) and converted to individual Redpanda OTEL v1 messages.\nEach signal (span, log record, or metric) becomes a separate message with embedded Resource and Scope metadata.\n\n## Endpoints\n\n- `/v1/traces` - OpenTelemetry traces\n- `/v1/logs` - OpenTelemetry logs\n- `/v1/metrics` - OpenTelemetry metrics\n\n## Protocols\n\nThis input supports OTLP/HTTP on the default port 4318. It accepts both:\n- `application/x-protobuf` - OTLP protobuf format\n- `application/json` - OTLP JSON format\n\n## Output Format\n\nEach OTLP export request is unbatched into individual messages:\n- **Traces**: One message per span\n- **Logs**: One message per log record\n- **Metrics**: One message per metric\n\nMessages are encoded in Redpanda OTEL v1 format (protobuf or JSON, configurable via `encoding` field).\n\nEach message includes the following metadata:\n- `otel_signal_type`: The signal type - \"trace\", \"log\", or \"metric\"\n- `otel_encoding` : The message encoding - \"json\" or \"protobuf\"\n\n## Authentication\n\nWhen `auth_token` is configured, clients must include the token in the HTTP Authorization header:\n\n**Go Client Example:**\n```go\nimport (\n \"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp\"\n)\n\nexporter, err := otlptracehttp.New(ctx,\n otlptracehttp.WithEndpoint(\"localhost:4318\"),\n otlptracehttp.WithInsecure(), // or WithTLSClientConfig() for TLS\n otlptracehttp.WithHeaders(map[string]string{\n \"Authorization\": \"Bearer your-token-here\",\n }),\n)\n```\n\n**cURL Example:**\n```bash\ncurl -X POST http://localhost:4318/v1/traces \\\n -H \"Content-Type: application/x-protobuf\" \\\n -H \"Authorization: Bearer your-token-here\" \\\n --data-binary @traces.pb\n```\n\n**Environment Variable:**\n```bash\nexport OTEL_EXPORTER_OTLP_HEADERS=\"Authorization=Bearer your-token-here\"\n```\n\n## Rate Limiting\n\nAn optional rate limit resource can be specified to throttle incoming requests. When the rate limit is breached, requests will receive a 429 (Too Many Requests) response.\n", + "name": "otlp_http", "plugin": true, - "status": "experimental", - "summary": "Reads and decodes https://parquet.apache.org/docs/[Parquet files^] into a stream of structured messages.", + "status": "stable", + "summary": "Receive OpenTelemetry traces, logs, and metrics via OTLP/HTTP protocol.", "type": "input", - "version": "4.8.0" + "version": "4.78.0" }, { "categories": [ @@ -20753,7 +27229,7 @@ "type": "bool" }, { - "description": "The name of the PostgreSQL logical replication slot to use. If not provided, a random name will be generated. You can create this slot manually before starting replication if desired.", + "description": "The name of the PostgreSQL logical replication slot to use. If not provided, a random name will be generated. You can create this slot manually before starting replication if desired.\n\nNote: To avoid needing to grant the replication user permission to create publications, you can manually create the publications ahead of time.\nThis connector uses the naming pattern `pglog_stream_`, so be sure to create them using this convention.\n\t\t\t", "examples": [ "my_test_slot" ], @@ -20795,6 +27271,7 @@ "__redpanda_connect_unchanged_toast_value__" ], "is_advanced": true, + "is_optional": true, "kind": "scalar", "name": "unchanged_toast_value", "type": "unknown" @@ -20811,71 +27288,295 @@ "name": "heartbeat_interval", "type": "string" }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, { "children": [ { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, "kind": "scalar", - "name": "count", - "type": "int" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, "kind": "scalar", - "name": "byte_size", - "type": "int" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "1s", - "1m", - "500ms" + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ], + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "period", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "bloblang": true, "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "this.type == \"end_of_transaction\"" + "./root_cas.pem" ], + "is_advanced": true, "kind": "scalar", - "name": "check", + "name": "root_cas_file", "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", "examples": [ [ { - "archive": { - "format": "concatenate" - } + "cert": "foo", + "key": "bar" } ], [ { - "archive": { - "format": "lines" - } + "cert_file": "./example.pem", + "key_file": "./example.key" } - ], - [ - { + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Enable AWS IAM authentication for PostgreSQL. When enabled, an IAM authentication token is generated and used as the password.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "description": "The AWS region where the PostgreSQL instance is located. If no region is specified then the environment default will be used.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "The PostgreSQL endpoint hostname (e.g., mydb.abc123.us-east-1.rds.amazonaws.com).", + "is_advanced": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Optional AWS IAM role ARN to assume for authentication. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "Optional external ID for the role assumption. Only used with the `role` field. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "AWS IAM role ARN to assume.", + "is_advanced": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "default": "", + "description": "Optional external ID for the role assumption.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional array of AWS IAM roles to assume for authentication. Roles can be assumed in sequence, enabling chaining for purposes such as cross-account access. Each role can optionally specify an external ID.", + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "roles", + "type": "object" + } + ], + "description": "AWS IAM authentication configuration for PostgreSQL instances. When enabled, IAM credentials are used to generate temporary authentication tokens instead of a static password.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "aws", + "type": "object" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", + "type": "bool" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { "archive": { "format": "json_array" } @@ -20915,7 +27616,7 @@ "name": "", "type": "object" }, - "description": "Streams changes from a PostgreSQL database for Change Data Capture (CDC).\nAdditionally, if `stream_snapshot` is set to true, then the existing data in the database is also streamed too.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n- table (Name of the table that the message originated from)\n- operation (Type of operation that generated the message: \"read\", \"insert\", \"update\", or \"delete\". \"read\" is from messages that are read in the initial snapshot phase. This will also be \"begin\" and \"commit\" if `include_transaction_markers` is enabled)\n- lsn (the log sequence number in postgres)\n\t\t", + "description": "Using this field overrides the SSL/TLS settings in the environment and DSN.", "name": "pg_stream", "plugin": true, "status": "deprecated", @@ -21013,7 +27714,7 @@ "type": "bool" }, { - "description": "The name of the PostgreSQL logical replication slot to use. If not provided, a random name will be generated. You can create this slot manually before starting replication if desired.", + "description": "The name of the PostgreSQL logical replication slot to use. If not provided, a random name will be generated. You can create this slot manually before starting replication if desired.\n\nNote: To avoid needing to grant the replication user permission to create publications, you can manually create the publications ahead of time.\nThis connector uses the naming pattern `pglog_stream_`, so be sure to create them using this convention.\n\t\t\t", "examples": [ "my_test_slot" ], @@ -21055,6 +27756,7 @@ "__redpanda_connect_unchanged_toast_value__" ], "is_advanced": true, + "is_optional": true, "kind": "scalar", "name": "unchanged_toast_value", "type": "unknown" @@ -21071,6 +27773,230 @@ "name": "heartbeat_interval", "type": "string" }, + { + "children": [ + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Enable AWS IAM authentication for PostgreSQL. When enabled, an IAM authentication token is generated and used as the password.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "description": "The AWS region where the PostgreSQL instance is located. If no region is specified then the environment default will be used.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "The PostgreSQL endpoint hostname (e.g., mydb.abc123.us-east-1.rds.amazonaws.com).", + "is_advanced": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Optional AWS IAM role ARN to assume for authentication. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "Optional external ID for the role assumption. Only used with the `role` field. Alternatively, use `roles` array for role chaining instead.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "AWS IAM role ARN to assume.", + "is_advanced": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "default": "", + "description": "Optional external ID for the role assumption.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional array of AWS IAM roles to assume for authentication. Roles can be assumed in sequence, enabling chaining for purposes such as cross-account access. Each role can optionally specify an external ID.", + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "roles", + "type": "object" + } + ], + "description": "AWS IAM authentication configuration for PostgreSQL instances. When enabled, IAM credentials are used to generate temporary authentication tokens instead of a static password.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "aws", + "type": "object" + }, { "default": true, "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", @@ -21175,261 +28101,317 @@ "name": "", "type": "object" }, - "description": "Streams changes from a PostgreSQL database for Change Data Capture (CDC).\nAdditionally, if `stream_snapshot` is set to true, then the existing data in the database is also streamed too.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n- table (Name of the table that the message originated from)\n- operation (Type of operation that generated the message: \"read\", \"insert\", \"update\", or \"delete\". \"read\" is from messages that are read in the initial snapshot phase. This will also be \"begin\" and \"commit\" if `include_transaction_markers` is enabled)\n- lsn (the log sequence number in postgres)\n\t\t", + "description": "Using this field overrides the SSL/TLS settings in the environment and DSN.", "name": "postgres_cdc", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Streams changes from a PostgreSQL database using logical replication.", "type": "input", "version": "4.39.0" }, { "categories": [ - "Services" + "Utility" ], "config": { "children": [ { - "description": "A URL to connect to.", - "examples": [ - "pulsar://localhost:6650", - "pulsar://pulsar.us-west.example.com:6650", - "pulsar+ssl://pulsar.us-west.example.com:6651" - ], + "description": "The child input to consume from.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "input", + "type": "input" }, { - "description": "A list of topics to subscribe to. This or topics_pattern must be set.", + "bloblang": true, + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether the input should now be closed.", + "examples": [ + "this.type == \"foo\"", + "count(\"messages\") >= 100" + ], "is_optional": true, - "kind": "array", - "name": "topics", + "kind": "scalar", + "name": "check", "type": "string" }, { - "description": "A regular expression matching the topics to subscribe to. This or topics must be set.", + "description": "The maximum amount of time without receiving new messages after which the input is closed.", + "examples": [ + "5s" + ], "is_optional": true, "kind": "scalar", - "name": "topics_pattern", + "name": "idle_timeout", "type": "string" }, { - "description": "Specify the subscription name for this consumer.", + "default": false, + "description": "Whether the input should be reopened if it closes itself before the condition has resolved to true.", + "kind": "scalar", + "name": "restart_input", + "type": "bool" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nMessages are read continuously while the query check returns false, when the query returns true the message that triggered the check is sent out and the input is closed. Use this to define inputs where the stream should end once a certain message appears.\n\nIf the idle timeout is configured, the input will be closed if no new messages arrive after that period of time. Use this field if you want to empty out and close an input that doesn't have a logical end.\n\nSometimes inputs close themselves. For example, when the `file` input type reaches the end of a file it will shut down. By default this type will also shut down. If you wish for the input type to be restarted every time it shuts down until the query check is met then set `restart_input` to `true`.\n\n== Metadata\n\nA metadata key `benthos_read_until` containing the value `final` is added to the first part of the message that triggers the input to stop.", + "examples": [ + { + "config": "\n# Only read 100 messages, and then exit.\ninput:\n read_until:\n check: count(\"messages\") >= 100\n input:\n kafka:\n addresses: [ TODO ]\n topics: [ foo, bar ]\n consumer_group: foogroup\n", + "summary": "A common reason to use this input is to consume only N messages from an input and then stop. This can easily be done with the xref:guides:bloblang/functions.adoc#count[`count` function]:", + "title": "Consume N Messages" + }, + { + "config": "\n# Consumes all messages and exit when the last message was consumed 5s ago.\ninput:\n read_until:\n idle_timeout: 5s\n input:\n kafka:\n addresses: [ TODO ]\n topics: [ foo, bar ]\n consumer_group: foogroup\n", + "summary": "A common reason to use this input is a job that consumes all messages and exits once its empty:", + "title": "Read from a kafka and close when empty" + } + ], + "name": "read_until", + "plugin": true, + "status": "stable", + "summary": "Reads messages from a child input until a consumed message passes a xref:guides:bloblang/about.adoc[Bloblang query], at which point the input closes. It is also possible to configure a timeout after which the input is closed if no new messages arrive in that period.", + "type": "input" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], "kind": "scalar", - "name": "subscription_name", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "default": "shared", - "description": "Specify the subscription type for this consumer.\n\n> NOTE: Using a `key_shared` subscription type will __allow out-of-order delivery__ since nack-ing messages sets non-zero nack delivery delay - this can potentially cause consumers to stall. See https://pulsar.apache.org/docs/en/2.8.1/concepts-messaging/#negative-acknowledgement[Pulsar documentation^] and https://github.com/apache/pulsar/issues/12208[this Github issue^] for more details.", + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"shared\": true,\n \"key_shared\": true,\n \"failover\": true,\n \"exclusive\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "subscription_type", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", "options": [ - "shared", - "key_shared", - "failover", - "exclusive" + "simple", + "cluster", + "failover" ], "type": "string" }, { - "default": "latest", - "description": "Specify the subscription initial position for this consumer.", - "kind": "scalar", - "linter": "\nlet options = {\n \"latest\": true,\n \"earliest\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "subscription_initial_position", - "options": [ - "latest", - "earliest" + "default": "", + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" ], + "is_advanced": true, + "kind": "scalar", + "name": "master", "type": "string" }, { - "children": [ - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - } - ], - "description": "Specify the path to a custom CA certificate to trust broker TLS service.", + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, "kind": "scalar", - "name": "tls", - "type": "object" + "name": "client_name", + "type": "string", + "version": "4.82.0" }, { "children": [ { - "children": [ - { - "default": false, - "description": "Whether OAuth2 is enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "OAuth2 audience.", - "is_advanced": true, - "kind": "scalar", - "name": "audience", - "type": "string" - }, + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ { "default": "", - "description": "OAuth2 issuer URL.", + "description": "A plain text certificate to use.", "is_advanced": true, "kind": "scalar", - "name": "issuer_url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "cert", "type": "string" }, { "default": "", - "description": "OAuth2 scope to request.", + "description": "A plain text certificate key to use.", "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "scope", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "The path to a file containing a private key.", + "description": "The path of a certificate to use.", "is_advanced": true, "kind": "scalar", - "name": "private_key_file", + "name": "cert_file", "type": "string" - } - ], - "description": "Parameters for Pulsar OAuth2 authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth2", - "type": "object" - }, - { - "children": [ + }, { - "default": false, - "description": "Whether Token Auth is enabled.", + "default": "", + "description": "The path of a certificate key to use.", "is_advanced": true, "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "key_file", + "type": "string" }, { "default": "", - "description": "Actual base64 encoded token.", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "token", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "description": "Parameters for Pulsar Token authentication.", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "token", + "kind": "array", + "name": "client_certs", "type": "object" } ], - "description": "Optional configuration of Pulsar authentication methods.", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "auth", - "type": "object", - "version": "3.60.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- pulsar_message_id\n- pulsar_key\n- pulsar_ordering_key\n- pulsar_event_time_unix\n- pulsar_publish_time_unix\n- pulsar_topic\n- pulsar_producer_name\n- pulsar_redelivery_count\n- All properties of the message\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n", - "name": "pulsar", - "plugin": true, - "status": "experimental", - "summary": "Reads messages from an Apache Pulsar server.", - "type": "input", - "version": "3.43.0" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ + "name": "tls", + "type": "object" + }, { - "description": "The child input to consume from.", + "description": "The key of a list to read from.", "kind": "scalar", - "name": "input", - "type": "input" + "name": "key", + "type": "string" }, { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether the input should now be closed.", - "examples": [ - "this.type == \"foo\"", - "count(\"messages\") >= 100" - ], - "is_optional": true, + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "check", - "type": "string" + "name": "auto_replay_nacks", + "type": "bool" }, { - "description": "The maximum amount of time without receiving new messages after which the input is closed.", - "examples": [ - "5s" - ], - "is_optional": true, + "default": 0, + "description": "Optionally sets a limit on the number of messages that can be flowing through a Redpanda Connect stream pending acknowledgment from the input at any given time. Once a message has been either acknowledged or rejected (nacked) it is no longer considered pending. If the input produces logical batches then each batch is considered a single count against the maximum. **WARNING**: Batching policies at the output level will stall if this field limits the number of messages below the batching threshold. Zero (default) or lower implies no limit.", + "is_advanced": true, "kind": "scalar", - "name": "idle_timeout", + "name": "max_in_flight", + "type": "int", + "version": "4.9.0" + }, + { + "default": "5s", + "description": "The length of time to poll for new messages before reattempting.", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", "type": "string" }, { - "default": false, - "description": "Whether the input should be reopened if it closes itself before the condition has resolved to true.", + "default": "blpop", + "description": "The command used to pop elements from the Redis list", + "is_advanced": true, "kind": "scalar", - "name": "restart_input", - "type": "bool" + "linter": "\nlet options = {\n \"blpop\": true,\n \"brpop\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "command", + "options": [ + "blpop", + "brpop" + ], + "type": "string", + "version": "4.22.0" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nMessages are read continuously while the query check returns false, when the query returns true the message that triggered the check is sent out and the input is closed. Use this to define inputs where the stream should end once a certain message appears.\n\nIf the idle timeout is configured, the input will be closed if no new messages arrive after that period of time. Use this field if you want to empty out and close an input that doesn't have a logical end.\n\nSometimes inputs close themselves. For example, when the `file` input type reaches the end of a file it will shut down. By default this type will also shut down. If you wish for the input type to be restarted every time it shuts down until the query check is met then set `restart_input` to `true`.\n\n== Metadata\n\nA metadata key `benthos_read_until` containing the value `final` is added to the first part of the message that triggers the input to stop.", - "examples": [ - { - "config": "\n# Only read 100 messages, and then exit.\ninput:\n read_until:\n check: count(\"messages\") >= 100\n input:\n kafka:\n addresses: [ TODO ]\n topics: [ foo, bar ]\n consumer_group: foogroup\n", - "summary": "A common reason to use this input is to consume only N messages from an input and then stop. This can easily be done with the xref:guides:bloblang/functions.adoc#count[`count` function]:", - "title": "Consume N Messages" - }, - { - "config": "\n# Consumes all messages and exit when the last message was consumed 5s ago.\ninput:\n read_until:\n idle_timeout: 5s\n input:\n kafka:\n addresses: [ TODO ]\n topics: [ foo, bar ]\n consumer_group: foogroup\n", - "summary": "A common reason to use this input is a job that consumes all messages and exits once its empty:", - "title": "Read from a kafka and close when empty" - } - ], - "name": "read_until", + "name": "redis_list", "plugin": true, "status": "stable", - "summary": "Reads messages from a child input until a consumed message passes a xref:guides:bloblang/about.adoc[Bloblang query], at which point the input closes. It is also possible to configure a timeout after which the input is closed if no new messages arrive in that period.", + "summary": "Pops messages from the beginning of a Redis list using the BLPop command.", "type": "input" }, { @@ -21478,6 +28460,15 @@ "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { @@ -21609,266 +28600,35 @@ "type": "object" }, { - "description": "The key of a list to read from.", - "kind": "scalar", - "name": "key", + "description": "A list of channels to consume from.", + "kind": "array", + "name": "channels", "type": "string" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "default": false, + "description": "Whether to use the PSUBSCRIBE command, allowing for glob-style patterns within target channel names.", "kind": "scalar", - "name": "auto_replay_nacks", + "name": "use_patterns", "type": "bool" }, { - "default": 0, - "description": "Optionally sets a limit on the number of messages that can be flowing through a Redpanda Connect stream pending acknowledgment from the input at any given time. Once a message has been either acknowledged or rejected (nacked) it is no longer considered pending. If the input produces logical batches then each batch is considered a single count against the maximum. **WARNING**: Batching policies at the output level will stall if this field limits the number of messages below the batching threshold. Zero (default) or lower implies no limit.", - "is_advanced": true, - "kind": "scalar", - "name": "max_in_flight", - "type": "int", - "version": "4.9.0" - }, - { - "default": "5s", - "description": "The length of time to poll for new messages before reattempting.", - "is_advanced": true, - "kind": "scalar", - "name": "timeout", - "type": "string" - }, - { - "default": "blpop", - "description": "The command used to pop elements from the Redis list", - "is_advanced": true, + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "linter": "\nlet options = {\n \"blpop\": true,\n \"brpop\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "command", - "options": [ - "blpop", - "brpop" - ], - "type": "string", - "version": "4.22.0" + "name": "auto_replay_nacks", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "name": "redis_list", + "description": "\nIn order to subscribe to channels using the `PSUBSCRIBE` command set the field `use_patterns` to `true`, then you can include glob-style patterns in your channel names. For example:\n\n- `h?llo` subscribes to hello, hallo and hxllo\n- `h*llo` subscribes to hllo and heeeello\n- `h[ae]llo` subscribes to hello and hallo, but not hillo\n\nUse `\\` to escape special characters if you want to match them verbatim.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- redis_pubsub_channel\n- redis_pubsub_pattern\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", + "name": "redis_pubsub", "plugin": true, "status": "stable", - "summary": "Pops messages from the beginning of a Redis list using the BLPop command.", - "type": "input" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", - "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" - ], - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" - ], - "type": "string" - }, - { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" - ], - "is_advanced": true, - "kind": "scalar", - "name": "master", - "type": "string" - }, - { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "description": "A list of channels to consume from.", - "kind": "array", - "name": "channels", - "type": "string" - }, - { - "default": false, - "description": "Whether to use the PSUBSCRIBE command, allowing for glob-style patterns within target channel names.", - "kind": "scalar", - "name": "use_patterns", - "type": "bool" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nIn order to subscribe to channels using the `PSUBSCRIBE` command set the field `use_patterns` to `true`, then you can include glob-style patterns in your channel names. For example:\n\n- `h?llo` subscribes to hello, hallo and hxllo\n- `h*llo` subscribes to hllo and heeeello\n- `h[ae]llo` subscribes to hello and hallo, but not hillo\n\nUse `\\` to escape special characters if you want to match them verbatim.", - "name": "redis_pubsub", - "plugin": true, - "status": "stable", - "summary": "Consume from a Redis publish/subscribe channel using either the SUBSCRIBE or PSUBSCRIBE commands.", + "summary": "Consume from a Redis publish/subscribe channel using either the SUBSCRIBE or PSUBSCRIBE commands.", "type": "input" }, { @@ -21917,6 +28677,15 @@ "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { @@ -22076,7 +28845,7 @@ "description": "Optionally, iterates only elements matching a blob-style pattern. For example:\n\n- `*foo*` iterates only keys which contain `foo` in it.\n- `foo*` iterates only keys starting with `foo`.\n\nThis input generates a message for each key value pair in the following format:\n\n```json\n{\"key\":\"foo\",\"value\":\"bar\"}\n```\n", "name": "redis_scan", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Scans the set of keys in the current selected database and gets their values, using the Scan and Get commands.", "type": "input", "version": "4.27.0" @@ -22127,6 +28896,15 @@ "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { @@ -22349,7 +29127,7 @@ "config": { "children": [ { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", + "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses. When this field is omitted the global `redpanda` block will be referenced for connection details.", "examples": [ [ "localhost:9092" @@ -22362,12 +29140,13 @@ "foo:9092,bar:9092" ] ], + "is_optional": true, "kind": "array", "name": "seed_brokers", "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -22520,6 +29299,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -22536,7 +29319,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -22592,6 +29375,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -22687,7 +29530,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -22710,6 +29553,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "description": "\nA list of topics to consume from. Multiple comma separated topics can be listed in a single element. When a `consumer_group` is specified partitions are automatically distributed across consumers of a topic, otherwise all partitions are consumed.\n\nAlternatively, it's possible to specify explicit partitions to consume from with a colon after the topic name, e.g. `foo:0` would consume the partition 0 of the topic foo. This syntax supports ranges, e.g. `foo:0-10` would consume partitions 0 through to 10 inclusive.\n\nFinally, it's also possible to specify an explicit offset to consume from by adding another colon after the partition, e.g. `foo:0:10` would consume the partition 0 of the topic foo starting from the offset 10. If the offset is not present (or remains unspecified) then the field `start_from_oldest` determines which offset to start from.", "examples": [ @@ -22735,17 +29638,42 @@ "foo:0-5" ] ], + "is_optional": true, "kind": "array", "name": "topics", "type": "string" }, { "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.", + "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.\n\nThis field is deprecated, use `regexp_topics_include` instead.", + "is_deprecated": true, "kind": "scalar", "name": "regexp_topics", "type": "bool" }, + { + "description": "A list of regular expression patterns for matching topics to consume from. When specified, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. This enables regex mode and cannot be used together with the `topics` field. Use `regexp_topics_exclude` to exclude specific patterns.", + "examples": [ + [ + "logs_.*", + "metrics_.*" + ], + [ + "events_[0-9]+" + ] + ], + "is_optional": true, + "kind": "array", + "name": "regexp_topics_include", + "type": "string" + }, + { + "description": "A list of regular expression patterns for excluding topics when regex mode is enabled (via `regexp_topics` or `regexp_topics_include`). Topics matching any of these patterns will be excluded from consumption, even if they match include patterns.", + "is_optional": true, + "kind": "array", + "name": "regexp_topics_exclude", + "type": "string" + }, { "default": "", "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", @@ -22772,7 +29700,7 @@ }, { "default": "1m", - "description": "When using a consumer group, `session_timeout` sets how long a member in hte group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", + "description": "When using a consumer group, `session_timeout` sets how long a member in the group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", "is_advanced": true, "kind": "scalar", "name": "session_timeout", @@ -22780,7 +29708,7 @@ }, { "default": "3s", - "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's sesion stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", + "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's session stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", "is_advanced": true, "kind": "scalar", "name": "heartbeat_interval", @@ -22901,12 +29829,134 @@ }, { "default": "32KB", - "description": "The maximum size (in bytes) for each batch yielded by this input. When routed to a redpanda output without modification this would roughly translate to the batch.bytes config field of a traditional producer.", + "description": "The maximum size (in bytes) for each batch yielded by this input. This value must be less than or equal to the `partition_buffer_bytes`. If using Redpanda output, this value should not be greater than the `max_message_bytes` option value (1MB by default), and for high-throughput scenarios they should be equal.", "is_advanced": true, "kind": "scalar", "name": "max_yield_batch_bytes", "type": "string" }, + { + "children": [ + { + "default": false, + "description": "Whether to enable the unordered processing of messages from a given partition.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": 1024, + "description": "Determines how many messages of the same partition can be processed in parallel before applying back pressure. When a message of a given offset is delivered to the output the offset is only allowed to be committed when all messages of prior offsets have also been delivered, this ensures at-least-once delivery guarantees. However, this mechanism also increases the likelihood of duplicates in the event of crashes or server faults, reducing the checkpoint limit will mitigate this.", + "is_advanced": true, + "kind": "scalar", + "name": "checkpoint_limit", + "type": "int" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "is_advanced": true, + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "is_advanced": true, + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "is_advanced": true, + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "Allows you to configure a xref:configuration:batching.adoc[batching policy] that applies to individual topic partitions in order to batch messages together before flushing them for processing. Batching can be beneficial for performance as well as useful for windowed processing, and doing so this way preserves the ordering of topic partitions.", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "is_advanced": true, + "kind": "", + "name": "batching", + "type": "object" + } + ], + "description": "Configures partition consumers to allow parallel and therefore unordered processing of messages of any given partition. This allows for better utilization of processing threads and asynchronous publishing at the output level. The maximum parallelization of each partition is determined by the checkpoint_limit field.", + "is_advanced": true, + "kind": "scalar", + "name": "unordered_processing", + "type": "object" + }, { "default": true, "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", @@ -22921,17 +29971,31 @@ "kind": "scalar", "name": "timely_nacks_maximum_wait", "type": "string" + }, + { + "bloblang": true, + "description": "EXPERIMENTAL: A xref:guides:bloblang/about.adoc[Bloblang mapping] that attempts to extract an object containing tracing propagation information, which will then be used as the root tracing span for the message. The specification of the extracted fields must match the format used by the service wide tracer.", + "examples": [ + "root = @", + "root = this.meta.span" + ], + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "extract_tracing_map", + "type": "string", + "version": "3.45.0" } ], "kind": "scalar", - "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", + "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\nlet has_topics = this.topics.length() > 0\nlet has_regexp_topics_include = this.regexp_topics_include.length() > 0 \nlet is_regex_mode = this.regexp_topics || $has_regexp_topics_include\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n if !$has_topics && !$has_regexp_topics_include {\n \"either topics or regexp_topics_include must be specified\"\n },\n if $has_topics && $has_regexp_topics_include {\n \"cannot specify both topics and regexp_topics_include, use one or the other\"\n },\n if this.regexp_topics_exclude.length() > 0 && !$is_regex_mode {\n \"regexp_topics_exclude can only be used when regexp_topics is set to true or regexp_topics_include is specified\"\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", "name": "", "type": "object" }, - "description": "\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\n== Delivery Guarantees\n\nWhen using consumer groups the offsets of \"delivered\" records will be committed automatically and continuously, and in the event of restarts these committed offsets will be used in order to resume from where the input left off. Redpanda Connect guarantees at least once delivery by ensuring that records are only considerd to be delivered when all configured outputs that the record is routed to have confirmed delivery.\n\n== Ordering\n\nIn order to preserve ordering of topic partitions, records consumed from each partition are processed and delivered in the order that they are received, and only one batch of records of a given partition will ever be processed at a time. This means that parallel processing can only occur when multiple topic partitions are being consumed, but ensures that data is processed in a sequential order as determined from the source partition.\n\nHowever, one way in which the order of records can be mixed is when delivery errors occur and error handling mechanisms kick in. Redpanda Connect always leans towards at least once delivery unless instructed otherwise, and this includes reattempting delivery of data when the ordering of that data can no longer be guaranteed.\n\nFor example, a batch of records may have been sent to an output broker and only a subset of records were delivered, in this case Redpanda Connect by default will reattempt to deliver the records that failed, even though these failed records may have come before records that were previously delivered successfully.\n\nIn order to avoid this scenario you must specify in your configuration an alternative way to handle delivery errors in the form of a xref:components:outputs/fallback.adoc[`fallback`] output. It is good practice to also disable the field `auto_retry_nacks` by setting it to `false` when you've added an explicit fallback output as this will improve the throughput of your pipeline. For example, the following config avoids ordering issues by specifying a fallback output into a DLQ topic, which is also retried indefinitely as a way to apply back pressure during connectivity issues:\n\n```yaml\noutput:\n fallback:\n - redpanda:\n seed_brokers: [ localhost:9092 ]\n topic: foo\n - retry:\n output:\n redpanda:\n seed_brokers: [ localhost:9092 ]\n topic: foo_dlq\n```\n\n== Batching\n\nRecords are processed and delivered from each partition in batches as received from brokers. These batch sizes are therefore dynamically sized in order to optimise throughput, but can be tuned with the config fields `fetch_max_partition_bytes` and `fetch_max_bytes`. Batches can be further broken down using the xref:components:processors/split.adoc[`split`] processor.\n\n== Metrics\n\nEmits a `redpanda_lag` metric with `topic` and `partition` labels for each consumed topic.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", + "description": "\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\n== Delivery Guarantees\n\nWhen using consumer groups the offsets of \"delivered\" records will be committed automatically and continuously, and in the event of restarts these committed offsets will be used in order to resume from where the input left off. Redpanda Connect guarantees at least once delivery by ensuring that records are only considered to be delivered when all configured outputs that the record is routed to have confirmed delivery.\n\n== Ordering\n\nIn order to preserve ordering of topic partitions, records consumed from each partition are processed and delivered in the order that they are received, and only one batch of records of a given partition will ever be processed at a time. This means that parallel processing can only occur when multiple topic partitions are being consumed, but ensures that data is processed in a sequential order as determined from the source partition.\n\nHowever, one way in which the order of records can be mixed is when delivery errors occur and error handling mechanisms kick in. Redpanda Connect always leans towards at least once delivery unless instructed otherwise, and this includes reattempting delivery of data when the ordering of that data can no longer be guaranteed.\n\nFor example, a batch of records may have been sent to an output broker and only a subset of records were delivered, in this case Redpanda Connect by default will reattempt to deliver the records that failed, even though these failed records may have come before records that were previously delivered successfully.\n\nIn order to avoid this scenario you must specify in your configuration an alternative way to handle delivery errors in the form of a xref:components:outputs/fallback.adoc[`fallback`] output. It is good practice to also disable the field `auto_retry_nacks` by setting it to `false` when you've added an explicit fallback output as this will improve the throughput of your pipeline. For example, the following config avoids ordering issues by specifying a fallback output into a DLQ topic, which is also retried indefinitely as a way to apply back pressure during connectivity issues:\n\n```yaml\noutput:\n fallback:\n - redpanda:\n seed_brokers: [ localhost:9092 ]\n topic: foo\n - retry:\n output:\n redpanda:\n seed_brokers: [ localhost:9092 ]\n topic: foo_dlq\n```\n\n== Batching\n\nRecords are processed and delivered from each partition in batches as received from brokers. These batch sizes are therefore dynamically sized in order to optimise throughput, but can be tuned with the config field `max_yield_batch_bytes`, or `unordered_processing.batching` when unordered processing is enabled. Batches can be further broken down using the xref:components:processors/split.adoc[`split`] processor.\n\n== Metrics\n\nEmits a `redpanda_lag` metric with `topic` and `partition` labels for each consumed topic.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", "name": "redpanda", "plugin": true, - "status": "beta", + "status": "stable", "summary": "A Kafka input using the https://github.com/twmb/franz-go[Franz Kafka client library^].", "type": "input" }, @@ -22966,17 +30030,42 @@ "foo:0-5" ] ], + "is_optional": true, "kind": "array", "name": "topics", "type": "string" }, { "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.", + "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.\n\nThis field is deprecated, use `regexp_topics_include` instead.", + "is_deprecated": true, "kind": "scalar", "name": "regexp_topics", "type": "bool" }, + { + "description": "A list of regular expression patterns for matching topics to consume from. When specified, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. This enables regex mode and cannot be used together with the `topics` field. Use `regexp_topics_exclude` to exclude specific patterns.", + "examples": [ + [ + "logs_.*", + "metrics_.*" + ], + [ + "events_[0-9]+" + ] + ], + "is_optional": true, + "kind": "array", + "name": "regexp_topics_include", + "type": "string" + }, + { + "description": "A list of regular expression patterns for excluding topics when regex mode is enabled (via `regexp_topics` or `regexp_topics_include`). Topics matching any of these patterns will be excluded from consumption, even if they match include patterns.", + "is_optional": true, + "kind": "array", + "name": "regexp_topics_exclude", + "type": "string" + }, { "default": "", "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", @@ -23003,7 +30092,7 @@ }, { "default": "1m", - "description": "When using a consumer group, `session_timeout` sets how long a member in hte group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", + "description": "When using a consumer group, `session_timeout` sets how long a member in the group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", "is_advanced": true, "kind": "scalar", "name": "session_timeout", @@ -23011,7 +30100,7 @@ }, { "default": "3s", - "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's sesion stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", + "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's session stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", "is_advanced": true, "kind": "scalar", "name": "heartbeat_interval", @@ -23132,7 +30221,7 @@ }, { "default": "32KB", - "description": "The maximum size (in bytes) for each batch yielded by this input. When routed to a redpanda output without modification this would roughly translate to the batch.bytes config field of a traditional producer.", + "description": "The maximum size (in bytes) for each batch yielded by this input. This value must be less than or equal to the `partition_buffer_bytes`. If using Redpanda output, this value should not be greater than the `max_message_bytes` option value (1MB by default), and for high-throughput scenarios they should be equal.", "is_advanced": true, "kind": "scalar", "name": "max_yield_batch_bytes", @@ -23155,14 +30244,14 @@ } ], "kind": "scalar", - "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", + "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\nlet has_topics = this.topics.length() > 0\nlet has_regexp_topics_include = this.regexp_topics_include.length() > 0 \nlet is_regex_mode = this.regexp_topics || $has_regexp_topics_include\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n if !$has_topics && !$has_regexp_topics_include {\n \"either topics or regexp_topics_include must be specified\"\n },\n if $has_topics && $has_regexp_topics_include {\n \"cannot specify both topics and regexp_topics_include, use one or the other\"\n },\n if this.regexp_topics_exclude.length() > 0 && !$is_regex_mode {\n \"regexp_topics_exclude can only be used when regexp_topics is set to true or regexp_topics_include is specified\"\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", "name": "", "type": "object" }, - "description": "\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\n== Delivery Guarantees\n\nWhen using consumer groups the offsets of \"delivered\" records will be committed automatically and continuously, and in the event of restarts these committed offsets will be used in order to resume from where the input left off. Redpanda Connect guarantees at least once delivery by ensuring that records are only considerd to be delivered when all configured outputs that the record is routed to have confirmed delivery.\n\n== Ordering\n\nIn order to preserve ordering of topic partitions, records consumed from each partition are processed and delivered in the order that they are received, and only one batch of records of a given partition will ever be processed at a time. This means that parallel processing can only occur when multiple topic partitions are being consumed, but ensures that data is processed in a sequential order as determined from the source partition.\n\nHowever, one way in which the order of records can be mixed is when delivery errors occur and error handling mechanisms kick in. Redpanda Connect always leans towards at least once delivery unless instructed otherwise, and this includes reattempting delivery of data when the ordering of that data can no longer be guaranteed.\n\nFor example, a batch of records may have been sent to an output broker and only a subset of records were delivered, in this case Redpanda Connect by default will reattempt to deliver the records that failed, even though these failed records may have come before records that were previously delivered successfully.\n\nIn order to avoid this scenario you must specify in your configuration an alternative way to handle delivery errors in the form of a xref:components:outputs/fallback.adoc[`fallback`] output. It is good practice to also disable the field `auto_retry_nacks` by setting it to `false` when you've added an explicit fallback output as this will improve the throughput of your pipeline. For example, the following config avoids ordering issues by specifying a fallback output into a DLQ topic, which is also retried indefinitely as a way to apply back pressure during connectivity issues:\n\n```yaml\noutput:\n fallback:\n - redpanda_common:\n topic: foo\n - retry:\n output:\n redpanda_common:\n topic: foo_dlq\n```\n\n== Batching\n\nRecords are processed and delivered from each partition in batches as received from brokers. These batch sizes are therefore dynamically sized in order to optimise throughput, but can be tuned with the config fields `fetch_max_partition_bytes` and `fetch_max_bytes`. Batches can be further broken down using the xref:components:processors/split.adoc[`split`] processor.\n\n== Metrics\n\nEmits a `redpanda_lag` metric with `topic` and `partition` labels for each consumed topic.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", + "description": "\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\n== Delivery Guarantees\n\nWhen using consumer groups the offsets of \"delivered\" records will be committed automatically and continuously, and in the event of restarts these committed offsets will be used in order to resume from where the input left off. Redpanda Connect guarantees at least once delivery by ensuring that records are only considered to be delivered when all configured outputs that the record is routed to have confirmed delivery.\n\n== Ordering\n\nIn order to preserve ordering of topic partitions, records consumed from each partition are processed and delivered in the order that they are received, and only one batch of records of a given partition will ever be processed at a time. This means that parallel processing can only occur when multiple topic partitions are being consumed, but ensures that data is processed in a sequential order as determined from the source partition.\n\nHowever, one way in which the order of records can be mixed is when delivery errors occur and error handling mechanisms kick in. Redpanda Connect always leans towards at least once delivery unless instructed otherwise, and this includes reattempting delivery of data when the ordering of that data can no longer be guaranteed.\n\nFor example, a batch of records may have been sent to an output broker and only a subset of records were delivered, in this case Redpanda Connect by default will reattempt to deliver the records that failed, even though these failed records may have come before records that were previously delivered successfully.\n\nIn order to avoid this scenario you must specify in your configuration an alternative way to handle delivery errors in the form of a xref:components:outputs/fallback.adoc[`fallback`] output. It is good practice to also disable the field `auto_retry_nacks` by setting it to `false` when you've added an explicit fallback output as this will improve the throughput of your pipeline. For example, the following config avoids ordering issues by specifying a fallback output into a DLQ topic, which is also retried indefinitely as a way to apply back pressure during connectivity issues:\n\n```yaml\noutput:\n fallback:\n - redpanda_common:\n topic: foo\n - retry:\n output:\n redpanda_common:\n topic: foo_dlq\n```\n\n== Batching\n\nRecords are processed and delivered from each partition in batches as received from brokers. These batch sizes are therefore dynamically sized in order to optimise throughput, but can be tuned with the config fields `fetch_max_partition_bytes` and `fetch_max_bytes`. Batches can be further broken down using the xref:components:processors/split.adoc[`split`] processor.\n\n== Metrics\n\nEmits a `redpanda_lag` metric with `topic` and `partition` labels for each consumed topic.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", "name": "redpanda_common", "plugin": true, - "status": "beta", + "status": "deprecated", "summary": "Consumes data from a Redpanda (Kafka) broker, using credentials defined in a common top-level `redpanda` config block.", "type": "input" }, @@ -23191,7 +30280,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -23344,6 +30433,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -23360,7 +30453,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -23416,6 +30509,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -23511,7 +30664,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -23534,6 +30687,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "description": "\nA list of topics to consume from. Multiple comma separated topics can be listed in a single element. When a `consumer_group` is specified partitions are automatically distributed across consumers of a topic, otherwise all partitions are consumed.\n\nAlternatively, it's possible to specify explicit partitions to consume from with a colon after the topic name, e.g. `foo:0` would consume the partition 0 of the topic foo. This syntax supports ranges, e.g. `foo:0-10` would consume partitions 0 through to 10 inclusive.\n\nFinally, it's also possible to specify an explicit offset to consume from by adding another colon after the partition, e.g. `foo:0:10` would consume the partition 0 of the topic foo starting from the offset 10. If the offset is not present (or remains unspecified) then the field `start_from_oldest` determines which offset to start from.", "examples": [ @@ -23559,17 +30772,42 @@ "foo:0-5" ] ], + "is_optional": true, "kind": "array", "name": "topics", "type": "string" }, { "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.", + "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics. When enabled, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. When topics are specified with explicit partitions this field must remain set to `false`.\n\nThis field is deprecated, use `regexp_topics_include` instead.", + "is_deprecated": true, "kind": "scalar", "name": "regexp_topics", "type": "bool" }, + { + "description": "A list of regular expression patterns for matching topics to consume from. When specified, the client will periodically refresh the list of matching topics based on the `metadata_max_age` interval. This enables regex mode and cannot be used together with the `topics` field. Use `regexp_topics_exclude` to exclude specific patterns.", + "examples": [ + [ + "logs_.*", + "metrics_.*" + ], + [ + "events_[0-9]+" + ] + ], + "is_optional": true, + "kind": "array", + "name": "regexp_topics_include", + "type": "string" + }, + { + "description": "A list of regular expression patterns for excluding topics when regex mode is enabled (via `regexp_topics` or `regexp_topics_include`). Topics matching any of these patterns will be excluded from consumption, even if they match include patterns.", + "is_optional": true, + "kind": "array", + "name": "regexp_topics_exclude", + "type": "string" + }, { "default": "", "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", @@ -23596,7 +30834,7 @@ }, { "default": "1m", - "description": "When using a consumer group, `session_timeout` sets how long a member in hte group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", + "description": "When using a consumer group, `session_timeout` sets how long a member in the group can go between heartbeats. If a member does not heartbeat in this timeout, the broker will remove the member from the group and initiate a rebalance.", "is_advanced": true, "kind": "scalar", "name": "session_timeout", @@ -23604,7 +30842,7 @@ }, { "default": "3s", - "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's sesion stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", + "description": "When using a consumer group, `heartbeat_interval` sets how long a group member goes between heartbeats to Kafka. Kafka uses heartbeats to ensure that a group member's session stays active. This value should be no higher than 1/3rd of the `session_timeout`. This is equivalent to the Java heartbeat.interval.ms setting.", "is_advanced": true, "kind": "scalar", "name": "heartbeat_interval", @@ -23725,539 +30963,307 @@ }, { "default": "32KB", - "description": "The maximum size (in bytes) for each batch yielded by this input. When routed to a redpanda output without modification this would roughly translate to the batch.bytes config field of a traditional producer.", + "description": "The maximum size (in bytes) for each batch yielded by this input. This value must be less than or equal to the `partition_buffer_bytes`. If using Redpanda output, this value should not be greater than the `max_message_bytes` option value (1MB by default), and for high-throughput scenarios they should be equal.", "is_advanced": true, "kind": "scalar", "name": "max_yield_batch_bytes", "type": "string" }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "description": "EXPERIMENTAL: Specify a maximum period of time in which each message can be consumed and awaiting either acknowledgement or rejection before rejection is instead forced. This can be useful for avoiding situations where certain downstream components can result in blocked confirmation of delivery that exceeds SLAs.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "timely_nacks_maximum_wait", - "type": "string" - }, - { - "default": "redpanda_migrator_output", - "description": "The label of the redpanda_migrator output in which the currently selected topics need to be created before attempting to read messages.", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "output_resource", - "type": "string" - }, - { - "default": true, - "description": "Use the specified replication factor when creating topics.", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "replication_factor_override", - "type": "bool" - }, - { - "default": 3, - "description": "Replication factor for created topics. This is only used when `replication_factor_override` is set to `true`.", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "replication_factor", - "type": "int" - }, - { - "default": false, - "description": "Decode headers into lists to allow handling of multiple values with the same key", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "multi_header", - "type": "bool" - }, - { - "default": 1024, - "description": "The maximum number of messages that should be accumulated into each batch.", - "is_advanced": true, - "is_deprecated": true, - "kind": "scalar", - "name": "batch_size", - "type": "int" - } - ], - "kind": "scalar", - "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", - "name": "", - "type": "object" - }, - "description": "\nReads a batch of messages from a Kafka broker and waits for the output to acknowledge the writes before updating the Kafka consumer group offset.\n\nThis input should be used in combination with a `redpanda_migrator` output.\n\nWhen a consumer group is specified this input consumes one or more topics where partitions will automatically balance across any other connected clients with the same consumer group. When a consumer group is not specified topics can either be consumed in their entirety or with explicit partitions.\n\nIt provides the same delivery guarantees and ordering semantics as the `redpanda` input.\n\n== Metrics\n\nEmits a `redpanda_lag` metric with `topic` and `partition` labels for each consumed topic.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_key\n- kafka_topic\n- kafka_partition\n- kafka_offset\n- kafka_lag\n- kafka_timestamp_ms\n- kafka_timestamp_unix\n- kafka_tombstone_message\n- All record headers\n```\n", - "name": "redpanda_migrator", - "plugin": true, - "status": "beta", - "summary": "A Redpanda Migrator input using the https://github.com/twmb/franz-go[Franz Kafka client library^].", - "type": "input", - "version": "4.37.0" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "The `redpanda_migrator` input configuration.\n", - "kind": "map", - "name": "redpanda_migrator", - "type": "unknown" - }, - { - "description": "The `schema_registry` input configuration.\n", - "kind": "map", - "name": "schema_registry", - "type": "unknown" - }, - { - "default": true, - "description": "Migrate all schemas first before starting to migrate data.\n", - "kind": "scalar", - "name": "migrate_schemas_before_data", - "type": "bool" - }, - { - "default": "15s", - "description": "Duration between OffsetFetch polling attempts in redpanda_migrator_offsets input.\n", - "kind": "scalar", - "name": "consumer_group_offsets_poll_interval", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "All-in-one input which reads messages and schemas from a Kafka or Redpanda cluster. This input is meant to be used\ntogether with the `redpanda_migrator_bundle` output.\n", - "name": "redpanda_migrator_bundle", - "plugin": true, - "status": "experimental", - "summary": "Redpanda Migrator bundle input", - "type": "input" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", - "examples": [ - [ - "localhost:9092" - ], - [ - "foo:9092", - "bar:9092" - ], - [ - "foo:9092,bar:9092" - ] - ], - "kind": "array", - "name": "seed_brokers", - "type": "string" - }, - { - "default": "benthos", - "description": "An identifier for the client connection.", - "is_advanced": true, - "kind": "scalar", - "name": "client_id", - "type": "string" - }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "description": "The base URL of the schema registry service. Required for schema migration functionality.", "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "http://localhost:8081", + "https://schema-registry.example.com:8081" ], - "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "url", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, + "default": "5s", + "description": "HTTP client timeout for schema registry requests.", + "is_optional": true, "kind": "scalar", - "name": "root_cas_file", + "name": "timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "key_file", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "foo", - "${KEY_PASSWORD}" + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ], "is_advanced": true, "is_secret": true, "kind": "scalar", - "name": "password", + "name": "root_cas", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "children": [ - { - "annotated_options": [ - [ - "AWS_MSK_IAM", - "AWS IAM based authentication as specified by the 'aws-msk-iam-auth' java library." - ], - [ - "OAUTHBEARER", - "OAuth Bearer based authentication." - ], - [ - "PLAIN", - "Plain text authentication." - ], - [ - "SCRAM-SHA-256", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "SCRAM-SHA-512", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "none", - "Disable sasl authentication" - ] - ], - "description": "The SASL mechanism to use.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "mechanism", - "type": "string" - }, - { - "default": "", - "description": "A username to provide for PLAIN or SCRAM-* authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to provide for PLAIN or SCRAM-* authentication.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The token to use for a single session's OAUTHBEARER authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "token", - "type": "string" - }, - { - "description": "Key/value pairs to add to OAUTHBEARER authentication requests.", - "is_advanced": true, - "is_optional": true, - "kind": "map", - "name": "extensions", - "type": "string" - }, - { - "children": [ - { - "description": "The AWS region to target.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "region", - "type": "string" }, { - "description": "Allows you to specify a custom endpoint for the AWS API.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "endpoint", + "name": "root_cas_file", "type": "string" }, { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "profile", - "type": "string" - }, - { - "description": "The ID of credentials to use.", + "default": "", + "description": "A plain text certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "id", + "name": "cert", "type": "string" }, { - "description": "The secret for the credentials being used.", + "default": "", + "description": "A plain text certificate key to use.", "is_advanced": true, - "is_optional": true, "is_secret": true, "kind": "scalar", - "name": "secret", + "name": "key", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": "", + "description": "The path of a certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "token", + "name": "cert_file", "type": "string" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" - }, - { - "description": "A role ARN to assume.", + "default": "", + "description": "The path of a certificate key to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role", + "name": "key_file", "type": "string" }, { - "description": "An external ID to provide when assuming a role.", + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, - "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "role_external_id", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "credentials", + "kind": "array", + "name": "client_certs", "type": "object" } ], - "description": "Contains AWS specific fields for when the `mechanism` is set to `AWS_MSK_IAM`.", + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "aws", + "name": "oauth", "type": "object" - } - ], - "description": "Specify one or more methods of SASL authentication. SASL is tried in order; if the broker supports the first mechanism, all connections will use that mechanism. If the first mechanism fails, the client will pick the first supported mechanism. If the broker does not support any client mechanisms, connections will fail.", - "examples": [ - [ - { - "mechanism": "SCRAM-SHA-512", - "password": "bar", - "username": "foo" - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "sasl", - "type": "object" - }, - { - "default": "5m", - "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", - "is_advanced": true, - "kind": "scalar", - "name": "metadata_max_age", - "type": "string" - }, - { - "default": "10s", - "description": "The request time overhead. Uses the given time as overhead while deadlining requests. Roughly equivalent to request.timeout.ms, but grants additional time to requests that have timeout fields.", - "is_advanced": true, - "kind": "scalar", - "name": "request_timeout_overhead", - "type": "string" - }, - { - "default": "20s", - "description": "The rough amount of time to allow connections to idle before they are closed.", - "is_advanced": true, - "kind": "scalar", - "name": "conn_idle_timeout", - "type": "string" - }, - { - "description": "\nA list of topics to consume from. Multiple comma separated topics can be listed in a single element. When a `consumer_group` is specified partitions are automatically distributed across consumers of a topic, otherwise all partitions are consumed.", - "examples": [ - [ - "foo", - "bar" - ], - [ - "things.*" - ], - [ - "foo,bar" - ] + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object" + } ], - "kind": "array", - "linter": "if this.length() == 0 { [\"at least one topic must be specified\"] }", - "name": "topics", - "type": "string" - }, - { - "default": false, - "description": "Whether listed topics should be interpreted as regular expression patterns for matching multiple topics.", - "kind": "scalar", - "name": "regexp_topics", - "type": "bool" - }, - { - "default": "", - "description": "A rack specifies where the client is physically located and changes fetch requests to consume from the closest replica as opposed to the leader replica.", - "is_advanced": true, - "kind": "scalar", - "name": "rack_id", - "type": "string" - }, - { - "default": "15s", - "description": "Duration between OffsetFetch polling attempts.", - "is_advanced": true, + "description": "Configuration for schema registry integration. Enables migration of schema subjects, versions, and compatibility settings between clusters.", + "is_optional": true, "kind": "scalar", - "name": "poll_interval", - "type": "string" + "name": "schema_registry", + "type": "object" }, { "default": true, @@ -24265,49 +31271,20 @@ "kind": "scalar", "name": "auto_replay_nacks", "type": "bool" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "consumer_group", - "type": "string" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "commit_period", - "type": "string" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "partition_buffer_bytes", - "type": "string" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "topic_lag_refresh_period", - "type": "string" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "max_yield_batch_bytes", - "type": "string" } ], "kind": "scalar", + "linter": "\nlet has_topic_partitions = this.topics.any(t -> t.contains(\":\"))\nlet has_topics = this.topics.length() > 0\nlet has_regexp_topics_include = this.regexp_topics_include.length() > 0 \nlet is_regex_mode = this.regexp_topics || $has_regexp_topics_include\n\nroot = [\n if $has_topic_partitions {\n if this.consumer_group.or(\"\") != \"\" {\n \"this input does not support both a consumer group and explicit topic partitions\"\n } else if this.regexp_topics {\n \"this input does not support both regular expression topics and explicit topic partitions\"\n }\n } else {\n if this.consumer_group.or(\"\") == \"\" {\n \"a consumer group is mandatory when not using explicit topic partitions\"\n }\n },\n if !$has_topics && !$has_regexp_topics_include {\n \"either topics or regexp_topics_include must be specified\"\n },\n if $has_topics && $has_regexp_topics_include {\n \"cannot specify both topics and regexp_topics_include, use one or the other\"\n },\n if this.regexp_topics_exclude.length() > 0 && !$is_regex_mode {\n \"regexp_topics_exclude can only be used when regexp_topics is set to true or regexp_topics_include is specified\"\n },\n # We don't have any way to distinguish between start_from_oldest set explicitly to true and not set at all, so we\n # assume users will be OK if start_offset overwrites it silently\n if this.start_from_oldest == false && this.start_offset == \"earliest\" {\n \"start_from_oldest cannot be set to false when start_offset is set to earliest\"\n }\n]\n", "name": "", "type": "object" }, - "description": "\nThis input reads consumer group updates via the `OffsetFetch` API and should be used in combination with the `redpanda_migrator_offsets` output.\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- kafka_offset_topic\n- kafka_offset_group\n- kafka_offset_partition\n- kafka_offset_commit_timestamp\n- kafka_offset_metadata\n- kafka_is_high_watermark\n```\n", - "name": "redpanda_migrator_offsets", + "description": "\nThe `redpanda_migrator` input simply consumes records from the source cluster and forwards them downstream.\nIt does not perform topic/schema/group synchronisation.\nAll migration features and coordination live in the paired `redpanda_migrator` output.\n\n**IMPORTANT:** This input requires a corresponding `redpanda_migrator` output in the same pipeline.\nEach pipeline must have both input and output components configured.\nFor capabilities, guarantees, scheduling, and examples, see the output documentation.\n\n**Performance tuning for high throughput:** For workloads with high message rates or large messages,\nadjust the following fields to increase buffer sizes and batch processing:\n\n- `partition_buffer_bytes: 2MB`\n- `max_yield_batch_bytes: 1MB`\n\nThese settings allow the consumer to buffer more data per partition and yield larger batches,\nreducing overhead and improving throughput at the cost of higher memory usage.", + "name": "redpanda_migrator", "plugin": true, - "status": "beta", - "summary": "Redpanda Migrator consumer group offsets input using the https://github.com/twmb/franz-go[Franz Kafka client library^].", + "status": "stable", + "summary": "Kafka consumer for migration pipelines. All migration logic is handled by the redpanda_migrator output.", "type": "input", - "version": "4.45.0" + "version": "4.67.0" }, { "categories": [ @@ -24328,809 +31305,775 @@ }, { "categories": [ - "Integration" + "Services" ], "config": { "children": [ { - "description": "The base URL of the schema registry service.", + "description": "Salesforce instance base URL for your org, protocol included and no trailing slash. Used as the base for both the OAuth token endpoint and REST queries. Production orgs use `https://{my-domain}.my.salesforce.com`; sandboxes use `https://{my-domain}.sandbox.my.salesforce.com`. Legacy instance URLs (`https://na123.salesforce.com`) still work but My Domain URLs are strongly recommended by Salesforce.", + "examples": [ + "https://acme.my.salesforce.com", + "https://acme--staging.sandbox.my.salesforce.com" + ], "kind": "scalar", - "name": "url", + "name": "org_url", "type": "string" }, { - "default": false, - "description": "Include deleted entities.", + "description": "Consumer Key of the Salesforce Connected App authorized for the OAuth Client Credentials flow. Create the Connected App under Setup → App Manager → New Connected App, enable OAuth settings, enable the Client Credentials Flow under `Flow Enablement`, then copy the Consumer Key from `Manage Consumer Details`.", + "kind": "scalar", + "name": "client_id", + "type": "string" + }, + { + "description": "Consumer Secret of the Salesforce Connected App, paired with `client_id`. Sensitive — prefer environment variable interpolation (`${SALESFORCE_CLIENT_SECRET}`) over inlining.", + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "v65.0", + "description": "Salesforce REST API version to target, prefixed with `v`. Affects endpoint paths (`/services/data/{api_version}/...`) and available fields/objects. Must be supported by your org — check Setup → Company Information. Older versions may lack recent fields.", + "examples": [ + "v65.0", + "v62.0" + ], + "kind": "scalar", + "name": "api_version", + "type": "string" + }, + { + "description": "The sObject API name to SELECT from. Case-sensitive; uses the API name, not the display label. Standard objects use the noun (`Account`, `Opportunity`); custom objects end with `__c`; Big Objects end with `__b`; External Objects end with `__x`. Confirm the exact API name in Setup → Object Manager.", + "examples": [ + "Account", + "Contact", + "MyCustom__c" + ], + "kind": "scalar", + "name": "object", + "type": "string" + }, + { + "description": "Ordered list of field API names to retrieve. SOQL does not accept `*` — every field must be listed explicitly. Standard fields use their documented names; custom fields end with `__c`. Relationship fields traverse parents via dot notation (`Account.Name`, `Owner.Manager.Email`) up to 5 levels deep. Requesting a non-existent or non-queryable field fails at Connect time with a SOQL compile error.", + "examples": [ + [ + "Id", + "Name", + "LastModifiedDate" + ], + [ + "Id", + "Account.Name", + "Owner.Email" + ], + [ + "Id", + "MyCustom__c" + ] + ], + "kind": "array", + "name": "columns", + "type": "string" + }, + { + "description": "Optional SOQL WHERE body, without the `WHERE` keyword. `?` placeholders are substituted client-side from `args_mapping` with SOQL literal escaping (quoted strings, ISO-8601 datetimes). Supports the full WHERE grammar: `AND`/`OR`/`NOT`, `LIKE`, `IN`, date literals (`TODAY`, `LAST_N_DAYS:7`), subqueries. Date/datetime comparisons require ISO-8601 with explicit timezone.", + "examples": [ + "LastModifiedDate > ?", + "Status__c = ? AND CreatedDate > ?", + "OwnerId IN (?, ?)" + ], + "is_optional": true, + "kind": "scalar", + "name": "where", + "type": "string" + }, + { + "bloblang": true, + "description": "Optional xref:guides:bloblang/about.adoc[Bloblang mapping] whose result must be an array of values matching the count of `?` placeholders in `where`. Values are SOQL-escaped: strings become quoted literals, timestamps become ISO-8601, booleans and numbers pass through. The mapping is evaluated once at startup with no message context — use `now()`, `env()`, or `cache()`.", + "examples": [ + "root = [ (now() - \"1h\").ts_format(\"2006-01-02T15:04:05Z\") ]", + "root = [ \"Active\", (now() - \"24h\").ts_format(\"2006-01-02T15:04:05Z\") ]" + ], + "is_optional": true, + "kind": "scalar", + "name": "args_mapping", + "type": "string" + }, + { + "description": "Optional SOQL fragment inserted before the SELECT keyword. Rarely needed — provided for forward compatibility with future SOQL extensions or Bulk API framing.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "include_deleted", - "type": "bool" + "name": "prefix", + "type": "string" }, { - "default": "", - "description": "Include only subjects which match the regular expression filter. All subjects are selected when not set.", + "description": "Optional SOQL fragment appended after the WHERE clause. Typical uses: `ORDER BY` for deterministic pagination, `LIMIT` to cap result size, `FOR REFERENCE` / `FOR VIEW` to mark records for Chatter tracking.", + "examples": [ + "ORDER BY LastModifiedDate DESC", + "ORDER BY Id LIMIT 1000", + "ORDER BY CreatedDate DESC LIMIT 10000" + ], "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "subject_filter", + "name": "suffix", "type": "string" }, { "default": true, - "description": "Fetch all schemas on connect and sort them by ID. Should be set to `true` when schema references are used.", - "is_advanced": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "fetch_in_order", - "type": "bool", - "version": "4.37.0" + "name": "auto_replay_nacks", + "type": "bool" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "5s", + "description": "HTTP request timeout.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "cert", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "", - "description": "A plain text certificate key to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "The path of a certificate key to use.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "key_file", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "foo", - "${KEY_PASSWORD}" + "./root_cas.pem" ], "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "root_cas_file", "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "tls", + "type": "object" }, { "default": "", - "description": "A value used to identify the client to the service provider.", + "description": "HTTP proxy URL. Empty string disables proxying.", "is_advanced": true, "kind": "scalar", - "name": "consumer_key", + "name": "proxy_url", "type": "string" }, { - "default": "", - "description": "A secret used to establish ownership of the consumer key.", + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "consumer_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "disable_http2", + "type": "bool" }, { - "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", "is_advanced": true, "kind": "scalar", - "name": "access_token", - "type": "string" + "name": "tps_limit", + "type": "float" }, { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", + "default": 1, + "description": "Maximum burst size for rate limiting.", "is_advanced": true, "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "tps_burst", + "type": "int" }, { - "default": "", - "description": "A username to authenticate as.", + "children": [ + { + "default": "1s", + "description": "Initial interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", "is_advanced": true, "kind": "scalar", - "name": "username", - "type": "string" + "name": "backoff", + "type": "object" }, { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "tcp", + "type": "object" }, { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } + ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "h2", + "type": "object" + } + ], + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", "is_advanced": true, "kind": "scalar", - "name": "private_key_file", - "type": "string" + "name": "http", + "type": "object" }, { "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, - "kind": "scalar", - "name": "signing_method", - "type": "string" - }, - { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" - }, - { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" - } - ], - "description": "BETA: Allows you to specify JWT authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "jwt", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- schema_registry_subject\n- schema_registry_subject_compatibility_level\n- schema_registry_version\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n", - "examples": [ - { - "config": "\ninput:\n schema_registry:\n url: http://localhost:8081\n include_deleted: true\n subject_filter: ^foo.*\n", - "summary": "Read all schemas (including deleted) from a Schema Registry instance which are associated with subjects matching the `^foo.*` filter.", - "title": "Read schemas" - } - ], - "name": "schema_registry", - "plugin": true, - "status": "beta", - "summary": "Reads schemas from SchemaRegistry.", - "type": "input", - "version": "4.32.2" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "children": [ - { - "default": "none", - "description": "The type of join to perform. A `full-outer` ensures that all identifiers seen in any of the input sequences are sent, and is performed by consuming all input sequences before flushing the joined results. An `outer` join consumes all input sequences but only writes data joined from the last input in the sequence, similar to a left or right outer join. With an `outer` join if an identifier appears multiple times within the final sequence input it will be flushed each time it appears. `full-outter` and `outter` have been deprecated in favour of `full-outer` and `outer`.", + "description": "Log level for HTTP request/response logging. Empty disables logging.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"none\": true,\n \"full-outer\": true,\n \"outer\": true,\n \"full-outter\": true,\n \"outter\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "type", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", "options": [ - "none", - "full-outer", - "outer", - "full-outter", - "outter" + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" ], "type": "string" }, { - "default": "", - "description": "A xref:configuration:field_paths.adoc[dot path] that points to a common field within messages of each fragmented data set and can be used to join them. Messages that are not structured or are missing this field will be dropped. This field must be set in order to enable joins.", - "is_advanced": true, - "kind": "scalar", - "name": "id_path", - "type": "string" - }, - { - "default": 1, - "description": "The total number of iterations (shards), increasing this number will increase the overall time taken to process the data, but reduces the memory used in the process. The real memory usage required is significantly higher than the real size of the data and therefore the number of iterations should be at least an order of magnitude higher than the available memory divided by the overall size of the dataset.", + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", "is_advanced": true, "kind": "scalar", - "name": "iterations", + "name": "access_log_body_limit", "type": "int" - }, - { - "default": "array", - "description": "The chosen strategy to use when a data join would otherwise result in a collision of field values. The strategy `array` means non-array colliding values are placed into an array and colliding arrays are merged. The strategy `replace` replaces old values with new values. The strategy `keep` keeps the old value.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"array\": true,\n \"replace\": true,\n \"keep\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "merge_strategy", - "options": [ - "array", - "replace", - "keep" - ], - "type": "string" } ], - "description": "EXPERIMENTAL: Provides a way to perform outer joins of arbitrarily structured and unordered data resulting from the input sequence, even when the overall size of the data surpasses the memory available on the machine.\n\nWhen configured the sequence of inputs will be consumed one or more times according to the number of iterations, and when more than one iteration is specified each iteration will process an entirely different set of messages by sharding them by the ID field. Increasing the number of iterations reduces the memory consumption at the cost of needing to fully parse the data each time.\n\nEach message must be structured (JSON or otherwise processed into a structured form) and the fields will be aggregated with those of other messages sharing the ID. At the end of each iteration the joined messages are flushed downstream before the next iteration begins, hence keeping memory usage limited.", + "description": "HTTP client configuration for Salesforce REST calls (OAuth token endpoint and, where applicable, data queries).", "is_advanced": true, "kind": "scalar", - "name": "sharded_join", - "type": "object", - "version": "3.40.0" - }, - { - "description": "An array of inputs to read from sequentially.", - "kind": "array", - "name": "inputs", - "type": "input" + "name": "http", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "This input is useful for consuming from inputs that have an explicit end but must not be consumed in parallel.", + "description": "Runs a SOQL query against the Salesforce REST API, paginates through all result pages, and emits one message per record. When results are exhausted the input shuts down, letting the pipeline terminate gracefully (or the next input in a xref:components:inputs/sequence.adoc[sequence] to take over).\n\n== When to use this input\n\nUse `salesforce` for:\n\n- One-shot extracts (e.g. dump all Accounts into a warehouse).\n- Periodic full-table refreshes via a scheduled pipeline or xref:components:inputs/sequence.adoc[sequence].\n- Backfills and ad-hoc queries.\n- Warming up a downstream pipeline before switching to CDC.\n\nUse a different Salesforce input instead if:\n\n- You need continuous change events — use xref:components:inputs/salesforce_cdc.adoc[`salesforce_cdc`].\n- You need a GraphQL query (cross-object in one request) — use xref:components:inputs/salesforce_graphql.adoc[`salesforce_graphql`].\n\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- `sobject`: The sObject API name (e.g. \"Account\").\n\n== Authentication\n\nUses the Salesforce OAuth 2.0 Client Credentials flow. Create a Connected App in Salesforce, enable OAuth settings and the Client Credentials Flow, then supply the Consumer Key as `client_id` and Consumer Secret as `client_secret`.\n", "examples": [ { - "config": "\ninput:\n sequence:\n inputs:\n - file:\n paths: [ ./dataset.csv ]\n scanner:\n csv: {}\n - generate:\n count: 1\n mapping: 'root = {\"status\":\"finished\"}'\n", - "summary": "A common use case for sequence might be to generate a message at the end of our main input. With the following config once the records within `./dataset.csv` are exhausted our final payload `{\"status\":\"finished\"}` will be routed through the pipeline.", - "title": "End of Stream Message" - }, - { - "config": "\ninput:\n sequence:\n sharded_join:\n type: full-outer\n id_path: uuid\n merge_strategy: array\n inputs:\n - file:\n paths:\n - ./hobbies.csv\n - ./main.csv\n scanner:\n csv: {}\n", - "summary": "Redpanda Connect can be used to join unordered data from fragmented datasets in memory by specifying a common identifier field and a number of sharded iterations. For example, given two CSV files, the first called \"main.csv\", which contains rows of user data:\n\n```csv\nuuid,name,age\nAAA,Melanie,34\nBBB,Emma,28\nCCC,Geri,45\n```\n\nAnd the second called \"hobbies.csv\" that, for each user, contains zero or more rows of hobbies:\n\n```csv\nuuid,hobby\nCCC,pokemon go\nAAA,rowing\nAAA,golf\n```\n\nWe can parse and join this data into a single dataset:\n\n```json\n{\"uuid\":\"AAA\",\"name\":\"Melanie\",\"age\":34,\"hobbies\":[\"rowing\",\"golf\"]}\n{\"uuid\":\"BBB\",\"name\":\"Emma\",\"age\":28}\n{\"uuid\":\"CCC\",\"name\":\"Geri\",\"age\":45,\"hobbies\":[\"pokemon go\"]}\n```\n\nWith the following config:", - "title": "Joining Data (Simple)" - }, - { - "config": "\ninput:\n sequence:\n sharded_join:\n type: full-outer\n id_path: uuid\n iterations: 10\n merge_strategy: array\n inputs:\n - file:\n paths: [ ./main.csv ]\n scanner:\n csv: {}\n - file:\n paths: [ ./hobbies.ndjson ]\n scanner:\n lines: {}\n processors:\n - mapping: |\n root.uuid = this.document.uuid\n root.hobbies = this.document.hobbies.map_each(this.type)\n", - "summary": "In this example we are able to join unordered and fragmented data from a combination of CSV files and newline-delimited JSON documents by specifying multiple sequence inputs with their own processors for extracting the structured data.\n\nThe first file \"main.csv\" contains straight forward CSV data:\n\n```csv\nuuid,name,age\nAAA,Melanie,34\nBBB,Emma,28\nCCC,Geri,45\n```\n\nAnd the second file called \"hobbies.ndjson\" contains JSON documents, one per line, that associate an identifier with an array of hobbies. However, these data objects are in a nested format:\n\n```json\n{\"document\":{\"uuid\":\"CCC\",\"hobbies\":[{\"type\":\"pokemon go\"}]}}\n{\"document\":{\"uuid\":\"AAA\",\"hobbies\":[{\"type\":\"rowing\"},{\"type\":\"golf\"}]}}\n```\n\nAnd so we will want to map these into a flattened structure before the join, and then we will end up with a single dataset that looks like this:\n\n```json\n{\"uuid\":\"AAA\",\"name\":\"Melanie\",\"age\":34,\"hobbies\":[\"rowing\",\"golf\"]}\n{\"uuid\":\"BBB\",\"name\":\"Emma\",\"age\":28}\n{\"uuid\":\"CCC\",\"name\":\"Geri\",\"age\":45,\"hobbies\":[\"pokemon go\"]}\n```\n\nWith the following config:", - "title": "Joining Data (Advanced)" + "config": "\ninput:\n salesforce:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n object: Account\n columns: [ Id, Name, LastModifiedDate ]\n where: LastModifiedDate > ?\n args_mapping: |\n root = [ (now() - \"1h\").ts_format(\"2006-01-02T15:04:05Z\") ]\n", + "summary": "Query Accounts modified in the last hour and emit one message per record:", + "title": "Recent Account Changes" } ], - "name": "sequence", + "name": "salesforce", "plugin": true, "status": "stable", - "summary": "Reads messages from a sequence of child inputs, starting with the first and once that input gracefully terminates starts consuming from the next, and so on.", + "summary": "Executes a Salesforce SOQL query once and emits a message for each record.", "type": "input" }, { "categories": [ - "Network" + "Services" ], "config": { "children": [ { - "description": "The address of the server to connect to.", + "description": "Salesforce instance base URL for your org, protocol included and no trailing slash. Used as the base for both the OAuth token endpoint and REST queries. Production orgs use `https://{my-domain}.my.salesforce.com`; sandboxes use `https://{my-domain}.sandbox.my.salesforce.com`. Legacy instance URLs (`https://na123.salesforce.com`) still work but My Domain URLs are strongly recommended by Salesforce.", + "examples": [ + "https://acme.my.salesforce.com", + "https://acme--staging.sandbox.my.salesforce.com" + ], "kind": "scalar", - "name": "address", + "name": "org_url", "type": "string" }, { - "default": "30s", - "description": "The connection timeout to use when connecting to the target server.", - "is_advanced": true, + "description": "Consumer Key of the Salesforce Connected App authorized for the OAuth Client Credentials flow. Create the Connected App under Setup → App Manager → New Connected App, enable OAuth settings, enable the Client Credentials Flow under `Flow Enablement`, then copy the Consumer Key from `Manage Consumer Details`.", "kind": "scalar", - "name": "connection_timeout", + "name": "client_id", "type": "string" }, { - "children": [ - { - "default": "", - "description": "The username to authenticate with the SFTP server.", - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "The password for the specified username to connect to the SFTP server.", - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "The path to the SFTP server's public key file, used for host key verification.", - "is_optional": true, - "kind": "scalar", - "name": "host_public_key_file", - "type": "string" - }, - { - "description": "The raw contents of the SFTP server's public key, used for host key verification.", - "is_optional": true, - "kind": "scalar", - "name": "host_public_key", - "type": "string" - }, - { - "description": "The path to the private key file, used for authenticating the username.", - "is_optional": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, - { - "description": "The raw contents of the private key, used for authenticating the username.", - "is_optional": true, - "is_secret": true, - "kind": "scalar", - "name": "private_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "Optional passphrase for decrypting the private key, if it's encrypted.", - "is_secret": true, - "kind": "scalar", - "name": "private_key_pass", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "The credentials to use to log into the target server.", - "kind": "scalar", - "linter": "\nroot = match {\n this.exists(\"host_public_key\") && this.exists(\"host_public_key_file\") => \"both host_public_key and host_public_key_file can't be set simultaneously\"\n this.exists(\"private_key\") && this.exists(\"private_key_file\") => \"both private_key and private_key_file can't be set simultaneously\"\n}", - "name": "credentials", - "type": "object" - }, - { - "default": 10, - "description": "The maximum number of SFTP sessions.", - "is_advanced": true, + "description": "Consumer Secret of the Salesforce Connected App, paired with `client_id`. Sensitive — prefer environment variable interpolation (`${SALESFORCE_CLIENT_SECRET}`) over inlining.", + "is_secret": true, "kind": "scalar", - "name": "max_sftp_sessions", - "type": "int" - }, - { - "description": "A list of paths to consume sequentially. Glob patterns are supported.", - "kind": "array", - "name": "paths", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "default": "v65.0", + "description": "Salesforce REST API version to target, prefixed with `v`. Affects endpoint paths (`/services/data/{api_version}/...`) and available fields/objects. Must be supported by your org — check Setup → Company Information. Older versions may lack recent fields.", + "examples": [ + "v65.0", + "v62.0" + ], "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" + "name": "api_version", + "type": "string" }, { - "annotated_options": [ - [ - "auto", - "EXPERIMENTAL: Attempts to derive a codec for each file based on information such as the extension. For example, a .tar.gz file would be consumed with the `gzip/tar` codec. Defaults to all-bytes." - ], - [ - "all-bytes", - "Consume the entire file as a single binary message." - ], - [ - "avro-ocf:marshaler=x", - "EXPERIMENTAL: Consume a stream of Avro OCF datum. The `marshaler` parameter is optional and has the options: `goavro` (default), `json`. Use `goavro` if OCF contains logical types." - ], - [ - "chunker:x", - "Consume the file in chunks of a given number of bytes." - ], - [ - "csv", - "Consume structured rows as comma separated values, the first row must be a header row." - ], - [ - "csv:x", - "Consume structured rows as values separated by a custom delimiter, the first row must be a header row. The custom delimiter must be a single character, e.g. the codec `\"csv:\\t\"` would consume a tab delimited file." - ], - [ - "csv-safe", - "Consume structured rows like `csv`, but sends messages with empty maps on failure to parse. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "csv-safe:x", - "Consume structured rows like `csv:x` as values separated by a custom delimiter, but sends messages with empty maps on failure to parse. The custom delimiter must be a single character, e.g. the codec `\"csv-safe:\\t\"` would consume a tab delimited file. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "delim:x", - "Consume the file in segments divided by a custom delimiter." - ], - [ - "gzip", - "Decompress a gzip file, this codec should precede another codec, e.g. `gzip/all-bytes`, `gzip/tar`, `gzip/csv`, etc." - ], - [ - "pgzip", - "Decompress a gzip file in parallel, this codec should precede another codec, e.g. `pgzip/all-bytes`, `pgzip/tar`, `pgzip/csv`, etc." - ], - [ - "lines", - "Consume the file in segments divided by linebreaks." - ], + "description": "Pub/Sub topics to subscribe to. Each entry is one of: a bare sObject name (`Account` → `/data/AccountChangeEvent`), an explicit CDC channel (`/data/AccountChangeEvent`), the CDC firehose (`/data/ChangeEvents`), or a Platform Event topic (`/event/Order__e`, `/event/LoginEventStream`). Each topic gets its own gRPC subscription with an independent replay cursor.", + "examples": [ [ - "multipart", - "Consumes the output of another codec and batches messages together. A batch ends when an empty message is consumed. For example, the codec `lines/multipart` could be used to consume multipart messages where an empty line indicates the end of each batch." + "Account", + "Contact" ], [ - "regex:(?m)^\\d\\d:\\d\\d:\\d\\d", - "Consume the file in segments divided by regular expression." + "/data/ChangeEvents" ], [ - "skipbom", - "Skip one or more byte order marks for each opened reader, this codec should precede another codec, e.g. `skipbom/csv`, etc." + "Account", + "/event/Order__e" ], [ - "tar", - "Parse the file as a tar archive, and consume each file of the archive as a message." + "Opportunity", + "MyCustom__c", + "/event/Sync_Requested__e" ] ], - "description": "The way in which the bytes of a data source should be converted into discrete messages, codecs are useful for specifying how large files or continuous streams of data might be processed in small chunks rather than loading it all in memory. It's possible to consume lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter. Codecs can be chained with `/`, for example a gzip compressed CSV file can be consumed with the codec `gzip/csv`.", + "kind": "array", + "name": "topics", + "type": "string" + }, + { + "default": true, + "description": "When true (default), paginate a full REST snapshot of every CDC sObject in `topics` before opening any streaming subscription. When false, skip the snapshot and start streaming immediately. Platform Event topics (`/event/...`) are always skipped — they have no REST equivalent.", + "kind": "scalar", + "name": "stream_snapshot", + "type": "bool" + }, + { + "default": "latest", + "description": "Initial replay position used per topic only on first run (when no checkpoint exists in the cache); ignored once a topic's replay ID has been written.\n\n- `latest`: Start from new events only; any changes between prior run and Connect are skipped.\n- `earliest`: Replay from the retention start (24h standard, 72h with enhanced retention). Use to recover missed events after outages.", + "kind": "scalar", + "linter": "\nlet options = {\n \"latest\": true,\n \"earliest\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "replay_preset", + "options": [ + "latest", + "earliest" + ], + "type": "string" + }, + { + "default": 2000, + "description": "Page size for the REST snapshot query — records per `/query` response. Must be between 200 and 2000 per Salesforce REST API limits. Larger pages reduce HTTP round trips; smaller pages reduce peak memory per fetch.", "examples": [ - "lines", - "delim:\t", - "delim:foobar", - "gzip/csv" + 2000, + 500 ], - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "codec", - "type": "string" + "name": "snapshot_max_batch_size", + "type": "int" }, { - "default": 1000000, - "is_deprecated": true, + "default": 100, + "description": "Number of events requested per gRPC `Fetch` call, per topic. Higher values improve throughput at the cost of peak batch memory; lower values give steadier latency under load.", + "examples": [ + 100, + 500 + ], "kind": "scalar", - "name": "max_buffer", + "name": "stream_batch_size", "type": "int" }, { - "default": { - "to_the_end": {} - }, - "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", - "is_optional": true, + "default": 1, + "description": "Number of sObjects snapshotted concurrently during the REST snapshot phase. Each in-flight snapshot consumes one HTTP connection and Salesforce API call quota. Default 1 serializes the work — raise when snapshotting many sObjects and your API limits permit.", "kind": "scalar", - "name": "scanner", - "type": "scanner", - "version": "4.25.0" + "name": "max_parallel_snapshot_objects", + "type": "int" }, { - "default": false, - "description": "Whether to delete files from the server once they are processed.", - "is_advanced": true, + "description": "Name of the cache resource used to persist snapshot cursor and per-topic replay IDs across restarts. The cache must be declared under the top-level `cache_resources` block. Choose a durable cache (Redis, Postgres, DynamoDB) for production; in-memory caches lose checkpoints on restart.", + "examples": [ + "persistent_cache" + ], "kind": "scalar", - "name": "delete_on_finish", - "type": "bool" + "name": "checkpoint_cache", + "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether file watching is enabled.", - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "1s", - "description": "The minimum period of time since a file was last updated before attempting to consume it. Increasing this period decreases the likelihood that a file will be consumed whilst it is still being written to.", - "examples": [ - "10s", - "1m", - "10m" - ], - "kind": "scalar", - "name": "minimum_age", - "type": "string" - }, - { - "default": "1s", - "description": "The interval between each attempt to scan the target paths for new files.", - "examples": [ - "100ms", - "1s" - ], - "kind": "scalar", - "name": "poll_interval", - "type": "string" - }, - { - "default": "", - "description": "A xref:components:caches/about.adoc[cache resource] for storing the paths of files already consumed.", - "kind": "scalar", - "name": "cache", - "type": "string" - } - ], - "description": "An experimental mode whereby the input will periodically scan the target paths for new files and consume them, when all files are consumed the input will continue polling for new files.", - "kind": "scalar", - "name": "watcher", - "type": "object", - "version": "3.42.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- sftp_path\n- sftp_mod_time\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", - "name": "sftp", - "plugin": true, - "status": "beta", - "summary": "Consumes files from an SFTP server.", - "type": "input", - "version": "3.39.0" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The Slack App token to use.", - "kind": "scalar", - "linter": "\n root = if !this.has_prefix(\"xapp-\") { [ \"field must start with xapp-\" ] }\n ", - "name": "app_token", - "type": "string" - }, - { - "description": "The Slack Bot User OAuth token to use.", - "kind": "scalar", - "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", - "name": "bot_token", - "type": "string" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Connects to Slack using https://api.slack.com/apis/socket-mode[^Socket Mode]. This allows for receiving events, interactions and slash commands. Each message emitted from this input has a @type metadata of the event type \"events_api\", \"interactions\" or \"slash_commands\".", - "examples": [ - { - "config": "\ninput:\n slack:\n app_token: \"${APP_TOKEN:xapp-demo}\"\n bot_token: \"${BOT_TOKEN:xoxb-demo}\"\npipeline:\n processors:\n - mutation: |\n # ignore hidden or non message events\n if this.event.type != \"message\" || (this.event.hidden | false) {\n root = deleted()\n }\n # Don't respond to our own messages\n if this.authorizations.any(auth -> auth.user_id == this.event.user) {\n root = deleted()\n }\noutput:\n slack_post:\n bot_token: \"${BOT_TOKEN:xoxb-demo}\"\n channel_id: \"${!this.event.channel}\"\n thread_ts: \"${!this.event.ts}\"\n text: \"ECHO: ${!this.event.text}\"\n ", - "summary": "A slackbot that echo messages from other users", - "title": "Echo Slackbot" - } - ], - "name": "slack", - "plugin": true, - "status": "experimental", - "type": "input" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The Slack Bot User OAuth token to use.", - "kind": "scalar", - "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", - "name": "bot_token", - "type": "string" - }, - { - "default": "", - "description": "The team ID to filter by", - "kind": "scalar", - "name": "team_id", - "type": "string" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Reads all users in a slack organization (optionally filtered by a team ID).", - "name": "slack_users", - "plugin": true, - "status": "experimental", - "type": "input" - }, - { - "categories": [ - "Network" - ], - "config": { - "children": [ - { - "description": "A network type to assume (unix|tcp).", + "default": "salesforce_cdc", + "description": "Key inside the checkpoint cache where this input's state is stored. Change when running multiple `salesforce_cdc` inputs against the same cache resource to avoid collisions.", "kind": "scalar", - "linter": "\nlet options = {\n \"unix\": true,\n \"tcp\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "network", - "options": [ - "unix", - "tcp" - ], + "name": "checkpoint_cache_key", "type": "string" }, { - "description": "The address to connect to.", - "examples": [ - "/tmp/benthos.sock", - "127.0.0.1:6000" - ], + "default": 1024, + "description": "Maximum number of unacknowledged batches in flight (per topic) before that topic pauses reading. Prevents unbounded memory growth when downstream components stall. Higher values increase throughput in steady state; lower values bound memory under backpressure.", "kind": "scalar", - "name": "address", - "type": "string" + "name": "checkpoint_limit", + "type": "int" }, { "default": true, @@ -25139,653 +32082,636 @@ "name": "auto_replay_nacks", "type": "bool" }, - { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to a string which will be sent upstream before the downstream data flow starts.", - "examples": [ - "root = \"username,password\"" - ], - "is_optional": true, - "kind": "scalar", - "name": "open_message_mapping", - "type": "string" - }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", + "default": "500ms", + "description": "Base delay for gRPC reconnection backoff.", "is_advanced": true, "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "reconnect_base_delay", + "type": "string" }, { - "default": false, - "description": "Whether to skip server side certificate verification.", + "default": "30s", + "description": "Maximum delay for gRPC reconnection backoff.", "is_advanced": true, "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" + "name": "reconnect_max_delay", + "type": "string" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "default": 0, + "description": "Maximum number of gRPC reconnection attempts. 0 means unlimited.", "is_advanced": true, "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "reconnect_max_attempts", + "type": "int" }, { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], + "default": "10s", + "description": "Timeout for graceful gRPC client shutdown.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "shutdown_timeout", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": 1000, + "description": "Size of the internal gRPC event receive buffer.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "buffer_size", + "type": "int" + } + ], + "description": "gRPC transport tuning for the Pub/Sub API connection.", + "is_advanced": true, + "kind": "scalar", + "name": "grpc", + "type": "object" + }, + { + "children": [ + { + "default": "5s", + "description": "HTTP request timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "cert", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "", - "description": "A plain text certificate key to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "The path of a certificate key to use.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "key_file", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "foo", - "${KEY_PASSWORD}" + "./root_cas.pem" ], "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "root_cas_file", "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "annotated_options": [ - [ - "auto", - "EXPERIMENTAL: Attempts to derive a codec for each file based on information such as the extension. For example, a .tar.gz file would be consumed with the `gzip/tar` codec. Defaults to all-bytes." - ], - [ - "all-bytes", - "Consume the entire file as a single binary message." - ], - [ - "avro-ocf:marshaler=x", - "EXPERIMENTAL: Consume a stream of Avro OCF datum. The `marshaler` parameter is optional and has the options: `goavro` (default), `json`. Use `goavro` if OCF contains logical types." - ], - [ - "chunker:x", - "Consume the file in chunks of a given number of bytes." - ], - [ - "csv", - "Consume structured rows as comma separated values, the first row must be a header row." - ], - [ - "csv:x", - "Consume structured rows as values separated by a custom delimiter, the first row must be a header row. The custom delimiter must be a single character, e.g. the codec `\"csv:\\t\"` would consume a tab delimited file." - ], - [ - "csv-safe", - "Consume structured rows like `csv`, but sends messages with empty maps on failure to parse. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "csv-safe:x", - "Consume structured rows like `csv:x` as values separated by a custom delimiter, but sends messages with empty maps on failure to parse. The custom delimiter must be a single character, e.g. the codec `\"csv-safe:\\t\"` would consume a tab delimited file. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "delim:x", - "Consume the file in segments divided by a custom delimiter." - ], - [ - "gzip", - "Decompress a gzip file, this codec should precede another codec, e.g. `gzip/all-bytes`, `gzip/tar`, `gzip/csv`, etc." - ], - [ - "pgzip", - "Decompress a gzip file in parallel, this codec should precede another codec, e.g. `pgzip/all-bytes`, `pgzip/tar`, `pgzip/csv`, etc." - ], - [ - "lines", - "Consume the file in segments divided by linebreaks." - ], - [ - "multipart", - "Consumes the output of another codec and batches messages together. A batch ends when an empty message is consumed. For example, the codec `lines/multipart` could be used to consume multipart messages where an empty line indicates the end of each batch." - ], - [ - "regex:(?m)^\\d\\d:\\d\\d:\\d\\d", - "Consume the file in segments divided by regular expression." - ], - [ - "skipbom", - "Skip one or more byte order marks for each opened reader, this codec should precede another codec, e.g. `skipbom/csv`, etc." - ], - [ - "tar", - "Parse the file as a tar archive, and consume each file of the archive as a message." - ] - ], - "description": "The way in which the bytes of a data source should be converted into discrete messages, codecs are useful for specifying how large files or continuous streams of data might be processed in small chunks rather than loading it all in memory. It's possible to consume lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter. Codecs can be chained with `/`, for example a gzip compressed CSV file can be consumed with the codec `gzip/csv`.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar", - "gzip/csv" - ], - "is_deprecated": true, - "is_optional": true, - "kind": "scalar", - "name": "codec", - "type": "string" - }, - { - "default": 1000000, - "is_deprecated": true, - "kind": "scalar", - "name": "max_buffer", - "type": "int" - }, - { - "default": { - "lines": {} - }, - "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", - "is_optional": true, - "kind": "scalar", - "name": "scanner", - "type": "scanner", - "version": "4.25.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "socket", - "plugin": true, - "status": "stable", - "summary": "Connects to a tcp or unix socket and consumes a continuous stream of messages.", - "type": "input" - }, - { - "categories": [ - "Network" - ], - "config": { - "children": [ - { - "description": "A network type to accept.", - "kind": "scalar", - "linter": "\nlet options = {\n \"unix\": true,\n \"tcp\": true,\n \"udp\": true,\n \"tls\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "network", - "options": [ - "unix", - "tcp", - "udp", - "tls" - ], - "type": "string" - }, - { - "description": "The address to listen from.", - "examples": [ - "/tmp/benthos.sock", - "0.0.0.0:6000" - ], - "kind": "scalar", - "name": "address", - "type": "string" - }, - { - "description": "An optional xref:components:caches/about.adoc[`cache`] within which this input should write it's bound address once known. The key of the cache item containing the address will be the label of the component suffixed with `_address` (e.g. `foo_address`), or `socket_server_address` when a label has not been provided. This is useful in situations where the address is dynamically allocated by the server (`127.0.0.1:0`) and you want to store the allocated address somewhere for reference by other systems and components.", - "is_optional": true, - "kind": "scalar", - "name": "address_cache", - "type": "string", - "version": "4.25.0" - }, - { - "children": [ - { - "description": "PEM encoded certificate for use with TLS.", - "is_optional": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "tls", + "type": "object" }, { - "description": "PEM encoded private key for use with TLS.", - "is_optional": true, + "default": "", + "description": "HTTP proxy URL. Empty string disables proxying.", + "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "proxy_url", "type": "string" }, { "default": false, - "description": "Whether to generate self signed certificates.", + "description": "Disable HTTP/2 and force HTTP/1.1.", + "is_advanced": true, "kind": "scalar", - "name": "self_signed", + "name": "disable_http2", "type": "bool" }, { - "annotated_options": [ - [ - "no", - "client certificate is not requested nor required." - ], - [ - "request", - "will request client certificate, not require it." - ], - [ - "require_any", - "will accept any client certificate, even if not valid." - ], - [ - "require_valid", - "requires a valid client certificate." - ], - [ - "verify_if_given", - "will verify a certificate, if one is sent by the client." - ] - ], - "default": "no", - "description": "How client authentication is handled.", + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", + "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"no\": true,\n \"request\": true,\n \"require_any\": true,\n \"require_valid\": true,\n \"verify_if_given\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "client_auth", - "type": "string", - "version": "4.44.1" - } - ], - "description": "TLS specific configuration, valid when the `network` is set to `tls`.", - "is_optional": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - }, - { - "annotated_options": [ - [ - "auto", - "EXPERIMENTAL: Attempts to derive a codec for each file based on information such as the extension. For example, a .tar.gz file would be consumed with the `gzip/tar` codec. Defaults to all-bytes." - ], - [ - "all-bytes", - "Consume the entire file as a single binary message." - ], - [ - "avro-ocf:marshaler=x", - "EXPERIMENTAL: Consume a stream of Avro OCF datum. The `marshaler` parameter is optional and has the options: `goavro` (default), `json`. Use `goavro` if OCF contains logical types." - ], - [ - "chunker:x", - "Consume the file in chunks of a given number of bytes." - ], - [ - "csv", - "Consume structured rows as comma separated values, the first row must be a header row." - ], - [ - "csv:x", - "Consume structured rows as values separated by a custom delimiter, the first row must be a header row. The custom delimiter must be a single character, e.g. the codec `\"csv:\\t\"` would consume a tab delimited file." - ], - [ - "csv-safe", - "Consume structured rows like `csv`, but sends messages with empty maps on failure to parse. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "csv-safe:x", - "Consume structured rows like `csv:x` as values separated by a custom delimiter, but sends messages with empty maps on failure to parse. The custom delimiter must be a single character, e.g. the codec `\"csv-safe:\\t\"` would consume a tab delimited file. Includes row number and parsing errors (if any) in the message's metadata." - ], - [ - "delim:x", - "Consume the file in segments divided by a custom delimiter." - ], - [ - "gzip", - "Decompress a gzip file, this codec should precede another codec, e.g. `gzip/all-bytes`, `gzip/tar`, `gzip/csv`, etc." - ], - [ - "pgzip", - "Decompress a gzip file in parallel, this codec should precede another codec, e.g. `pgzip/all-bytes`, `pgzip/tar`, `pgzip/csv`, etc." - ], - [ - "lines", - "Consume the file in segments divided by linebreaks." - ], - [ - "multipart", - "Consumes the output of another codec and batches messages together. A batch ends when an empty message is consumed. For example, the codec `lines/multipart` could be used to consume multipart messages where an empty line indicates the end of each batch." - ], - [ - "regex:(?m)^\\d\\d:\\d\\d:\\d\\d", - "Consume the file in segments divided by regular expression." - ], - [ - "skipbom", - "Skip one or more byte order marks for each opened reader, this codec should precede another codec, e.g. `skipbom/csv`, etc." - ], - [ - "tar", - "Parse the file as a tar archive, and consume each file of the archive as a message." - ] - ], - "description": "The way in which the bytes of a data source should be converted into discrete messages, codecs are useful for specifying how large files or continuous streams of data might be processed in small chunks rather than loading it all in memory. It's possible to consume lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter. Codecs can be chained with `/`, for example a gzip compressed CSV file can be consumed with the codec `gzip/csv`.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar", - "gzip/csv" - ], - "is_deprecated": true, - "is_optional": true, - "kind": "scalar", - "name": "codec", - "type": "string" - }, - { - "default": 1000000, - "is_deprecated": true, - "kind": "scalar", - "name": "max_buffer", - "type": "int" - }, - { - "default": { - "lines": {} - }, - "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", - "is_optional": true, - "kind": "scalar", - "name": "scanner", - "type": "scanner", - "version": "4.25.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "socket_server", - "plugin": true, - "status": "stable", - "summary": "Creates a server that receives a stream of messages over a TCP, UDP or Unix socket.", - "type": "input" - }, - { - "categories": [ - "Services", - "SpiceDB" - ], - "config": { - "children": [ - { - "description": "The SpiceDB endpoint.", - "examples": [ - "grpc.authzed.com:443" - ], - "kind": "scalar", - "name": "endpoint", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": "", - "description": "The SpiceDB Bearer token used to authenticate against the SpiceDB instance.", - "examples": [ - "t_your_token_here_1234567deadbeef" - ], - "is_secret": true, - "kind": "scalar", - "name": "bearer_token", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "4MB", - "description": "Maximum message size in bytes the SpiceDB client can receive.", - "examples": [ - "100MB", - "50mib" - ], - "is_advanced": true, - "kind": "scalar", - "name": "max_receive_message_bytes", - "type": "string" - }, - { - "description": "A cache resource to use for performing unread message backfills, the ID of the last message received will be stored in this cache and used for subsequent requests.", - "kind": "scalar", - "name": "cache", - "type": "string" - }, - { - "default": "authzed.com/spicedb/watch/last_zed_token", - "description": "The key identifier used when storing the ID of the last message received.", - "is_advanced": true, - "kind": "scalar", - "name": "cache_key", - "type": "string" - }, - { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" + "name": "tps_limit", + "type": "float" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "default": 1, + "description": "Maximum burst size for rate limiting.", "is_advanced": true, "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "tps_burst", + "type": "int" }, { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "children": [ + { + "default": "1s", + "description": "Initial interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "backoff", + "type": "object" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } ], + "description": "TCP socket configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "root_cas_file", - "type": "string" + "name": "tcp", + "type": "object" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", "is_advanced": true, "kind": "scalar", - "name": "cert", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", "type": "string" }, { - "default": "", - "description": "A plain text certificate key to use.", + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "tls_handshake_timeout", "type": "string" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", + "name": "expect_continue_timeout", "type": "string" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "response_header_timeout", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "h2", + "type": "object" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" + } + ], + "description": "HTTP client configuration for Salesforce REST calls (OAuth token endpoint and, where applicable, data queries).", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", "examples": [ [ { - "cert": "foo", - "key": "bar" + "archive": { + "format": "concatenate" + } } ], [ { - "cert_file": "./example.pem", - "key_file": "./example.key" + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } } ] ], "is_advanced": true, + "is_optional": true, "kind": "array", - "name": "client_certs", - "type": "object" + "name": "processors", + "type": "processor" } ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", "type": "object" } ], @@ -25793,11 +32719,33 @@ "name": "", "type": "object" }, - "description": "\nThe SpiceDB input allows you to consume messages from the Watch API of a SpiceDB instance.\nThis input is useful for applications that need to react to changes in the data managed by SpiceDB in real-time.\n\n== Credentials\n\nYou need to provide the endpoint of your SpiceDB instance and a Bearer token for authentication.\n\n== Cache\n\nThe zed token of the newest update consumed and acked is stored in a cache in order to start reading from it each time the input is initialised.\nIdeally this cache should be persisted across restarts.\n", - "name": "spicedb_watch", + "description": "Subscribes to one or more Salesforce Pub/Sub topics in parallel and emits a message per event. Topics may be:\n\n- `/data/ChangeEvent` — per-sObject CDC channel.\n- `/data/ChangeEvents` — CDC firehose (every CDC-enabled sObject).\n- `/event/__e` — custom Platform Event.\n- `/event/` — standard Platform Event (e.g. `LoginEventStream`).\n- A bare sObject name (e.g. `Account`) is shorthand for `/data/AccountChangeEvent`.\n\nOptionally runs a REST snapshot for the CDC sObjects before opening the streaming subscriptions, so the pipeline sees the current state plus continuous changes. Per-topic replay state persists in a cache resource so each subscription resumes across restarts independently.\n\n== When to use this input\n\nUse `salesforce_cdc` for:\n\n- Continuous ingestion with both historical state (snapshot) and live changes (CDC).\n- Real-time custom or standard Platform Events.\n- Mixed CDC + Platform Event pipelines under a single component.\n\nUse a different Salesforce input instead if:\n\n- You only need a one-off extract or periodic SOQL query — use xref:components:inputs/salesforce.adoc[`salesforce`].\n- You need a GraphQL query (cross-object in one request) — use xref:components:inputs/salesforce_graphql.adoc[`salesforce_graphql`].\n\n== Metadata\n\nEvery emitted message has:\n\n- `topic`: The full Pub/Sub topic path (e.g. \"/event/Order__e\").\n- `replay_id`: The Pub/Sub replay ID in hex (streaming events only).\n\nCDC events also carry:\n\n- `operation`: \"read\" for snapshot rows; \"create\", \"update\", \"delete\", or \"undelete\" for CDC events.\n- `sobject`: The sObject API name (e.g. \"Account\").\n- `record_ids`: Comma-separated record IDs affected by the event (when present).\n\nPlatform Events also carry:\n\n- `event_uuid`: The Salesforce `EventUuid` extracted from the payload (the canonical dedup key), when present.\n\n== Authentication\n\nUses the Salesforce OAuth 2.0 Client Credentials flow. Create a Connected App in Salesforce, enable OAuth settings and the Client Credentials Flow, ensure Change Data Capture is enabled for the target sObjects (Setup → Change Data Capture), then supply the Consumer Key as `client_id` and Consumer Secret as `client_secret`.\n\n== Prerequisites\n\nEach `/data/...` topic requires Change Data Capture to be enabled for the corresponding sObject (Setup → Change Data Capture). Each `/event/...` topic requires the corresponding Platform Event to exist in Salesforce; standard events may require Event Monitoring licenses or specific permissions.\n", + "examples": [ + { + "config": "\ninput:\n salesforce_cdc:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n topics: [ Account, Contact, Opportunity ]\n stream_snapshot: true\n replay_preset: latest\n checkpoint_cache: persistent_cache\n\ncache_resources:\n - label: persistent_cache\n redis:\n url: redis://localhost:6379\n", + "summary": "Snapshot Accounts, Contacts, and Opportunities, then stream their CDC events:", + "title": "Snapshot + CDC for core objects" + }, + { + "config": "\ninput:\n salesforce_cdc:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n topics: [ /data/ChangeEvents ]\n stream_snapshot: false\n replay_preset: latest\n checkpoint_cache: persistent_cache\n", + "summary": "Subscribe to every CDC-enabled sObject via the firehose, no historical snapshot:", + "title": "CDC firehose without snapshot" + }, + { + "config": "\ninput:\n salesforce_cdc:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n topics:\n - Account\n - /event/Order_Created__e\n stream_snapshot: true\n replay_preset: latest\n checkpoint_cache: persistent_cache\n", + "summary": "Combine Account CDC with a custom Platform Event in one pipeline:", + "title": "Mixed CDC + Platform Events" + }, + { + "config": "\ninput:\n salesforce_cdc:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n topics:\n - /event/Order__e\n - /event/LoginEventStream\n replay_preset: latest\n checkpoint_cache: persistent_cache\n", + "summary": "Stream a custom event and a standard login stream — snapshot is skipped automatically:", + "title": "Platform Events only" + } + ], + "name": "salesforce_cdc", "plugin": true, "status": "stable", - "summary": "Consume messages from the Watch API from SpiceDB.", + "summary": "Streams Salesforce Change Data Capture (CDC) and Platform Events from the Pub/Sub gRPC API, optionally preceded by a REST snapshot of the configured sObjects.", "type": "input" }, { @@ -25807,36 +32755,618 @@ "config": { "children": [ { - "description": "Full HTTP Search API endpoint URL.", + "description": "Salesforce instance base URL for your org, protocol included and no trailing slash. Used as the base for both the OAuth token endpoint and REST queries. Production orgs use `https://{my-domain}.my.salesforce.com`; sandboxes use `https://{my-domain}.sandbox.my.salesforce.com`. Legacy instance URLs (`https://na123.salesforce.com`) still work but My Domain URLs are strongly recommended by Salesforce.", "examples": [ - "https://foobar.splunkcloud.com/services/search/v2/jobs/export" + "https://acme.my.salesforce.com", + "https://acme--staging.sandbox.my.salesforce.com" ], "kind": "scalar", - "name": "url", + "name": "org_url", "type": "string" }, { - "description": "Splunk account user.", + "description": "Consumer Key of the Salesforce Connected App authorized for the OAuth Client Credentials flow. Create the Connected App under Setup → App Manager → New Connected App, enable OAuth settings, enable the Client Credentials Flow under `Flow Enablement`, then copy the Consumer Key from `Manage Consumer Details`.", "kind": "scalar", - "name": "user", + "name": "client_id", "type": "string" }, { - "description": "Splunk account password.", + "description": "Consumer Secret of the Salesforce Connected App, paired with `client_id`. Sensitive — prefer environment variable interpolation (`${SALESFORCE_CLIENT_SECRET}`) over inlining.", "is_secret": true, "kind": "scalar", - "name": "password", + "name": "client_secret", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Splunk search query.", + "default": "v65.0", + "description": "Salesforce REST API version to target, prefixed with `v`. Affects endpoint paths (`/services/data/{api_version}/...`) and available fields/objects. Must be supported by your org — check Setup → Company Information. Older versions may lack recent fields.", + "examples": [ + "v65.0", + "v62.0" + ], + "kind": "scalar", + "name": "api_version", + "type": "string" + }, + { + "description": "The GraphQL query document as a single string. Must target the Salesforce UIAPI schema (`uiapi.query.*` or `uiapi.mutation.*`). Typically follows the UIAPI convention of nested `edges { node { field { value } } }`. Variables are referenced with `$name` and supplied via the `variables` field. For automatic pagination, include `pageInfo { hasNextPage endCursor }` in the relevant connection.", + "examples": [ + "query Accounts { uiapi { query { Account { edges { node { Id { value } Name { value } } } pageInfo { hasNextPage endCursor } } } } }", + "query Accounts($first: Int) { uiapi { query { Account(first: $first) { edges { node { Id { value } Name { value } } } pageInfo { hasNextPage endCursor } } } } }" + ], "kind": "scalar", "name": "query", "type": "string" }, { - "children": [ + "bloblang": true, + "description": "Optional xref:guides:bloblang/about.adoc[Bloblang mapping] whose result must be an object whose keys are GraphQL variable names referenced by `query`. Values pass through as JSON in the request body's `variables` field. The mapping is evaluated once at startup with no message context — use `env()`, `now()`, `cache()`, or static literals.", + "examples": [ + "root = {\"first\": 100}", + "root = {\"since\": now().ts_format(\"2006-01-02T15:04:05Z\"), \"limit\": 500}" + ], + "is_optional": true, + "kind": "scalar", + "name": "variables", + "type": "string" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", + "type": "bool" + }, + { + "children": [ + { + "default": "5s", + "description": "HTTP request timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "default": "", + "description": "HTTP proxy URL. Empty string disables proxying.", + "is_advanced": true, + "kind": "scalar", + "name": "proxy_url", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_http2", + "type": "bool" + }, + { + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_limit", + "type": "float" + }, + { + "default": 1, + "description": "Maximum burst size for rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_burst", + "type": "int" + }, + { + "children": [ + { + "default": "1s", + "description": "Initial interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", + "is_advanced": true, + "kind": "scalar", + "name": "backoff", + "type": "object" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } + ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "h2", + "type": "object" + } + ], + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" + } + ], + "description": "HTTP client configuration for Salesforce REST calls (OAuth token endpoint and, where applicable, data queries).", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "Executes a GraphQL query against the Salesforce UIAPI (`POST /services/data/{api_version}/graphql`), walks the response tree, and emits one message per record. When records are exhausted the input shuts down, letting the pipeline terminate gracefully (or the next input in a xref:components:inputs/sequence.adoc[sequence] to take over).\n\n== When to use this input\n\nUse `salesforce_graphql` for:\n\n- Cross-object queries in a single request (parent + children + grandchildren).\n- Response shapes that already match your downstream schema.\n- Queries benefiting from GraphQL's field-level selection and nesting.\n- Adopting Salesforce's UIAPI / future-forward query surface.\n\nUse a different Salesforce input instead if:\n\n- You only need single-object SELECTs — use xref:components:inputs/salesforce.adoc[`salesforce`] (simpler, no GraphQL schema knowledge needed).\n- You need continuous change events — use xref:components:inputs/salesforce_cdc.adoc[`salesforce_cdc`].\n\n== Pagination\n\nWhen the query selects an `edges`/`pageInfo` connection, the input transparently paginates by injecting `after: \"\"` into the query string between requests. The query must include `pageInfo { hasNextPage endCursor }` for pagination to terminate cleanly. Responses without an `edges` array are emitted as a single message and the input completes.\n\n== Metadata\n\nThis input adds no Salesforce-specific metadata. GraphQL response shapes vary by query, so record identity and context travel in the message body.\n\n== Authentication\n\nUses the Salesforce OAuth 2.0 Client Credentials flow. Create a Connected App in Salesforce, enable OAuth settings and the Client Credentials Flow, grant the `api` OAuth scope, then supply the Consumer Key as `client_id` and Consumer Secret as `client_secret`. The Connected App must have access to the target UIAPI objects.\n", + "examples": [ + { + "config": "\ninput:\n salesforce_graphql:\n org_url: https://acme.my.salesforce.com\n client_id: ${SALESFORCE_CLIENT_ID}\n client_secret: ${SALESFORCE_CLIENT_SECRET}\n query: |\n query Accounts($limit: Int) {\n uiapi {\n query {\n Account(first: $limit) {\n edges {\n node {\n Id { value }\n Name { value }\n LastModifiedDate { value }\n }\n }\n pageInfo { hasNextPage endCursor }\n }\n }\n }\n }\n variables: |\n root = {\"limit\": 500}\n", + "summary": "Fetch the first 500 Accounts with a nested UIAPI query:", + "title": "Account snapshot via GraphQL" + } + ], + "name": "salesforce_graphql", + "plugin": true, + "status": "stable", + "summary": "Executes a Salesforce GraphQL (UIAPI) query once and emits a message for each record.", + "type": "input" + }, + { + "categories": [ + "Integration" + ], + "config": { + "children": [ + { + "description": "The base URL of the schema registry service.", + "kind": "scalar", + "name": "url", + "type": "string" + }, + { + "default": false, + "description": "Include deleted entities.", + "is_advanced": true, + "kind": "scalar", + "name": "include_deleted", + "type": "bool" + }, + { + "default": "", + "description": "Include only subjects which match the regular expression filter. All subjects are selected when not set.", + "is_advanced": true, + "kind": "scalar", + "name": "subject_filter", + "type": "string" + }, + { + "default": true, + "description": "Fetch all schemas on connect and sort them by ID. Should be set to `true` when schema references are used.", + "is_advanced": true, + "kind": "scalar", + "name": "fetch_in_order", + "type": "bool", + "version": "4.37.0" + }, + { + "children": [ { "default": false, "description": "Whether custom TLS settings are enabled.", @@ -25965,83 +33495,6 @@ "name": "tls", "type": "object" }, - { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", - "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "splunk", - "plugin": true, - "status": "beta", - "summary": "Consumes messages from Splunk.", - "type": "input", - "version": "4.30.0" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", - "examples": [ - "SELECT * FROM footable WHERE user_id = $1;" - ], - "kind": "scalar", - "name": "query", - "type": "string" - }, - { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], - "is_optional": true, - "kind": "scalar", - "name": "args_mapping", - "type": "string" - }, { "default": true, "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", @@ -26050,186 +33503,357 @@ "type": "bool" }, { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - [ - "./init/*.sql" - ], - [ - "./foo.sql", - "./bar.sql" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" - }, - { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], + "description": "Allows you to specify open authentication via OAuth version 1.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" - }, - { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, - { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_life_time", - "type": "string" + "name": "oauth", + "type": "object" }, { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "conn_max_idle", - "type": "int" + "name": "basic_auth", + "type": "object" }, { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "conn_max_open", - "type": "int" + "name": "jwt", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "Once the rows from the query are exhausted this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- schema_registry_subject\n- schema_registry_subject_compatibility_level\n- schema_registry_version\n```\n\nYou can access these metadata fields using\nxref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n", "examples": [ { - "config": "\ninput:\n sql_raw:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n query: \"SELECT name, count(*) FROM person WHERE last_updated < $1 GROUP BY name;\"\n args_mapping: |\n root = [\n now().ts_unix() - 3600\n ]\n", - "summary": "\nHere we preform an aggregate over a list of names in a table that are less than 3600 seconds old.", - "title": "Consumes an SQL table using a query as an input." + "config": "\ninput:\n schema_registry:\n url: http://localhost:8081\n include_deleted: true\n subject_filter: ^foo.*\n", + "summary": "Read all schemas (including deleted) from a Schema Registry instance which are associated with subjects matching the `^foo.*` filter.", + "title": "Read schemas" } ], - "name": "sql_raw", + "name": "schema_registry", "plugin": true, - "status": "beta", - "summary": "Executes a select query and creates a message for each row received.", + "status": "stable", + "summary": "Reads schemas from SchemaRegistry.", "type": "input", - "version": "4.10.0" + "version": "4.32.2" }, { "categories": [ - "Services" + "Utility" ], "config": { "children": [ { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "The table to select from.", - "examples": [ - "foo" + "children": [ + { + "default": "none", + "description": "The type of join to perform. A `full-outer` ensures that all identifiers seen in any of the input sequences are sent, and is performed by consuming all input sequences before flushing the joined results. An `outer` join consumes all input sequences but only writes data joined from the last input in the sequence, similar to a left or right outer join. With an `outer` join if an identifier appears multiple times within the final sequence input it will be flushed each time it appears. `full-outter` and `outter` have been deprecated in favour of `full-outer` and `outer`.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"none\": true,\n \"full-outer\": true,\n \"outer\": true,\n \"full-outter\": true,\n \"outter\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "type", + "options": [ + "none", + "full-outer", + "outer", + "full-outter", + "outter" + ], + "type": "string" + }, + { + "default": "", + "description": "A xref:configuration:field_paths.adoc[dot path] that points to a common field within messages of each fragmented data set and can be used to join them. Messages that are not structured or are missing this field will be dropped. This field must be set in order to enable joins.", + "is_advanced": true, + "kind": "scalar", + "name": "id_path", + "type": "string" + }, + { + "default": 1, + "description": "The total number of iterations (shards), increasing this number will increase the overall time taken to process the data, but reduces the memory used in the process. The real memory usage required is significantly higher than the real size of the data and therefore the number of iterations should be at least an order of magnitude higher than the available memory divided by the overall size of the dataset.", + "is_advanced": true, + "kind": "scalar", + "name": "iterations", + "type": "int" + }, + { + "default": "array", + "description": "The chosen strategy to use when a data join would otherwise result in a collision of field values. The strategy `array` means non-array colliding values are placed into an array and colliding arrays are merged. The strategy `replace` replaces old values with new values. The strategy `keep` keeps the old value.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"array\": true,\n \"replace\": true,\n \"keep\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "merge_strategy", + "options": [ + "array", + "replace", + "keep" + ], + "type": "string" + } ], + "description": "EXPERIMENTAL: Provides a way to perform outer joins of arbitrarily structured and unordered data resulting from the input sequence, even when the overall size of the data surpasses the memory available on the machine.\n\nWhen configured the sequence of inputs will be consumed one or more times according to the number of iterations, and when more than one iteration is specified each iteration will process an entirely different set of messages by sharding them by the ID field. Increasing the number of iterations reduces the memory consumption at the cost of needing to fully parse the data each time.\n\nEach message must be structured (JSON or otherwise processed into a structured form) and the fields will be aggregated with those of other messages sharing the ID. At the end of each iteration the joined messages are flushed downstream before the next iteration begins, hence keeping memory usage limited.", + "is_advanced": true, "kind": "scalar", - "name": "table", - "type": "string" + "name": "sharded_join", + "type": "object", + "version": "3.40.0" }, { - "description": "A list of columns to select.", - "examples": [ - [ - "*" - ], - [ - "foo", - "bar", - "baz" - ] - ], + "description": "An array of inputs to read from sequentially.", "kind": "array", - "name": "columns", + "name": "inputs", + "type": "input" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "This input is useful for consuming from inputs that have an explicit end but must not be consumed in parallel.", + "examples": [ + { + "config": "\ninput:\n sequence:\n inputs:\n - file:\n paths: [ ./dataset.csv ]\n scanner:\n csv: {}\n - generate:\n count: 1\n mapping: 'root = {\"status\":\"finished\"}'\n", + "summary": "A common use case for sequence might be to generate a message at the end of our main input. With the following config once the records within `./dataset.csv` are exhausted our final payload `{\"status\":\"finished\"}` will be routed through the pipeline.", + "title": "End of Stream Message" + }, + { + "config": "\ninput:\n sequence:\n sharded_join:\n type: full-outer\n id_path: uuid\n merge_strategy: array\n inputs:\n - file:\n paths:\n - ./hobbies.csv\n - ./main.csv\n scanner:\n csv: {}\n", + "summary": "Redpanda Connect can be used to join unordered data from fragmented datasets in memory by specifying a common identifier field and a number of sharded iterations. For example, given two CSV files, the first called \"main.csv\", which contains rows of user data:\n\n```csv\nuuid,name,age\nAAA,Melanie,34\nBBB,Emma,28\nCCC,Geri,45\n```\n\nAnd the second called \"hobbies.csv\" that, for each user, contains zero or more rows of hobbies:\n\n```csv\nuuid,hobby\nCCC,pokemon go\nAAA,rowing\nAAA,golf\n```\n\nWe can parse and join this data into a single dataset:\n\n```json\n{\"uuid\":\"AAA\",\"name\":\"Melanie\",\"age\":34,\"hobbies\":[\"rowing\",\"golf\"]}\n{\"uuid\":\"BBB\",\"name\":\"Emma\",\"age\":28}\n{\"uuid\":\"CCC\",\"name\":\"Geri\",\"age\":45,\"hobbies\":[\"pokemon go\"]}\n```\n\nWith the following config:", + "title": "Joining Data (Simple)" + }, + { + "config": "\ninput:\n sequence:\n sharded_join:\n type: full-outer\n id_path: uuid\n iterations: 10\n merge_strategy: array\n inputs:\n - file:\n paths: [ ./main.csv ]\n scanner:\n csv: {}\n - file:\n paths: [ ./hobbies.ndjson ]\n scanner:\n lines: {}\n processors:\n - mapping: |\n root.uuid = this.document.uuid\n root.hobbies = this.document.hobbies.map_each(this.type)\n", + "summary": "In this example we are able to join unordered and fragmented data from a combination of CSV files and newline-delimited JSON documents by specifying multiple sequence inputs with their own processors for extracting the structured data.\n\nThe first file \"main.csv\" contains straight forward CSV data:\n\n```csv\nuuid,name,age\nAAA,Melanie,34\nBBB,Emma,28\nCCC,Geri,45\n```\n\nAnd the second file called \"hobbies.ndjson\" contains JSON documents, one per line, that associate an identifier with an array of hobbies. However, these data objects are in a nested format:\n\n```json\n{\"document\":{\"uuid\":\"CCC\",\"hobbies\":[{\"type\":\"pokemon go\"}]}}\n{\"document\":{\"uuid\":\"AAA\",\"hobbies\":[{\"type\":\"rowing\"},{\"type\":\"golf\"}]}}\n```\n\nAnd so we will want to map these into a flattened structure before the join, and then we will end up with a single dataset that looks like this:\n\n```json\n{\"uuid\":\"AAA\",\"name\":\"Melanie\",\"age\":34,\"hobbies\":[\"rowing\",\"golf\"]}\n{\"uuid\":\"BBB\",\"name\":\"Emma\",\"age\":28}\n{\"uuid\":\"CCC\",\"name\":\"Geri\",\"age\":45,\"hobbies\":[\"pokemon go\"]}\n```\n\nWith the following config:", + "title": "Joining Data (Advanced)" + } + ], + "name": "sequence", + "plugin": true, + "status": "stable", + "summary": "Reads messages from a sequence of child inputs, starting with the first and once that input gracefully terminates starts consuming from the next, and so on.", + "type": "input" + }, + { + "categories": [ + "Network" + ], + "config": { + "children": [ + { + "description": "The address of the server to connect to.", + "kind": "scalar", + "name": "address", "type": "string" }, { - "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks, and will automatically be converted to dollar syntax when the postgres or clickhouse drivers are used.", - "examples": [ - "type = ? and created_at > ?", - "user_id = ?" - ], - "is_optional": true, + "default": "30s", + "description": "The connection timeout to use when connecting to the target server.", + "is_advanced": true, "kind": "scalar", - "name": "where", + "name": "connection_timeout", "type": "string" }, { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", - "examples": [ - "root = [ \"article\", now().ts_format(\"2006-01-02\") ]" + "children": [ + { + "default": "", + "description": "The username to authenticate with the SFTP server.", + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "The password for the specified username to connect to the SFTP server.", + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The path to the SFTP server's public key file, used for host key verification.", + "is_optional": true, + "kind": "scalar", + "name": "host_public_key_file", + "type": "string" + }, + { + "description": "The raw contents of the SFTP server's public key, used for host key verification.", + "is_optional": true, + "kind": "scalar", + "name": "host_public_key", + "type": "string" + }, + { + "description": "The path to the private key file, used for authenticating the username.", + "is_optional": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "description": "The raw contents of the private key, used for authenticating the username.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "private_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "Optional passphrase for decrypting the private key, if it's encrypted.", + "is_secret": true, + "kind": "scalar", + "name": "private_key_pass", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], - "is_optional": true, + "description": "The credentials to use to log into the target server.", "kind": "scalar", - "name": "args_mapping", - "type": "string" + "linter": "\nroot = match {\n this.exists(\"host_public_key\") && this.exists(\"host_public_key_file\") => \"both host_public_key and host_public_key_file can't be set simultaneously\"\n this.exists(\"private_key\") && this.exists(\"private_key_file\") => \"both private_key and private_key_file can't be set simultaneously\"\n}", + "name": "credentials", + "type": "object" }, { - "description": "An optional prefix to prepend to the select query (before SELECT).", + "default": 10, + "description": "The maximum number of SFTP sessions.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "prefix", - "type": "string" + "name": "max_sftp_sessions", + "type": "int" }, { - "description": "An optional suffix to append to the select query.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "suffix", + "description": "A list of paths to consume sequentially. Glob patterns are supported.", + "kind": "array", + "name": "paths", "type": "string" }, { @@ -26239,95 +33863,6 @@ "name": "auto_replay_nacks", "type": "bool" }, - { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - [ - "./init/*.sql" - ], - [ - "./foo.sql", - "./bar.sql" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" - }, - { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" - }, - { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, - { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_life_time", - "type": "string" - }, - { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle", - "type": "int" - }, - { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_open", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Once the rows from the query are exhausted this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", - "examples": [ - { - "config": "\ninput:\n sql_select:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n table: footable\n columns: [ '*' ]\n where: created_at >= ?\n args_mapping: |\n root = [\n now().ts_unix() - 3600\n ]\n", - "summary": "\nHere we define a pipeline that will consume all rows from a table created within the last hour by comparing the unix timestamp stored in the row column \"created_at\":", - "title": "Consume a Table (PostgreSQL)" - } - ], - "name": "sql_select", - "plugin": true, - "status": "beta", - "summary": "Executes a select query and creates a message for each row received.", - "type": "input", - "version": "3.59.0" - }, - { - "categories": [ - "Local" - ], - "config": { - "children": [ { "annotated_options": [ [ @@ -26417,7 +33952,7 @@ }, { "default": { - "lines": {} + "to_the_end": {} }, "description": "The xref:components:scanners/about.adoc[scanner] by which the stream of bytes consumed will be broken out into individual messages. Scanners are useful for processing large sources of data without holding the entirety of it within memory. For example, the `csv` scanner allows you to process individual CSV rows without loading the entire CSV file in memory at once.", "is_optional": true, @@ -26427,308 +33962,205 @@ "version": "4.25.0" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "default": false, + "description": "Whether to delete files from the server once they are processed.", + "is_advanced": true, "kind": "scalar", - "name": "auto_replay_nacks", + "name": "delete_on_finish", "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "stdin", - "plugin": true, - "status": "stable", - "summary": "Consumes data piped to stdin, chopping it into individual messages according to the specified scanner.", - "type": "input" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "description": "The command to execute as a subprocess.", - "examples": [ - "cat", - "sed", - "awk" - ], - "kind": "scalar", - "name": "name", - "type": "string" - }, - { - "default": [], - "description": "A list of arguments to provide the command.", - "kind": "array", - "name": "args", - "type": "string" }, { - "default": "lines", - "description": "The way in which messages should be consumed from the subprocess.", - "kind": "scalar", - "linter": "\nlet options = {\n \"lines\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "codec", - "options": [ - "lines" + "children": [ + { + "default": false, + "description": "Whether file watching is enabled.", + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "1s", + "description": "The minimum period of time since a file was last updated before attempting to consume it. Increasing this period decreases the likelihood that a file will be consumed whilst it is still being written to.", + "examples": [ + "10s", + "1m", + "10m" + ], + "kind": "scalar", + "name": "minimum_age", + "type": "string" + }, + { + "default": "1s", + "description": "The interval between each attempt to scan the target paths for new files.", + "examples": [ + "100ms", + "1s" + ], + "kind": "scalar", + "name": "poll_interval", + "type": "string" + }, + { + "default": "", + "description": "A xref:components:caches/about.adoc[cache resource] for storing the paths of files already consumed.", + "kind": "scalar", + "name": "cache", + "type": "string" + } ], - "type": "string" - }, - { - "default": false, - "description": "Whether the command should be re-executed each time the subprocess ends.", - "kind": "scalar", - "name": "restart_on_exit", - "type": "bool" - }, - { - "default": 65536, - "description": "The maximum expected size of an individual message.", - "is_advanced": true, + "description": "An experimental mode whereby the input will periodically scan the target paths for new files and consume them, when all files are consumed the input will continue polling for new files.", "kind": "scalar", - "name": "max_buffer", - "type": "int" + "name": "watcher", + "type": "object", + "version": "3.42.0" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nMessages are consumed according to a specified codec. The command is executed once and if it terminates the input also closes down gracefully. Alternatively, the field `restart_on_close` can be set to `true` in order to have Redpanda Connect re-execute the command each time it stops.\n\nThe field `max_buffer` defines the maximum message size able to be read from the subprocess. This value should be set significantly above the real expected maximum message size.\n\nThe execution environment of the subprocess is the same as the Redpanda Connect instance, including environment variables and the current working directory.", - "name": "subprocess", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n- sftp_path\n- sftp_mod_time\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].", + "name": "sftp", "plugin": true, - "status": "beta", - "summary": "Executes a command, runs it as a subprocess, and consumes messages from it over stdout.", - "type": "input" + "status": "stable", + "summary": "Consumes files from an SFTP server.", + "type": "input", + "version": "3.39.0" }, { - "categories": [ - "Services" - ], + "categories": null, "config": { "children": [ { - "description": "The query to run", - "examples": [ - "select * from iot", - "select count(*) from table(iot)" - ], - "kind": "scalar", - "name": "query", - "type": "string" - }, - { - "default": "tcp://localhost:8463", - "description": "The url should always include schema and host.", - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "description": "ID of the workspace. Required when reads from Timeplus Enterprise.", - "is_optional": true, - "kind": "scalar", - "name": "workspace", - "type": "string" - }, - { - "description": "The API key. Required when reads from Timeplus Enterprise Cloud", - "is_optional": true, - "is_secret": true, + "description": "The Slack App token to use.", "kind": "scalar", - "name": "apikey", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "linter": "\n root = if !this.has_prefix(\"xapp-\") { [ \"field must start with xapp-\" ] }\n ", + "name": "app_token", "type": "string" }, { - "description": "The username. Required when reads from Timeplus Enterprise (self-hosted) or Timeplusd", - "is_optional": true, + "description": "The Slack Bot User OAuth token to use.", "kind": "scalar", - "name": "username", + "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", + "name": "bot_token", "type": "string" }, { - "description": "The password. Required when reads from Timeplus Enterprise (self-hosted) or Timeplusd", - "is_optional": true, - "is_secret": true, + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "auto_replay_nacks", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nThis input can execute a query on Timeplus Enterprise Cloud, Timeplus Enterprise (self-hosted) or Timeplusd. A structured message will be created\nfrom each row received.\n\nIf it is a streaming query, this input will keep running until the query is terminated. If it is a table query, this input will shut down once the rows from the query are exhausted.", + "description": "Connects to Slack using https://api.slack.com/apis/socket-mode[^Socket Mode]. This allows for receiving events, interactions and slash commands. Each message emitted from this input has a @type metadata of the event type \"events_api\", \"interactions\" or \"slash_commands\".", "examples": [ { - "config": "\ninput:\n timeplus:\n url: https://us-west-2.timeplus.cloud\n workspace: my_workspace_id\n query: select * from iot\n apikey: ", - "summary": "You will need to create API Key on Timeplus Enterprise Cloud Web console first and then set the `apikey` field.", - "title": "From Timeplus Enterprise Cloud via HTTP" - }, - { - "config": "\ninput:\n timeplus:\n url: http://localhost:8000\n workspace: my_workspace_id\n query: select * from iot\n username: username\n password: pw", - "summary": "For self-housted Timeplus Enterprise, you will need to specify the username and password as well as the URL of the App server", - "title": "From Timeplus Enterprise (self-hosted) via HTTP" - }, - { - "config": "\ninput:\n timeplus:\n url: tcp://localhost:8463\n query: select * from iot\n username: timeplus\n password: timeplus", - "summary": "Make sure the the schema of url is tcp", - "title": "From Timeplus Enterprise (self-hosted) via TCP" + "config": "\ninput:\n slack:\n app_token: \"${APP_TOKEN:xapp-demo}\"\n bot_token: \"${BOT_TOKEN:xoxb-demo}\"\npipeline:\n processors:\n - mutation: |\n # ignore hidden or non message events\n if this.event.type != \"message\" || (this.event.hidden | false) {\n root = deleted()\n }\n # Don't respond to our own messages\n if this.authorizations.any(auth -> auth.user_id == this.event.user) {\n root = deleted()\n }\noutput:\n slack_post:\n bot_token: \"${BOT_TOKEN:xoxb-demo}\"\n channel_id: \"${!this.event.channel}\"\n thread_ts: \"${!this.event.ts}\"\n text: \"ECHO: ${!this.event.text}\"\n ", + "summary": "A slackbot that echo messages from other users", + "title": "Echo Slackbot" } ], - "name": "timeplus", + "name": "slack", "plugin": true, - "status": "experimental", - "summary": "Executes a query on Timeplus Enterprise and creates a message from each row received", + "status": "stable", "type": "input" }, { - "categories": [ - "Services", - "Social" - ], + "categories": null, "config": { "children": [ { - "description": "A search expression to use.", - "kind": "scalar", - "name": "query", - "type": "string" - }, - { - "default": [], - "description": "An optional list of additional fields to obtain for each tweet, by default only the fields `id` and `text` are returned. For more info refer to the https://developer.twitter.com/en/docs/twitter-api/fields[twitter API docs^].", - "kind": "array", - "name": "tweet_fields", - "type": "string" - }, - { - "default": "1m", - "description": "The length of time (as a duration string) to wait between each search request. This field can be set empty, in which case requests are made at the limit set by the rate limit. This field also supports cron expressions.", - "kind": "scalar", - "name": "poll_period", - "type": "string" - }, - { - "default": "5m", - "description": "A duration string indicating the maximum age of tweets to acquire when starting a search.", - "kind": "scalar", - "name": "backfill_period", - "type": "string" - }, - { - "description": "A cache resource to use for request pagination.", - "kind": "scalar", - "name": "cache", - "type": "string" - }, - { - "default": "last_tweet_id", - "description": "The key identifier used when storing the ID of the last tweet received.", - "is_advanced": true, + "description": "The Slack Bot User OAuth token to use.", "kind": "scalar", - "name": "cache_key", + "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", + "name": "bot_token", "type": "string" }, { "default": "", - "description": "An optional rate limit resource to restrict API requests with.", - "is_advanced": true, - "kind": "scalar", - "name": "rate_limit", - "type": "string" - }, - { - "description": "An API key for OAuth 2.0 authentication. It is recommended that you populate this field using xref:configuration:interpolation.adoc[environment variables].", + "description": "The team ID to filter by", "kind": "scalar", - "name": "api_key", + "name": "team_id", "type": "string" }, { - "description": "An API secret for OAuth 2.0 authentication. It is recommended that you populate this field using xref:configuration:interpolation.adoc[environment variables].", + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "api_secret", - "type": "string" + "name": "auto_replay_nacks", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "Continuously polls the https://developer.twitter.com/en/docs/twitter-api/tweets/search/api-reference/get-tweets-search-recent[Twitter recent search V2 API^] for tweets that match a given search query.\n\nEach tweet received is emitted as a JSON object message, with a field `id` and `text` by default. Extra fields https://developer.twitter.com/en/docs/twitter-api/fields[can be obtained from the search API^] when listed with the `tweet_fields` field.\n\nIn order to paginate requests that are made the ID of the latest received tweet is stored in a xref:components:caches/about.adoc[cache resource], which is then used by subsequent requests to ensure only tweets after it are consumed. It is recommended that the cache you use is persistent so that Redpanda Connect can resume searches at the correct place on a restart.\n\nAuthentication is done using OAuth 2.0 credentials which can be generated within the https://developer.twitter.com[Twitter developer portal^].\n", - "name": "twitter_search", + "description": "Reads all users in a slack organization (optionally filtered by a team ID).", + "name": "slack_users", "plugin": true, - "status": "experimental", - "summary": "Consumes tweets matching a given search using the Twitter recent search V2 API.", + "status": "stable", "type": "input" }, { "categories": [ - "Network" + "Services", + "SpiceDB" ], "config": { "children": [ { - "description": "The URL to connect to.", + "description": "The SpiceDB endpoint.", "examples": [ - "ws://localhost:4195/get/ws" + "grpc.authzed.com:443" ], "kind": "scalar", - "name": "url", + "name": "endpoint", "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "description": "An optional HTTP proxy URL.", - "is_advanced": true, - "is_optional": true, + "default": "", + "description": "The SpiceDB Bearer token used to authenticate against the SpiceDB instance.", + "examples": [ + "t_your_token_here_1234567deadbeef" + ], + "is_secret": true, "kind": "scalar", - "name": "proxy_url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "bearer_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "An optional message to send to the server upon connection.", + "default": "4MB", + "description": "Maximum message size in bytes the SpiceDB client can receive.", + "examples": [ + "100MB", + "50mib" + ], "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "open_message", + "name": "max_receive_message_bytes", "type": "string" }, { - "annotated_options": [ - [ - "binary", - "Binary data open_message." - ], - [ - "text", - "Text data open_message. The text message payload is interpreted as UTF-8 encoded text data." - ] - ], - "default": "binary", - "description": "An optional flag to indicate the data type of open_message.", - "is_advanced": true, + "description": "A cache resource to use for performing unread message backfills, the ID of the last message received will be stored in this cache and used for subsequent requests.", "kind": "scalar", - "linter": "\nlet options = {\n \"binary\": true,\n \"text\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "open_message_type", + "name": "cache", "type": "string" }, { - "default": true, - "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "default": "authzed.com/spicedb/watch/last_zed_token", + "description": "The key identifier used when storing the ID of the last message received.", + "is_advanced": true, "kind": "scalar", - "name": "auto_replay_nacks", - "type": "bool" + "name": "cache_key", + "type": "string" }, { "children": [ @@ -26859,365 +34291,103 @@ "kind": "scalar", "name": "tls", "type": "object" - }, - { - "children": [ - { - "description": "An optional limit to the number of consecutive retry attempts that will be made before abandoning the connection altogether and gracefully terminating the input. When all inputs terminate in this way the service (or stream) will shut down. If set to zero connections will never be reattempted upon a failure. If set below zero this field is ignored (effectively unset).", - "examples": [ - -1, - 10 - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "max_retries", - "type": "int" - } - ], - "description": "Customise how websocket connection attempts are made.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "connection", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A value used to identify the client to the service provider.", - "is_advanced": true, - "kind": "scalar", - "name": "consumer_key", - "type": "string" - }, - { - "default": "", - "description": "A secret used to establish ownership of the consumer key.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "consumer_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", - "is_advanced": true, - "kind": "scalar", - "name": "access_token", - "type": "string" - }, - { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", - "is_advanced": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, - { - "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, - "kind": "scalar", - "name": "signing_method", - "type": "string" - }, - { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" - }, - { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" - } - ], - "description": "BETA: Allows you to specify JWT authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "jwt", - "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "It is possible to configure an `open_message`, which when set to a non-empty string will be sent to the websocket server each time a connection is first established.", - "name": "websocket", + "description": "\nThe SpiceDB input allows you to consume messages from the Watch API of a SpiceDB instance.\nThis input is useful for applications that need to react to changes in the data managed by SpiceDB in real-time.\n\n== Credentials\n\nYou need to provide the endpoint of your SpiceDB instance and a Bearer token for authentication.\n\n== Cache\n\nThe zed token of the newest update consumed and acked is stored in a cache in order to start reading from it each time the input is initialised.\nIdeally this cache should be persisted across restarts.\n", + "name": "spicedb_watch", "plugin": true, "status": "stable", - "summary": "Connects to a websocket server and continuously receives messages.", + "summary": "Consume messages from the Watch API from SpiceDB.", "type": "input" - } - ], - "metrics": [ + }, { - "categories": null, + "categories": [ + "Services" + ], "config": { "children": [ { - "default": "Benthos", - "description": "The namespace used to distinguish metrics from other services.", + "description": "Full HTTP Search API endpoint URL.", + "examples": [ + "https://foobar.splunkcloud.com/services/search/v2/jobs/export" + ], "kind": "scalar", - "name": "namespace", + "name": "url", "type": "string" }, { - "default": "100ms", - "description": "The period of time between PutMetricData requests.", - "is_advanced": true, + "description": "Splunk account user.", "kind": "scalar", - "name": "flush_period", + "name": "user", "type": "string" }, { - "description": "The AWS region to target.", - "is_advanced": true, - "is_optional": true, + "description": "Splunk account password.", + "is_secret": true, "kind": "scalar", - "name": "region", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Allows you to specify a custom endpoint for the AWS API.", - "is_advanced": true, - "is_optional": true, + "description": "Splunk search query.", "kind": "scalar", - "name": "endpoint", + "name": "query", "type": "string" }, { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "profile", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "description": "The ID of credentials to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "id", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "description": "The secret for the credentials being used.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, - "is_optional": true, - "is_secret": true, "kind": "scalar", - "name": "secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, - "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "token", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" - }, - { - "description": "A role ARN to assume.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role", - "type": "string" - }, - { - "description": "An external ID to provide when assuming a role.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "role_external_id", - "type": "string" - } - ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "credentials", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Timing metrics\n\nThe smallest timing unit that CloudWatch supports is microseconds, therefore timing metrics are automatically downgraded to microseconds (by dividing delta values by 1000). This conversion will also apply to custom timing metrics produced with a `metric` processor.\n\n== Billing\n\nAWS bills per metric series exported, it is therefore STRONGLY recommended that you reduce the metrics that are exposed with a `mapping` like this:\n\n```yaml\nmetrics:\n mapping: |\n if ![\n \"input_received\",\n \"input_latency\",\n \"output_sent\",\n ].contains(this) { deleted() }\n aws_cloudwatch:\n namespace: Foo\n```", - "name": "aws_cloudwatch", - "plugin": true, - "status": "stable", - "summary": "Send metrics to AWS CloudWatch using the PutMetricData endpoint.", - "type": "metrics", - "version": "3.36.0" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "A URL of the format `[https|http|udp]://host:port` to the InfluxDB host.", - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "description": "The name of the database to use.", - "kind": "scalar", - "name": "db", - "type": "string" - }, - { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", + "name": "root_cas_file", "type": "string" }, { @@ -27300,352 +34470,467 @@ "type": "object" }, { - "default": "", - "description": "A username (when applicable).", - "is_advanced": true, + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "username", - "type": "string" - }, + "name": "auto_replay_nacks", + "type": "bool" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "splunk", + "plugin": true, + "status": "stable", + "summary": "Consumes messages from Splunk.", + "type": "input", + "version": "4.30.0" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ { - "default": "", - "description": "A password (when applicable).", - "is_advanced": true, - "is_secret": true, + "description": "A database <> to use.", "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "driver", + "options": [ + "mysql", + "postgres", + "pgx", + "clickhouse", + "mssql", + "sqlite", + "oracle", + "snowflake", + "trino", + "gocosmos", + "spanner", + "databricks" + ], "type": "string" }, { - "children": [ - { - "default": "", - "description": "A duration string indicating how often to poll and collect runtime metrics. Leave empty to disable this metric", - "examples": [ - "1m" - ], - "is_advanced": true, - "kind": "scalar", - "name": "runtime", - "type": "string" - }, - { - "default": "", - "description": "A duration string indicating how often to poll and collect GC metrics. Leave empty to disable this metric.", - "examples": [ - "1m" - ], - "is_advanced": true, - "kind": "scalar", - "name": "debug_gc", - "type": "string" - } + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" ], - "description": "Optional additional metrics to collect, enabling these metrics may have some performance implications as it acquires a global semaphore and does `stoptheworld()`.", - "is_advanced": true, "kind": "scalar", - "name": "include", - "type": "object" + "name": "dsn", + "type": "string" }, { - "default": "1m", - "description": "A duration string indicating how often metrics should be flushed.", - "is_advanced": true, + "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `pgx` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "examples": [ + "SELECT * FROM footable WHERE user_id = $1;" + ], "kind": "scalar", - "name": "interval", + "name": "query", "type": "string" }, { - "default": "20s", - "description": "A duration string indicating how often to ping the host.", - "is_advanced": true, + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", + "examples": [ + "root = [ this.cat.meow, this.doc.woofs[0] ]", + "root = [ meta(\"user.id\") ]" + ], + "is_optional": true, "kind": "scalar", - "name": "ping_interval", + "name": "args_mapping", "type": "string" }, { - "default": "s", - "description": "[ns|us|ms|s] timestamp precision passed to write api.", - "is_advanced": true, + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", "kind": "scalar", - "name": "precision", - "type": "string" + "name": "auto_replay_nacks", + "type": "bool" }, { - "default": "5s", - "description": "How long to wait for response for both ping and writing metrics.", + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], "is_advanced": true, - "kind": "scalar", - "name": "timeout", - "type": "string" + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" }, { - "default": {}, - "description": "Global tags added to each metric.", + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", "examples": [ - { - "hostname": "localhost", - "zone": "danger" - } + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" ], "is_advanced": true, - "kind": "map", - "name": "tags", - "type": "string" + "is_optional": true, + "kind": "scalar", + "name": "init_statement", + "type": "string", + "version": "4.10.0" }, { - "description": "Sets the retention policy for each write.", + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "retention_policy", + "name": "conn_max_idle_time", "type": "string" }, { - "description": "[any|one|quorum|all] sets write consistency when available.", + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "write_consistency", + "name": "conn_max_life_time", "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "See https://docs.influxdata.com/influxdb/v1.8/tools/api/#write-http-endpoint for further details on the write API.", - "name": "influxdb", - "plugin": true, - "status": "beta", - "summary": "Send metrics to InfluxDB 1.x using the `/write` endpoint.", - "type": "metrics", - "version": "3.36.0" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "This metrics type is useful for debugging as it provides a human readable format that you can parse with tools such as `jq`", - "name": "json_api", - "plugin": true, - "status": "stable", - "summary": "Serves metrics as JSON object with the service wide HTTP service at the endpoints `/stats` and `/metrics`.", - "type": "metrics" - }, - { - "categories": null, - "config": { - "children": [ + }, { - "description": "An optional period of time to continuously print all metrics.", + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "push_interval", - "type": "string" + "name": "conn_max_idle", + "type": "int" }, { - "default": false, - "description": "Whether counters and timing metrics should be reset to 0 each time metrics are printed.", + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "flush_metrics", - "type": "bool" + "name": "conn_max_open", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nPrints each metric produced by Redpanda Connect as a log event (level `info` by default) during shutdown, and optionally on an interval.\n\nThis metrics type is useful for debugging pipelines when you only have access to the logger output and not the service-wide server. Otherwise it's recommended that you use either the `prometheus` or `json_api`types.", - "name": "logger", - "plugin": true, - "status": "beta", - "summary": "Prints aggregated metrics through the logger.", - "type": "metrics" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "none", + "description": "Once the rows from the query are exhausted this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", + "examples": [ + { + "config": "\ninput:\n sql_raw:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n query: \"SELECT name, count(*) FROM person WHERE last_updated < $1 GROUP BY name;\"\n args_mapping: |\n root = [\n now().ts_unix() - 3600\n ]\n", + "summary": "\nHere we perform an aggregate over a list of names in a table that are less than 3600 seconds old.", + "title": "Consumes an SQL table using a query as an input." + } + ], + "name": "sql_raw", "plugin": true, "status": "stable", - "summary": "Disable metrics entirely.", - "type": "metrics" + "summary": "Executes a select query and creates a message for each row received.", + "type": "input", + "version": "4.10.0" }, { - "categories": null, + "categories": [ + "Services" + ], "config": { "children": [ { - "default": false, - "description": "Whether to export timing metrics as a histogram, if `false` a summary is used instead. When exporting histogram timings the delta values are converted from nanoseconds into seconds in order to better fit within bucket definitions. For more information on histograms and summaries refer to: https://prometheus.io/docs/practices/histograms/.", - "is_advanced": true, + "description": "A database <> to use.", "kind": "scalar", - "name": "use_histogram_timing", - "type": "bool", - "version": "3.63.0" + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "driver", + "options": [ + "mysql", + "postgres", + "pgx", + "clickhouse", + "mssql", + "sqlite", + "oracle", + "snowflake", + "trino", + "gocosmos", + "spanner", + "databricks" + ], + "type": "string" }, { - "default": [], - "description": "Timing metrics histogram buckets (in seconds). If left empty defaults to DefBuckets (https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#pkg-variables). Applicable when `use_histogram_timing` is set to `true`.", - "is_advanced": true, - "kind": "array", - "name": "histogram_buckets", - "type": "float", - "version": "3.63.0" + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" + ], + "kind": "scalar", + "name": "dsn", + "type": "string" }, { - "children": [ - { - "default": 0, - "description": "Quantile value.", - "is_advanced": true, - "kind": "scalar", - "name": "quantile", - "type": "float" - }, - { - "default": 0, - "description": "Permissible margin of error for quantile calculations. Precise calculations in a streaming context (without prior knowledge of the full dataset) can be resource-intensive. To balance accuracy with computational efficiency, an error margin is introduced. For instance, if the 90th quantile (`0.9`) is determined to be `100ms` with a 1% error margin (`0.01`), the true value will fall within the `[99ms, 101ms]` range.)", - "is_advanced": true, - "kind": "scalar", - "name": "error", - "type": "float" - } - ], - "default": [ - { - "error": 0.05, - "quantile": 0.5 - }, - { - "error": 0.01, - "quantile": 0.9 - }, - { - "error": 0.001, - "quantile": 0.99 - } + "description": "The table to select from.", + "examples": [ + "foo" ], - "description": "A list of timing metrics summary buckets (as quantiles). Applicable when `use_histogram_timing` is set to `false`.", + "kind": "scalar", + "name": "table", + "type": "string" + }, + { + "description": "A list of columns to select.", "examples": [ [ - { - "error": 0.05, - "quantile": 0.5 - }, - { - "error": 0.01, - "quantile": 0.9 - }, - { - "error": 0.001, - "quantile": 0.99 - } + "*" + ], + [ + "foo", + "bar", + "baz" ] ], - "is_advanced": true, "kind": "array", - "name": "summary_quantiles_objectives", - "type": "object", - "version": "4.23.0" + "name": "columns", + "type": "string" }, { - "default": false, - "description": "Whether to export process metrics such as CPU and memory usage in addition to Redpanda Connect metrics.", + "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks, and will automatically be converted to dollar syntax when the postgres or clickhouse drivers are used.", + "examples": [ + "type = ? and created_at > ?", + "user_id = ?" + ], + "is_optional": true, + "kind": "scalar", + "name": "where", + "type": "string" + }, + { + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", + "examples": [ + "root = [ \"article\", now().ts_format(\"2006-01-02\") ]" + ], + "is_optional": true, + "kind": "scalar", + "name": "args_mapping", + "type": "string" + }, + { + "description": "An optional prefix to prepend to the select query (before SELECT).", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "add_process_metrics", - "type": "bool" + "name": "prefix", + "type": "string" }, { - "default": false, - "description": "Whether to export Go runtime metrics such as GC pauses in addition to Redpanda Connect metrics.", + "description": "An optional suffix to append to the select query.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "add_go_metrics", + "name": "suffix", + "type": "string" + }, + { + "default": true, + "description": "Whether messages that are rejected (nacked) at the output level should be automatically replayed indefinitely, eventually resulting in back pressure if the cause of the rejections is persistent. If set to `false` these messages will instead be deleted. Disabling auto replays can greatly improve memory efficiency of high throughput streams as the original shape of the data can be discarded immediately upon consumption and mutation.", + "kind": "scalar", + "name": "auto_replay_nacks", "type": "bool" }, { - "description": "An optional <> to push metrics to.", + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" + }, + { + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" + ], "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "push_url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "init_statement", + "type": "string", + "version": "4.10.0" }, { - "description": "The period of time between each push when sending metrics to a Push Gateway.", + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "push_interval", + "name": "conn_max_idle_time", "type": "string" }, { - "default": "benthos_push", - "description": "An identifier for push jobs.", + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "push_job_name", + "name": "conn_max_life_time", "type": "string" }, { - "children": [ - { - "default": "", - "description": "The Basic Authentication username.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "The Basic Authentication password.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "The Basic Authentication credentials.", + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "push_basic_auth", - "type": "object" + "name": "conn_max_idle", + "type": "int" }, { - "default": "", - "description": "An optional file path to write all prometheus metrics on service shutdown.", + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "file_output_path", - "type": "string" + "name": "conn_max_open", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "footnotes": "\n== Push gateway\n\nThe field `push_url` is optional and when set will trigger a push of metrics to a https://prometheus.io/docs/instrumenting/pushing/[Prometheus Push Gateway^] once Redpanda Connect shuts down. It is also possible to specify a `push_interval` which results in periodic pushes.\n\nThe Push Gateway is useful for when Redpanda Connect instances are short lived. Do not include the \"/metrics/jobs/...\" path in the push URL.\n\nIf the Push Gateway requires HTTP Basic Authentication it can be configured with `push_basic_auth`.", - "name": "prometheus", + "description": "Once the rows from the query are exhausted this input shuts down, allowing the pipeline to gracefully terminate (or the next input in a xref:components:inputs/sequence.adoc[sequence] to execute).", + "examples": [ + { + "config": "\ninput:\n sql_select:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n table: footable\n columns: [ '*' ]\n where: created_at >= ?\n args_mapping: |\n root = [\n now().ts_unix() - 3600\n ]\n", + "summary": "\nHere we define a pipeline that will consume all rows from a table created within the last hour by comparing the unix timestamp stored in the row column \"created_at\":", + "title": "Consume a Table (PostgreSQL)" + } + ], + "name": "sql_select", "plugin": true, "status": "stable", - "summary": "Host endpoints (`/metrics` and `/stats`) for Prometheus scraping.", + "summary": "Executes a select query and creates a message for each row received.", + "type": "input", + "version": "3.59.0" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "The query to run", + "examples": [ + "select * from iot", + "select count(*) from table(iot)" + ], + "kind": "scalar", + "name": "query", + "type": "string" + }, + { + "default": "tcp://localhost:8463", + "description": "The url should always include schema and host.", + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "description": "ID of the workspace. Required when reads from Timeplus Enterprise.", + "is_optional": true, + "kind": "scalar", + "name": "workspace", + "type": "string" + }, + { + "description": "The API key. Required when reads from Timeplus Enterprise Cloud", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "apikey", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The username. Required when reads from Timeplus Enterprise (self-hosted) or Timeplusd", + "is_optional": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "description": "The password. Required when reads from Timeplus Enterprise (self-hosted) or Timeplusd", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis input can execute a query on Timeplus Enterprise Cloud, Timeplus Enterprise (self-hosted) or Timeplusd. A structured message will be created\nfrom each row received.\n\nIf it is a streaming query, this input will keep running until the query is terminated. If it is a table query, this input will shut down once the rows from the query are exhausted.", + "examples": [ + { + "config": "\ninput:\n timeplus:\n url: https://us-west-2.timeplus.cloud\n workspace: my_workspace_id\n query: select * from iot\n apikey: ", + "summary": "You will need to create API Key on Timeplus Enterprise Cloud Web console first and then set the `apikey` field.", + "title": "From Timeplus Enterprise Cloud via HTTP" + }, + { + "config": "\ninput:\n timeplus:\n url: http://localhost:8000\n workspace: my_workspace_id\n query: select * from iot\n username: username\n password: pw", + "summary": "For self-housted Timeplus Enterprise, you will need to specify the username and password as well as the URL of the App server", + "title": "From Timeplus Enterprise (self-hosted) via HTTP" + }, + { + "config": "\ninput:\n timeplus:\n url: tcp://localhost:8463\n query: select * from iot\n username: timeplus\n password: timeplus", + "summary": "Make sure the the schema of url is tcp", + "title": "From Timeplus Enterprise (self-hosted) via TCP" + } + ], + "name": "timeplus", + "plugin": true, + "status": "stable", + "summary": "Executes a query on Timeplus Enterprise and creates a message from each row received", + "type": "input" + } + ], + "metrics": [ + { + "categories": null, + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "none", + "plugin": true, + "status": "stable", + "summary": "Disable metrics entirely.", "type": "metrics" }, { @@ -27653,29 +34938,251 @@ "config": { "children": [ { - "description": "The address to send metrics to.", + "default": "benthos", + "description": "The name of the service in metrics.", "kind": "scalar", - "name": "address", + "name": "service", "type": "string" }, { - "default": "100ms", - "description": "The time interval between metrics flushes.", + "children": [ + { + "description": "The endpoint of a collector to send events to.", + "examples": [ + "localhost:4318" + ], + "is_optional": true, + "kind": "scalar", + "name": "address", + "type": "string" + }, + { + "default": "localhost:4318", + "description": "The URL of a collector to send events to.", + "is_deprecated": true, + "kind": "scalar", + "name": "url", + "type": "string" + }, + { + "default": false, + "description": "Connect to the collector over HTTPS", + "kind": "scalar", + "name": "secure", + "type": "bool" + } + ], + "description": "A list of http collectors.", + "kind": "array", + "name": "http", + "type": "object" + }, + { + "children": [ + { + "description": "The endpoint of a collector to send events to.", + "examples": [ + "localhost:4317" + ], + "is_optional": true, + "kind": "scalar", + "name": "address", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "localhost:4317", + "description": "The URL of a collector to send events to.", + "is_deprecated": true, + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": false, + "description": "Connect to the collector with client transport security", + "kind": "scalar", + "name": "secure", + "type": "bool" + } + ], + "description": "A list of grpc collectors.", + "kind": "array", + "name": "grpc", + "type": "object" + }, + { + "default": {}, + "description": "A map of tags to add to all exported spans and metrics.", + "is_advanced": true, + "kind": "map", + "name": "tags", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "open_telemetry_collector", + "plugin": true, + "status": "stable", + "summary": "Send metrics to an https://opentelemetry.io/docs/collector/[Open Telemetry collector^].", + "type": "metrics" + }, + { + "categories": null, + "config": { + "children": [ + { + "default": false, + "description": "Whether to export timing metrics as a histogram, if `false` a summary is used instead. When exporting histogram timings the delta values are converted from nanoseconds into seconds in order to better fit within bucket definitions. For more information on histograms and summaries refer to: https://prometheus.io/docs/practices/histograms/.", + "is_advanced": true, + "kind": "scalar", + "name": "use_histogram_timing", + "type": "bool", + "version": "3.63.0" + }, + { + "default": [], + "description": "Timing metrics histogram buckets (in seconds). If left empty defaults to DefBuckets (https://pkg.go.dev/github.com/prometheus/client_golang/prometheus#pkg-variables). Applicable when `use_histogram_timing` is set to `true`.", + "is_advanced": true, + "kind": "array", + "name": "histogram_buckets", + "type": "float", + "version": "3.63.0" + }, + { + "children": [ + { + "default": 0, + "description": "Quantile value.", + "is_advanced": true, + "kind": "scalar", + "name": "quantile", + "type": "float" + }, + { + "default": 0, + "description": "Permissible margin of error for quantile calculations. Precise calculations in a streaming context (without prior knowledge of the full dataset) can be resource-intensive. To balance accuracy with computational efficiency, an error margin is introduced. For instance, if the 90th quantile (`0.9`) is determined to be `100ms` with a 1% error margin (`0.01`), the true value will fall within the `[99ms, 101ms]` range.)", + "is_advanced": true, + "kind": "scalar", + "name": "error", + "type": "float" + } + ], + "default": [ + { + "error": 0.05, + "quantile": 0.5 + }, + { + "error": 0.01, + "quantile": 0.9 + }, + { + "error": 0.001, + "quantile": 0.99 + } + ], + "description": "A list of timing metrics summary buckets (as quantiles). Applicable when `use_histogram_timing` is set to `false`.", + "examples": [ + [ + { + "error": 0.05, + "quantile": 0.5 + }, + { + "error": 0.01, + "quantile": 0.9 + }, + { + "error": 0.001, + "quantile": 0.99 + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "summary_quantiles_objectives", + "type": "object", + "version": "4.23.0" + }, + { + "default": false, + "description": "Whether to export process metrics such as CPU and memory usage in addition to Redpanda Connect metrics.", + "is_advanced": true, + "kind": "scalar", + "name": "add_process_metrics", + "type": "bool" + }, + { + "default": false, + "description": "Whether to export Go runtime metrics such as GC pauses in addition to Redpanda Connect metrics.", + "is_advanced": true, + "kind": "scalar", + "name": "add_go_metrics", + "type": "bool" + }, + { + "description": "An optional <> to push metrics to.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "flush_period", + "name": "push_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "default": "none", - "description": "Metrics tagging is supported in a variety of formats.", + "description": "The period of time between each push when sending metrics to a Push Gateway.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"none\": true,\n \"datadog\": true,\n \"influxdb\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "tag_format", - "options": [ - "none", - "datadog", - "influxdb" + "name": "push_interval", + "type": "string" + }, + { + "default": "benthos_push", + "description": "An identifier for push jobs.", + "is_advanced": true, + "kind": "scalar", + "name": "push_job_name", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "The Basic Authentication username.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "The Basic Authentication password.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], + "description": "The Basic Authentication credentials.", + "is_advanced": true, + "kind": "scalar", + "name": "push_basic_auth", + "type": "object" + }, + { + "default": "", + "description": "An optional file path to write all prometheus metrics on service shutdown.", + "is_advanced": true, + "kind": "scalar", + "name": "file_output_path", "type": "string" } ], @@ -27683,10 +35190,11 @@ "name": "", "type": "object" }, - "name": "statsd", + "footnotes": "\n== Push gateway\n\nThe field `push_url` is optional and when set will trigger a push of metrics to a https://prometheus.io/docs/instrumenting/pushing/[Prometheus Push Gateway^] once Redpanda Connect shuts down. It is also possible to specify a `push_interval` which results in periodic pushes.\n\nThe Push Gateway is useful for when Redpanda Connect instances are short lived. Do not include the \"/metrics/jobs/...\" path in the push URL.\n\nIf the Push Gateway requires HTTP Basic Authentication it can be configured with `push_basic_auth`.", + "name": "prometheus", "plugin": true, "status": "stable", - "summary": "Pushes metrics using the https://github.com/statsd/statsd[StatsD protocol^]. Supported tagging formats are 'none', 'datadog' and 'influxdb'.", + "summary": "Host endpoints (`/metrics` and `/stats`) for Prometheus scraping.", "type": "metrics" } ], @@ -28084,57 +35592,18 @@ "config": { "children": [ { - "description": "A URL to connect to.", - "examples": [ - "amqp://localhost:5672/", - "amqps://guest:guest@localhost:5672/" - ], - "is_deprecated": true, - "is_optional": true, + "description": "Base URL of the target service (e.g., https://api.example.com). TLS is enabled automatically for https URLs.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "base_url", "type": "string" }, { - "description": "A list of URLs to connect to. The first URL to successfully establish a connection will be used until the connection is closed. If an item of the list contains commas it will be expanded into multiple URLs.", - "examples": [ - [ - "amqp://guest:guest@127.0.0.1:5672/" - ], - [ - "amqp://127.0.0.1:5672/,amqp://127.0.0.2:5672/" - ], - [ - "amqp://127.0.0.1:5672/", - "amqp://127.0.0.2:5672/" - ] - ], - "is_optional": true, - "kind": "array", - "name": "urls", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string", - "version": "4.23.0" - }, - { - "description": "The target address to write to.", - "examples": [ - "/foo", - "queue:/bar", - "topic:/baz" - ], + "default": "5s", + "description": "HTTP request timeout.", "kind": "scalar", - "name": "target_address", + "name": "timeout", "type": "string" }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, { "children": [ { @@ -28266,120 +35735,558 @@ "type": "object" }, { - "bloblang": true, - "description": "An optional Bloblang mapping that can be defined in order to set the `application-properties` on output messages.", + "default": "", + "description": "HTTP proxy URL. Empty string disables proxying.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "application_properties_map", + "name": "proxy_url", "type": "string" }, + { + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_http2", + "type": "bool" + }, + { + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_limit", + "type": "float" + }, + { + "default": 1, + "description": "Maximum burst size for rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_burst", + "type": "int" + }, { "children": [ { - "annotated_options": [ - [ - "anonymous", - "Anonymous SASL authentication." - ], - [ - "none", - "No SASL based authentication." - ], - [ - "plain", - "Plain text SASL authentication." - ] - ], - "default": "none", - "description": "The SASL authentication mechanism to use.", + "default": "1s", + "description": "Initial interval between retries on 429 responses.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"anonymous\": true,\n \"none\": true,\n \"plain\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "mechanism", + "name": "initial_interval", "type": "string" }, { - "default": "", - "description": "A SASL plain text username. It is recommended that you use environment variables to populate this field.", - "examples": [ - "${USER}" - ], + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", "is_advanced": true, "kind": "scalar", - "name": "user", + "name": "max_interval", "type": "string" }, { - "default": "", - "description": "A SASL plain text password. It is recommended that you use environment variables to populate this field.", - "examples": [ - "${PASSWORD}" - ], + "default": 3, + "description": "Maximum number of retries on 429 responses.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "max_retries", + "type": "int" } ], - "description": "Enables SASL authentication.", + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "sasl", + "name": "backoff", "type": "object" }, { "children": [ { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to be excluded when adding metadata to sent messages.", - "kind": "array", - "name": "exclude_prefixes", + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", "type": "string" } ], - "description": "Specify criteria for which metadata values are attached to messages as headers.", + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "metadata", + "name": "tcp", "type": "object" }, { - "default": "opaque_binary", - "description": "Specify the message body content type. The option `string` will transfer the message as an AMQP value of type string. Consider choosing the option `string` if your intention is to transfer UTF-8 string messages (like JSON messages) to the destination.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"opaque_binary\": true,\n \"string\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "content_type", - "options": [ - "opaque_binary", - "string" - ], - "type": "string" - } - ], - "kind": "scalar", - "linter": "\nroot = if this.url.or(\"\") == \"\" && this.urls.or([]).length() == 0 {\n \"field 'urls' must be set\"\n}\n", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nMessage metadata is added to each AMQP message as string annotations. In order to control which metadata keys are added use the `metadata` config field.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "amqp_1", - "plugin": true, - "status": "stable", - "summary": "Sends messages to an AMQP (1.0) server.", - "type": "output" - }, - { - "categories": [ - "Services", - "AWS" - ], - "config": { - "children": [ - { + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } + ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "h2", + "type": "object" + } + ], + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" + }, + { + "description": "Bearer token for authentication.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "default", + "description": "The target database name.", + "kind": "scalar", + "name": "database", + "type": "string" + }, + { + "description": "The measurement (table) name. Supports interpolation functions.", + "examples": [ + "cpu_metrics", + "${!metadata(\"measurement\")}", + "${!json(\"type\")}" + ], + "interpolated": true, + "kind": "scalar", + "name": "measurement", + "type": "string" + }, + { + "default": "columnar", + "description": "The payload format. `columnar` transposes batch messages into column arrays for best performance. `row` sends each message as an individual record.", + "kind": "scalar", + "linter": "\nlet options = {\n \"columnar\": true,\n \"row\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "format", + "options": [ + "columnar", + "row" + ], + "type": "string" + }, + { + "default": "", + "description": "The field name within each message containing the timestamp. If empty, the current time is used. Supports Unix timestamps and RFC3339 strings.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "timestamp_field", + "type": "string" + }, + { + "default": "auto", + "description": "The unit of a numeric timestamp field. `auto` detects the unit based on magnitude. Ignored when `timestamp_field` is empty.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"us\": true,\n \"ms\": true,\n \"s\": true,\n \"ns\": true,\n \"auto\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "timestamp_unit", + "options": [ + "us", + "ms", + "s", + "ns", + "auto" + ], + "type": "string" + }, + { + "bloblang": true, + "description": "An optional Bloblang mapping to extract tags from each message. Only used in `row` format. The result must be a `map[string]string`.", + "examples": [ + "root = {\"host\": this.hostname, \"region\": this.region}" + ], + "is_optional": true, + "kind": "scalar", + "name": "tags_mapping", + "type": "string" + }, + { + "default": "zstd", + "description": "Compression algorithm for the request body. `zstd` is recommended for best decompression performance in Arc.", + "kind": "scalar", + "linter": "\nlet options = {\n \"zstd\": true,\n \"gzip\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "compression", + "options": [ + "zstd", + "gzip", + "none" + ], + "type": "string" + }, + { + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis output sends data to an https://github.com/Basekick-Labs/arc[Arc^] columnar analytical database using its high-performance MessagePack ingestion endpoint.\n\nArc supports two payload formats:\n\n- *columnar* (default): Transposes batched messages into column arrays. This is the recommended format, offering significantly faster ingestion.\n- *row*: Sends each message as an individual row record with fields and optional tags.\n\nData is encoded as MessagePack and optionally compressed with zstd (recommended) or gzip before being sent to the Arc endpoint.\n\nNOTE: In columnar mode, all messages within a single batch must have the same set of fields. Arc validates that all column arrays have equal length and rejects batches with mismatched columns. Schema evolution across separate batches is fully supported. Use row format if messages within a batch have varying schemas.\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "arc", + "plugin": true, + "status": "stable", + "summary": "Writes data to an Arc database via the msgpack ingestion endpoint.", + "type": "output", + "version": "4.88.0" + }, + { + "categories": [ + "Services", + "AWS" + ], + "config": { + "children": [ + { "description": "The table to store messages in.", "kind": "scalar", "name": "table", @@ -28548,6 +36455,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -28819,6 +36786,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -29070,6 +37097,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -29277,15 +37364,6 @@ "name": "content_language", "type": "string" }, - { - "default": "", - "description": "The content MD5 to set for each object.", - "interpolated": true, - "is_advanced": true, - "kind": "scalar", - "name": "content_md5", - "type": "string" - }, { "default": "", "description": "The website redirect location to set for each object.", @@ -29385,13 +37463,14 @@ "type": "string" }, { - "default": "private", - "description": "The object canned ACL value.", + "default": "", + "description": "The object canned ACL value. Leave empty to omit the ACL from upload requests, which is required for buckets that have ACLs disabled (the AWS default since 2023).", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"private\": true,\n \"public-read\": true,\n \"public-read-write\": true,\n \"authenticated-read\": true,\n \"aws-exec-read\": true,\n \"bucket-owner-read\": true,\n \"bucket-owner-full-control\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"\": true,\n \"private\": true,\n \"public-read\": true,\n \"public-read-write\": true,\n \"authenticated-read\": true,\n \"aws-exec-read\": true,\n \"bucket-owner-read\": true,\n \"bucket-owner-full-control\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "object_canned_acl", "options": [ + "", "private", "public-read", "public-read-write", @@ -29510,6 +37589,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -29624,6 +37763,14 @@ "type": "string", "version": "3.60.0" }, + { + "description": "An optional subject to set for messages.", + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "subject", + "type": "string" + }, { "default": 64, "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", @@ -29671,6 +37818,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -29930,6 +38137,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -30156,7 +38423,7 @@ "description": "\nIn order to have a different path for each object you should use function\ninterpolations described xref:configuration:interpolation.adoc#bloblang-queries[here], which are\ncalculated per message of a batch.\n\nSupports multiple authentication methods but only one of the following is required:\n\n- `storage_connection_string`\n- `storage_account` and `storage_access_key`\n- `storage_account` and `storage_sas_token`\n- `storage_account` to access via https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#DefaultAzureCredential[DefaultAzureCredential^]\n\nIf multiple are set then the `storage_connection_string` is given priority.\n\nIf the `storage_connection_string` does not contain the `AccountName` parameter, please specify it in the\n`storage_account` field.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", "name": "azure_blob_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Sends message parts as objects to an Azure Blob Storage Account container. Each object is uploaded with the filename specified with the `container` field.", "type": "output", "version": "3.36.0" @@ -30482,7 +38749,7 @@ "footnotes": "\n\n== CosmosDB emulator\n\nIf you wish to run the CosmosDB emulator that is referenced in the documentation https://learn.microsoft.com/en-us/azure/cosmos-db/linux-emulator[here^], the following Docker command should do the trick:\n\n```bash\n> docker run --rm -it -p 8081:8081 --name=cosmosdb -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10 -e AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=false mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator\n```\n\nNote: `AZURE_COSMOS_EMULATOR_PARTITION_COUNT` controls the number of partitions that will be supported by the emulator. The bigger the value, the longer it takes for the container to start up.\n\nAdditionally, instead of installing the container self-signed certificate which is exposed via `https://localhost:8081/_explorer/emulator.pem`, you can run https://mitmproxy.org/[mitmproxy^] like so:\n\n```bash\n> mitmproxy -k --mode \"reverse:https://localhost:8081\"\n```\n\nThen you can access the CosmosDB UI via `http://localhost:8080/_explorer/index.html` and use `http://localhost:8080` as the CosmosDB endpoint.\n", "name": "azure_cosmosdb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Creates or updates messages as JSON documents in https://learn.microsoft.com/en-us/azure/cosmos-db/introduction[Azure CosmosDB^].", "type": "output", "version": "v4.25.0" @@ -30561,7 +38828,7 @@ "description": "\nIn order to have a different path for each file you should use function\ninterpolations described xref:configuration:interpolation.adoc#bloblang-queries[here], which are\ncalculated per message of a batch.\n\nSupports multiple authentication methods but only one of the following is required:\n\n- `storage_connection_string`\n- `storage_account` and `storage_access_key`\n- `storage_account` and `storage_sas_token`\n- `storage_account` to access via https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#DefaultAzureCredential[DefaultAzureCredential^]\n\nIf multiple are set then the `storage_connection_string` is given priority.\n\nIf the `storage_connection_string` does not contain the `AccountName` parameter, please specify it in the\n`storage_account` field.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", "name": "azure_data_lake_gen2", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Sends message parts as files to an Azure Data Lake Gen2 filesystem. Each file is uploaded with the filename specified with the `path` field.", "type": "output", "version": "4.38.0" @@ -30730,7 +38997,7 @@ "description": "\nOnly one authentication method is required, `storage_connection_string` or `storage_account` and `storage_access_key`. If both are set then the `storage_connection_string` is given priority.\n\nIn order to set the `queue_name` you can use function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here], which are calculated per message of a batch.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "name": "azure_queue_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Sends messages to an Azure Storage Queue.", "type": "output", "version": "3.36.0" @@ -30971,45 +39238,11 @@ "description": "\nOnly one authentication method is required, `storage_connection_string` or `storage_account` and `storage_access_key`. If both are set then the `storage_connection_string` is given priority.\n\nIn order to set the `table_name`, `partition_key` and `row_key` you can use function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here], which are calculated per message of a batch.\n\nIf the `properties` are not set in the config, all the `json` fields are marshalled and stored in the table, which will be created if it does not exist.\n\nThe `object` and `array` fields are marshaled as strings. e.g.:\n\nThe JSON message:\n\n```json\n{\n \"foo\": 55,\n \"bar\": {\n \"baz\": \"a\",\n \"bez\": \"b\"\n },\n \"diz\": [\"a\", \"b\"]\n}\n```\n\nWill store in the table the following properties:\n\n```yml\nfoo: '55'\nbar: '{ \"baz\": \"a\", \"bez\": \"b\" }'\ndiz: '[\"a\", \"b\"]'\n```\n\nIt's also possible to use function interpolations to get or transform the properties values, e.g.:\n\n```yml\nproperties:\n device: '${! json(\"device\") }'\n timestamp: '${! json(\"timestamp\") }'\n```\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "name": "azure_table_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Stores messages in an Azure Table Storage table.", "type": "output", "version": "3.36.0" }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "An address to connect to.", - "examples": [ - "127.0.0.1:11300" - ], - "kind": "scalar", - "name": "address", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "beanstalkd", - "plugin": true, - "status": "experimental", - "summary": "Write messages to a Beanstalkd queue.", - "type": "output", - "version": "4.7.0" - }, { "categories": [ "Utility" @@ -31211,309 +39444,11 @@ "type": "output" }, { - "categories": null, + "categories": [ + "AI" + ], "config": { "children": [ - { - "description": "A list of Cassandra nodes to connect to. Multiple comma separated addresses can be specified on a single line.", - "examples": [ - [ - "localhost:9042" - ], - [ - "foo:9042", - "bar:9042" - ], - [ - "foo:9042,bar:9042" - ] - ], - "kind": "array", - "name": "addresses", - "type": "string" - }, - { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use password authentication", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "The username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "The password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Optional configuration of Cassandra authentication parameters.", - "is_advanced": true, - "kind": "scalar", - "name": "password_authenticator", - "type": "object" - }, - { - "default": false, - "description": "If enabled the driver will not attempt to get host info from the system.peers table. This can speed up queries but will mean that data_centre, rack and token information will not be available.", - "is_advanced": true, - "kind": "scalar", - "name": "disable_initial_host_lookup", - "type": "bool" - }, - { - "default": 3, - "description": "The maximum number of retries before giving up on a request.", - "is_advanced": true, - "kind": "scalar", - "name": "max_retries", - "type": "int" - }, - { - "children": [ - { - "default": "1s", - "description": "The initial period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "initial_interval", - "type": "string" - }, - { - "default": "5s", - "description": "The maximum period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "max_interval", - "type": "string" - } - ], - "description": "Control time intervals between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "backoff", - "type": "object" - }, - { - "default": "600ms", - "description": "The client connection timeout.", - "kind": "scalar", - "name": "timeout", - "type": "string" - }, - { - "children": [ - { - "description": "The local DC to use, enables DC aware policy.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "local_dc", - "type": "string" - }, - { - "description": "The local rack to use, requires local_dc to be set, enables rack aware policy.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "local_rack", - "type": "string" - } - ], - "description": "Optional host selection policy configurations. Highly recommended in deployments with multiple DCs. Host selection is always token aware if the token can be calculated from query. By default the underlying policy is round robin over all nodes. Users can specify a local DC and rack to use for the DC Aware & Rack Aware policies. ", - "is_advanced": true, - "kind": "scalar", - "linter": "root = if this.local_rack != \"\" && (!this.exists(\"local_dc\") || this.local_dc == \"\") { \"local_dc must be set if local_rack is set\" }", - "name": "host_selection_policy", - "type": "object" - }, - { - "description": "A query to execute for each message.", - "kind": "scalar", - "name": "query", - "type": "string" - }, - { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] that can be used to provide arguments to Cassandra queries. The result of the query must be an array containing a matching number of elements to the query arguments.", - "is_optional": true, - "kind": "scalar", - "name": "args_mapping", - "type": "string", - "version": "3.55.0" - }, - { - "default": "QUORUM", - "description": "The consistency level to use.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"any\": true,\n \"one\": true,\n \"two\": true,\n \"three\": true,\n \"quorum\": true,\n \"all\": true,\n \"local_quorum\": true,\n \"each_quorum\": true,\n \"local_one\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "consistency", - "options": [ - "ANY", - "ONE", - "TWO", - "THREE", - "QUORUM", - "ALL", - "LOCAL_QUORUM", - "EACH_QUORUM", - "LOCAL_ONE" - ], - "type": "string" - }, - { - "default": true, - "description": "If enabled the driver will perform a logged batch. Disabling this prompts unlogged batches to be used instead, which are less efficient but necessary for alternative storages that do not support logged batches.", - "is_advanced": true, - "kind": "scalar", - "name": "logged_batch", - "type": "bool" - }, { "default": 64, "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", @@ -31612,280 +39547,192 @@ "kind": "", "name": "batching", "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nQuery arguments can be set using a bloblang array for the fields using the `args_mapping` field.\n\nWhen populating timestamp columns the value must either be a string in ISO 8601 format (2006-01-02T15:04:05Z07:00), or an integer representing unix time in seconds.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "examples": [ - { - "config": "\noutput:\n cassandra:\n addresses:\n - localhost:9042\n query: 'INSERT INTO foo.bar (id, content, created_at) VALUES (?, ?, ?)'\n args_mapping: |\n root = [\n this.id,\n this.content,\n this.timestamp\n ]\n batching:\n count: 500\n period: 1s\n", - "summary": "If we were to create a table with some basic columns with `CREATE TABLE foo.bar (id int primary key, content text, created_at timestamp);`, and were processing JSON documents of the form `{\"id\":\"342354354\",\"content\":\"hello world\",\"timestamp\":1605219406}` using logged batches, we could populate our table with the following config:", - "title": "Basic Inserts" - }, - { - "config": "\noutput:\n cassandra:\n addresses:\n - localhost:9042\n query: 'INSERT INTO foospace.footable JSON ?'\n args_mapping: 'root = [ this ]'\n batching:\n count: 500\n period: 1s\n", - "summary": "The following example inserts JSON documents into the table `footable` of the keyspace `foospace` using INSERT JSON (https://cassandra.apache.org/doc/latest/cql/json.html#insert-json).", - "title": "Insert JSON Documents" - } - ], - "name": "cassandra", - "plugin": true, - "status": "beta", - "summary": "Runs a query against a Cassandra database for each message in order to insert data.", - "type": "output" - }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ + }, { - "description": "Couchbase connection string.", + "description": "The host for the CyborgDB instance.", "examples": [ - "couchbase://localhost:11210" + "api.cyborg.com", + "localhost:8000" ], "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "description": "Username to connect to the cluster.", - "is_optional": true, - "kind": "scalar", - "name": "username", + "name": "host", "type": "string" }, { - "description": "Password to connect to the cluster.", - "is_optional": true, + "description": "The CyborgDB API key for authentication.", "is_secret": true, "kind": "scalar", - "name": "password", + "name": "api_key", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Couchbase bucket.", + "default": "redpanda-vectors", + "description": "The name of the index to write to.", "kind": "scalar", - "name": "bucket", + "name": "index_name", "type": "string" }, { - "description": "Bucket collection.", - "is_advanced": true, - "is_optional": true, + "description": "The base64-encoded encryption key for the index. Must be exactly 32 bytes when decoded.", + "examples": [ + "your-base64-encoded-32-byte-key" + ], + "is_secret": true, "kind": "scalar", - "name": "collection", + "name": "index_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Bucket scope.", + "default": false, + "description": "If true, create the index if it doesn't exist. CyborgDB will auto-detect dimension and optimize the index.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "scope", - "type": "string" + "name": "create_if_missing", + "type": "bool" }, { - "annotated_options": [ - [ - "json", - "JSONTranscoder implements the default transcoding behavior and applies JSON transcoding to all values. This will apply the following behavior to the value: binary ([]byte) -> error. default -> JSON value, JSON Flags." - ], - [ - "legacy", - "LegacyTranscoder implements the behavior for a backward-compatible transcoder. This transcoder implements behavior matching that of gocb v1.This will apply the following behavior to the value: binary ([]byte) -> binary bytes, Binary expectedFlags. string -> string bytes, String expectedFlags. default -> JSON value, JSON expectedFlags." - ], - [ - "raw", - "RawBinaryTranscoder implements passthrough behavior of raw binary data. This transcoder does not apply any serialization. This will apply the following behavior to the value: binary ([]byte) -> binary bytes, binary expectedFlags. default -> error." - ], - [ - "rawjson", - "RawJSONTranscoder implements passthrough behavior of JSON data. This transcoder does not apply any serialization. It will forward data across the network without incurring unnecessary parsing costs. This will apply the following behavior to the value: binary ([]byte) -> JSON bytes, JSON expectedFlags. string -> JSON bytes, JSON expectedFlags. default -> error." - ], - [ - "rawstring", - "RawStringTranscoder implements passthrough behavior of raw string data. This transcoder does not apply any serialization. This will apply the following behavior to the value: string -> string bytes, string expectedFlags. default -> error." - ] - ], - "default": "legacy", - "description": "Couchbase transcoder to use.", - "is_advanced": true, + "default": "upsert", + "description": "The operation to perform against the CyborgDB index.", "kind": "scalar", - "linter": "\nlet options = {\n \"json\": true,\n \"legacy\": true,\n \"raw\": true,\n \"rawjson\": true,\n \"rawstring\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "transcoder", + "linter": "\nlet options = {\n \"upsert\": true,\n \"delete\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "operation", + "options": [ + "upsert", + "delete" + ], "type": "string" }, { - "default": "15s", - "description": "Operation timeout.", - "is_advanced": true, + "description": "The ID for the vector entry in CyborgDB.", + "interpolated": true, "kind": "scalar", - "name": "timeout", + "name": "id", "type": "string" }, { - "description": "Document id.", + "bloblang": true, + "description": "The mapping to extract out the vector from the document. The result must be a floating point array. Required for upsert operations.", "examples": [ - "${! json(\"id\") }" + "root = this.embeddings_vector", + "root = [1.2, 0.5, 0.76]" ], - "interpolated": true, + "is_optional": true, "kind": "scalar", - "name": "id", + "name": "vector_mapping", "type": "string" }, { "bloblang": true, - "description": "Document content.", + "description": "An optional mapping of message to metadata for the vector entry.", + "examples": [ + "root = @", + "root = metadata()", + "root = {\"summary\": this.summary, \"category\": this.category}" + ], "is_optional": true, "kind": "scalar", - "name": "content", + "name": "metadata_mapping", "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis output allows you to write vectors to a CyborgDB encrypted index. CyborgDB provides\nend-to-end encrypted vector storage with automatic dimension detection and index optimization.\n\nAll vector data is encrypted client-side before being sent to the server, ensuring complete\ndata privacy. The encryption key never leaves your infrastructure.\n", + "name": "cyborgdb", + "plugin": true, + "status": "stable", + "summary": "Inserts items into a CyborgDB encrypted vector index.", + "type": "output" + }, + { + "categories": [ + "Utility" + ], + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "drop", + "plugin": true, + "status": "stable", + "summary": "Drops all messages.", + "type": "output" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ + { + "default": false, + "description": "Whether messages should be dropped when the child output returns an error of any type. For example, this could be when an `http_client` output gets a 4XX response code. In order to instead drop only on specific error patterns use the `error_matches` field instead.", + "kind": "scalar", + "name": "error", + "type": "bool" }, { - "annotated_options": [ - [ - "insert", - "insert a new document." - ], - [ - "remove", - "delete a document." - ], + "description": "A list of regular expressions (re2) where if the child output returns an error that matches any part of any of these patterns the message will be dropped.", + "examples": [ [ - "replace", - "replace the contents of a document." + "and that was really bad$" ], [ - "upsert", - "creates a new document if it does not exist, if it does exist then it updates it." + "roughly [0-9]+ issues occurred" ] ], - "default": "upsert", - "description": "Couchbase operation to perform.", - "kind": "scalar", - "linter": "\nlet options = {\n \"insert\": true,\n \"remove\": true,\n \"replace\": true,\n \"upsert\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operation", - "type": "string" + "is_optional": true, + "kind": "array", + "name": "error_patterns", + "type": "string", + "version": "4.27.0" }, { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "description": "An optional duration string that determines the maximum length of time to wait for a given message to be accepted by the child output before the message should be dropped instead. The most common reason for an output to block is when waiting for a lost connection to be re-established. Once a message has been dropped due to back pressure all subsequent messages are dropped immediately until the output is ready to process them again. Note that if `error` is set to `false` and this field is specified then messages dropped due to back pressure will return an error response (are nacked or reattempted).", + "examples": [ + "30s", + "1m" + ], + "is_optional": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "back_pressure", + "type": "string" }, { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" + "description": "A child output to wrap with this drop mechanism.", + "kind": "scalar", + "name": "output", + "type": "output" } ], "kind": "scalar", - "linter": "root = if ((this.operation == \"insert\" || this.operation == \"replace\" || this.operation == \"upsert\") && !this.exists(\"content\")) { [ \"content must be set for insert, replace and upsert operations.\" ] }", "name": "", "type": "object" }, - "description": "When inserting, replacing or upserting documents, each must have the `content` property set.\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "couchbase", + "description": "Regular Redpanda Connect outputs will apply back pressure when downstream services aren't accessible, and Redpanda Connect retries (or nacks) all messages that fail to be delivered. However, in some circumstances, or for certain output types, we instead might want to relax these mechanisms, which is when this output becomes useful.", + "examples": [ + { + "config": "\noutput:\n broker:\n pattern: fan_out\n outputs:\n - kafka:\n addresses: [ foobar:6379 ]\n topic: foo\n - drop_on:\n error: true\n output:\n http_client:\n url: http://example.com/foo/messages\n verb: POST\n", + "summary": "In this example we have a fan_out broker, where we guarantee delivery to our Kafka output, but drop messages if they fail our secondary HTTP client output.", + "title": "Dropping failed HTTP requests" + }, + { + "config": "\noutput:\n drop_on:\n back_pressure: 10s\n output:\n websocket:\n url: ws://example.com/foo/messages\n", + "summary": "Most outputs that attempt to establish and long-lived connection will apply back-pressure when the connection is lost. The following example has a websocket output where if it takes longer than 10 seconds to establish a connection, or recover a lost one, pending messages are dropped.", + "title": "Dropping from outputs that cannot connect" + } + ], + "name": "drop_on", "plugin": true, - "status": "experimental", - "summary": "Performs operations against Couchbase for each message, allowing you to store or delete data.", - "type": "output", - "version": "4.37.0" + "status": "stable", + "summary": "Attempts to write messages to a child output and if the write fails for one of a list of configurable reasons the message is dropped (acked) instead of being reattempted (or nacked).", + "type": "output" }, { "categories": [ @@ -31894,90 +39741,76 @@ "config": { "children": [ { - "description": "The connection URI to connect to.\nSee https://neo4j.com/docs/go-manual/current/connect-advanced/[Neo4j's documentation^] for more information. ", + "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", "examples": [ - "neo4j://demo.neo4jlabs.com", - "neo4j+s://aura.databases.neo4j.io", - "neo4j+ssc://self-signed.demo.neo4jlabs.com", - "bolt://127.0.0.1:7687", - "bolt+s://core.db.server:7687", - "bolt+ssc://10.0.0.43" + [ + "http://localhost:9200" + ] ], + "kind": "array", + "name": "urls", + "type": "string" + }, + { + "description": "The index to place messages.", + "interpolated": true, + "kind": "scalar", + "name": "index", + "type": "string" + }, + { + "description": "The action to take on the document. This field must resolve to one of the following action types: `index`, `update`, `delete`, `create` or `upsert`. See the `Updating Documents` example for more on how the `update` action works and the `Create Documents` and `Upserting Documents` examples for how to use the `create` and `upsert` actions respectively.", + "interpolated": true, "kind": "scalar", - "name": "uri", + "name": "action", "type": "string" }, { - "description": "The cypher expression to execute against the graph database.", + "description": "The ID for indexed messages. Interpolation should be used in order to create a unique ID for each message.", "examples": [ - "MERGE (p:Person {name: $name})", - "MATCH (o:Organization {id: $orgId})\nMATCH (p:Person {name: $name})\nMERGE (p)-[:WORKS_FOR]->(o)" + "${!counter()}-${!timestamp_unix()}" ], + "interpolated": true, "kind": "scalar", - "name": "cypher", + "name": "id", "type": "string" }, { "default": "", - "description": "Set the target database for which expressions are evaluated against.", + "description": "An optional pipeline id to preprocess incoming documents.", + "interpolated": true, + "is_advanced": true, "kind": "scalar", - "name": "database_name", + "name": "pipeline", "type": "string" }, { - "bloblang": true, - "description": "The mapping from the message to the data that is passed in as parameters to the cypher expression. Must be an object. By default the entire payload is used.", - "examples": [ - "root.name = this.displayName", - "root = {\"orgId\": this.org.id, \"name\": this.user.name}" - ], - "is_optional": true, + "default": "", + "description": "The routing key to use for the document.", + "interpolated": true, + "is_advanced": true, "kind": "scalar", - "name": "args_mapping", + "name": "routing", "type": "string" }, + { + "default": 0, + "description": "Specify how many times should an update operation be retried when a conflict occurs", + "is_advanced": true, + "kind": "scalar", + "name": "retry_on_conflict", + "type": "int" + }, { "children": [ { "default": false, - "description": "Whether to use basic authentication in requests.", + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, - { - "default": "", - "description": "A username to authenticate as.", - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The realm for authentication challenges.", - "is_advanced": true, - "kind": "scalar", - "name": "realm", - "type": "string" - } - ], - "description": "Allows you to specify basic authentication.", - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object" - }, - { - "children": [ { "default": false, "description": "Whether to skip server side certificate verification.", @@ -32098,6 +39931,58 @@ "name": "tls", "type": "object" }, + { + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" + }, + { + "default": "", + "description": "An API key to authenticate with. If set, it supersedes basic authentication.", + "is_secret": true, + "kind": "scalar", + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, { "children": [ { @@ -32189,69 +40074,49 @@ "kind": "", "name": "batching", "type": "object" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "The cypher output type writes a batch of messages to any graph database that supports the Neo4j or Bolt protocols.", + "description": "\nBoth the `id` and `index` fields can be dynamically set using function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here]. When sending batched messages these interpolations are performed per message part.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "examples": [ { - "config": "\noutput:\n cypher:\n uri: neo4j+s://example.databases.neo4j.io\n cypher: |\n MERGE (product:Product {id: $id})\n ON CREATE SET product.name = $product,\n product.title = $title,\n product.description = $description,\n args_mapping: |\n root = {}\n root.id = this.product.id \n root.product = this.product.summary.name\n root.title = this.product.summary.displayName\n root.description = this.product.fullDescription\n basic_auth:\n enabled: true\n username: \"${NEO4J_USER}\"\n password: \"${NEO4J_PASSWORD}\"\n", - "summary": "This is an example of how to write to Neo4j Aura", - "title": "Write to Neo4j Aura" + "config": "\n# Partial document update\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # Performs a partial update on the document.\n root.doc = this\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n\n# Scripted update\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # Increments the field \"counter\" by 1.\n root.script.source = \"ctx._source.counter += 1\"\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n\n# Upsert\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # If the product with the ID exists, its price will be updated to 100.\n # If the product does not exist, a new document with ID 1 and a price\n # of 50 will be inserted.\n root.doc.product_price = 50\n root.upsert.product_price = 100\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n", + "summary": "When updating documents, the request body should contain a combination of a `doc`, `upsert`, and/or `script` fields at the top level, this should be done via mapping processors. `doc` updates using a partial document, `script` performs an update using a scripting language such as the built in Painless language, and `upsert` updates an existing document or inserts a new one if it doesn’t exist. For more information on the structures and behaviors of these fields, please see the https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html[Elasticsearch Update API^]", + "title": "Updating Documents" + }, + { + "config": "\ninput:\n redpanda:\n seed_brokers: [localhost:19092]\n topics: [\"things\"]\n consumer_group: \"rpcn3\"\n processors:\n - mapping: |\n meta id = this.id\n root = this\noutput:\n elasticsearch_v8:\n urls: ['http://localhost:9200']\n index: \"things\"\n action: \"index\"\n id: ${! meta(\"id\") }\n", + "summary": "Here we read messages from a Redpanda cluster and write them to an Elasticsearch index using a field from the message as the ID for the Elasticsearch document.", + "title": "Indexing documents from Redpanda" + }, + { + "config": "\ninput:\n aws_s3:\n bucket: \"my-cool-bucket\"\n prefix: \"bug-facts/\"\n scanner:\n to_the_end: {}\noutput:\n elasticsearch_v8:\n urls: ['http://localhost:9200']\n index: \"cool-bug-facts\"\n action: \"index\"\n id: ${! meta(\"s3_key\") }\n", + "summary": "Here we read messages from a AWS S3 bucket and write them to an Elasticsearch index using the S3 key as the ID for the Elasticsearch document.", + "title": "Indexing documents from S3" + }, + { + "config": "\noutput:\n elasticsearch_v8:\n urls: ['https://localhost:9200']\n index: \"things\"\n action: \"index\"\n id: ${! json(\"id\") }\n api_key: \"${ELASTICSEARCH_API_KEY}\"\n", + "summary": "Set the `api_key` field to authenticate requests with an Elasticsearch API key. If `api_key` is set, it supersedes the `basic_auth` configuration.", + "title": "Authenticating with an API key" + }, + { + "config": "\noutput:\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! json(\"id\") }\n action: create\n", + "summary": "When using the `create` action, a new document will be created if the document ID does not already exist. If the document ID already exists, the operation will fail.", + "title": "Create Documents" + }, + { + "config": "\noutput:\n processors:\n - mapping: |\n meta id = this.id\n root = this.doc\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: upsert\n", + "summary": "When using the `upsert` action, if the document ID already exists, it will be updated. If the document ID does not exist, a new document will be inserted. The request body should contain the document to be indexed.", + "title": "Upserting Documents" } ], - "name": "cypher", - "plugin": true, - "status": "experimental", - "type": "output", - "version": "4.37.0" - }, - { - "categories": [ - "Services", - "Social" - ], - "config": { - "children": [ - { - "description": "A discord channel ID to write messages to.", - "kind": "scalar", - "name": "channel_id", - "type": "string" - }, - { - "description": "A bot token used for authentication.", - "kind": "scalar", - "name": "bot_token", - "type": "string" - }, - { - "default": "An optional rate limit resource to restrict API requests with.", - "is_deprecated": true, - "kind": "scalar", - "name": "rate_limit", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis output POSTs messages to the `/channels/\\{channel_id}/messages` Discord API endpoint authenticated as a bot using token based authentication.\n\nIf the format of a message is a JSON object matching the https://discord.com/developers/docs/resources/channel#message-object[Discord API message type^] then it is sent directly, otherwise an object matching the API type is created with the content of the message added as a string.\n", - "name": "discord", + "name": "elasticsearch_v8", "plugin": true, - "status": "experimental", - "summary": "Writes messages to a Discord channel.", + "status": "stable", + "summary": "Publishes messages into an Elasticsearch index. If the index does not exist then it is created with a dynamic mapping.", "type": "output" }, { @@ -32259,358 +40124,196 @@ "Utility" ], "config": { - "default": {}, - "kind": "scalar", + "default": [], + "kind": "array", "name": "", - "type": "object" + "type": "output" }, - "name": "drop", + "description": "\nThis pattern is useful for triggering events in the case where certain output targets have broken. For example, if you had an output type `http_client` but wished to reroute messages whenever the endpoint becomes unreachable you could use this pattern:\n\n```yaml\noutput:\n fallback:\n - http_client:\n url: http://foo:4195/post/might/become/unreachable\n retries: 3\n retry_period: 1s\n - http_client:\n url: http://bar:4196/somewhere/else\n retries: 3\n retry_period: 1s\n processors:\n - mapping: 'root = \"failed to send this message to foo: \" + content()'\n - file:\n path: /usr/local/benthos/everything_failed.jsonl\n```\n\n== Metadata\n\nWhen a given output fails the message routed to the following output will have a metadata value named `fallback_error` containing a string error message outlining the cause of the failure. The content of this string will depend on the particular output and can be used to enrich the message or provide information used to broker the data to an appropriate output using something like a `switch` output.\n\n== Batching\n\nWhen an output within a fallback sequence uses batching, like so:\n\n```yaml\noutput:\n fallback:\n - aws_dynamodb:\n table: foo\n string_columns:\n id: ${!json(\"id\")}\n content: ${!content()}\n batching:\n count: 10\n period: 1s\n - file:\n path: /usr/local/benthos/failed_stuff.jsonl\n```\n\nRedpanda Connect makes a best attempt at inferring which specific messages of the batch failed, and only propagates those individual messages to the next fallback tier.\n\nHowever, depending on the output and the error returned it is sometimes not possible to determine the individual messages that failed, in which case the whole batch is passed to the next tier in order to preserve at-least-once delivery guarantees.", + "name": "fallback", "plugin": true, "status": "stable", - "summary": "Drops all messages.", - "type": "output" + "summary": "Attempts to send each message to a child output, starting from the first output on the list. If an output attempt fails then the next output in the list is attempted, and so on.", + "type": "output", + "version": "3.58.0" }, { "categories": [ - "Utility" + "GCP", + "Services" ], "config": { "children": [ { - "default": false, - "description": "Whether messages should be dropped when the child output returns an error of any type. For example, this could be when an `http_client` output gets a 4XX response code. In order to instead drop only on specific error patterns use the `error_matches` field instead.", + "default": "", + "description": "The project ID of the dataset to insert data to. If not set, it will be inferred from the credentials or read from the GOOGLE_CLOUD_PROJECT environment variable.", "kind": "scalar", - "name": "error", - "type": "bool" - }, - { - "description": "A list of regular expressions (re2) where if the child output returns an error that matches any part of any of these patterns the message will be dropped.", - "examples": [ - [ - "and that was really bad$" - ], - [ - "roughly [0-9]+ issues occurred" - ] - ], - "is_optional": true, - "kind": "array", - "name": "error_patterns", - "type": "string", - "version": "4.27.0" + "name": "project", + "type": "string" }, { - "description": "An optional duration string that determines the maximum length of time to wait for a given message to be accepted by the child output before the message should be dropped instead. The most common reason for an output to block is when waiting for a lost connection to be re-established. Once a message has been dropped due to back pressure all subsequent messages are dropped immediately until the output is ready to process them again. Note that if `error` is set to `false` and this field is specified then messages dropped due to back pressure will return an error response (are nacked or reattempted).", - "examples": [ - "30s", - "1m" - ], - "is_optional": true, + "default": "", + "description": "The project ID in which jobs will be executed. If not set, project will be used.", "kind": "scalar", - "name": "back_pressure", + "name": "job_project", "type": "string" }, { - "description": "A child output to wrap with this drop mechanism.", + "description": "The BigQuery Dataset ID.", "kind": "scalar", - "name": "output", - "type": "output" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Regular Redpanda Connect outputs will apply back pressure when downstream services aren't accessible, and Redpanda Connect retries (or nacks) all messages that fail to be delivered. However, in some circumstances, or for certain output types, we instead might want to relax these mechanisms, which is when this output becomes useful.", - "examples": [ - { - "config": "\noutput:\n broker:\n pattern: fan_out\n outputs:\n - kafka:\n addresses: [ foobar:6379 ]\n topic: foo\n - drop_on:\n error: true\n output:\n http_client:\n url: http://example.com/foo/messages\n verb: POST\n", - "summary": "In this example we have a fan_out broker, where we guarantee delivery to our Kafka output, but drop messages if they fail our secondary HTTP client output.", - "title": "Dropping failed HTTP requests" - }, - { - "config": "\noutput:\n drop_on:\n back_pressure: 10s\n output:\n websocket:\n url: ws://example.com/foo/messages\n", - "summary": "Most outputs that attempt to establish and long-lived connection will apply back-pressure when the connection is lost. The following example has a websocket output where if it takes longer than 10 seconds to establish a connection, or recover a lost one, pending messages are dropped.", - "title": "Dropping from outputs that cannot connect" - } - ], - "name": "drop_on", - "plugin": true, - "status": "stable", - "summary": "Attempts to write messages to a child output and if the write fails for one of a list of configurable reasons the message is dropped (acked) instead of being reattempted (or nacked).", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "default": {}, - "description": "A map of outputs to statically create.", - "kind": "map", - "name": "outputs", - "type": "output" + "name": "dataset", + "type": "string" }, { - "default": "", - "description": "A path prefix for HTTP endpoints that are registered.", + "description": "The table to insert messages to.", "kind": "scalar", - "name": "prefix", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "The broker pattern used is always `fan_out`, meaning each message will be delivered to each dynamic output.", - "footnotes": "\n== Endpoints\n\n=== GET `/outputs`\n\nReturns a JSON object detailing all dynamic outputs, providing information such as their current uptime and configuration.\n\n=== GET `/outputs/\\{id}`\n\nReturns the configuration of an output.\n\n=== POST `/outputs/\\{id}`\n\nCreates or updates an output with a configuration provided in the request body (in YAML or JSON format).\n\n=== DELETE `/outputs/\\{id}`\n\nStops and removes an output.\n\n=== GET `/outputs/\\{id}/uptime`\n\nReturns the uptime of an output as a duration string (of the form \"72h3m0.5s\").", - "name": "dynamic", - "plugin": true, - "status": "stable", - "summary": "A special broker type where the outputs are identified by unique labels and can be created, changed and removed during runtime via a REST API.", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", - "examples": [ - [ - "http://localhost:9200" - ] - ], - "kind": "array", - "name": "urls", + "name": "table", "type": "string" }, { - "description": "The index to place messages.", - "interpolated": true, + "default": "NEWLINE_DELIMITED_JSON", + "description": "The format of each incoming message.", "kind": "scalar", - "name": "index", + "linter": "\nlet options = {\n \"newline_delimited_json\": true,\n \"csv\": true,\n \"parquet\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "format", + "options": [ + "NEWLINE_DELIMITED_JSON", + "CSV", + "PARQUET" + ], "type": "string" }, { - "description": "The action to take on the document. This field must resolve to one of the following action types: `index`, `update`, `delete`, `create` or `upsert`. See the `Updating Documents` example for more on how the `update` action works and the `Create Documents` and `Upserting Documents` examples for how to use the `create` and `upsert` actions respectively.", - "interpolated": true, + "default": 64, + "description": "The maximum number of message batches to have in flight at a given time. Increase this to improve throughput.", "kind": "scalar", - "name": "action", - "type": "string" + "name": "max_in_flight", + "type": "int" }, { - "description": "The ID for indexed messages. Interpolation should be used in order to create a unique ID for each message.", - "examples": [ - "${!counter()}-${!timestamp_unix()}" - ], - "interpolated": true, + "default": "WRITE_APPEND", + "description": "Specifies how existing data in a destination table is treated.", + "is_advanced": true, "kind": "scalar", - "name": "id", + "linter": "\nlet options = {\n \"write_append\": true,\n \"write_empty\": true,\n \"write_truncate\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "write_disposition", + "options": [ + "WRITE_APPEND", + "WRITE_EMPTY", + "WRITE_TRUNCATE" + ], "type": "string" }, { - "default": "", - "description": "An optional pipeline id to preprocess incoming documents.", - "interpolated": true, + "default": "CREATE_IF_NEEDED", + "description": "Specifies the circumstances under which destination table will be created. If CREATE_IF_NEEDED is used the GCP BigQuery will create the table if it does not already exist and tables are created atomically on successful completion of a job. The CREATE_NEVER option ensures the table must already exist and will not be automatically created.", "is_advanced": true, "kind": "scalar", - "name": "pipeline", + "linter": "\nlet options = {\n \"create_if_needed\": true,\n \"create_never\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "create_disposition", + "options": [ + "CREATE_IF_NEEDED", + "CREATE_NEVER" + ], "type": "string" }, { - "default": "", - "description": "The routing key to use for the document.", - "interpolated": true, + "default": false, + "description": "Causes values not matching the schema to be tolerated. Unknown values are ignored. For CSV this ignores extra values at the end of a line. For JSON this ignores named values that do not match any column name. If this field is set to false (the default value), records containing unknown values are treated as bad records. The max_bad_records field can be used to customize how bad records are handled.", "is_advanced": true, "kind": "scalar", - "name": "routing", - "type": "string" + "name": "ignore_unknown_values", + "type": "bool" }, { "default": 0, - "description": "Specify how many times should an update operation be retried when a conflict occurs", + "description": "The maximum number of bad records that will be ignored when reading data.", "is_advanced": true, "kind": "scalar", - "name": "retry_on_conflict", + "name": "max_bad_records", "type": "int" }, + { + "default": false, + "description": "Indicates if we should automatically infer the options and schema for CSV and JSON sources. If the table doesn't exist and this field is set to `false` the output may not be able to insert data and will throw insertion error. Be careful using this field since it delegates to the GCP BigQuery service the schema detection and values like `\"no\"` may be treated as booleans for the CSV format.", + "is_advanced": true, + "kind": "scalar", + "name": "auto_detect", + "type": "bool" + }, + { + "default": {}, + "description": "A list of labels to add to the load job.", + "kind": "map", + "name": "job_labels", + "type": "string" + }, + { + "default": "", + "description": "An optional field to set Google Service Account Credentials json.", + "is_secret": true, + "kind": "scalar", + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "default": [], + "description": "A list of values to use as header for each batch of messages. If not specified the first line of each message will be used as header.", + "kind": "array", + "name": "header", + "type": "string" }, { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, + "default": ",", + "description": "The separator for fields in a CSV file, used when reading or exporting data.", "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "field_delimiter", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": false, + "description": "Causes missing trailing optional columns to be tolerated when reading CSV data. Missing values are treated as nulls.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", - "type": "string" + "name": "allow_jagged_rows", + "type": "bool" }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, - { - "children": [ { "default": false, - "description": "Whether to use basic authentication in requests.", + "description": "Sets whether quoted data sections containing newlines are allowed when reading CSV data.", "is_advanced": true, "kind": "scalar", - "name": "enabled", + "name": "allow_quoted_newlines", "type": "bool" }, { - "default": "", - "description": "A username to authenticate as.", + "default": "UTF-8", + "description": "Encoding is the character encoding of data to be read.", "is_advanced": true, "kind": "scalar", - "name": "username", + "linter": "\nlet options = {\n \"utf-8\": true,\n \"iso-8859-1\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "encoding", + "options": [ + "UTF-8", + "ISO-8859-1" + ], "type": "string" }, { - "default": "", - "description": "A password to authenticate with.", + "default": 1, + "description": "The number of rows at the top of a CSV file that BigQuery will skip when reading data. The default value is 1 since Redpanda Connect will add the specified header in the first line of each batch sent to BigQuery.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "skip_leading_rows", + "type": "int" } ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, + "description": "Specify how CSV data should be interpreted.", "kind": "scalar", - "name": "basic_auth", + "name": "csv", "type": "object" }, { @@ -32710,119 +40413,13 @@ "name": "", "type": "object" }, - "description": "\nBoth the `id` and `index` fields can be dynamically set using function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here]. When sending batched messages these interpolations are performed per message part.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "examples": [ - { - "config": "\n# Partial document update\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # Performs a partial update ont he document.\n root.doc = this\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n\n# Scripted update\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # Increments the field \"counter\" by 1.\n root.script.source = \"ctx._source.counter += 1\"\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n\n# Upsert\noutput:\n processors:\n - mapping: |\n meta id = this.id\n # If the product with the ID exists, its price will be updated to 100.\n # If the product does not exist, a new document with ID 1 and a price\n # of 50 will be inserted.\n root.doc.product_price = 50\n root.upsert.product_price = 100\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: update\n", - "summary": "When updating documents, the request body should contain a combination of a `doc`, `upsert`, and/or `script` fields at the top level, this should be done via mapping processors. `doc` updates using a partial document, `script` performs an update using a scripting language such as the built in Painless language, and `upsert` updates an existing document or inserts a new one if it doesn’t exist. For more information on the structures and behaviors of these fields, please see the https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-update.html[Elasticsearch Update API^]", - "title": "Updating Documents" - }, - { - "config": "\ninput:\n redpanda:\n seed_brokers: [localhost:19092]\n topics: [\"things\"]\n consumer_group: \"rpcn3\"\n processors:\n - mapping: |\n meta id = this.id\n root = this\noutput:\n elasticsearch_v8:\n urls: ['http://localhost:9200']\n index: \"things\"\n action: \"index\"\n id: ${! meta(\"id\") }\n", - "summary": "Here we read messages from a Redpanda cluster and write them to an Elasticsearch index using a field from the message as the ID for the Elasticsearch document.", - "title": "Indexing documents from Redpanda" - }, - { - "config": "\ninput:\n aws_s3:\n bucket: \"my-cool-bucket\"\n prefix: \"bug-facts/\"\n scanner:\n to_the_end: {}\noutput:\n elasticsearch_v8:\n urls: ['http://localhost:9200']\n index: \"cool-bug-facts\"\n action: \"index\"\n id: ${! meta(\"s3_key\") }\n", - "summary": "Here we read messages from a AWS S3 bucket and write them to an Elasticsearch index using the S3 key as the ID for the Elasticsearch document.", - "title": "Indexing documents from S3" - }, - { - "config": "\noutput:\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! json(\"id\") }\n action: create\n", - "summary": "When using the `create` action, a new document will be created if the document ID does not already exist. If the document ID already exists, the operation will fail.", - "title": "Create Documents" - }, - { - "config": "\noutput:\n processors:\n - mapping: |\n meta id = this.id\n root = this.doc\n elasticsearch_v8:\n urls: [localhost:9200]\n index: foo\n id: ${! @id }\n action: upsert\n", - "summary": "When using the `upsert` action, if the document ID already exists, it will be updated. If the document ID does not exist, a new document will be inserted. The request body should contain the document to be indexed.", - "title": "Upserting Documents" - } - ], - "name": "elasticsearch_v8", - "plugin": true, - "status": "stable", - "summary": "Publishes messages into an Elasticsearch index. If the index does not exist then it is created with a dynamic mapping.", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "default": [], - "kind": "array", - "name": "", - "type": "output" - }, - "description": "\nThis pattern is useful for triggering events in the case where certain output targets have broken. For example, if you had an output type `http_client` but wished to reroute messages whenever the endpoint becomes unreachable you could use this pattern:\n\n```yaml\noutput:\n fallback:\n - http_client:\n url: http://foo:4195/post/might/become/unreachable\n retries: 3\n retry_period: 1s\n - http_client:\n url: http://bar:4196/somewhere/else\n retries: 3\n retry_period: 1s\n processors:\n - mapping: 'root = \"failed to send this message to foo: \" + content()'\n - file:\n path: /usr/local/benthos/everything_failed.jsonl\n```\n\n== Metadata\n\nWhen a given output fails the message routed to the following output will have a metadata value named `fallback_error` containing a string error message outlining the cause of the failure. The content of this string will depend on the particular output and can be used to enrich the message or provide information used to broker the data to an appropriate output using something like a `switch` output.\n\n== Batching\n\nWhen an output within a fallback sequence uses batching, like so:\n\n```yaml\noutput:\n fallback:\n - aws_dynamodb:\n table: foo\n string_columns:\n id: ${!json(\"id\")}\n content: ${!content()}\n batching:\n count: 10\n period: 1s\n - file:\n path: /usr/local/benthos/failed_stuff.jsonl\n```\n\nRedpanda Connect makes a best attempt at inferring which specific messages of the batch failed, and only propagates those individual messages to the next fallback tier.\n\nHowever, depending on the output and the error returned it is sometimes not possible to determine the individual messages that failed, in which case the whole batch is passed to the next tier in order to preserve at-least-once delivery guarantees.", - "name": "fallback", + "description": "\n== Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to GCP services. You can find out more in xref:guides:cloud/gcp.adoc[].\n\n== Format\n\nThis output currently supports only CSV, NEWLINE_DELIMITED_JSON and PARQUET, formats. Learn more about how to use GCP BigQuery with them here:\n\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-json[`NEWLINE_DELIMITED_JSON`^]\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-csv[`CSV`^]\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-parquet[`PARQUET`^]\n\nEach message may contain multiple elements separated by newlines. For example a single message containing:\n\n```json\n{\"key\": \"1\"}\n{\"key\": \"2\"}\n```\n\nIs equivalent to two separate messages:\n\n```json\n{\"key\": \"1\"}\n```\n\nAnd:\n\n```json\n{\"key\": \"2\"}\n```\n\nThe same is true for the CSV format.\n\n=== CSV\n\nFor the CSV format when the field `csv.header` is specified a header row will be inserted as the first line of each message batch. If this field is not provided then the first message of each message batch must include a header line.\n\n=== Parquet\n\nFor parquet, the data can be encoded using the `parquet_encode` processor and each message that is sent to the output must be a full parquet message.\n\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "gcp_bigquery", "plugin": true, "status": "stable", - "summary": "Attempts to send each message to a child output, starting from the first output on the list. If an output attempt fails then the next output in the list is attempted, and so on.", + "summary": "Sends messages as new rows to a Google Cloud BigQuery table.", "type": "output", - "version": "3.58.0" - }, - { - "categories": [ - "Local" - ], - "config": { - "children": [ - { - "description": "The file to write to, if the file does not yet exist it will be created.", - "examples": [ - "/tmp/data.txt", - "/tmp/${! timestamp_unix() }.txt", - "/tmp/${! json(\"document.id\") }.json" - ], - "interpolated": true, - "kind": "scalar", - "name": "path", - "type": "string", - "version": "3.33.0" - }, - { - "annotated_options": [ - [ - "all-bytes", - "Only applicable to file based outputs. Writes each message to a file in full, if the file already exists the old content is deleted." - ], - [ - "append", - "Append each message to the output stream without any delimiter or special encoding." - ], - [ - "lines", - "Append each message to the output stream followed by a line break." - ], - [ - "delim:x", - "Append each message to the output stream followed by a custom delimiter." - ] - ], - "default": "lines", - "description": "The way in which the bytes of messages should be written out into the output data stream. It's possible to write lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar" - ], - "kind": "scalar", - "name": "codec", - "type": "string", - "version": "3.33.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Messages can be written to different files by using xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions] in the path field. However, only one file is ever open at a given time, and therefore when the path changes the previously open file is closed.", - "name": "file", - "plugin": true, - "status": "stable", - "summary": "Writes messages to files on disk based on a chosen codec.", - "type": "output" + "version": "3.55.0" }, { "categories": [ @@ -32833,176 +40430,186 @@ "children": [ { "default": "", - "description": "The project ID of the dataset to insert data to. If not set, it will be inferred from the credentials or read from the GOOGLE_CLOUD_PROJECT environment variable.", + "description": "The GCP project ID. If empty, the project is auto-detected from the environment.", "kind": "scalar", "name": "project", "type": "string" }, { - "default": "", - "description": "The project ID in which jobs will be exectuted. If not set, project will be used.", - "kind": "scalar", - "name": "job_project", - "type": "string" - }, - { - "description": "The BigQuery Dataset ID.", + "description": "The BigQuery dataset ID.", "kind": "scalar", "name": "dataset", "type": "string" }, { - "description": "The table to insert messages to.", + "description": "The BigQuery table ID. Supports interpolation functions. When batching, resolved from the first message in each batch.", + "interpolated": true, "kind": "scalar", "name": "table", "type": "string" }, { - "default": "NEWLINE_DELIMITED_JSON", - "description": "The format of each incoming message.", + "default": "json", + "description": "The format of input messages. Use 'json' to have the component convert JSON to proto automatically. Use 'protobuf' to supply raw proto-encoded bytes.", "kind": "scalar", - "linter": "\nlet options = {\n \"newline_delimited_json\": true,\n \"csv\": true,\n \"parquet\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "format", + "linter": "\nlet options = {\n \"json\": true,\n \"protobuf\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "message_format", "options": [ - "NEWLINE_DELIMITED_JSON", - "CSV", - "PARQUET" + "json", + "protobuf" ], "type": "string" }, { - "default": 64, - "description": "The maximum number of message batches to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, - { - "default": "WRITE_APPEND", - "description": "Specifies how existing data in a destination table is treated.", + "default": "default_stream", + "description": "How the output writes to BigQuery. `default_stream` uses the multiplexed default stream (at-least-once, lowest latency). `pending_stream` allocates a per-batch pending stream that commits atomically, providing exactly-once semantics within a single committed batch. `upsert` writes UPSERT-only rows to a BigQuery CDC-enabled table; the target table must have a PRIMARY KEY. `upsert_delete` allows both UPSERT and DELETE rows. Both CDC modes use the default stream as required by BigQuery.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"write_append\": true,\n \"write_empty\": true,\n \"write_truncate\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "write_disposition", + "linter": "\nlet options = {\n \"default_stream\": true,\n \"pending_stream\": true,\n \"upsert\": true,\n \"upsert_delete\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "write_mode", "options": [ - "WRITE_APPEND", - "WRITE_EMPTY", - "WRITE_TRUNCATE" + "default_stream", + "pending_stream", + "upsert", + "upsert_delete" ], "type": "string" }, { - "default": "CREATE_IF_NEEDED", - "description": "Specifies the circumstances under which destination table will be created. If CREATE_IF_NEEDED is used the GCP BigQuery will create the table if it does not already exist and tables are created atomically on successful completion of a job. The CREATE_NEVER option ensures the table must already exist and will not be automatically created.", - "is_advanced": true, + "description": "Bloblang expression resolving to the `_CHANGE_TYPE` pseudo-column value for each row. Must resolve to `UPSERT` or `DELETE` (case-insensitive). Required when `write_mode` is `upsert` or `upsert_delete`. Example: `${! metadata(\"operation\") }`.", + "interpolated": true, + "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"create_if_needed\": true,\n \"create_never\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "create_disposition", - "options": [ - "CREATE_IF_NEEDED", - "CREATE_NEVER" - ], + "name": "change_type", "type": "string" }, { - "default": false, - "description": "Causes values not matching the schema to be tolerated. Unknown values are ignored. For CSV this ignores extra values at the end of a line. For JSON this ignores named values that do not match any column name. If this field is set to false (the default value), records containing unknown values are treated as bad records. The max_bad_records field can be used to customize how bad records are handled.", - "is_advanced": true, + "description": "Optional Bloblang expression resolving to the `_CHANGE_SEQUENCE_NUMBER` pseudo-column value. Format: 1 to 4 sections of 1 to 16 hexadecimal characters each, separated by `/`. Example: `${! metadata(\"scn\") }` or `${! \"0/0/0/0\" }`. When unset, BigQuery resolves ordering by arrival time.", + "interpolated": true, + "is_optional": true, "kind": "scalar", - "name": "ignore_unknown_values", - "type": "bool" + "name": "change_sequence_number", + "type": "string" }, { - "default": 0, - "description": "The maximum number of bad records that will be ignored when reading data.", - "is_advanced": true, - "kind": "scalar", - "name": "max_bad_records", - "type": "int" + "description": "Optional list of primary-key column names. Required when `auto_create_table` is true and `write_mode` is `upsert` or `upsert_delete`. A pre-existing table must already declare its PRIMARY KEY — this field cannot add one; when both are set they must match exactly (same columns, same order). Up to 16 columns; composite keys are supported in the same order they are listed.", + "is_optional": true, + "kind": "array", + "name": "primary_keys", + "type": "string" }, { "default": false, - "description": "Indicates if we should automatically infer the options and schema for CSV and JSON sources. If the table doesn't exist and this field is set to `false` the output may not be able to insert data and will throw insertion error. Be careful using this field since it delegates to the GCP BigQuery service the schema detection and values like `\"no\"` may be treated as booleans for the CSV format.", + "description": "If true and the target table does not exist, the output creates it using the configured `schema`, `time_partitioning`, and `clustering`. AlreadyExists errors from concurrent creators are treated as success. When the table name is interpolated, every auto-created table receives the same schema and partition/clustering settings.", "is_advanced": true, "kind": "scalar", - "name": "auto_detect", + "name": "auto_create_table", "type": "bool" }, - { - "default": {}, - "description": "A list of labels to add to the load job.", - "kind": "map", - "name": "job_labels", - "type": "string" - }, - { - "default": "", - "description": "An optional field to set Google Service Account Credentials json.", - "is_secret": true, - "kind": "scalar", - "name": "credentials_json", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, { "children": [ { - "default": [], - "description": "A list of values to use as header for each batch of messages. If not specified the first line of each message will be used as header.", - "kind": "array", - "name": "header", + "description": "Column name.", + "is_advanced": true, + "kind": "scalar", + "name": "name", "type": "string" }, { - "default": ",", - "description": "The separator for fields in a CSV file, used when reading or exporting data.", + "description": "BigQuery column type (STRING, BYTES, INTEGER/INT64, FLOAT/FLOAT64, NUMERIC, BIGNUMERIC, BOOLEAN/BOOL, TIMESTAMP, DATE, TIME, DATETIME, GEOGRAPHY, JSON, RECORD).", + "is_advanced": true, "kind": "scalar", - "name": "field_delimiter", + "name": "type", "type": "string" }, { - "default": false, - "description": "Causes missing trailing optional columns to be tolerated when reading CSV data. Missing values are treated as nulls.", + "default": "NULLABLE", + "description": "Column mode: NULLABLE (default), REQUIRED, or REPEATED.", "is_advanced": true, "kind": "scalar", - "name": "allow_jagged_rows", - "type": "bool" + "name": "mode", + "type": "string" }, { - "default": false, - "description": "Sets whether quoted data sections containing newlines are allowed when reading CSV data.", + "description": "For RECORD columns, the list of nested fields. Same shape as the top-level schema list.", "is_advanced": true, - "kind": "scalar", - "name": "allow_quoted_newlines", - "type": "bool" - }, + "is_optional": true, + "kind": "array", + "name": "fields", + "type": "unknown" + } + ], + "default": [], + "description": "Column definitions used by `auto_create_table`. Required when `auto_create_table` is true.", + "is_advanced": true, + "kind": "array", + "name": "schema", + "type": "object" + }, + { + "children": [ { - "default": "UTF-8", - "description": "Encoding is the character encoding of data to be read.", + "description": "Partitioning granularity.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"utf-8\": true,\n \"iso-8859-1\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "encoding", + "linter": "\nlet options = {\n \"day\": true,\n \"hour\": true,\n \"month\": true,\n \"year\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "type", "options": [ - "UTF-8", - "ISO-8859-1" + "DAY", + "HOUR", + "MONTH", + "YEAR" ], "type": "string" }, { - "default": 1, - "description": "The number of rows at the top of a CSV file that BigQuery will skip when reading data. The default value is 1 since Redpanda Connect will add the specified header in the first line of each batch sent to BigQuery.", + "default": "", + "description": "Column to partition on. Must be of type DATE, TIMESTAMP, or DATETIME. If empty, the table uses ingestion-time partitioning (`_PARTITIONTIME`).", "is_advanced": true, "kind": "scalar", - "name": "skip_leading_rows", - "type": "int" + "name": "field", + "type": "string" + }, + { + "default": "0s", + "description": "Optional partition expiration. Zero means no expiration.", + "is_advanced": true, + "kind": "scalar", + "name": "expiration", + "type": "string" + }, + { + "default": false, + "description": "If true, queries against the table must filter on the partition column.", + "is_advanced": true, + "kind": "scalar", + "name": "require_filter", + "type": "bool" } ], - "description": "Specify how CSV data should be interpretted.", + "description": "Optional time-partitioning settings applied during `auto_create_table`. Setting `type` is the trigger — when omitted, the block is treated as absent.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "csv", + "name": "time_partitioning", "type": "object" }, + { + "default": [], + "description": "Optional clustering columns (up to 4) applied during `auto_create_table`. All names must appear in `schema`.", + "is_advanced": true, + "kind": "array", + "name": "clustering", + "type": "string" + }, + { + "default": 4, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" + }, { "children": [ { @@ -33094,19 +40701,109 @@ "kind": "", "name": "batching", "type": "object" + }, + { + "default": "", + "description": "An optional JSON string containing GCP credentials. If empty, credentials are loaded from the environment.", + "is_secret": true, + "kind": "scalar", + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "Service account email to impersonate. When set, the output obtains tokens acting as this service account. Requires the caller to have roles/iam.serviceAccountTokenCreator on the target.", + "is_advanced": true, + "kind": "scalar", + "name": "target_principal", + "type": "string" + }, + { + "default": [], + "description": "Optional delegation chain for chained service account impersonation. Each service account must be granted roles/iam.serviceAccountTokenCreator on the next in the chain.", + "is_advanced": true, + "kind": "array", + "name": "delegates", + "type": "string" + }, + { + "default": "5m", + "description": "How long a cached stream can remain unused before being closed. Relevant when the table field uses interpolation to route to many tables.", + "is_advanced": true, + "kind": "scalar", + "name": "stream_idle_timeout", + "type": "string" + }, + { + "default": "1m", + "description": "How often to check for idle streams to close.", + "is_advanced": true, + "kind": "scalar", + "name": "stream_sweep_interval", + "type": "string" + }, + { + "default": 1024, + "description": "Soft cap on the number of cached streams. When the cache exceeds this size, the least-recently-used stream is evicted. Set to 0 for unlimited (rely on idle-timeout sweeping only). Relevant when the table field uses interpolation to route to many tables.", + "is_advanced": true, + "kind": "scalar", + "name": "max_cached_streams", + "type": "int" + }, + { + "default": "15s", + "description": "How long a single BigQuery table-metadata fetch can run before being aborted. Coalesced concurrent resolves share one fetch, so this bounds the time a wedged backend can stall every batch routing to the same table. On the auto_create_table path the budget covers Metadata→Create→Metadata, so it needs to absorb transient backend slowness on top of the metadata fetch itself.", + "is_advanced": true, + "kind": "scalar", + "name": "schema_resolve_timeout", + "type": "string" + }, + { + "default": "30s", + "description": "Total time budget for a single schema evolution attempt (Metadata + Update across all CAS retries on HTTP 412). Bounds how long the WriteBatch retry loop can be starved by a wedged backend.", + "is_advanced": true, + "kind": "scalar", + "name": "schema_evolution_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "Override the BigQuery HTTP endpoint. Useful for local emulators.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "string" + }, + { + "default": "", + "description": "Override the BigQuery Storage gRPC endpoint. Useful for local emulators.", + "is_advanced": true, + "kind": "scalar", + "name": "grpc", + "type": "string" + } + ], + "description": "Optional endpoint overrides for the BigQuery and Storage Write API clients.", + "is_advanced": true, + "kind": "scalar", + "name": "endpoint", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\n== Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to GCP services. You can find out more in xref:guides:cloud/gcp.adoc[].\n\n== Format\n\nThis output currently supports only CSV, NEWLINE_DELIMITED_JSON and PARQUET, formats. Learn more about how to use GCP BigQuery with them here:\n\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-json[`NEWLINE_DELIMITED_JSON`^]\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-csv[`CSV`^]\n- https://cloud.google.com/bigquery/docs/loading-data-cloud-storage-parquet[`PARQUET`^]\n\nEach message may contain multiple elements separated by newlines. For example a single message containing:\n\n```json\n{\"key\": \"1\"}\n{\"key\": \"2\"}\n```\n\nIs equivalent to two separate messages:\n\n```json\n{\"key\": \"1\"}\n```\n\nAnd:\n\n```json\n{\"key\": \"2\"}\n```\n\nThe same is true for the CSV format.\n\n=== CSV\n\nFor the CSV format when the field `csv.header` is specified a header row will be inserted as the first line of each message batch. If this field is not provided then the first message of each message batch must include a header line.\n\n=== Parquet\n\nFor parquet, the data can be encoded using the `parquet_encode` processor and each message that is sent to the output must be a full parquet message.\n\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "gcp_bigquery", + "description": "\nWrites messages to a BigQuery table using the Storage Write API.\nThis provides higher throughput and lower latency than the legacy streaming API or load jobs.\n\nMessages can be formatted as JSON (default) or raw protobuf bytes.\nWhen using JSON format the component automatically fetches the table schema and converts each message to the corresponding proto representation.\n\nWARNING: protojson encodes int64 and uint64 values as strings, bytes as base64-encoded strings, and timestamps as RFC 3339 strings.\nJSON messages must follow these conventions (e.g. `\"age\": \"30\"`, `\"data\": \"aGVsbG8=\"`, `\"created_at\": \"2026-01-02T15:04:05Z\"`); otherwise the write will fail with an unmarshalling error.\n\nWhen batching is enabled the table name is resolved from the first message in each batch.\nAll messages in the same batch are written to that table.\n\nThe interpolated table name is sanitized for BigQuery: dots, hyphens, slashes and whitespace are replaced with underscores, non-ASCII-alphanumeric characters are stripped, leading digits are prefixed with `_`, and the result is truncated to 1024 characters.\nA name that sanitizes to the empty string is rejected as a permanent error.\n\n== Write modes\n\nThe `write_mode` field selects between two write paths:\n\n- `default_stream` (default): the multiplexed default stream. Lowest latency, at-least-once semantics.\n- `pending_stream`: a fresh pending stream is allocated per batch; rows are written with sequential offsets, the stream is finalized, then atomically committed. Provides exactly-once semantics within a single committed batch.\n\n== Auto-create\n\nWhen `auto_create_table` is true, the output creates missing tables on the fly using the configured `schema`, `time_partitioning`, and `clustering`. `AlreadyExists` errors from concurrent creators are treated as success. When the table name is interpolated, every auto-created table receives the same configuration.\n\n== Exactly-once caveat\n\nThe exactly-once guarantee of `pending_stream` is \"exactly-once within a stream\". If a BatchCommitWriteStreams RPC succeeds but its response is lost to a network failure, benthos retries the batch through a new pending stream and the data lands twice. This is a fundamental limitation of the BigQuery Storage Write API exactly-once contract and applies to every implementation.\n\n== CDC migration\n\nWhen migrating from the load-jobs based `gcp_bigquery` output to CDC mode, see the xref:outputs/bigquery_cdc_migration.adoc[CDC migration guide].\n", + "name": "gcp_bigquery_write_api", "plugin": true, - "status": "beta", - "summary": "Sends messages as new rows to a Google Cloud BigQuery table.", + "status": "stable", + "summary": "Streams data into BigQuery using the Storage Write API.", "type": "output", - "version": "3.55.0" + "version": "4.90.0" }, { "categories": [ @@ -33304,7 +41001,7 @@ "description": "\nIn order to have a different path for each object you should use function interpolations described in xref:configuration:interpolation.adoc#bloblang-queries[Bloblang queries], which are calculated per message of a batch.\n\n== Metadata\n\nMetadata fields on messages will be sent as headers, in order to mutate these values (or remove them) check out the xref:configuration:metadata.adoc[metadata docs].\n\n== Credentials\n\nBy default Redpanda Connect will use a shared credentials file when connecting to GCP services. You can find out more in xref:guides:cloud/gcp.adoc[].\n\n== Batching\n\nIt's common to want to upload messages to Google Cloud Storage as batched archives, the easiest way to do this is to batch your messages at the output level and join the batch of messages with an xref:components:processors/archive.adoc[`archive`] and/or xref:components:processors/compress.adoc[`compress`] processor.\n\nFor example, if we wished to upload messages as a .tar.gz archive of documents we could achieve that with the following config:\n\n```yaml\noutput:\n gcp_cloud_storage:\n bucket: TODO\n path: ${!counter()}-${!timestamp_unix_nano()}.tar.gz\n batching:\n count: 100\n period: 10s\n processors:\n - archive:\n format: tar\n - compress:\n algorithm: gzip\n```\n\nAlternatively, if we wished to upload JSON documents as a single large document containing an array of objects we can do that with:\n\n```yaml\noutput:\n gcp_cloud_storage:\n bucket: TODO\n path: ${!counter()}-${!timestamp_unix_nano()}.json\n batching:\n count: 100\n processors:\n - archive:\n format: json_array\n```\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "name": "gcp_cloud_storage", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Sends message parts as objects to a Google Cloud Storage bucket. Each object is uploaded with the path specified with the `path` field.", "type": "output", "version": "3.43.0" @@ -33566,154 +41263,6 @@ "summary": "Sends messages to a GCP Cloud Pub/Sub topic. xref:configuration:metadata.adoc[Metadata] from messages are sent as attributes.", "type": "output" }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A list of target host addresses to connect to.", - "examples": [ - "localhost:9000" - ], - "kind": "array", - "name": "hosts", - "type": "string" - }, - { - "default": "", - "description": "A user ID to connect as.", - "kind": "scalar", - "name": "user", - "type": "string" - }, - { - "description": "A directory to store message files within. If the directory does not exist it will be created.", - "interpolated": true, - "kind": "scalar", - "name": "directory", - "type": "string" - }, - { - "default": "${!counter()}-${!timestamp_unix_nano()}.txt", - "description": "The path to upload messages as, interpolation functions should be used in order to generate unique file paths.", - "interpolated": true, - "kind": "scalar", - "name": "path", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, - { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Each file is written with the path specified with the 'path' field, in order to have a different path for each object you should use function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "hdfs", - "plugin": true, - "status": "stable", - "summary": "Sends message parts as files to a HDFS directory.", - "type": "output" - }, { "categories": [ "Network" @@ -34473,108 +42022,791 @@ }, { "categories": [ - "Network" + "Services" ], "config": { "children": [ { - "default": "", - "description": "An alternative address to host from. If left empty the service wide address is used.", + "children": [ + { + "description": "The REST catalog endpoint URL.", + "examples": [ + "http://localhost:8181/api/catalog", + "https://polaris.example.com/api/catalog", + "https://glue.us-east-1.amazonaws.com/iceberg" + ], + "kind": "scalar", + "name": "url", + "type": "string" + }, + { + "description": "The REST catalog warehouse.", + "examples": [ + "redpanda-catalog" + ], + "is_optional": true, + "kind": "scalar", + "name": "warehouse", + "type": "string" + }, + { + "children": [ + { + "children": [ + { + "default": "/v1/oauth/tokens", + "description": "OAuth2 token endpoint URI.", + "kind": "scalar", + "name": "server_uri", + "type": "string" + }, + { + "description": "OAuth2 client identifier.", + "kind": "scalar", + "name": "client_id", + "type": "string" + }, + { + "description": "OAuth2 client secret.", + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "OAuth2 scope to request.", + "is_optional": true, + "kind": "scalar", + "name": "scope", + "type": "string" + } + ], + "description": "OAuth2 authentication configuration.", + "is_optional": true, + "kind": "scalar", + "name": "oauth2", + "type": "object" + }, + { + "description": "Static bearer token for authentication. For testing only, not recommended for production.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "bearer", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "children": [ + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Allows you to specify a custom endpoint for the AWS API.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" + }, + { + "description": "AWS service name for SigV4 signing.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "service", + "type": "string" + } + ], + "description": "AWS SigV4 authentication (for AWS Glue Data Catalog or API Gateway).", + "is_optional": true, + "kind": "scalar", + "name": "aws_sigv4", + "type": "object" + } + ], + "description": "Authentication configuration for the REST catalog. Only one authentication method can be active at a time.", + "is_optional": true, + "kind": "scalar", + "name": "auth", + "type": "object" + }, + { + "description": "Custom HTTP headers to include in all requests to the catalog.", + "examples": [ + { + "X-Api-Key": "your-api-key" + } + ], + "is_advanced": true, + "is_optional": true, + "kind": "map", + "name": "headers", + "type": "string" + }, + { + "default": false, + "description": "Skip TLS certificate verification. Not recommended for production.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_skip_verify", + "type": "bool" + } + ], + "description": "REST catalog configuration.", "kind": "scalar", - "name": "address", - "type": "string" + "name": "catalog", + "type": "object" }, { - "default": "/get", - "description": "The path from which discrete messages can be consumed.", + "description": "The Iceberg namespace for the table, dot delimiters are split as nested namespaces.", + "examples": [ + "analytics.events", + "production" + ], + "interpolated": true, "kind": "scalar", - "name": "path", + "name": "namespace", "type": "string" }, { - "default": "/get/stream", - "description": "The path from which a continuous stream of messages can be consumed.", + "description": "The Iceberg table name. Supports interpolation functions for dynamic table names.", + "examples": [ + "user_events", + "events_${!meta(\"topic\")}" + ], + "interpolated": true, "kind": "scalar", - "name": "stream_path", + "name": "table", "type": "string" }, { - "default": "/get/ws", - "description": "The path from which websocket connections can be established.", + "default": true, + "description": "Controls how message field names are matched against table column names, and how column references in the partition spec are resolved. When `true` (the default), names must match exactly. When `false`, matching is case-insensitive — set this when your downstream catalog or query engine treats column names as case-insensitive (the iceberg specification's recommended convention) so that, for example, a message keyed `\"COLUMN\"` lands in an existing `column` rather than triggering schema evolution. Ambiguous case-only duplicates in the input are rejected.", + "is_advanced": true, "kind": "scalar", - "name": "ws_path", - "type": "string" + "name": "case_sensitive_columns", + "type": "bool" }, { - "default": [ - "GET" + "default": "insert", + "description": "The row-level operation to apply for each message: `insert` (append), `upsert` (replace rows matching `identifier_fields`, then append), or `delete` (remove rows matching `identifier_fields`). Supports interpolation so the operation can be driven by the data — e.g. a change-data-capture stream's operation field. Defaults to `insert`, preserving the original append-only behaviour.\n\nSee the <> section above for the full semantics, the format-version-2 upgrade, batching behaviour, and important caveats.", + "examples": [ + "insert", + "${! metadata(\"op\") }", + "${! this.op == \"d\" ? \"delete\" : \"upsert\" }" ], - "description": "An array of verbs that are allowed for the `path` and `stream_path` HTTP endpoint.", - "kind": "array", - "name": "allowed_verbs", - "type": "string" - }, - { - "default": "5s", - "description": "The maximum time to wait before a blocking, inactive connection is dropped (only applies to the `path` endpoint).", + "interpolated": true, "is_advanced": true, "kind": "scalar", - "name": "timeout", + "name": "row_operation", "type": "string" }, { - "default": "", - "description": "Enable TLS by specifying a certificate and key file. Only valid with a custom `address`.", + "default": [], + "description": "The columns forming the row identity (the Iceberg identifier fields / equality-delete key) used by `upsert` and `delete`. Required when `row_operation` can evaluate to `upsert` or `delete`, and must reference existing table columns of a primitive, non-floating-point type.\n\nSee the <> section above for the full constraints, including the temporal-type and partitioning rules and when the requirement is enforced.", + "examples": [ + [ + "id" + ], + [ + "tenant_id", + "user_id" + ] + ], "is_advanced": true, - "kind": "scalar", - "name": "cert_file", + "kind": "array", + "name": "identifier_fields", "type": "string" }, { - "default": "", - "description": "Enable TLS by specifying a certificate and key file. Only valid with a custom `address`.", - "is_advanced": true, + "children": [ + { + "children": [ + { + "description": "The S3 bucket name.", + "examples": [ + "my-iceberg-data" + ], + "kind": "scalar", + "name": "bucket", + "type": "string" + }, + { + "description": "The AWS region.", + "examples": [ + "us-west-2" + ], + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Custom endpoint for S3-compatible storage (e.g., MinIO).", + "examples": [ + "http://localhost:9000" + ], + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "default": false, + "description": "Forces the client API to use path style URLs, which is often required when connecting to custom endpoints.", + "is_advanced": true, + "kind": "scalar", + "name": "force_path_style_urls", + "type": "bool" + }, + { + "children": [ + { + "description": "The AWS access key ID.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The AWS secret access key.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The AWS session token, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + } + ], + "description": "Static AWS credentials for S3 access. When not specified, credentials are loaded from the default AWS credential chain.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" + } + ], + "description": "S3 storage configuration.", + "is_optional": true, + "kind": "scalar", + "name": "aws_s3", + "type": "object" + }, + { + "children": [ + { + "description": "The GCS bucket name.", + "examples": [ + "my-iceberg-data" + ], + "kind": "scalar", + "name": "bucket", + "type": "string" + }, + { + "description": "Custom endpoint for GCS-compatible storage.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "description": "The type of credentials to use. Valid values: `service_account`, `authorized_user`, `impersonated_service_account`, `external_account`.", + "examples": [ + "service_account" + ], + "is_optional": true, + "kind": "scalar", + "name": "credentials_type", + "type": "string" + }, + { + "description": "Path to a GCP credentials JSON file.", + "is_optional": true, + "kind": "scalar", + "name": "credentials_file", + "type": "string" + }, + { + "description": "GCP credentials JSON content. Use this or `credentials_file`, not both.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "credentials_json", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Google Cloud Storage configuration.", + "is_optional": true, + "kind": "scalar", + "name": "gcp_cloud_storage", + "type": "object" + }, + { + "children": [ + { + "description": "The Azure storage account name.", + "examples": [ + "mystorageaccount" + ], + "kind": "scalar", + "name": "storage_account", + "type": "string" + }, + { + "description": "The Azure blob container name.", + "examples": [ + "iceberg-data" + ], + "kind": "scalar", + "name": "container", + "type": "string" + }, + { + "description": "Custom endpoint for Azure-compatible storage.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "description": "SAS token for authentication. Prefix with the container name followed by a dot if container-specific.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "storage_sas_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "Azure storage connection string. Use this or other auth methods, not both.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "storage_connection_string", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "Azure storage access key for shared key authentication.", + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "storage_access_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Azure Blob Storage (ADLS Gen2) configuration.", + "is_optional": true, + "kind": "scalar", + "name": "azure_blob_storage", + "type": "object" + } + ], + "description": "Storage backend configuration for data files. Exactly one of `aws_s3`, `gcp_cloud_storage`, or `azure_blob_storage` must be specified.", "kind": "scalar", - "name": "key_file", - "type": "string" + "name": "storage", + "type": "object" }, { "children": [ { "default": false, - "description": "Whether to allow CORS requests.", + "description": "Enable automatic schema evolution. When enabled, new columns will be automatically added to the table.", "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, { - "default": [], - "description": "An explicit list of origins that are allowed for CORS requests.", + "default": "()", + "description": "A bloblang expression to evaluate when a new table is created to determine the table's partition spec. The result of the mapping should be an iceberg partition spec in the same string format as the https://docs.redpanda.com/current/manage/iceberg/about-iceberg-topics/#use-custom-partitioning[^Redpanda Streaming Topic Property]", + "examples": [ + "(col1)", + "(nested.col)", + "(year(my_ts_col))", + "(year(my_ts_col), col2)", + "(hour(my_ts_col), truncate(42, col2))", + "(day(my_ts_col), bucket(4, nested.col))", + "(day(my_ts_col), void(`non.nested column.with.dots`), identity(nested.column))" + ], + "interpolated": true, "is_advanced": true, - "kind": "array", - "name": "allowed_origins", + "kind": "scalar", + "name": "partition_spec", + "type": "string" + }, + { + "description": "A prefix used as the location for new tables when the catalog does not automatically assign one. For example, AWS Glue requires explicit table locations. When set, table locations are derived as `{prefix}{namespace}/{table}`.", + "examples": [ + "s3://my-iceberg-bucket/" + ], + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "table_location", + "type": "string" + }, + { + "default": "", + "description": "The name of a message metadata field containing a schema definition. When set, the schema is used to determine column types during schema evolution and table creation instead of inferring types from values. The schema must be in the standard common schema format (the same format used by the `parquet_encode` processor's `schema_metadata` field). For batches of messages, the first message's schema is used. Record presence drives schema shape: fields declared in the schema metadata that are absent from the record are not added to the table, while the metadata controls column ordering, naming, and types for fields that are present. In case-insensitive mode, top-level column names use the metadata's casing — record keys are matched by case-folding and the metadata's name is what lands in the table.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "schema_metadata", + "type": "string" + }, + { + "bloblang": true, + "description": "An optional Bloblang mapping to customize column types during schema evolution. This mapping is executed for each new column and can override the inferred or schema-metadata-derived type. The mapping receives an object with fields `name` (column name), `path` (dot-separated path), `value` (sample value), `inferred_type` (the type that would be used without this mapping), `message` (the full message body), `namespace`, and `table`. It must return a string with a valid Iceberg type name: `boolean`, `int`, `long`, `float`, `double`, `string`, `binary`, `date`, `time`, `timestamp`, `timestamptz`, `uuid`, `decimal(p,s)`, or `fixed[n]`.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "new_column_type_mapping", "type": "string" + }, + { + "default": false, + "description": "When `true`, writing a numeric value into a `timestamp`, `timestamptz`, `date`, or `time` column without `schema_metadata` registered for that column is a hard error. The default `false` permits a fallback path that interprets bare numeric timestamps as Unix seconds and bare numeric times as already-microseconds — convenient, but silently wrong if upstream produced milliseconds. Enable this when you cannot guarantee the upstream attaches schema metadata and want to fail loudly rather than corrupt dates by ~50,000 years. No effect on time-typed columns receiving `time.Time`/`time.Duration` Go values, which carry their own unit unambiguously, and no effect on non-time columns. Requires `schema_metadata` to be set.", + "is_advanced": true, + "kind": "scalar", + "name": "require_schema_metadata", + "type": "bool" } ], - "description": "Adds Cross-Origin Resource Sharing headers. Only valid with a custom `address`.", + "description": "Schema evolution configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "cors", - "type": "object", - "version": "3.63.0" + "name": "schema_evolution", + "type": "object" + }, + { + "children": [ + { + "default": true, + "description": "Merge small manifest files during commits to reduce metadata overhead.", + "is_advanced": true, + "kind": "scalar", + "name": "manifest_merge_enabled", + "type": "bool" + }, + { + "default": "24h", + "description": "Maximum age of snapshots to retain for time-travel queries. Set to zero to disable removing old snapshots.", + "is_advanced": true, + "kind": "scalar", + "name": "max_snapshot_age", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of times to retry a failed transaction commit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Commit behavior configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "commit", + "type": "object" + }, + { + "children": [ + { + "default": "delta_length_byte_array", + "description": "The encoding to use for string and binary columns. Use `plain` for compatibility with readers that do not support `DELTA_LENGTH_BYTE_ARRAY` encoding, such as AWS Redshift Spectrum.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"plain\": true,\n \"delta_length_byte_array\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "string_encoding", + "options": [ + "plain", + "delta_length_byte_array" + ], + "type": "string" + } + ], + "description": "Parquet writer configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "parquet", + "type": "object" + }, + { + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", + "type": "object" + }, + { + "default": 4, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" } ], "kind": "scalar", + "linter": "root = if this.row_operation.or(\"insert\") != \"insert\" && this.max_in_flight.or(4) > 1 {\n [ \"row_operation can resolve to upsert/delete, which rely on per-key write ordering; with max_in_flight > 1 concurrent batches may commit out of order and corrupt the last-writer-wins result. Set max_in_flight: 1 for keyed (change-data-capture) workloads, or use a static row_operation: insert for append-only writes.\" ]\n}", "name": "", "type": "object" }, - "description": "Sets up an HTTP server that will send messages over HTTP(S) GET requests. If the `address` config field is left blank the xref:components:http/about.adoc[service-wide HTTP server] will be used.\n\nThree endpoints will be registered at the paths specified by the fields `path`, `stream_path` and `ws_path`. Which allow you to consume a single message batch, a continuous stream of line delimited messages, or a websocket of messages for each request respectively.\n\nWhen messages are batched the `path` endpoint encodes the batch according to https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html[RFC1341^]. This behavior can be overridden by xref:configuration:batching.adoc#post-batch-processing[archiving your batches].\n\nPlease note, messages are considered delivered as soon as the data is written to the client. There is no concept of at least once delivery on this output.\n\n\n[CAUTION]\n.Endpoint caveats\n====\nComponents within a Redpanda Connect config will register their respective endpoints in a non-deterministic order. This means that establishing precedence of endpoints that are registered via multiple `http_server` inputs or outputs (either within brokers or from cohabiting streams) is not possible in a predictable way.\n\nThis ambiguity makes it difficult to ensure that paths which are both a subset of a path registered by a separate component, and end in a slash (`/`) and will therefore match against all extensions of that path, do not prevent the more specific path from matching against requests.\n\nIt is therefore recommended that you ensure paths of separate components do not collide unless they are explicitly non-competing.\n\nFor example, if you were to deploy two separate `http_server` inputs, one with a path `/foo/` and the other with a path `/foo/bar`, it would not be possible to ensure that the path `/foo/` does not swallow requests made to `/foo/bar`.\n====\n", - "name": "http_server", + "description": "\nWrite streaming data to Apache Iceberg tables using the REST catalog API. This output supports:\n\n* Multiple storage backends (S3, GCS, Azure)\n* Automatic table creation with schema detection\n* Partition transforms (year, month, day, hour, bucket, truncate)\n* Schema evolution (automatic column addition)\n* Transaction retry logic for concurrent writes\n\nThis output is designed to work with REST catalog implementations like Apache Polaris, AWS Glue Data Catalog, and the Databricks Unity Catalog.\n\nCurrently only version 2 of the Iceberg specification is supported. Any pre-existing version 1 tables will be upgraded to version 2 automatically.\n\n=== Apache Polaris\n\nTo use with https://polaris.apache.org[Apache Polaris^]:\n\n* Set `catalog.url` to the Polaris REST endpoint (e.g., `http://localhost:8181/api/catalog`).\n* Set `catalog.warehouse` to the catalog name configured in Polaris.\n* Configure `catalog.auth.oauth2` with client credentials granted access to the catalog.\n\n=== AWS Glue Data Catalog\n\nTo use with AWS Glue Data Catalog:\n\n* Set `catalog.url` to `https://glue..amazonaws.com/iceberg` (the REST client appends the API version automatically).\n* Set `catalog.warehouse` to your AWS account ID (the Glue catalog identifier).\n* Set `schema_evolution.table_location` to an S3 prefix (e.g., `s3://my-bucket/`) since Glue does not automatically assign table locations.\n* Configure `catalog.auth.aws_sigv4` with the appropriate region and set `service` to `glue`.\n* Configure `storage.aws_s3` with the same bucket and region.\n\n=== Azure Blob Storage (ADLS Gen2)\n\nTo use with Azure Data Lake Storage Gen2:\n\n* Configure `storage.azure_blob_storage` with your storage account name and container.\n* Authenticate using one of: `storage_access_key` (shared key), `storage_sas_token`, or `storage_connection_string`.\n* The storage account must have hierarchical namespace (HNS) enabled for ADLS Gen2 compatibility.\n\n[%header,format=dsv]\n|===\nBloblang type:Iceberg type\nstring:string\nbytes:binary\nbool:boolean\nnumber:double\ntimestamp:timestamp (with timezone)\nobject:struct\narray:list\n|===\n\n\n== Row-level operations\n\nBy default this output is append-only — every message becomes a new row (`row_operation: insert`), and existing configurations are unaffected. Set `row_operation` to apply a per-message operation, with `identifier_fields` defining the row identity:\n\n* `insert` — append the row. This is an unconditional append: it is *not* keyed or de-duplicated.\n* `upsert` — replace any existing rows matching `identifier_fields`, then append this row (equivalent to Iceberg's Flink UPSERT mode).\n* `delete` — remove rows matching `identifier_fields`.\n\n`row_operation` supports interpolation, so the operation can be driven by the data itself — for example by mapping a change-data-capture stream's operation field — but no CDC-specific format is assumed (see the change-data-capture example below). It is named `row_operation` to distinguish it from Iceberg's snapshot-level operation.\n\n`upsert` and `delete` require `identifier_fields` and use Iceberg merge-on-read equality deletes, which require table format version 2. A version-1 table is automatically upgraded to version 2 on the first `upsert`/`delete`; *this upgrade is irreversible*.\n\n*Identifier fields.* `identifier_fields` must reference existing table columns of a primitive, non-floating-point type. A static `upsert`/`delete` is validated at startup; an interpolated `row_operation` is validated per message at write time, so an empty `identifier_fields` is not caught until the first `upsert`/`delete` message arrives. Identifier columns of a temporal type (`timestamp`, `timestamptz`, `date`, `time`) must arrive as time values, not bare numbers — a numeric epoch is ambiguous as a delete key and is rejected at write time; convert it to a timestamp upstream. If the table is partitioned, every partition source column must be one of the `identifier_fields`, since equality deletes are partition-scoped.\n\nWhen this output auto-creates a table (via `schema_evolution`), the `identifier_fields` columns are created as *required* and registered as the table's Iceberg identifier-field-ids, so downstream engines and other writers see the primary key. A consequence is that a null or missing value in an identifier column is rejected on write, even for `insert`. Identifier columns must therefore be present at creation — in the first message or declared via `schema_metadata`. Pre-existing tables are never modified.\n\n*Batching and ordering.* Within a single batch the last `upsert`/`delete` per `identifier_fields` key wins. Each batch containing an `upsert`/`delete` is committed as its own snapshot (these commits are never coalesced, which is required for correctness), so a high-throughput mutation workload produces one snapshot per batch. Size batches accordingly and run regular table maintenance (snapshot expiry and compaction) to keep metadata manageable. Pure `insert`-only batches keep the original append fast path, which does coalesce commits.\n\nOrdering only holds *within* a batch. With more than one batch in flight, concurrent batches can commit out of order, so a stale `upsert` may overwrite a newer one for the same key. Set `max_in_flight: 1` for keyed (change-data-capture) workloads to preserve per-key order — this is enforced by config linting whenever `row_operation` is anything other than a static `insert`.\n\n[CAUTION]\n====\n`insert` is an unconditional append and is *not* keyed or de-duplicated. For keyed data (including change-data-capture), map create/read events to `upsert`, never `insert` — mixing `insert` with `upsert`/`delete` on the same key in one batch produces duplicate rows.\n====\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "examples": [ + { + "config": "\ninput:\n redpanda:\n seed_brokers: [ localhost:9092 ]\n topics: [ dbserver.inventory.customers ]\n consumer_group: iceberg_sink\n\npipeline:\n processors:\n - mapping: |\n meta op = match this.op {\n \"d\" => \"delete\",\n _ => \"upsert\",\n }\n # Debezium puts the row image in 'after' (or 'before' for deletes).\n root = this.after | this.before\n\noutput:\n iceberg:\n catalog:\n url: http://localhost:8181/api/catalog\n namespace: inventory\n table: customers\n row_operation: ${! metadata(\"op\") }\n identifier_fields: [ id ]\n # Keyed writes must stay ordered: a single batch in flight prevents\n # concurrent batches from committing a stale update over a newer one.\n max_in_flight: 1\n storage:\n aws_s3:\n bucket: my-iceberg-data\n region: us-east-1\n", + "summary": "Materialize a change-data-capture stream into an Iceberg table. A mapping derives the row operation from the source's operation field (here Debezium's `op`: `c`reate / `r`ead / `u`pdate map to `upsert`, `d`elete maps to `delete`) and selects the row image, while `identifier_fields` is the primary key. Note that creates map to `upsert`, never `insert`, so re-delivered or snapshot rows do not duplicate.", + "title": "Change-data-capture upsert/delete" + } + ], + "name": "iceberg", "plugin": true, "status": "stable", - "summary": "Sets up an HTTP server that will send messages over HTTP(S) GET requests. HTTP 2.0 is supported when using TLS, which is enabled when key and cert files are specified.", - "type": "output" + "summary": "Write data to Apache Iceberg tables via REST catalog.", + "type": "output", + "version": "4.80.0" }, { "categories": [ @@ -35248,7 +43480,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -35401,6 +43633,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -35417,7 +43653,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -35476,9 +43712,69 @@ { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, - "is_optional": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", "name": "profile", "type": "string" @@ -35568,7 +43864,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -35591,6 +43887,66 @@ "name": "conn_idle_timeout", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "description": "A topic to write messages to.", "interpolated": true, @@ -35821,12 +44177,35 @@ }, { "default": true, - "description": "Enable the idempotent write producer option. This requires the `IDEMPOTENT_WRITE` permission on `CLUSTER` and can be disabled if this permission is not available.", + "description": "Enable the idempotent write producer option. When enabled, the producer initializes a producer ID and uses it to guarantee exactly-once semantics per partition (no duplicates on retries). This requires the `IDEMPOTENT_WRITE` permission on the `CLUSTER` resource. If your cluster does not grant this permission or uses ACLs restrictively, disable this option. Note: Idempotent writes are strictly a win for data integrity but may be unavailable in restricted environments (e.g., some managed Kafka services, Redpanda with strict ACLs). Disabling this option is safe and only affects retry behavior—duplicates may occur on producer retries, but the pipeline will continue to function normally.", "is_advanced": true, "kind": "scalar", "name": "idempotent_write", "type": "bool" }, + { + "annotated_options": [ + [ + "all", + "Wait for all in-sync replicas to acknowledge (acks=-1). Required when idempotent_write is enabled." + ], + [ + "leader", + "Wait for the leader broker to acknowledge (acks=1). Messages are lost if the leader fails before replication." + ], + [ + "none", + "Do not wait for any acknowledgement (acks=0). Highest throughput but messages may be lost." + ] + ], + "default": "all", + "description": "The number of acknowledgements the leader broker must receive from ISR brokers before responding to the produce request. When `idempotent_write` is enabled this must be set to `all`.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"all\": true,\n \"leader\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "acks", + "type": "string" + }, { "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", "is_advanced": true, @@ -35861,7 +44240,7 @@ }, { "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", + "description": "The maximum size of a produced record batch in bytes. A `MESSAGE_TOO_LARGE` error is returned if a batch exceeds this limit. This field maps to the `max.message.bytes` Kafka property. Ensure the Redpanda broker's `kafka_batch_max_bytes` property is at least as large as this value, see https://docs.redpanda.com/current/reference/properties/cluster-properties/#kafka_batch_max_bytes.", "examples": [ "100MB", "50mib" @@ -35882,17 +44261,61 @@ "kind": "scalar", "name": "broker_write_max_bytes", "type": "string" + }, + { + "default": 10000, + "description": "The maximum number of records the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered and space frees up. Increase this value for high-throughput pipelines to avoid back-pressure stalls.", + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_records", + "type": "int" + }, + { + "default": "0", + "description": "The maximum number of bytes the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered. Set to `0` to disable the byte-level limit (only `max_buffered_records` applies). This limit is checked after `max_buffered_records`.", + "examples": [ + "256MB", + "50mib" + ], + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_bytes", + "type": "string" + }, + { + "default": 1, + "description": "The maximum number of produce requests in flight per broker connection. When `idempotent_write` is enabled, this is capped at 5 by the Kafka protocol (and at 1 for Kafka < v1.0.0). When `idempotent_write` is disabled, higher values improve throughput by pipelining requests but may cause out-of-order delivery.", + "is_advanced": true, + "kind": "scalar", + "name": "max_in_flight_requests", + "type": "int" + }, + { + "default": 0, + "description": "The maximum number of times a record produce is retried on failure before the record is failed. When a record fails, all records buffered in the same partition are also failed to preserve gapless ordering. Set to `0` for unlimited retries (the default). With `idempotent_write` enabled, retries are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_retries", + "type": "int" + }, + { + "default": "0s", + "description": "The maximum time a record can sit in the producer buffer before it is failed, roughly equivalent to Kafka's `delivery.timeout.ms`. This is evaluated before writing a request or after a produce response. When a record times out, all records in the same partition are also failed. Set to `0s` for no timeout (the default). With `idempotent_write` enabled, timeouts are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_delivery_timeout", + "type": "string" } ], "kind": "scalar", - "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n}", + "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n this.idempotent_write == true && this.acks.or(\"all\") != \"all\" => \"idempotent_write requires acks to be set to all\"\n}", "name": "", "type": "object" }, "description": "\nWrites a batch of messages to Kafka brokers and waits for acknowledgement before propagating it back to the input.\n\nThis output often out-performs the traditional `kafka` output as well as providing more useful logs and error messages.\n", "name": "kafka_franz", "plugin": true, - "status": "beta", + "status": "stable", "summary": "A Kafka output using the https://github.com/twmb/franz-go[Franz Kafka client library^].", "type": "output", "version": "3.61.0" @@ -36189,7 +44612,7 @@ "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "name": "mongodb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Inserts items into a MongoDB collection.", "type": "output", "version": "3.43.0" @@ -36513,64 +44936,6 @@ "summary": "Pushes messages to an MQTT broker.", "type": "output" }, - { - "categories": [ - "Network" - ], - "config": { - "children": [ - { - "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", - "kind": "array", - "name": "urls", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": false, - "description": "Whether the URLs listed should be bind (otherwise they are connected to).", - "kind": "scalar", - "name": "bind", - "type": "bool" - }, - { - "default": "PUSH", - "description": "The socket type to send with.", - "kind": "scalar", - "linter": "\nlet options = {\n \"push\": true,\n \"pub\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "socket_type", - "options": [ - "PUSH", - "PUB" - ], - "type": "string" - }, - { - "default": "5s", - "description": "The maximum period of time to wait for a message to send before the request is abandoned and reattempted.", - "kind": "scalar", - "name": "poll_timeout", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Currently only PUSH and PUB sockets are supported.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "nanomsg", - "plugin": true, - "status": "stable", - "summary": "Send messages over a Nanomsg socket.", - "type": "output" - }, { "categories": [ "Services" @@ -36868,6 +45233,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -36895,7 +45288,7 @@ "name": "", "type": "object" }, - "description": "This output will interpolate functions within the subject field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "This output will interpolate functions within the subject field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats", "plugin": true, "status": "stable", @@ -37202,6 +45595,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -37229,7 +45650,7 @@ "name": "", "type": "object" }, - "description": "== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_jetstream", "plugin": true, "status": "stable", @@ -37488,6 +45909,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -37501,10 +45950,10 @@ "name": "", "type": "object" }, - "description": "\nThe field `key` supports\nxref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing\nyou to create a unique key for each message.\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\nThe field `key` supports\nxref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing\nyou to create a unique key for each message.\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_kv", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Put messages in a NATS key-value bucket.", "type": "output", "version": "4.12.0" @@ -37519,10 +45968,7 @@ "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", "examples": [ [ - "nats://127.0.0.1:4222" - ], - [ - "nats://username:password@127.0.0.1:4222" + "http://localhost:9200" ] ], "kind": "array", @@ -37530,38 +45976,46 @@ "type": "string" }, { - "description": "The maximum number of times to attempt to reconnect to the server. If negative, it will never stop trying to reconnect.", - "is_advanced": true, - "is_optional": true, + "description": "The index to place messages.", + "interpolated": true, "kind": "scalar", - "name": "max_reconnects", - "type": "int" + "name": "index", + "type": "string" }, { - "description": "The cluster ID to publish to.", + "description": "The action to take on the document. This field must resolve to one of the following action types: `index`, `update` or `delete`.", + "interpolated": true, "kind": "scalar", - "name": "cluster_id", + "name": "action", "type": "string" }, { - "description": "The subject to publish to.", + "description": "The ID for indexed messages. Interpolation should be used in order to create a unique ID for each message.", + "examples": [ + "${!counter()}-${!timestamp_unix()}" + ], + "interpolated": true, "kind": "scalar", - "name": "subject", + "name": "id", "type": "string" }, { "default": "", - "description": "The client ID to connect with.", + "description": "An optional pipeline id to preprocess incoming documents.", + "interpolated": true, + "is_advanced": true, "kind": "scalar", - "name": "client_id", + "name": "pipeline", "type": "string" }, { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "default": "", + "description": "The routing key to use for the document.", + "interpolated": true, + "is_advanced": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "routing", + "type": "string" }, { "children": [ @@ -37694,102 +46148,319 @@ "type": "object" }, { - "default": false, - "description": "Perform a TLS handshake before sending the INFO protocol message.", + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls_handshake_first", - "type": "bool" + "name": "basic_auth", + "type": "object" }, { "children": [ { - "description": "An optional file containing a NKey seed.", + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", "examples": [ - "./seed.nk" + "1s", + "1m", + "500ms" ], - "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "nkey_file", + "name": "period", "type": "string" }, { - "description": "The NKey seed.", + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", "examples": [ - "UDXU4RCSJNZOIQHZNWXHXORDPRTGNJAHAHFRGZNEEJCPQTT2M7NLCNF4" + "this.type == \"end_of_transaction\"" ], - "is_advanced": true, - "is_optional": true, - "is_secret": true, "kind": "scalar", - "name": "nkey", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string", - "version": "4.38.0" + "name": "check", + "type": "string" }, { - "description": "An optional file containing user credentials which consist of an user JWT and corresponding NKey seed.", + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", "examples": [ - "./user.creds" + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] ], "is_advanced": true, "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to connect to Amazon Elastic Service.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "user_credentials_file", + "name": "region", "type": "string" }, { - "description": "An optional plain text user JWT (given along with the corresponding user NKey Seed).", + "description": "Allows you to specify a custom endpoint for the AWS API.", "is_advanced": true, "is_optional": true, - "is_secret": true, "kind": "scalar", - "name": "user_jwt", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "endpoint", "type": "string" }, { - "description": "An optional plain text user NKey Seed (given along with the corresponding user JWT).", + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", "is_advanced": true, "is_optional": true, - "is_secret": true, "kind": "scalar", - "name": "user_nkey_seed", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" } ], - "description": "Optional configuration of NATS authentication parameters.", + "description": "Enables and customises connectivity to Amazon Elastic Service.", "is_advanced": true, "kind": "scalar", - "name": "auth", + "name": "aws", "type": "object" - }, - { - "bloblang": true, - "description": "EXPERIMENTAL: A xref:guides:bloblang/about.adoc[Bloblang mapping] used to inject an object containing tracing propagation information into outbound messages. The specification of the injected fields will match the format used by the service wide tracer.", - "examples": [ - "meta = @.merge(this)", - "root.meta.span = this" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "inject_tracing_map", - "type": "string", - "version": "4.23.0" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\n[CAUTION]\n.Deprecation notice\n====\nThe NATS Streaming Server is being deprecated. Critical bug fixes and security fixes will be applied until June of 2023. NATS-enabled applications requiring persistence should use https://docs.nats.io/nats-concepts/jetstream[JetStream^].\n====\n\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "nats_stream", + "description": "\nBoth the `id` and `index` fields can be dynamically set using function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here]. When sending batched messages these interpolations are performed per message part.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "examples": [ + { + "config": "\noutput:\n processors:\n - mapping: |\n meta id = this.id\n root.doc = this\n opensearch:\n urls: [ TODO ]\n index: foo\n id: ${! @id }\n action: update\n", + "summary": "When https://opensearch.org/docs/latest/api-reference/document-apis/update-document/[updating documents^] the request body should contain a combination of a `doc`, `upsert`, and/or `script` fields at the top level, this should be done via mapping processors.", + "title": "Updating Documents" + } + ], + "name": "opensearch", "plugin": true, "status": "stable", - "summary": "Publish to a NATS Stream subject.", + "summary": "Publishes messages into an Elasticsearch index. If the index does not exist then it is created with a dynamic mapping.", "type": "output" }, { @@ -37799,30 +46470,52 @@ "config": { "children": [ { - "description": "The address of the target NSQD server.", + "description": "The gRPC endpoint of the remote OTLP collector.", "kind": "scalar", - "name": "nsqd_tcp_address", + "name": "endpoint", "type": "string" }, { - "description": "The topic to publish to.", + "default": {}, + "description": "A map of headers to add to the gRPC request metadata.", + "examples": [ + { + "X-Custom-Header": "value", + "traceparent": "${! tracing_span().traceparent }" + } + ], "interpolated": true, + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "string" + }, + { + "default": "30s", + "description": "Timeout for gRPC requests.", + "is_advanced": true, "kind": "scalar", - "name": "topic", + "name": "timeout", "type": "string" }, { - "description": "A user agent to assume when connecting.", - "is_optional": true, + "default": "gzip", + "description": "Compression type for gRPC requests. Options: 'gzip' or 'none'.", + "is_advanced": true, "kind": "scalar", - "name": "user_agent", + "linter": "\nlet options = {\n \"gzip\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "compression", + "options": [ + "gzip", + "none" + ], "type": "string" }, { "children": [ { "default": false, - "description": "Whether custom TLS settings are enabled.", + "description": "Enable TLS connections.", "is_advanced": true, "kind": "scalar", "name": "enabled", @@ -37830,122 +46523,167 @@ }, { "default": false, - "description": "Whether to skip server side certificate verification.", + "description": "Skip certificate verification (insecure).", "is_advanced": true, "kind": "scalar", "name": "skip_cert_verify", "type": "bool" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "default": "", + "description": "Path to the TLS certificate file for client authentication.", "is_advanced": true, "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "cert_file", + "type": "string" }, { "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], + "description": "Path to the TLS key file for client authentication.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "key_file", "type": "string" - }, + } + ], + "description": "TLS configuration for gRPC client.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", + "name": "idle", "type": "string" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "interval", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "count", + "type": "int" } ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 2 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "client_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the client key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The URL of the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "token_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "description": "A list of optional requested permissions.", + "is_advanced": true, + "kind": "array", + "name": "scopes", + "type": "string" + }, + { + "default": {}, + "description": "A list of optional endpoint parameters, values should be arrays of strings.", "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] + { + "audience": [ + "https://example.com" + ], + "resource": [ + "https://api.example.com" + ] + } ], "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" + "is_optional": true, + "kind": "map", + "linter": "\nroot = if this.type() == \"object\" {\n this.values().map_each(ele -> if ele.type() != \"array\" {\n \"field must be an object containing arrays of strings, got %s (%v)\".format(ele.format_json(no_indent: true), ele.type())\n } else {\n ele.map_each(str -> if str.type() != \"string\" {\n \"field values must be strings, got %s (%v)\".format(str.format_json(no_indent: true), str.type())\n } else { deleted() })\n }).\n flatten()\n}\n", + "name": "endpoint_params", + "type": "unknown" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "Allows you to specify open authentication via OAuth version 2 using the client credentials token flow.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "oauth2", "type": "object" }, { @@ -37960,12 +46698,13 @@ "name": "", "type": "object" }, - "description": "The `topic` field can be dynamically set using function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here]. When sending batched messages these interpolations are performed per message part.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "nsq", + "description": "\nSends OpenTelemetry telemetry data to a remote collector via OTLP/gRPC protocol.\n\nAccepts batches of Redpanda OTEL v1 protobuf messages (spans, log records, or metrics) and converts them to OTLP format for transmission to OpenTelemetry collectors.\n\n## Input Format\n\nExpects messages in Redpanda OTEL v1 protobuf format with metadata:\n- `signal_type`: \"trace\", \"log\", or \"metric\"\n\nEach batch must contain messages of the same signal type.\nThe entire batch is converted to a single OTLP export request and sent via gRPC.\n\n## Authentication\n\nSupports multiple authentication methods:\n- Bearer token authentication (via auth_token field)\n- OAuth v2 (via oauth2 configuration block)\n\nNote: OAuth2 requires TLS to be enabled.\n", + "name": "otlp_grpc", "plugin": true, "status": "stable", - "summary": "Publish to an NSQ topic.", - "type": "output" + "summary": "Send OpenTelemetry traces, logs, and metrics via OTLP/gRPC protocol.", + "type": "output", + "version": "4.78.0" }, { "categories": [ @@ -37974,495 +46713,577 @@ "config": { "children": [ { - "children": [ + "description": "The HTTP endpoint of the remote OTLP collector (without the signal path).", + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "default": "protobuf", + "description": "Content type for HTTP requests. Options: 'protobuf' or 'json'.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"protobuf\": true,\n \"json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "content_type", + "options": [ + "protobuf", + "json" + ], + "type": "string" + }, + { + "default": {}, + "description": "A map of headers to add to the request.", + "examples": [ { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", - "examples": [ - [ - "localhost:9092" - ], - [ - "foo:9092", - "bar:9092" - ], - [ - "foo:9092,bar:9092" - ] - ], - "is_optional": true, - "kind": "array", - "name": "seed_brokers", + "X-Custom-Header": "value", + "traceparent": "${! tracing_span().traceparent }" + } + ], + "interpolated": true, + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "string" + }, + { + "default": "30s", + "description": "Timeout for HTTP requests.", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", + "type": "string" + }, + { + "default": "", + "description": "An optional HTTP proxy URL.", + "is_advanced": true, + "kind": "scalar", + "name": "proxy_url", + "type": "string" + }, + { + "default": false, + "description": "Transparently follow redirects, i.e. responses with 300-399 status codes. If disabled, the response message will contain the body, status, and headers from the redirect response and the processor will not make a request to the URL set in the Location header of the response.", + "is_advanced": true, + "kind": "scalar", + "name": "follow_redirects", + "type": "bool" + }, + { + "default": false, + "description": "Whether or not to disable HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_http2", + "type": "bool" + }, + { + "children": [ + { + "default": false, + "description": "Enable TLS connections.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Skip certificate verification (insecure).", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": "", + "description": "Path to the TLS certificate file for client authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "Path to the TLS key file for client authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + } + ], + "description": "TLS configuration for HTTP client.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "idle", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "interval", "type": "string" }, { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" + "kind": "scalar", + "name": "count", + "type": "int" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "TCP keep-alive probe configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "keep_alive", "type": "object" }, { - "default": 10, - "description": "The maximum number of batches to be sending in parallel at any given time.", + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" }, { - "annotated_options": [ - [ - "least_backup", - "Chooses the least backed up partition (the partition with the fewest amount of buffered records). Partitions are selected per batch." - ], - [ - "manual", - "Manually select a partition for each message, requires the field `partition` to be specified." - ], - [ - "murmur2_hash", - "Kafka's default hash algorithm that uses a 32-bit murmur2 hash of the key to compute which partition the record will be on." - ], - [ - "round_robin", - "Round-robin's messages through all available partitions. This algorithm has lower throughput and causes higher CPU load on brokers, but can be useful if you want to ensure an even distribution of records to partitions." - ] - ], - "description": "Override the default murmur2 hashing partitioner.", + "default": "", + "description": "A value used to identify the client to the service provider.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"least_backup\": true,\n \"manual\": true,\n \"murmur2_hash\": true,\n \"round_robin\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "partitioner", + "name": "consumer_key", "type": "string" }, { - "default": true, - "description": "Enable the idempotent write producer option. This requires the `IDEMPOTENT_WRITE` permission on `CLUSTER` and can be disabled if this permission is not available.", + "default": "", + "description": "A secret used to establish ownership of the consumer key.", "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "idempotent_write", - "type": "bool" + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" }, { - "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"lz4\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"none\": true,\n \"zstd\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "compression", - "options": [ - "lz4", - "snappy", - "gzip", - "none", - "zstd" - ], + "name": "access_token", "type": "string" }, { - "default": true, - "description": "Enables topics to be auto created if they do not exist when fetching their metadata.", + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "oauth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", "is_advanced": true, "kind": "scalar", - "name": "allow_auto_topic_creation", + "name": "enabled", "type": "bool" }, { - "default": "10s", - "description": "The maximum period of time to wait for message sends before abandoning the request and retrying", + "default": "", + "description": "A username to authenticate as.", "is_advanced": true, "kind": "scalar", - "name": "timeout", + "name": "username", "type": "string" }, { - "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", - "examples": [ - "100MB", - "50mib" - ], + "default": "", + "description": "A password to authenticate with.", "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "max_message_bytes", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" }, { - "default": "100MiB", - "description": "The upper bound for the number of bytes written to a broker connection in a single write. This field corresponds to Kafka's `socket.request.max.bytes`.", - "examples": [ - "128MB", - "50mib" - ], + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", "is_advanced": true, "kind": "scalar", - "name": "broker_write_max_bytes", + "name": "private_key_file", "type": "string" }, { - "description": "A topic to write messages to.", - "interpolated": true, + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, "kind": "scalar", - "name": "topic", + "name": "signing_method", "type": "string" }, { - "description": "An optional key to populate for each message.", - "interpolated": true, - "is_optional": true, + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 2 in requests.", + "is_advanced": true, "kind": "scalar", - "name": "key", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", - "examples": [ - "${! meta(\"partition\") }" - ], - "interpolated": true, - "is_optional": true, + "default": "", + "description": "A value used to identify the client to the token provider.", + "is_advanced": true, "kind": "scalar", - "name": "partition", + "name": "client_key", "type": "string" }, { - "children": [ - { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "kind": "array", - "name": "include_prefixes", - "type": "string" - }, - { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", - "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] - ], - "kind": "array", - "name": "include_patterns", - "type": "string" - } - ], - "description": "Determine which (if any) metadata values should be added to messages as headers.", - "is_optional": true, + "default": "", + "description": "A secret used to establish ownership of the client key.", + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "metadata", - "type": "object" + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" }, { - "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", - "examples": [ - "${! timestamp_unix() }", - "${! metadata(\"kafka_timestamp_unix\") }" - ], - "interpolated": true, + "default": "", + "description": "The URL of the token provider.", "is_advanced": true, - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "timestamp", + "name": "token_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": [], + "description": "A list of optional requested permissions.", + "is_advanced": true, + "kind": "array", + "name": "scopes", "type": "string" }, { - "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", + "default": {}, + "description": "A list of optional endpoint parameters, values should be arrays of strings.", "examples": [ - "${! timestamp_unix_milli() }", - "${! metadata(\"kafka_timestamp_ms\") }" + { + "audience": [ + "https://example.com" + ], + "resource": [ + "https://api.example.com" + ] + } ], - "interpolated": true, "is_advanced": true, "is_optional": true, - "kind": "scalar", - "name": "timestamp_ms", - "type": "string" + "kind": "map", + "linter": "\nroot = if this.type() == \"object\" {\n this.values().map_each(ele -> if ele.type() != \"array\" {\n \"field must be an object containing arrays of strings, got %s (%v)\".format(ele.format_json(no_indent: true), ele.type())\n } else {\n ele.map_each(str -> if str.type() != \"string\" {\n \"field values must be strings, got %s (%v)\".format(str.format_json(no_indent: true), str.type())\n } else { deleted() })\n }).\n flatten()\n}\n", + "name": "endpoint_params", + "type": "unknown" } ], + "description": "Allows you to specify open authentication via OAuth version 2 using the client credentials token flow.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "kafka", + "name": "oauth2", "type": "object" }, { - "default": false, + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", "kind": "scalar", - "name": "disable_content_encryption", - "type": "bool" + "name": "max_in_flight", + "type": "int" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nSends OpenTelemetry telemetry data to a remote collector via OTLP/HTTP protocol.\n\nAccepts batches of Redpanda OTEL v1 protobuf messages (spans, log records, or metrics) and converts them to OTLP format for transmission to OpenTelemetry collectors.\n\n## Input Format\n\nExpects messages in Redpanda OTEL v1 protobuf format with metadata:\n- `signal_type`: \"trace\", \"log\", or \"metric\"\n\nEach batch must contain messages of the same signal type. The entire batch is converted to a single OTLP export request and sent via HTTP POST.\n\n## Endpoints\n\nThe output automatically appends the signal type path to the base endpoint:\n- Traces: `{endpoint}/v1/traces`\n- Logs: `{endpoint}/v1/logs`\n- Metrics: `{endpoint}/v1/metrics`\n\n## Content Types\n\nSupports two content types:\n- `protobuf` (default): `application/x-protobuf`\n- `json`: `application/json`\n\n## Authentication\n\nSupports multiple authentication methods:\n- Basic authentication\n- OAuth v1\n- OAuth v2\n- JWT\n", + "name": "otlp_http", + "plugin": true, + "status": "stable", + "summary": "Send OpenTelemetry traces, logs, and metrics via OTLP/HTTP protocol.", + "type": "output", + "version": "4.78.0" + }, + { + "categories": [ + "AI" + ], + "config": { + "children": [ + { + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" }, { - "is_optional": true, + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "examples": [ + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } + ], + "kind": "", + "name": "batching", + "type": "object" + }, + { + "description": "The host for the Pinecone index.", "kind": "scalar", - "name": "enrollment_ticket", + "linter": "root = if this.has_prefix(\"https://\") { [\"host field must be a FQDN not a URL (remove the https:// prefix)\"] }", + "name": "host", "type": "string" }, { - "is_optional": true, + "description": "The Pinecone api key.", + "is_secret": true, "kind": "scalar", - "name": "identity_name", + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": "self", - "is_optional": true, + "default": "upsert-vectors", + "description": "The operation to perform against the Pinecone index.", "kind": "scalar", - "name": "allow", + "linter": "\nlet options = {\n \"update-vector\": true,\n \"upsert-vectors\": true,\n \"delete-vectors\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "operation", + "options": [ + "update-vector", + "upsert-vectors", + "delete-vectors" + ], "type": "string" }, { - "default": "self", + "default": "", + "description": "The namespace to write to - writes to the default namespace by default.", + "interpolated": true, + "is_advanced": true, "kind": "scalar", - "name": "route_to_kafka_outlet", + "name": "namespace", "type": "string" }, { - "default": "self", + "description": "The ID for the index entry in Pinecone.", + "interpolated": true, "kind": "scalar", - "name": "allow_consumer", + "name": "id", "type": "string" }, { - "default": "/ip4/127.0.0.1/tcp/6262", + "bloblang": true, + "description": "The mapping to extract out the vector from the document. The result must be a floating point array. Required if not a delete operation.", + "examples": [ + "root = this.embeddings_vector", + "root = [1.2, 0.5, 0.76]" + ], + "is_optional": true, "kind": "scalar", - "name": "route_to_consumer", + "name": "vector_mapping", "type": "string" }, { - "default": [], - "description": "The fields to encrypt in the kafka messages, assuming the record is a valid JSON map. By default, the whole record is encrypted.", - "kind": "array", - "name": "encrypted_fields", + "bloblang": true, + "description": "An optional mapping of message to metadata in the Pinecone index entry.", + "examples": [ + "root = @", + "root = metadata()", + "root = {\"summary\": this.summary, \"foo\": this.other_field}" + ], + "is_optional": true, + "kind": "scalar", + "name": "metadata_mapping", "type": "string" } ], @@ -38470,69 +47291,136 @@ "name": "", "type": "object" }, - "name": "ockam_kafka", + "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "pinecone", "plugin": true, - "status": "experimental", - "summary": "Ockam", - "type": "output" + "status": "stable", + "summary": "Inserts items into a Pinecone index.", + "type": "output", + "version": "4.31.0" }, { "categories": [ - "Services" + "AI" ], "config": { "children": [ { - "description": "A list of URLs to connect to. If an item of the list contains commas it will be expanded into multiple URLs.", - "examples": [ - [ - "http://localhost:9200" - ] - ], - "kind": "array", - "name": "urls", - "type": "string" - }, - { - "description": "The index to place messages.", - "interpolated": true, - "kind": "scalar", - "name": "index", - "type": "string" - }, - { - "description": "The action to take on the document. This field must resolve to one of the following action types: `index`, `update` or `delete`.", - "interpolated": true, + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", "kind": "scalar", - "name": "action", - "type": "string" + "name": "max_in_flight", + "type": "int" }, { - "description": "The ID for indexed messages. Interpolation should be used in order to create a unique ID for each message.", + "children": [ + { + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "kind": "scalar", + "name": "byte_size", + "type": "int" + }, + { + "default": "", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "examples": [ + "1s", + "1m", + "500ms" + ], + "kind": "scalar", + "name": "period", + "type": "string" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "examples": [ + "this.type == \"end_of_transaction\"" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "examples": [ + [ + { + "archive": { + "format": "concatenate" + } + } + ], + [ + { + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } + } + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", "examples": [ - "${!counter()}-${!timestamp_unix()}" + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } ], - "interpolated": true, - "kind": "scalar", - "name": "id", - "type": "string" + "kind": "", + "name": "batching", + "type": "object" }, { - "default": "", - "description": "An optional pipeline id to preprocess incoming documents.", - "interpolated": true, - "is_advanced": true, + "description": "The gRPC host of the Qdrant server.", + "examples": [ + "localhost:6334", + "xyz-example.eu-central.aws.cloud.qdrant.io:6334" + ], "kind": "scalar", - "name": "pipeline", + "name": "grpc_host", "type": "string" }, { "default": "", - "description": "The routing key to use for the document.", - "interpolated": true, - "is_advanced": true, + "description": "The Qdrant API token for authentication. Defaults to an empty string.", + "is_secret": true, "kind": "scalar", - "name": "routing", + "name": "api_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { @@ -38659,54 +47547,83 @@ "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "TLS(HTTPS) config to use when connecting", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" }, { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "description": "The name of the collection in Qdrant.", + "interpolated": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "collection_name", + "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } + "bloblang": true, + "description": "The ID of the point to insert. Can be a UUID string or positive integer.", + "examples": [ + "root = \"dc88c126-679f-49f5-ab85-04b77e8c2791\"", + "root = 832" ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "basic_auth", - "type": "object" + "name": "id", + "type": "string" + }, + { + "bloblang": true, + "description": "The mapping to extract the vector from the document.", + "examples": [ + "root = {\"dense_vector\": [0.352,0.532,0.754],\"sparse_vector\": {\"indices\": [23,325,532],\"values\": [0.352,0.532,0.532]}, \"multi_vector\": [[0.352,0.532],[0.352,0.532]]}", + "root = [1.2, 0.5, 0.76]", + "root = this.vector", + "root = [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]", + "root = {\"some_sparse\": {\"indices\":[23,325,532],\"values\":[0.352,0.532,0.532]}}", + "root = {\"some_multi\": [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]}", + "root = {\"some_dense\": [0.352,0.532,0.532,0.234]}" + ], + "kind": "scalar", + "name": "vector_mapping", + "type": "string" + }, + { + "bloblang": true, + "default": "root = {}", + "description": "An optional mapping of message to payload associated with the point.", + "examples": [ + "root = {\"field\": this.value, \"field_2\": 987}", + "root = metadata()" + ], + "kind": "scalar", + "name": "payload_mapping", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "qdrant", + "plugin": true, + "status": "stable", + "summary": "Adds items to a https://qdrant.tech/[Qdrant^] collection", + "type": "output", + "version": "4.33.0" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" }, { "children": [ @@ -38804,309 +47721,265 @@ "children": [ { "default": false, - "description": "Whether to connect to Amazon Elastic Service.", + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, { - "description": "The AWS region to target.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "region", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "Allows you to specify a custom endpoint for the AWS API.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "endpoint", + "name": "root_cas_file", "type": "string" }, { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "profile", - "type": "string" - }, - { - "description": "The ID of credentials to use.", + "default": "", + "description": "A plain text certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "id", + "name": "cert", "type": "string" }, { - "description": "The secret for the credentials being used.", + "default": "", + "description": "A plain text certificate key to use.", "is_advanced": true, - "is_optional": true, "is_secret": true, "kind": "scalar", - "name": "secret", + "name": "key", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": "", + "description": "The path of a certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "token", + "name": "cert_file", "type": "string" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "default": "", + "description": "The path of a certificate key to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" + "name": "key_file", + "type": "string" }, { - "description": "A role ARN to assume.", + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, - "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "role", - "type": "string" - }, - { - "description": "An external ID to provide when assuming a role.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "role_external_id", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "credentials", - "type": "object" - } - ], - "description": "Enables and customises connectivity to Amazon Elastic Service.", - "is_advanced": true, - "kind": "scalar", - "name": "aws", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nBoth the `id` and `index` fields can be dynamically set using function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here]. When sending batched messages these interpolations are performed per message part.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "examples": [ - { - "config": "\noutput:\n processors:\n - mapping: |\n meta id = this.id\n root.doc = this\n opensearch:\n urls: [ TODO ]\n index: foo\n id: ${! @id }\n action: update\n", - "summary": "When https://opensearch.org/docs/latest/api-reference/document-apis/update-document/[updating documents^] the request body should contain a combination of a `doc`, `upsert`, and/or `script` fields at the top level, this should be done via mapping processors.", - "title": "Updating Documents" - } - ], - "name": "opensearch", - "plugin": true, - "status": "stable", - "summary": "Publishes messages into an Elasticsearch index. If the index does not exist then it is created with a dynamic mapping.", - "type": "output" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, - { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", "examples": [ [ { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } + "cert": "foo", + "key": "bar" } ], [ { - "archive": { - "format": "json_array" - } + "cert_file": "./example.pem", + "key_file": "./example.key" } ] ], "is_advanced": true, - "is_optional": true, "kind": "array", - "name": "processors", - "type": "processor" + "name": "client_certs", + "type": "object" } ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "description": "Address of the QuestDB server's HTTP port (excluding protocol)", "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } + "localhost:9000" ], - "kind": "", - "name": "batching", - "type": "object" + "kind": "scalar", + "name": "address", + "type": "string" }, { - "description": "The host for the Pinecone index.", + "description": "Username for HTTP basic auth", + "is_optional": true, + "is_secret": true, "kind": "scalar", - "linter": "root = if this.has_prefix(\"https://\") { [\"host field must be a FQDN not a URL (remove the https:// prefix)\"] }", - "name": "host", + "name": "username", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The Pinecone api key.", + "description": "Password for HTTP basic auth", + "is_optional": true, "is_secret": true, "kind": "scalar", - "name": "api_key", + "name": "password", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": "upsert-vectors", - "description": "The operation to perform against the Pinecone index.", + "description": "Bearer token for HTTP auth (takes precedence over basic auth username & password)", + "is_optional": true, + "is_secret": true, "kind": "scalar", - "linter": "\nlet options = {\n \"update-vector\": true,\n \"upsert-vectors\": true,\n \"delete-vectors\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operation", - "options": [ - "update-vector", - "upsert-vectors", - "delete-vectors" - ], + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": "", - "description": "The namespace to write to - writes to the default namespace by default.", - "interpolated": true, + "description": "The time to continue retrying after a failed HTTP request. The interval between retries is an exponential backoff starting at 10ms and doubling after each failed attempt up to a maximum of 1 second.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "namespace", + "name": "retry_timeout", "type": "string" }, { - "description": "The ID for the index entry in Pinecone.", - "interpolated": true, + "description": "The time to wait for a response from the server. This is in addition to the calculation derived from the request_min_throughput parameter.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "id", + "name": "request_timeout", "type": "string" }, { - "bloblang": true, - "description": "The mapping to extract out the vector from the document. The result must be a floating point array. Required if not a delete operation.", + "description": "Minimum expected throughput in bytes per second for HTTP requests. If the throughput is lower than this value, the connection will time out. This is used to calculate an additional timeout on top of request_timeout. This is useful for large requests. You can set this value to 0 to disable this logic.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "request_min_throughput", + "type": "int" + }, + { + "description": "Destination table", "examples": [ - "root = this.embeddings_vector", - "root = [1.2, 0.5, 0.76]" + "trades" ], + "kind": "scalar", + "name": "table", + "type": "string" + }, + { + "description": "Name of the designated timestamp field", "is_optional": true, "kind": "scalar", - "name": "vector_mapping", + "name": "designated_timestamp_field", "type": "string" }, { - "bloblang": true, - "description": "An optional mapping of message to metadata in the Pinecone index entry.", - "examples": [ - "root = @", - "root = metadata()", - "root = {\"summary\": this.summary, \"foo\": this.other_field}" - ], + "default": "auto", + "description": "Designated timestamp field units", "is_optional": true, "kind": "scalar", - "name": "metadata_mapping", + "linter": "root = if [\"nanos\",\"micros\",\"millis\",\"seconds\",\"auto\"].contains(this) != true { [ \"valid options are \\\"nanos\\\", \\\"micros\\\", \\\"millis\\\", \\\"seconds\\\", \\\"auto\\\"\" ] }", + "name": "designated_timestamp_unit", "type": "string" + }, + { + "description": "String fields with textual timestamps", + "is_optional": true, + "kind": "array", + "name": "timestamp_string_fields", + "type": "string" + }, + { + "default": "Jan _2 15:04:05.000000Z0700", + "description": "Timestamp format, used when parsing timestamp string fields. Specified in golang's time.Parse layout", + "is_optional": true, + "kind": "scalar", + "name": "timestamp_string_format", + "type": "string" + }, + { + "description": "Columns that should be the SYMBOL type (string values default to STRING)", + "is_optional": true, + "kind": "array", + "name": "symbols", + "type": "string" + }, + { + "description": "Columns that should be double type, (int is default)", + "is_optional": true, + "kind": "array", + "name": "doubles", + "type": "string" + }, + { + "default": false, + "description": "Mark a message as errored if it is empty after field validation", + "is_optional": true, + "kind": "scalar", + "name": "error_on_empty_messages", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "pinecone", + "description": "Important: We recommend that the dedupe feature is enabled on the QuestDB server. Please visit https://questdb.io/docs/ for more information about deploying, configuring, and using QuestDB.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "questdb", "plugin": true, - "status": "experimental", - "summary": "Inserts items into a Pinecone index.", - "type": "output", - "version": "4.31.0" + "status": "stable", + "summary": "Pushes messages to a QuestDB table", + "type": "output" }, { "categories": [ @@ -39115,11 +47988,14 @@ "config": { "children": [ { - "description": "A URL to connect to.", + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", "examples": [ - "pulsar://localhost:6650", - "pulsar://pulsar.us-west.example.com:6650", - "pulsar+ssl://pulsar.us-west.example.com:6651" + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" ], "kind": "scalar", "name": "url", @@ -39127,152 +48003,221 @@ "type": "string" }, { - "description": "The topic to publish to.", + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, "kind": "scalar", - "name": "topic", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - } + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" ], - "description": "Specify the path to a custom CA certificate to trust broker TLS service.", - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "default": "", - "description": "The key to publish messages with.", - "interpolated": true, - "kind": "scalar", - "name": "key", "type": "string" }, { "default": "", - "description": "The ordering key to publish messages with.", - "interpolated": true, + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, "kind": "scalar", - "name": "ordering_key", + "name": "master", "type": "string" }, { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "client_name", + "type": "string", + "version": "4.82.0" }, { "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, { "children": [ - { - "default": false, - "description": "Whether OAuth2 is enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, { "default": "", - "description": "OAuth2 audience.", + "description": "A plain text certificate to use.", "is_advanced": true, "kind": "scalar", - "name": "audience", + "name": "cert", "type": "string" }, { "default": "", - "description": "OAuth2 issuer URL.", + "description": "A plain text certificate key to use.", "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "issuer_url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "OAuth2 scope to request.", + "description": "The path of a certificate to use.", "is_advanced": true, "kind": "scalar", - "name": "scope", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "cert_file", "type": "string" }, { "default": "", - "description": "The path to a file containing a private key.", + "description": "The path of a certificate key to use.", "is_advanced": true, "kind": "scalar", - "name": "private_key_file", + "name": "key_file", "type": "string" - } - ], - "description": "Parameters for Pulsar OAuth2 authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth2", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether Token Auth is enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" }, { "default": "", - "description": "Actual base64 encoded token.", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "token", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "description": "Parameters for Pulsar Token authentication.", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "token", + "kind": "array", + "name": "client_certs", "type": "object" } ], - "description": "Optional configuration of Pulsar authentication methods.", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "auth", - "type": "object", - "version": "3.60.0" + "name": "tls", + "type": "object" + }, + { + "description": "The key for each message, function interpolations should be used to create a unique key per message.", + "examples": [ + "${! @.kafka_key }", + "${! this.doc.id }", + "${! counter() }" + ], + "interpolated": true, + "kind": "scalar", + "name": "key", + "type": "string" + }, + { + "default": false, + "description": "Whether all metadata fields of messages should be walked and added to the list of hash fields to set.", + "kind": "scalar", + "name": "walk_metadata", + "type": "bool" + }, + { + "default": false, + "description": "Whether to walk each message as a JSON object and add each key/value pair to the list of hash fields to set.", + "kind": "scalar", + "name": "walk_json_object", + "type": "bool" + }, + { + "default": {}, + "description": "A map of key/value pairs to set as hash fields.", + "interpolated": true, + "kind": "map", + "name": "fields", + "type": "string" + }, + { + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "kind": "scalar", + "name": "max_in_flight", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "name": "pulsar", + "description": "\nThe field `key` supports xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing you to create a unique key for each message.\n\nThe field `fields` allows you to specify an explicit map of field names to interpolated values, also evaluated per message of a batch:\n\n```yaml\noutput:\n redis_hash:\n url: tcp://localhost:6379\n key: ${!json(\"id\")}\n fields:\n topic: ${!meta(\"kafka_topic\")}\n partition: ${!meta(\"kafka_partition\")}\n content: ${!json(\"document.text\")}\n```\n\nIf the field `walk_metadata` is set to `true` then Redpanda Connect will walk all metadata fields of messages and add them to the list of hash fields to set.\n\nIf the field `walk_json_object` is set to `true` then Redpanda Connect will walk each message as a JSON object, extracting keys and the string representation of their value and adds them to the list of hash fields to set.\n\nThe order of hash field extraction is as follows:\n\n1. Metadata (if enabled)\n2. JSON object (if enabled)\n3. Explicit fields\n\nWhere latter stages will overwrite matching field names of a former stage.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", + "name": "redis_hash", "plugin": true, - "status": "experimental", - "summary": "Write messages to an Apache Pulsar server.", - "type": "output", - "version": "3.43.0" + "status": "stable", + "summary": "Sets Redis hash objects using the HSET command.", + "type": "output" }, { "categories": [ @@ -39281,170 +48226,197 @@ "config": { "children": [ { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], + "type": "string" + }, + { + "default": "", + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, + "kind": "scalar", + "name": "master", + "type": "string" + }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, + { + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "1s", - "1m", - "500ms" + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ], + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "period", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "bloblang": true, "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "this.type == \"end_of_transaction\"" + "./root_cas.pem" ], + "is_advanced": true, "kind": "scalar", - "name": "check", + "name": "root_cas_file", "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", "examples": [ [ { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } + "cert": "foo", + "key": "bar" } ], [ { - "archive": { - "format": "json_array" - } + "cert_file": "./example.pem", + "key_file": "./example.key" } ] ], "is_advanced": true, - "is_optional": true, "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "maximum batch size is 10 (limit of the pusher library)", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" + "name": "client_certs", + "type": "object" } ], - "kind": "", - "name": "batching", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", "type": "object" }, { - "description": "Pusher channel to publish to. Interpolation functions can also be used", + "description": "The key for each message, function interpolations can be optionally used to create a unique key per message.", "examples": [ - "my_channel", - "${!json(\"id\")}" + "some_list", + "${! @.kafka_key }", + "${! this.doc.id }", + "${! counter() }" ], "interpolated": true, "kind": "scalar", - "name": "channel", - "type": "string" - }, - { - "description": "Event to publish to", - "kind": "scalar", - "name": "event", - "type": "string" - }, - { - "description": "Pusher app id", - "kind": "scalar", - "name": "appId", - "type": "string" - }, - { - "description": "Pusher key", - "kind": "scalar", "name": "key", "type": "string" }, - { - "description": "Pusher secret", - "kind": "scalar", - "name": "secret", - "type": "string" - }, - { - "description": "Pusher cluster", - "kind": "scalar", - "name": "cluster", - "type": "string" - }, - { - "default": true, - "description": "Enable SSL encryption", - "kind": "scalar", - "name": "secure", - "type": "bool" - }, - { - "default": 1, - "description": "The maximum number of parallel message batches to have in flight at any given time.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "pusher", - "plugin": true, - "status": "experimental", - "summary": "Output for publishing messages to Pusher API (https://pusher.com)", - "type": "output", - "version": "4.3.0" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ { "default": 64, "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", @@ -39545,24 +48517,86 @@ "type": "object" }, { - "description": "The gRPC host of the Qdrant server.", + "default": "rpush", + "description": "The command used to push elements to the Redis list", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"rpush\": true,\n \"lpush\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "command", + "options": [ + "rpush", + "lpush" + ], + "type": "string", + "version": "4.22.0" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "The field `key` supports xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing you to create a unique key for each message.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "redis_list", + "plugin": true, + "status": "stable", + "summary": "Pushes messages onto the end of a Redis list (which is created if it doesn't already exist) using the RPUSH command.", + "type": "output" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", "examples": [ - "localhost:6334", - "xyz-example.eu-central.aws.cloud.qdrant.io:6334" + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" ], "kind": "scalar", - "name": "grpc_host", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], "type": "string" }, { "default": "", - "description": "The Qdrant API token for authentication. Defaults to an empty string.", - "is_secret": true, + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, "kind": "scalar", - "name": "api_token", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { @@ -39687,77 +48721,19 @@ "type": "object" } ], - "description": "TLS(HTTPS) config to use when connecting", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" }, { - "description": "The name of the collection in Qdrant.", + "description": "The channel to publish messages to.", "interpolated": true, "kind": "scalar", - "name": "collection_name", - "type": "string" - }, - { - "bloblang": true, - "description": "The ID of the point to insert. Can be a UUID string or positive integer.", - "examples": [ - "root = \"dc88c126-679f-49f5-ab85-04b77e8c2791\"", - "root = 832" - ], - "kind": "scalar", - "name": "id", - "type": "string" - }, - { - "bloblang": true, - "description": "The mapping to extract the vector from the document.", - "examples": [ - "root = {\"dense_vector\": [0.352,0.532,0.754],\"sparse_vector\": {\"indices\": [23,325,532],\"values\": [0.352,0.532,0.532]}, \"multi_vector\": [[0.352,0.532],[0.352,0.532]]}", - "root = [1.2, 0.5, 0.76]", - "root = this.vector", - "root = [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]", - "root = {\"some_sparse\": {\"indices\":[23,325,532],\"values\":[0.352,0.532,0.532]}}", - "root = {\"some_multi\": [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]}", - "root = {\"some_dense\": [0.352,0.532,0.532,0.234]}" - ], - "kind": "scalar", - "name": "vector_mapping", + "name": "channel", "type": "string" }, - { - "bloblang": true, - "default": "root = {}", - "description": "An optional mapping of message to payload associated with the point.", - "examples": [ - "root = {\"field\": this.value, \"field_2\": 987}", - "root = metadata()" - ], - "kind": "scalar", - "name": "payload_mapping", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "qdrant", - "plugin": true, - "status": "experimental", - "summary": "Adds items to a https://qdrant.tech/[Qdrant^] collection", - "type": "output", - "version": "4.33.0" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ { "default": 64, "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", @@ -39856,6 +48832,73 @@ "kind": "", "name": "batching", "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis output will interpolate functions within the channel field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "redis_pubsub", + "plugin": true, + "status": "stable", + "summary": "Publishes messages through the Redis PubSub model. It is not possible to guarantee that messages have been received.", + "type": "output" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], + "type": "string" + }, + { + "default": "", + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, + "kind": "scalar", + "name": "master", + "type": "string" + }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" }, { "children": [ @@ -39981,373 +49024,171 @@ "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" }, { - "description": "Address of the QuestDB server's HTTP port (excluding protocol)", - "examples": [ - "localhost:9000" - ], + "description": "The stream to add messages to.", + "interpolated": true, "kind": "scalar", - "name": "address", + "name": "stream", "type": "string" }, { - "description": "Username for HTTP basic auth", - "is_optional": true, - "is_secret": true, + "default": "*", + "description": "The entry ID for the stream message. Allows function interpolations. When set to `*` (the default), Redis auto-generates a unique ID based on the current time. Set a custom ID to control message ordering, for example to replay messages in upstream order.", + "examples": [ + "*", + "${! @redis_stream }", + "${! this.id }", + "${! counter() }-0" + ], + "interpolated": true, "kind": "scalar", - "name": "username", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "id", "type": "string" }, { - "description": "Password for HTTP basic auth", - "is_optional": true, - "is_secret": true, + "default": "body", + "description": "A key to set the raw body of the message to.", "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "body_key", "type": "string" }, { - "description": "Bearer token for HTTP auth (takes precedence over basic auth username & password)", - "is_optional": true, - "is_secret": true, + "default": 0, + "description": "When greater than zero enforces a rough cap on the length of the target stream.", "kind": "scalar", - "name": "token", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "max_length", + "type": "int" }, { - "description": "The time to continue retrying after a failed HTTP request. The interval between retries is an exponential backoff starting at 10ms and doubling after each failed attempt up to a maximum of 1 second.", - "is_advanced": true, - "is_optional": true, + "default": 64, + "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", "kind": "scalar", - "name": "retry_timeout", - "type": "string" + "name": "max_in_flight", + "type": "int" }, { - "description": "The time to wait for a response from the server. This is in addition to the calculation derived from the request_min_throughput parameter.", - "is_advanced": true, - "is_optional": true, + "children": [ + { + "default": [], + "description": "Provide a list of explicit metadata key prefixes to be excluded when adding metadata to sent messages.", + "kind": "array", + "name": "exclude_prefixes", + "type": "string" + } + ], + "description": "Specify criteria for which metadata values are included in the message body.", "kind": "scalar", - "name": "request_timeout", - "type": "string" - }, - { - "description": "Minimum expected throughput in bytes per second for HTTP requests. If the throughput is lower than this value, the connection will time out. This is used to calculate an additional timeout on top of request_timeout. This is useful for large requests. You can set this value to 0 to disable this logic.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "request_min_throughput", - "type": "int" - }, - { - "description": "Destination table", - "examples": [ - "trades" - ], - "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "description": "Name of the designated timestamp field", - "is_optional": true, - "kind": "scalar", - "name": "designated_timestamp_field", - "type": "string" - }, - { - "default": "auto", - "description": "Designated timestamp field units", - "is_optional": true, - "kind": "scalar", - "linter": "root = if [\"nanos\",\"micros\",\"millis\",\"seconds\",\"auto\"].contains(this) != true { [ \"valid options are \\\"nanos\\\", \\\"micros\\\", \\\"millis\\\", \\\"seconds\\\", \\\"auto\\\"\" ] }", - "name": "designated_timestamp_unit", - "type": "string" - }, - { - "description": "String fields with textual timestamps", - "is_optional": true, - "kind": "array", - "name": "timestamp_string_fields", - "type": "string" - }, - { - "default": "Jan _2 15:04:05.000000Z0700", - "description": "Timestamp format, used when parsing timestamp string fields. Specified in golang's time.Parse layout", - "is_optional": true, - "kind": "scalar", - "name": "timestamp_string_format", - "type": "string" - }, - { - "description": "Columns that should be the SYMBOL type (string values default to STRING)", - "is_optional": true, - "kind": "array", - "name": "symbols", - "type": "string" - }, - { - "description": "Columns that should be double type, (int is default)", - "is_optional": true, - "kind": "array", - "name": "doubles", - "type": "string" - }, - { - "default": false, - "description": "Mark a message as errored if it is empty after field validation", - "is_optional": true, - "kind": "scalar", - "name": "error_on_empty_messages", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "Important: We recommend that the dedupe feature is enabled on the QuestDB server. Please visit https://questdb.io/docs/ for more information about deploying, configuring, and using QuestDB.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "questdb", - "plugin": true, - "status": "experimental", - "summary": "Pushes messages to a QuestDB table", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", - "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" - ], - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" - ], - "type": "string" - }, - { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" - ], - "is_advanced": true, - "kind": "scalar", - "name": "master", - "type": "string" + "name": "metadata", + "type": "object" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, + "default": 0, + "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" + "name": "count", + "type": "int" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, + "default": 0, + "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "byte_size", + "type": "int" }, { "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "description": "A period in which an incomplete batch should be flushed regardless of its size.", "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "1s", + "1m", + "500ms" ], - "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "period", "type": "string" }, { + "bloblang": true, "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", "examples": [ - "./root_cas.pem" + "this.type == \"end_of_transaction\"" ], - "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "check", "type": "string" }, { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", "examples": [ [ { - "cert": "foo", - "key": "bar" + "archive": { + "format": "concatenate" + } } ], [ { - "cert_file": "./example.pem", - "key_file": "./example.key" + "archive": { + "format": "lines" + } + } + ], + [ + { + "archive": { + "format": "json_array" + } } ] ], "is_advanced": true, + "is_optional": true, "kind": "array", - "name": "client_certs", - "type": "object" + "name": "processors", + "type": "processor" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "description": "The key for each message, function interpolations should be used to create a unique key per message.", + "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", "examples": [ - "${! @.kafka_key }", - "${! this.doc.id }", - "${! counter() }" + { + "byte_size": 5000, + "count": 0, + "period": "1s" + }, + { + "count": 10, + "period": "1s" + }, + { + "check": "this.contains(\"END BATCH\")", + "count": 0, + "period": "1m" + } ], - "interpolated": true, - "kind": "scalar", - "name": "key", - "type": "string" - }, - { - "default": false, - "description": "Whether all metadata fields of messages should be walked and added to the list of hash fields to set.", - "kind": "scalar", - "name": "walk_metadata", - "type": "bool" - }, - { - "default": false, - "description": "Whether to walk each message as a JSON object and add each key/value pair to the list of hash fields to set.", - "kind": "scalar", - "name": "walk_json_object", - "type": "bool" - }, - { - "default": {}, - "description": "A map of key/value pairs to set as hash fields.", - "interpolated": true, - "kind": "map", - "name": "fields", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "kind": "", + "name": "batching", + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nThe field `key` supports xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing you to create a unique key for each message.\n\nThe field `fields` allows you to specify an explicit map of field names to interpolated values, also evaluated per message of a batch:\n\n```yaml\noutput:\n redis_hash:\n url: tcp://localhost:6379\n key: ${!json(\"id\")}\n fields:\n topic: ${!meta(\"kafka_topic\")}\n partition: ${!meta(\"kafka_partition\")}\n content: ${!json(\"document.text\")}\n```\n\nIf the field `walk_metadata` is set to `true` then Redpanda Connect will walk all metadata fields of messages and add them to the list of hash fields to set.\n\nIf the field `walk_json_object` is set to `true` then Redpanda Connect will walk each message as a JSON object, extracting keys and the string representation of their value and adds them to the list of hash fields to set.\n\nThe order of hash field extraction is as follows:\n\n1. Metadata (if enabled)\n2. JSON object (if enabled)\n3. Explicit fields\n\nWhere latter stages will overwrite matching field names of a former stage.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", - "name": "redis_hash", + "description": "\nIt's possible to specify a maximum length of the target stream by setting it to a value greater than 0, in which case this cap is applied only when Redis is able to remove a whole macro node, for efficiency.\n\nRedis stream entries are key/value pairs, as such it is necessary to specify the key to be set to the body of the message. All metadata fields of the message will also be set as key/value pairs, if there is a key collision between a metadata item and the body then the body takes precedence.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", + "name": "redis_streams", "plugin": true, "status": "stable", - "summary": "Sets Redis hash objects using the HMSET command.", + "summary": "Pushes messages to a Redis (v5.0+) Stream (which is created if it doesn't already exist) using the XADD command.", "type": "output" }, { @@ -40357,43 +49198,30 @@ "config": { "children": [ { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses. When this field is omitted the global `redpanda` block will be referenced for connection details.", "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" - ], - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" + [ + "localhost:9092" + ], + [ + "foo:9092", + "bar:9092" + ], + [ + "foo:9092,bar:9092" + ] ], + "is_optional": true, + "kind": "array", + "name": "seed_brokers", "type": "string" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" - ], + "default": "redpanda-connect", + "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", - "name": "master", + "name": "client_id", "type": "string" }, { @@ -40520,336 +49348,441 @@ "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" }, - { - "description": "The key for each message, function interpolations can be optionally used to create a unique key per message.", - "examples": [ - "some_list", - "${! @.kafka_key }", - "${! this.doc.id }", - "${! counter() }" - ], - "interpolated": true, - "kind": "scalar", - "name": "key", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, { "children": [ { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", + "annotated_options": [ + [ + "AWS_MSK_IAM", + "AWS IAM based authentication as specified by the 'aws-msk-iam-auth' java library." + ], + [ + "OAUTHBEARER", + "OAuth Bearer based authentication." + ], + [ + "PLAIN", + "Plain text authentication." + ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], + [ + "SCRAM-SHA-256", + "SCRAM based authentication as specified in RFC5802." + ], + [ + "SCRAM-SHA-512", + "SCRAM based authentication as specified in RFC5802." + ], + [ + "none", + "Disable sasl authentication" + ] + ], + "description": "The SASL mechanism to use.", + "is_advanced": true, "kind": "scalar", - "name": "count", - "type": "int" + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "mechanism", + "type": "string" }, { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", + "default": "", + "description": "A username to provide for PLAIN or SCRAM-* authentication.", + "is_advanced": true, "kind": "scalar", - "name": "byte_size", - "type": "int" + "name": "username", + "type": "string" }, { "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], + "description": "A password to provide for PLAIN or SCRAM-* authentication.", + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "period", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "bloblang": true, "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], + "description": "The token to use for a single session's OAUTHBEARER authentication.", + "is_advanced": true, "kind": "scalar", - "name": "check", + "name": "token", "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], + "description": "Key/value pairs to add to OAUTHBEARER authentication requests.", "is_advanced": true, "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" + "kind": "map", + "name": "extensions", + "type": "string" }, { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" - }, - { - "default": "rpush", - "description": "The command used to push elements to the Redis list", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"rpush\": true,\n \"lpush\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "command", - "options": [ - "rpush", - "lpush" - ], - "type": "string", - "version": "4.22.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "The field `key` supports xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions], allowing you to create a unique key for each message.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "redis_list", - "plugin": true, - "status": "stable", - "summary": "Pushes messages onto the end of a Redis list (which is created if it doesn't already exist) using the RPUSH command.", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "children": [ + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Allows you to specify a custom endpoint for the AWS API.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" + } + ], + "description": "Contains AWS specific fields for when the `mechanism` is set to `AWS_MSK_IAM`.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "aws", + "type": "object" + } + ], + "description": "Specify one or more methods of SASL authentication. SASL is tried in order; if the broker supports the first mechanism, all connections will use that mechanism. If the first mechanism fails, the client will pick the first supported mechanism. If the broker does not support any client mechanisms, connections will fail.", "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" + [ + { + "mechanism": "SCRAM-SHA-512", + "password": "bar", + "username": "foo" + } + ] ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "sasl", + "type": "object" + }, + { + "default": "1m", + "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", + "is_advanced": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "metadata_max_age", "type": "string" }, { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "default": "10s", + "description": "The request time overhead. Uses the given time as overhead while deadlining requests. Roughly equivalent to request.timeout.ms, but grants additional time to requests that have timeout fields.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" - ], + "name": "request_timeout_overhead", "type": "string" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" - ], + "default": "20s", + "description": "The rough amount of time to allow connections to idle before they are closed.", "is_advanced": true, "kind": "scalar", - "name": "master", + "name": "conn_idle_timeout", "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", + "name": "idle", "type": "string" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "interval", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "count", + "type": "int" } ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "description": "A topic to write messages to.", + "interpolated": true, + "kind": "scalar", + "name": "topic", + "type": "string" + }, + { + "description": "An optional key to populate for each message.", + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "key", + "type": "string" + }, + { + "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", + "examples": [ + "${! meta(\"partition\") }" + ], + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "partition", + "type": "string" + }, + { + "children": [ + { "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "description": "Provide a list of explicit metadata key prefixes to match against.", "examples": [ [ - { - "cert": "foo", - "key": "bar" - } + "foo_", + "bar_" ], [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } + "kafka_" + ], + [ + "content-" ] ], - "is_advanced": true, "kind": "array", - "name": "client_certs", - "type": "object" + "name": "include_prefixes", + "type": "string" + }, + { + "default": [], + "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", + "examples": [ + [ + ".*" + ], + [ + "_timestamp_unix$" + ] + ], + "kind": "array", + "name": "include_patterns", + "type": "string" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", - "is_advanced": true, + "description": "Determine which (if any) metadata values should be added to messages as headers.", + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "metadata", "type": "object" }, { - "description": "The channel to publish messages to.", + "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", + "examples": [ + "${! timestamp_unix() }", + "${! metadata(\"kafka_timestamp_unix\") }" + ], "interpolated": true, + "is_advanced": true, + "is_deprecated": true, + "is_optional": true, "kind": "scalar", - "name": "channel", + "name": "timestamp", "type": "string" }, { - "default": 64, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", + "examples": [ + "${! timestamp_unix_milli() }", + "${! metadata(\"kafka_timestamp_ms\") }" + ], + "interpolated": true, + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "timestamp_ms", + "type": "string" + }, + { + "default": 256, + "description": "The maximum number of batches to be sending in parallel at any given time.", "kind": "scalar", "name": "max_in_flight", "type": "int" @@ -40925,7 +49858,7 @@ "type": "processor" } ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", + "description": "Optional explicit batching policy for the output. Note that when batches are formed at the input level they can be expanded by this policy, but not contracted. When consuming data from a Redpanda input it is recommended to tune batches from the input config via the `max_yield_batch_bytes` field, or the `unordered_processing.batching` field if appropriate.", "examples": [ { "byte_size": 5000, @@ -40945,238 +49878,308 @@ "kind": "", "name": "batching", "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis output will interpolate functions within the channel field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "redis_pubsub", - "plugin": true, - "status": "stable", - "summary": "Publishes messages through the Redis PubSub model. It is not possible to guarantee that messages have been received.", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ + }, { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "bloblang": true, + "description": "EXPERIMENTAL: A xref:guides:bloblang/about.adoc[Bloblang mapping] used to inject an object containing tracing propagation information into outbound messages. The specification of the injected fields will match the format used by the service wide tracer.", "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" + "meta = @.merge(this)", + "root.meta.span = this" ], + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "inject_tracing_map", + "type": "string", + "version": "3.45.0" + }, + { + "annotated_options": [ + [ + "least_backup", + "Chooses the least backed up partition (the partition with the fewest amount of buffered records). Partitions are selected per batch." + ], + [ + "manual", + "Manually select a partition for each message, requires the field `partition` to be specified." + ], + [ + "murmur2_hash", + "Kafka's default hash algorithm that uses a 32-bit murmur2 hash of the key to compute which partition the record will be on." + ], + [ + "round_robin", + "Round-robin's messages through all available partitions. This algorithm has lower throughput and causes higher CPU load on brokers, but can be useful if you want to ensure an even distribution of records to partitions." + ] + ], + "description": "Override the default murmur2 hashing partitioner.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"least_backup\": true,\n \"manual\": true,\n \"murmur2_hash\": true,\n \"round_robin\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "partitioner", "type": "string" }, { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "default": true, + "description": "Enable the idempotent write producer option. When enabled, the producer initializes a producer ID and uses it to guarantee exactly-once semantics per partition (no duplicates on retries). This requires the `IDEMPOTENT_WRITE` permission on the `CLUSTER` resource. If your cluster does not grant this permission or uses ACLs restrictively, disable this option. Note: Idempotent writes are strictly a win for data integrity but may be unavailable in restricted environments (e.g., some managed Kafka services, Redpanda with strict ACLs). Disabling this option is safe and only affects retry behavior—duplicates may occur on producer retries, but the pipeline will continue to function normally.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", + "name": "idempotent_write", + "type": "bool" + }, + { + "annotated_options": [ + [ + "all", + "Wait for all in-sync replicas to acknowledge (acks=-1). Required when idempotent_write is enabled." + ], + [ + "leader", + "Wait for the leader broker to acknowledge (acks=1). Messages are lost if the leader fails before replication." + ], + [ + "none", + "Do not wait for any acknowledgement (acks=0). Highest throughput but messages may be lost." + ] + ], + "default": "all", + "description": "The number of acknowledgements the leader broker must receive from ISR brokers before responding to the produce request. When `idempotent_write` is enabled this must be set to `all`.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"all\": true,\n \"leader\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "acks", + "type": "string" + }, + { + "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"lz4\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"none\": true,\n \"zstd\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "compression", "options": [ - "simple", - "cluster", - "failover" + "lz4", + "snappy", + "gzip", + "none", + "zstd" ], "type": "string" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", + "default": true, + "description": "Enables topics to be auto created if they do not exist when fetching their metadata.", + "is_advanced": true, + "kind": "scalar", + "name": "allow_auto_topic_creation", + "type": "bool" + }, + { + "default": "10s", + "description": "The maximum period of time to wait for message sends before abandoning the request and retrying", + "is_advanced": true, + "kind": "scalar", + "name": "timeout", + "type": "string" + }, + { + "default": "1MiB", + "description": "The maximum size of a produced record batch in bytes. A `MESSAGE_TOO_LARGE` error is returned if a batch exceeds this limit. This field maps to the `max.message.bytes` Kafka property. Ensure the Redpanda broker's `kafka_batch_max_bytes` property is at least as large as this value, see https://docs.redpanda.com/current/reference/properties/cluster-properties/#kafka_batch_max_bytes.", "examples": [ - "mymaster" + "100MB", + "50mib" ], "is_advanced": true, "kind": "scalar", - "name": "master", + "name": "max_message_bytes", "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, + "default": "100MiB", + "description": "The upper bound for the number of bytes written to a broker connection in a single write. This field corresponds to Kafka's `socket.request.max.bytes`.", + "examples": [ + "128MB", + "50mib" + ], + "is_advanced": true, + "kind": "scalar", + "name": "broker_write_max_bytes", + "type": "string" + }, + { + "default": 10000, + "description": "The maximum number of records the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered and space frees up. Increase this value for high-throughput pipelines to avoid back-pressure stalls.", + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_records", + "type": "int" + }, + { + "default": "0", + "description": "The maximum number of bytes the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered. Set to `0` to disable the byte-level limit (only `max_buffered_records` applies). This limit is checked after `max_buffered_records`.", + "examples": [ + "256MB", + "50mib" + ], + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_bytes", + "type": "string" + }, + { + "default": 1, + "description": "The maximum number of produce requests in flight per broker connection. When `idempotent_write` is enabled, this is capped at 5 by the Kafka protocol (and at 1 for Kafka < v1.0.0). When `idempotent_write` is disabled, higher values improve throughput by pipelining requests but may cause out-of-order delivery.", + "is_advanced": true, + "kind": "scalar", + "name": "max_in_flight_requests", + "type": "int" + }, + { + "default": 0, + "description": "The maximum number of times a record produce is retried on failure before the record is failed. When a record fails, all records buffered in the same partition are also failed to preserve gapless ordering. Set to `0` for unlimited retries (the default). With `idempotent_write` enabled, retries are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_retries", + "type": "int" + }, + { + "default": "0s", + "description": "The maximum time a record can sit in the producer buffer before it is failed, roughly equivalent to Kafka's `delivery.timeout.ms`. This is evaluated before writing a request or after a produce response. When a record times out, all records in the same partition are also failed. Set to `0s` for no timeout (the default). With `idempotent_write` enabled, timeouts are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_delivery_timeout", + "type": "string" + } + ], + "kind": "scalar", + "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n this.idempotent_write == true && this.acks.or(\"all\") != \"all\" => \"idempotent_write requires acks to be set to all\"\n}", + "name": "", + "type": "object" + }, + "description": "\nWrites a batch of messages to Kafka brokers and waits for acknowledgement before propagating it back to the input.\n", + "examples": [ + { + "config": "\ninput:\n generate:\n interval: 1s\n mapping: 'root.name = fake(\"name\")'\n\npipeline:\n processors:\n - mutation: |\n root.id = uuid_v4()\n root.loud_name = this.name.uppercase()\n\noutput:\n redpanda:\n topic: bar\n key: ${! @id }\n\nredpanda:\n seed_brokers: [ \"127.0.0.1:9092\" ]\n tls:\n enabled: true\n sasl:\n - mechanism: SCRAM-SHA-512\n password: bar\n username: foo\n", + "summary": "Data is generated and written to a topic bar, targeting the cluster configured within the redpanda block at the bottom. This is useful as it allows us to configure TLS and SASL only once for potentially multiple inputs and outputs.", + "title": "Simple Common Output" + } + ], + "name": "redpanda", + "plugin": true, + "status": "stable", + "summary": "A Kafka output using the https://github.com/twmb/franz-go[Franz Kafka client library^].", + "type": "output" + }, + { + "categories": [ + "Services" + ], + "config": { + "children": [ + { + "description": "A topic to write messages to.", + "interpolated": true, + "kind": "scalar", + "name": "topic", + "type": "string" + }, + { + "description": "An optional key to populate for each message.", + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "key", + "type": "string" + }, + { + "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", + "examples": [ + "${! meta(\"partition\") }" + ], + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "partition", + "type": "string" + }, + { + "children": [ { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "default": [], + "description": "Provide a list of explicit metadata key prefixes to match against.", "examples": [ - "./root_cas.pem" + [ + "foo_", + "bar_" + ], + [ + "kafka_" + ], + [ + "content-" + ] ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", + "kind": "array", + "name": "include_prefixes", "type": "string" }, { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", "examples": [ [ - { - "cert": "foo", - "key": "bar" - } + ".*" ], [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } + "_timestamp_unix$" ] ], - "is_advanced": true, "kind": "array", - "name": "client_certs", - "type": "object" + "name": "include_patterns", + "type": "string" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", - "is_advanced": true, + "description": "Determine which (if any) metadata values should be added to messages as headers.", + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "metadata", "type": "object" }, { - "description": "The stream to add messages to.", + "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", + "examples": [ + "${! timestamp_unix() }", + "${! metadata(\"kafka_timestamp_unix\") }" + ], "interpolated": true, + "is_advanced": true, + "is_deprecated": true, + "is_optional": true, "kind": "scalar", - "name": "stream", + "name": "timestamp", "type": "string" }, { - "default": "body", - "description": "A key to set the raw body of the message to.", + "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", + "examples": [ + "${! timestamp_unix_milli() }", + "${! metadata(\"kafka_timestamp_ms\") }" + ], + "interpolated": true, + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "body_key", + "name": "timestamp_ms", "type": "string" }, { - "default": 0, - "description": "When greater than zero enforces a rough cap on the length of the target stream.", - "kind": "scalar", - "name": "max_length", - "type": "int" - }, - { - "default": 64, + "default": 10, "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", "kind": "scalar", "name": "max_in_flight", "type": "int" }, - { - "children": [ - { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to be excluded when adding metadata to sent messages.", - "kind": "array", - "name": "exclude_prefixes", - "type": "string" - } - ], - "description": "Specify criteria for which metadata values are included in the message body.", - "kind": "scalar", - "name": "metadata", - "type": "object" - }, { "children": [ { @@ -41271,14 +50274,14 @@ } ], "kind": "scalar", + "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n this.idempotent_write == true && this.acks.or(\"all\") != \"all\" => \"idempotent_write requires acks to be set to all\"\n}", "name": "", "type": "object" }, - "description": "\nIt's possible to specify a maximum length of the target stream by setting it to a value greater than 0, in which case this cap is applied only when Redis is able to remove a whole macro node, for efficiency.\n\nRedis stream entries are key/value pairs, as such it is necessary to specify the key to be set to the body of the message. All metadata fields of the message will also be set as key/value pairs, if there is a key collision between a metadata item and the body then the body takes precedence.\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", - "name": "redis_streams", + "name": "redpanda_common", "plugin": true, - "status": "stable", - "summary": "Pushes messages to a Redis (v5.0+) Stream (which is created if it doesn't already exist) using the XADD command.", + "status": "deprecated", + "summary": "Sends data to a Redpanda (Kafka) broker, using credentials defined in a common top-level `redpanda` config block.", "type": "output" }, { @@ -41306,7 +50309,7 @@ "type": "string" }, { - "default": "benthos", + "default": "redpanda-connect", "description": "An identifier for the client connection.", "is_advanced": true, "kind": "scalar", @@ -41459,6 +50462,10 @@ "PLAIN", "Plain text authentication." ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], [ "SCRAM-SHA-256", "SCRAM based authentication as specified in RFC5802." @@ -41475,7 +50482,7 @@ "description": "The SASL mechanism to use.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "mechanism", "type": "string" }, @@ -41531,6 +50538,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -41626,7 +50693,7 @@ "type": "object" }, { - "default": "5m", + "default": "1m", "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", "is_advanced": true, "kind": "scalar", @@ -41649,108 +50716,65 @@ "name": "conn_idle_timeout", "type": "string" }, - { - "description": "A topic to write messages to.", - "interpolated": true, - "kind": "scalar", - "name": "topic", - "type": "string" - }, - { - "description": "An optional key to populate for each message.", - "interpolated": true, - "is_optional": true, - "kind": "scalar", - "name": "key", - "type": "string" - }, - { - "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", - "examples": [ - "${! meta(\"partition\") }" - ], - "interpolated": true, - "is_optional": true, - "kind": "scalar", - "name": "partition", - "type": "string" - }, { "children": [ { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "kind": "array", - "name": "include_prefixes", + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", "type": "string" }, { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", - "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } ], - "kind": "array", - "name": "include_patterns", + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", "type": "string" } ], - "description": "Determine which (if any) metadata values should be added to messages as headers.", - "is_optional": true, - "kind": "scalar", - "name": "metadata", - "type": "object" - }, - { - "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", - "examples": [ - "${! timestamp_unix() }", - "${! metadata(\"kafka_timestamp_unix\") }" - ], - "interpolated": true, - "is_advanced": true, - "is_deprecated": true, - "is_optional": true, - "kind": "scalar", - "name": "timestamp", - "type": "string" - }, - { - "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", - "examples": [ - "${! timestamp_unix_milli() }", - "${! metadata(\"kafka_timestamp_ms\") }" - ], - "interpolated": true, + "description": "TCP socket configuration.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "timestamp_ms", - "type": "string" - }, - { - "default": 256, - "description": "The maximum number of batches to be sending in parallel at any given time.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "tcp", + "type": "object" }, { "annotated_options": [ @@ -41781,12 +50805,35 @@ }, { "default": true, - "description": "Enable the idempotent write producer option. This requires the `IDEMPOTENT_WRITE` permission on `CLUSTER` and can be disabled if this permission is not available.", + "description": "Enable the idempotent write producer option. When enabled, the producer initializes a producer ID and uses it to guarantee exactly-once semantics per partition (no duplicates on retries). This requires the `IDEMPOTENT_WRITE` permission on the `CLUSTER` resource. If your cluster does not grant this permission or uses ACLs restrictively, disable this option. Note: Idempotent writes are strictly a win for data integrity but may be unavailable in restricted environments (e.g., some managed Kafka services, Redpanda with strict ACLs). Disabling this option is safe and only affects retry behavior—duplicates may occur on producer retries, but the pipeline will continue to function normally.", "is_advanced": true, "kind": "scalar", "name": "idempotent_write", "type": "bool" }, + { + "annotated_options": [ + [ + "all", + "Wait for all in-sync replicas to acknowledge (acks=-1). Required when idempotent_write is enabled." + ], + [ + "leader", + "Wait for the leader broker to acknowledge (acks=1). Messages are lost if the leader fails before replication." + ], + [ + "none", + "Do not wait for any acknowledgement (acks=0). Highest throughput but messages may be lost." + ] + ], + "default": "all", + "description": "The number of acknowledgements the leader broker must receive from ISR brokers before responding to the produce request. When `idempotent_write` is enabled this must be set to `all`.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"all\": true,\n \"leader\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "acks", + "type": "string" + }, { "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", "is_advanced": true, @@ -41821,7 +50868,7 @@ }, { "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", + "description": "The maximum size of a produced record batch in bytes. A `MESSAGE_TOO_LARGE` error is returned if a batch exceeds this limit. This field maps to the `max.message.bytes` Kafka property. Ensure the Redpanda broker's `kafka_batch_max_bytes` property is at least as large as this value, see https://docs.redpanda.com/current/reference/properties/cluster-properties/#kafka_batch_max_bytes.", "examples": [ "100MB", "50mib" @@ -41842,1815 +50889,1502 @@ "kind": "scalar", "name": "broker_write_max_bytes", "type": "string" - } - ], - "kind": "scalar", - "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n}", - "name": "", - "type": "object" - }, - "description": "\nWrites a batch of messages to Kafka brokers and waits for acknowledgement before propagating it back to the input.\n", - "name": "redpanda", - "plugin": true, - "status": "beta", - "summary": "A Kafka output using the https://github.com/twmb/franz-go[Franz Kafka client library^].", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A topic to write messages to.", - "interpolated": true, - "kind": "scalar", - "name": "topic", - "type": "string" - }, - { - "description": "An optional key to populate for each message.", - "interpolated": true, - "is_optional": true, - "kind": "scalar", - "name": "key", - "type": "string" - }, - { - "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", - "examples": [ - "${! meta(\"partition\") }" - ], - "interpolated": true, - "is_optional": true, - "kind": "scalar", - "name": "partition", - "type": "string" }, { - "children": [ - { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "kind": "array", - "name": "include_prefixes", - "type": "string" - }, - { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", - "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] - ], - "kind": "array", - "name": "include_patterns", - "type": "string" - } - ], - "description": "Determine which (if any) metadata values should be added to messages as headers.", - "is_optional": true, + "default": 10000, + "description": "The maximum number of records the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered and space frees up. Increase this value for high-throughput pipelines to avoid back-pressure stalls.", + "is_advanced": true, "kind": "scalar", - "name": "metadata", - "type": "object" + "name": "max_buffered_records", + "type": "int" }, { - "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", + "default": "0", + "description": "The maximum number of bytes the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered. Set to `0` to disable the byte-level limit (only `max_buffered_records` applies). This limit is checked after `max_buffered_records`.", "examples": [ - "${! timestamp_unix() }", - "${! metadata(\"kafka_timestamp_unix\") }" + "256MB", + "50mib" ], - "interpolated": true, "is_advanced": true, - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "timestamp", + "name": "max_buffered_bytes", "type": "string" }, { - "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", - "examples": [ - "${! timestamp_unix_milli() }", - "${! metadata(\"kafka_timestamp_ms\") }" - ], - "interpolated": true, + "default": 1, + "description": "The maximum number of produce requests in flight per broker connection. When `idempotent_write` is enabled, this is capped at 5 by the Kafka protocol (and at 1 for Kafka < v1.0.0). When `idempotent_write` is disabled, higher values improve throughput by pipelining requests but may cause out-of-order delivery.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "timestamp_ms", - "type": "string" + "name": "max_in_flight_requests", + "type": "int" }, { - "default": 10, - "description": "The maximum number of messages to have in flight at a given time. Increase this to improve throughput.", + "default": 0, + "description": "The maximum number of times a record produce is retried on failure before the record is failed. When a record fails, all records buffered in the same partition are also failed to preserve gapless ordering. Set to `0` for unlimited retries (the default). With `idempotent_write` enabled, retries are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, "kind": "scalar", - "name": "max_in_flight", + "name": "record_retries", "type": "int" }, { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" - } - ], - "kind": "scalar", - "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n}", - "name": "", - "type": "object" - }, - "examples": [ - { - "config": "\ninput:\n generate:\n interval: 1s\n mapping: 'root.name = fake(\"name\")'\n\npipeline:\n processors:\n - mutation: |\n root.id = uuid_v4()\n root.loud_name = this.name.uppercase()\n\noutput:\n redpanda_common:\n topic: bar\n key: ${! @id }\n\nredpanda:\n seed_brokers: [ \"127.0.0.1:9092\" ]\n tls:\n enabled: true\n sasl:\n - mechanism: SCRAM-SHA-512\n password: bar\n username: foo\n", - "summary": "Data is generated and written to a topic bar, targetting the cluster configured within the redpanda block at the bottom. This is useful as it allows us to configure TLS and SASL only once for potentially multiple inputs and outputs.", - "title": "Simple Output" - } - ], - "name": "redpanda_common", - "plugin": true, - "status": "beta", - "summary": "Sends data to a Redpanda (Kafka) broker, using credentials defined in a common top-level `redpanda` config block.", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", - "examples": [ - [ - "localhost:9092" - ], - [ - "foo:9092", - "bar:9092" - ], - [ - "foo:9092,bar:9092" - ] - ], - "kind": "array", - "name": "seed_brokers", - "type": "string" - }, - { - "default": "benthos", - "description": "An identifier for the client connection.", + "default": "0s", + "description": "The maximum time a record can sit in the producer buffer before it is failed, roughly equivalent to Kafka's `delivery.timeout.ms`. This is evaluated before writing a request or after a produce response. When a record times out, all records in the same partition are also failed. Set to `0s` for no timeout (the default). With `idempotent_write` enabled, timeouts are only enforced when safe to do so without creating invalid sequence numbers.", "is_advanced": true, "kind": "scalar", - "name": "client_id", + "name": "record_delivery_timeout", "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "description": "The base URL of the schema registry service. Required for schema migration functionality.", "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + "http://localhost:8081", + "https://schema-registry.example.com:8081" ], - "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "url", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, + "default": "5s", + "description": "HTTP client timeout for schema registry requests.", + "is_optional": true, "kind": "scalar", - "name": "root_cas_file", + "name": "timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "cert", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "", - "description": "A plain text certificate key to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "The path of a certificate key to use.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "key_file", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "foo", - "${KEY_PASSWORD}" + "./root_cas.pem" ], "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "children": [ - { - "annotated_options": [ - [ - "AWS_MSK_IAM", - "AWS IAM based authentication as specified by the 'aws-msk-iam-auth' java library." - ], - [ - "OAUTHBEARER", - "OAuth Bearer based authentication." - ], - [ - "PLAIN", - "Plain text authentication." - ], - [ - "SCRAM-SHA-256", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "SCRAM-SHA-512", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "none", - "Disable sasl authentication" - ] - ], - "description": "The SASL mechanism to use.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "mechanism", - "type": "string" - }, - { - "default": "", - "description": "A username to provide for PLAIN or SCRAM-* authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to provide for PLAIN or SCRAM-* authentication.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The token to use for a single session's OAUTHBEARER authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "token", - "type": "string" - }, - { - "description": "Key/value pairs to add to OAUTHBEARER authentication requests.", - "is_advanced": true, - "is_optional": true, - "kind": "map", - "name": "extensions", - "type": "string" - }, - { - "children": [ - { - "description": "The AWS region to target.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "region", - "type": "string" - }, - { - "description": "Allows you to specify a custom endpoint for the AWS API.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "endpoint", + "name": "root_cas_file", "type": "string" }, { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "profile", - "type": "string" - }, - { - "description": "The ID of credentials to use.", + "default": "", + "description": "A plain text certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "id", + "name": "cert", "type": "string" }, { - "description": "The secret for the credentials being used.", + "default": "", + "description": "A plain text certificate key to use.", "is_advanced": true, - "is_optional": true, "is_secret": true, "kind": "scalar", - "name": "secret", + "name": "key", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": "", + "description": "The path of a certificate to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "token", + "name": "cert_file", "type": "string" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" - }, - { - "description": "A role ARN to assume.", + "default": "", + "description": "The path of a certificate key to use.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role", + "name": "key_file", "type": "string" }, { - "description": "An external ID to provide when assuming a role.", + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, - "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "role_external_id", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "credentials", + "kind": "array", + "name": "client_certs", "type": "object" } ], - "description": "Contains AWS specific fields for when the `mechanism` is set to `AWS_MSK_IAM`.", + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "aws", + "name": "oauth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", "type": "object" + }, + { + "default": true, + "description": "Whether schema registry migration is enabled. When disabled, no schema operations are performed.", + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "5m", + "description": "How often to synchronise schema registry subjects. Set to 0s for one-time sync at startup only.", + "examples": [ + "0s # One-time sync only", + "5m # Sync every 5 minutes", + "30m # Sync every 30 minutes" + ], + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "description": "Regular expressions for schema subjects to include in migration. If empty, all subjects are included (unless excluded). Note: the migrator consumer group is always ignored.", + "examples": [ + "[\"prod-.*\", \"staging-.*\"]", + "[\"user-.*\", \"order-.*\"]" + ], + "is_optional": true, + "kind": "array", + "name": "include", + "type": "string" + }, + { + "description": "Regular expressions for schema subjects to exclude from migration. Takes precedence over include patterns. Note: the migrator consumer group is always ignored.", + "examples": [ + "[\".*-test\", \".*-temp\"]", + "[\"dev-.*\", \"local-.*\"]" + ], + "is_optional": true, + "kind": "array", + "name": "exclude", + "type": "string" + }, + { + "description": "Template for transforming subject names during migration. Use interpolation to rename subjects systematically.", + "examples": [ + "prod_${! metadata(\"schema_registry_subject\") }", + "${! metadata(\"schema_registry_subject\") | replace(\"dev_\", \"prod_\") }" + ], + "interpolated": true, + "is_optional": true, + "kind": "scalar", + "name": "subject", + "type": "string" + }, + { + "default": "all", + "description": "Which schema versions to migrate. 'latest' migrates only the current version, 'all' migrates complete version history for better compatibility.", + "kind": "scalar", + "linter": "\nlet options = {\n \"latest\": true,\n \"all\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "versions", + "options": [ + "latest", + "all" + ], + "type": "string" + }, + { + "default": false, + "description": "Whether to include soft-deleted schemas in migration. Useful for complete migration but may not be supported by all schema registries.", + "kind": "scalar", + "name": "include_deleted", + "type": "bool" + }, + { + "default": false, + "description": "Whether to translate schema IDs during migration.", + "kind": "scalar", + "name": "translate_ids", + "type": "bool" + }, + { + "default": false, + "description": "Whether to normalize schemas when creating them in the destination registry.", + "kind": "scalar", + "name": "normalize", + "type": "bool" + }, + { + "default": false, + "description": "Error on unknown schema IDs. Only relevant when translate_ids is true. When false (default), unknown schema IDs are passed through unchanged, allowing migration of topics with mixed message formats. Note: messages with 0-byte prefixes (e.g., protobuf) cannot be distinguished from schema registry headers and may fail when strict is enabled.", + "kind": "scalar", + "name": "strict", + "type": "bool" + }, + { + "default": 10, + "description": "Maximum number of parallel HTTP requests to the schema registry. Controls concurrency when syncing multiple schemas.", + "kind": "scalar", + "linter": "root = if this < 1 { \"max_parallel_http_requests must be at least 1\" }", + "name": "max_parallel_http_requests", + "type": "int" } ], - "description": "Specify one or more methods of SASL authentication. SASL is tried in order; if the broker supports the first mechanism, all connections will use that mechanism. If the first mechanism fails, the client will pick the first supported mechanism. If the broker does not support any client mechanisms, connections will fail.", - "examples": [ - [ - { - "mechanism": "SCRAM-SHA-512", - "password": "bar", - "username": "foo" - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "sasl", - "type": "object" - }, - { - "default": "5m", - "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", - "is_advanced": true, - "kind": "scalar", - "name": "metadata_max_age", - "type": "string" - }, - { - "default": "10s", - "description": "The request time overhead. Uses the given time as overhead while deadlining requests. Roughly equivalent to request.timeout.ms, but grants additional time to requests that have timeout fields.", - "is_advanced": true, - "kind": "scalar", - "name": "request_timeout_overhead", - "type": "string" - }, - { - "default": "20s", - "description": "The rough amount of time to allow connections to idle before they are closed.", - "is_advanced": true, - "kind": "scalar", - "name": "conn_idle_timeout", - "type": "string" - }, - { - "description": "A topic to write messages to.", - "interpolated": true, - "kind": "scalar", - "name": "topic", - "type": "string" - }, - { - "description": "An optional key to populate for each message.", - "interpolated": true, - "is_optional": true, - "kind": "scalar", - "name": "key", - "type": "string" - }, - { - "description": "An optional explicit partition to set for each message. This field is only relevant when the `partitioner` is set to `manual`. The provided interpolation string must be a valid integer.", - "examples": [ - "${! meta(\"partition\") }" - ], - "interpolated": true, + "description": "Configuration for schema registry integration. Enables migration of schema subjects, versions, and compatibility settings between clusters.", "is_optional": true, "kind": "scalar", - "name": "partition", - "type": "string" + "name": "schema_registry", + "type": "object" }, { "children": [ { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", + "default": true, + "description": "Whether consumer group offset migration is enabled. When disabled, no consumer group operations are performed.", + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "1m", + "description": "How often to synchronise consumer group offsets. Regular syncing helps maintain offset accuracy during ongoing migration.", "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] + "0s # Disabled", + "30s # Sync every 30 seconds", + "5m # Sync every 5 minutes" + ], + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for data when fetching records for timestamp-based offset translation. Increase for clusters with low message throughput.", + "examples": [ + "1s # Fast clusters", + "10s # Slower clusters" ], + "kind": "scalar", + "name": "fetch_timeout", + "type": "string" + }, + { + "description": "Regular expressions for consumer groups to include in offset migration. If empty, all groups are included (unless excluded).", + "examples": [ + "[\"prod-.*\", \"staging-.*\"]", + "[\"app-.*\", \"service-.*\"]" + ], + "is_optional": true, "kind": "array", - "name": "include_prefixes", + "name": "include", "type": "string" }, { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", + "description": "Regular expressions for consumer groups to exclude from offset migration. Takes precedence over include patterns. Useful for excluding system or temporary groups.", "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] + "[\".*-test\", \".*-temp\", \"connect-.*\"]", + "[\"dev-.*\", \"local-.*\"]" ], + "is_optional": true, "kind": "array", - "name": "include_patterns", + "name": "exclude", "type": "string" + }, + { + "default": false, + "description": "Whether to only migrate Empty consumer groups. When false (default), all statuses except Dead are included; when true, only Empty groups are migrated.", + "kind": "scalar", + "name": "only_empty", + "type": "bool" } ], - "description": "Determine which (if any) metadata values should be added to messages as headers.", "is_optional": true, "kind": "scalar", - "name": "metadata", + "name": "consumer_groups", "type": "object" }, { - "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", + "default": "${! @kafka_topic }", + "description": "The topic to write messages to. Use interpolation to derive destination topic names from source topics. The source topic name is available as 'kafka_topic' metadata.", "examples": [ - "${! timestamp_unix() }", - "${! metadata(\"kafka_timestamp_unix\") }" + "prod_${! @kafka_topic }" ], "interpolated": true, - "is_advanced": true, - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "timestamp", + "name": "topic", "type": "string" }, { - "description": "An optional timestamp to set for each message expressed in milliseconds. When left empty, the current timestamp is used.", + "description": "The replication factor for created topics. If not specified, inherits the replication factor from source topics. Useful when migrating to clusters with different sizes.", "examples": [ - "${! timestamp_unix_milli() }", - "${! metadata(\"kafka_timestamp_ms\") }" + "3", + "1 # For single-node clusters" ], - "interpolated": true, - "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "timestamp_ms", - "type": "string" + "name": "topic_replication_factor", + "type": "int" }, { - "default": "", - "description": "The topic prefix.", - "interpolated": true, + "default": "5m", + "description": "How often to synchronize topics from the source cluster to the destination. This creates destination topics for any new source topics, including empty topics with no message flow. Set to 0s to disable periodic sync (topics are still created on first message).", + "examples": [ + "0s # Disable periodic sync", + "1m # Sync every minute", + "5m # Sync every 5 minutes" + ], "is_advanced": true, "kind": "scalar", - "name": "topic_prefix", + "name": "sync_topic_interval", "type": "string" }, { - "default": 256, - "description": "The maximum number of batches to be sending in parallel at any given time.", + "default": false, + "description": "Whether to synchronise topic ACLs from source to destination cluster. ACLs are transformed safely: ALLOW WRITE permissions are excluded, and ALLOW ALL is downgraded to ALLOW READ to prevent conflicts.", "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "sync_topic_acls", + "type": "bool" }, { - "default": "redpanda_migrator_input", - "description": "The label of the redpanda_migrator input from which to read the configurations for topics and ACLs which need to be created.", + "default": false, + "description": "Enable serverless mode for Redpanda Cloud serverless clusters. This restricts topic configurations and schema features to those supported by serverless environments.", "is_advanced": true, "kind": "scalar", - "name": "input_resource", - "type": "string" + "name": "serverless", + "type": "bool" }, { - "default": true, - "description": "Use the specified replication factor when creating topics.", + "default": "redpanda-migrator-provenance", + "description": "Header name to add to migrated records indicating their source cluster. If empty, no provenance header is added.", "is_advanced": true, "kind": "scalar", - "name": "replication_factor_override", - "type": "bool" + "name": "provenance_header", + "type": "string" }, { - "default": 3, - "description": "Replication factor for created topics. This is only used when `replication_factor_override` is set to `true`.", + "default": "redpanda-migrator-offset", + "description": "Header name to add to migrated records containing the source offset for exact consumer group migration. If empty, no offset header is added and exact offset translation is disabled. When disabled, consumer groups are still migrated but precision for empty groups may not be ideal if there are multiple records with the same timestamp, as timestamps have millisecond resolution. When consumer group migration is disabled, this header is not added.", "is_advanced": true, "kind": "scalar", - "name": "replication_factor", - "type": "int" + "name": "offset_header", + "type": "string" }, { - "default": false, - "description": "Translate schema IDs.", - "is_advanced": true, - "kind": "scalar", - "name": "translate_schema_ids", - "type": "bool" - }, - { - "default": false, - "description": "Set this to `true` when using Serverless clusters in Redpanda Cloud.", - "is_advanced": true, + "default": 10, + "description": "Maximum number of batches to have in flight at any given time. For optimal throughput, set this to the total number of partitions being copied in parallel (up to all partitions in the cluster). Setting it higher than the number of consumed partitions is ineffective.", + "examples": [ + "64 # For a cluster with 64 partitions", + "128 # For multiple topics with combined 128 partitions" + ], "kind": "scalar", - "name": "is_serverless", - "type": "bool" - }, + "name": "max_in_flight", + "type": "int" + } + ], + "kind": "scalar", + "linter": "\nroot = [\n if this.key.or(\"\") != \"\" {\n \"key field is not supported by migrator, setting it could break consumer group migration\"\n },\n if this.partitioner.or(\"\") != \"\" {\n \"partitioner field is not supported by migrator, setting it could break consumer group migration\"\n },\n if this.partition.or(\"\") != \"\" {\n \"partition field is not supported by migrator, setting it could break consumer group migration\"\n },\n if this.timestamp.or(\"\") != \"\" {\n \"timestamp field is not supported by migrator, setting it could break consumer group migration\"\n },\n if this.timestamp_ms.or(\"\") != \"\" {\n \"timestamp_ms field is not supported by migrator, setting it could break consumer group migration\"\n },\n if this.schema_registry.strict.or(false) && !this.schema_registry.translate_ids.or(false) {\n \"strict is only relevant when translate_ids is true\"\n }\n]\n", + "name": "", + "type": "object" + }, + "description": "\nThe `redpanda_migrator` output performs all migration work.\nIt coordinates topics, schema registry, and consumer groups to migrate data from a source Kafka/Redpanda cluster to a destination cluster.\n\n**IMPORTANT:** This output requires a corresponding `redpanda_migrator` input in the same pipeline.\nEach pipeline must have both input and output components configured.\n\n**Multiple migrator pairs:** When using multiple migrator pairs in a single pipeline,\nthe mapping between input and output components is done based on the label field.\nThe label of the input and output must match exactly for proper coordination.\n\n**Performance tuning for high throughput:** For workloads with high message rates or large messages,\nadjust the following settings to optimize throughput:\n\nOn the paired input component:\n- `partition_buffer_bytes: 2MB` - increases per-partition buffer size\n- `max_yield_batch_bytes: 1MB` - allows larger batches to be yielded\n\nOn this output component:\n- `max_in_flight` - set to the total number of partitions being copied in parallel (up to all partitions in the cluster)\n\nWhat gets synchronised:\n\n- Topics\n - Name resolution with interpolation (default: preserve source name)\n - Automatic creation with mirrored partition counts\n - Selectable replication factor (default: inherit from source)\n - Copy of supported topic configuration keys (serverless-aware subset)\n - Optional ACL replication with safe transforms:\n - Excludes `ALLOW WRITE` entries\n - Downgrades `ALLOW ALL` to `READ`\n - Preserves resource pattern type and host filters\n\n- Schema Registry\n - One-shot or periodic syncing\n - Subject selection via include/exclude regex\n - Subject renaming with interpolation\n - Versions: `latest` or `all` (default: `all`)\n - Optional include of soft-deleted subjects\n - ID handling: translate IDs (create-or-reuse) or keep fixed IDs and versions\n - Optional schema normalisation on create\n - Optional per-subject compatibility propagation when explicitly set on source (global mode is not forced)\n - Serverless note: schema metadata and rule sets are not copied in serverless mode\n\n- Consumer Groups\n - Periodic syncing\n - Group selection via include/exclude regex\n - Only groups in `Empty` state are migrated (active groups are skipped)\n - Timestamp-based offset translation (approximate) per partition using previous-record timestamp and `ListOffsetsAfterMilli`\n - No rewind guarantee: destination offsets are never moved backwards\n - Commit performed in parallel with per-group metrics\n - Requires matching partition counts between source and destination topics\n\nHow it runs:\n\n- Topics: synced on demand. The first write triggers discovery and creation; subsequent writes create on first encounter per topic.\n- Schema Registry: one sync at connect, then triggered when topic record has unknown schema; optional background loop controlled by `schema_registry.interval`.\n- Consumer Groups: background loop controlled by `consumer_groups.interval` and filtered by the current topic mappings.\n\nGuarantees:\n\n- Topics are created with the intended partitioning and configured replication factor. Existing topics are respected; partition mismatches are logged and consumer group migration for mismatched topics is skipped.\n- Consumer group offsets are never rewound. Only translated forward positions are committed.\n- ACL replication excludes `ALLOW WRITE` operations and downgrades `ALLOW ALL` to `READ` to avoid unsafe grants.\n\nLimitations and requirements:\n\n- Destination Schema Registry must be in `READWRITE` or `IMPORT` mode.\n- Offset translation is best-effort: if the previous-offset timestamp cannot be read, or no destination offset exists after the timestamp, that partition is skipped.\n- Consumer group migration requires identical partition counts for source and destination topics.\n\nMetrics:\n\nThe component exposes comprehensive metrics for monitoring migration operations:\n\nTopic Migration Metrics:\n- `redpanda_migrator_topics_created_total` (counter): Total number of topics successfully created on the destination cluster\n- `redpanda_migrator_topic_create_errors_total` (counter): Total number of errors encountered when creating topics\n- `redpanda_migrator_topic_create_latency_ns` (timer): Latency in nanoseconds for topic creation operations\n\nSchema Registry Migration Metrics:\n- `redpanda_migrator_sr_schemas_created_total` (counter): Total number of schemas successfully created in the destination schema registry\n- `redpanda_migrator_sr_schema_create_errors_total` (counter): Total number of errors encountered when creating schemas\n- `redpanda_migrator_sr_schema_create_latency_ns` (timer): Latency in nanoseconds for schema creation operations\n- `redpanda_migrator_sr_compatibility_updates_total` (counter): Total number of compatibility level updates applied to subjects\n- `redpanda_migrator_sr_compatibility_update_errors_total` (counter): Total number of errors encountered when updating compatibility levels\n- `redpanda_migrator_sr_compatibility_update_latency_ns` (timer): Latency in nanoseconds for compatibility level update operations\n\nConsumer Group Migration Metrics (with group label):\n- `redpanda_migrator_cg_offsets_translated_total` (counter): Total number of offsets successfully translated per consumer group\n- `redpanda_migrator_cg_offset_translation_errors_total` (counter): Total number of errors encountered when translating offsets per consumer group\n- `redpanda_migrator_cg_offset_translation_latency_ns` (timer): Latency in nanoseconds for offset translation operations per consumer group\n- `redpanda_migrator_cg_offsets_committed_total` (counter): Total number of offsets successfully committed per consumer group\n- `redpanda_migrator_cg_offset_commit_errors_total` (counter): Total number of errors encountered when committing offsets per consumer group\n- `redpanda_migrator_cg_offset_commit_latency_ns` (timer): Latency in nanoseconds for offset commit operations per consumer group\n\nConsumer Lag Metrics (with topic and partition labels):\n- `redpanda_lag` (gauge): Current consumer lag in messages for each topic partition being consumed by the migrator input. This metric shows the difference between the high water mark and the current consumer position, providing visibility into how far behind the consumer is on each partition. The metric includes labels for topic name and partition number to enable per-partition monitoring.\n\nThis component must be paired with the `redpanda_migrator` input in the same pipeline.", + "examples": [ + { + "config": "input:\n redpanda_migrator:\n seed_brokers: [\"source:9092\"]\n topics: [\"orders\", \"payments\"]\n consumer_group: \"migration\"\n\noutput:\n redpanda_migrator:\n seed_brokers: [\"destination:9092\"]\n # Write to the same topic name\n topic: ${! metadata(\"kafka_topic\") }\n schema_registry:\n url: \"http://dest-registry:8081\"\n translate_ids: true\n consumer_groups:\n interval: 1m\n", + "summary": "Migrate topics, schemas and consumer groups from source to destination.", + "title": "Basic migration" + }, + { + "config": "input:\n redpanda_migrator:\n seed_brokers: [\"source-kafka:9092\"]\n regexp_topics_include:\n - '.'\n regexp_topics_exclude:\n - '^_'\n consumer_group: \"migrator_cg\"\n schema_registry:\n url: \"http://source-registry:8081\"\n\noutput:\n redpanda_migrator:\n seed_brokers: [\"serverless-cluster.redpanda.com:9092\"]\n tls:\n enabled: true\n sasl:\n - mechanism: SCRAM-SHA-256\n username: \"migrator\"\n password: \"migrator\"\n schema_registry:\n url: \"https://serverless-cluster.redpanda.com:8081\"\n basic_auth:\n enabled: true\n username: \"migrator\"\n password: \"migrator\"\n translate_ids: true\n consumer_groups:\n exclude:\n - \"migrator_cg\" # Exclude the migration consumer group itself\n serverless: true # Enable serverless mode for restricted configurations\n", + "summary": "Migrate from Confluent/Kafka to Redpanda Cloud serverless cluster with authentication.", + "title": "Migration to Redpanda Serverless" + } + ], + "name": "redpanda_migrator", + "plugin": true, + "status": "stable", + "summary": "A specialised Kafka producer for comprehensive data migration between Apache Kafka and Redpanda clusters.", + "type": "output", + "version": "4.67.0" + }, + { + "categories": [ + "Utility" + ], + "config": { + "default": "", + "kind": "scalar", + "name": "", + "type": "string" + }, + "description": "\nThe routing of messages after this output depends on the type of input it came from. For inputs that support propagating nacks upstream such as AMQP or NATS the message will be nacked. However, for inputs that are sequential such as files or Kafka the messages will simply be reprocessed from scratch.\n\nTo learn when this output could be useful, see [the <>.", + "examples": [ + { + "config": "\noutput:\n switch:\n retry_until_success: false\n cases:\n - check: '!errored()'\n output:\n amqp_1:\n urls: [ amqps://guest:guest@localhost:5672/ ]\n target_address: queue:/the_foos\n\n - output:\n reject: \"processing failed due to: ${! error() }\"\n", + "summary": "\nThis input is particularly useful for routing messages that have failed during processing, where instead of routing them to some sort of dead letter queue we wish to push the error upstream. We can do this with a switch broker:", + "title": "Rejecting Failed Messages" + } + ], + "name": "reject", + "plugin": true, + "status": "stable", + "summary": "Rejects all messages, treating them as though the output destination failed to publish them.", + "type": "output" + }, + { + "categories": [ + "Utility" + ], + "config": { + "kind": "scalar", + "name": "", + "type": "output" + }, + "description": "\nThe routing of messages rejected by this output depends on the type of input it came from. For inputs that support propagating nacks upstream such as AMQP or NATS the message will be nacked. However, for inputs that are sequential such as files or Kafka the messages will simply be reprocessed from scratch.", + "examples": [ + { + "config": "\ninput:\n nats_jetstream:\n urls: [ nats://127.0.0.1:4222 ]\n subject: foos.pending\n\npipeline:\n processors:\n - mutation: 'root.age = this.fuzzy.age.int64()'\n\noutput:\n reject_errored:\n nats_jetstream:\n urls: [ nats://127.0.0.1:4222 ]\n subject: foos.processed\n", + "summary": "\nThe most straight forward use case for this output type is to nack messages that have failed their processing steps. In this example our mapping might fail, in which case the messages that failed are rejected and will be nacked by our input:", + "title": "Rejecting Failed Messages" + }, + { + "config": "\npipeline:\n processors:\n - mutation: 'root.age = this.fuzzy.age.int64()'\n\noutput:\n fallback:\n - reject_errored:\n http_client:\n url: http://foo:4195/post/might/become/unreachable\n retries: 3\n retry_period: 1s\n - http_client:\n url: http://bar:4196/somewhere/else\n retries: 3\n retry_period: 1s\n", + "summary": "\nAnother use case for this output is to send failed messages straight into a dead-letter queue. You use it within a xref:components:outputs/fallback.adoc[fallback output] that allows you to specify where these failed messages should go to next.", + "title": "DLQing Failed Messages" + } + ], + "name": "reject_errored", + "plugin": true, + "status": "stable", + "summary": "Rejects messages that have failed their processing steps, resulting in nack behavior at the input level, otherwise sends them to a child output.", + "type": "output" + }, + { + "categories": [ + "Utility" + ], + "config": { + "default": "", + "kind": "scalar", + "name": "", + "type": "string" + }, + "description": "Resources allow you to tidy up deeply nested configs. For example, the config:\n\n```yaml\noutput:\n broker:\n pattern: fan_out\n outputs:\n - kafka:\n addresses: [ TODO ]\n topic: foo\n - gcp_pubsub:\n project: bar\n topic: baz\n```\n\nCould also be expressed as:\n\n```yaml\noutput:\n broker:\n pattern: fan_out\n outputs:\n - resource: foo\n - resource: bar\n\noutput_resources:\n - label: foo\n kafka:\n addresses: [ TODO ]\n topic: foo\n\n - label: bar\n gcp_pubsub:\n project: bar\n topic: baz\n```\n\nYou can find out more about resources in xref:configuration:resources.adoc[]", + "name": "resource", + "plugin": true, + "status": "stable", + "summary": "Resource is an output type that channels messages to a resource output, identified by its name.", + "type": "output" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ { - "default": "schema_registry_output", - "description": "The label of the schema_registry output to use for fetching schema IDs.", + "default": 0, + "description": "The maximum number of retries before giving up on the request. If set to zero there is no discrete limit.", "is_advanced": true, "kind": "scalar", - "name": "schema_registry_output_resource", - "type": "string" - }, - { - "is_deprecated": true, - "kind": "scalar", - "name": "rack_id", - "type": "string" + "name": "max_retries", + "type": "int" }, { "children": [ { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "is_deprecated": true, - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "is_deprecated": true, - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "is_deprecated": true, + "default": "500ms", + "description": "The initial period to wait between retry attempts.", + "is_advanced": true, "kind": "scalar", - "name": "period", + "name": "initial_interval", "type": "string" }, { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "is_deprecated": true, + "default": "3s", + "description": "The maximum period to wait between retry attempts.", + "is_advanced": true, "kind": "scalar", - "name": "check", + "name": "max_interval", "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], + "default": "0s", + "description": "The maximum period to wait before retry attempts are abandoned. If zero then no limit is used.", "is_advanced": true, - "is_deprecated": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" + "kind": "scalar", + "name": "max_elapsed_time", + "type": "string" } ], - "is_deprecated": true, - "kind": "", - "name": "batching", - "type": "object" - }, - { - "annotated_options": [ - [ - "least_backup", - "Chooses the least backed up partition (the partition with the fewest amount of buffered records). Partitions are selected per batch." - ], - [ - "manual", - "Manually select a partition for each message, requires the field `partition` to be specified." - ], - [ - "murmur2_hash", - "Kafka's default hash algorithm that uses a 32-bit murmur2 hash of the key to compute which partition the record will be on." - ], - [ - "round_robin", - "Round-robin's messages through all available partitions. This algorithm has lower throughput and causes higher CPU load on brokers, but can be useful if you want to ensure an even distribution of records to partitions." - ] - ], - "description": "Override the default murmur2 hashing partitioner.", + "description": "Control time intervals between retry attempts.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"least_backup\": true,\n \"manual\": true,\n \"murmur2_hash\": true,\n \"round_robin\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "partitioner", - "type": "string" + "name": "backoff", + "type": "object" }, { - "default": true, - "description": "Enable the idempotent write producer option. This requires the `IDEMPOTENT_WRITE` permission on `CLUSTER` and can be disabled if this permission is not available.", - "is_advanced": true, + "description": "A child output.", "kind": "scalar", - "name": "idempotent_write", - "type": "bool" - }, + "name": "output", + "type": "output" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nAll messages in Redpanda Connect are always retried on an output error, but this would usually involve propagating the error back to the source of the message, whereby it would be reprocessed before reaching the output layer once again.\n\nThis output type is useful whenever we wish to avoid reprocessing a message on the event of a failed send. We might, for example, have a deduplication processor that we want to avoid reapplying to the same message more than once in the pipeline.\n\nRather than retrying the same output you may wish to retry the send using a different output target (a dead letter queue). In which case you should instead use the xref:components:outputs/fallback.adoc[`fallback`] output type.", + "name": "retry", + "plugin": true, + "status": "stable", + "summary": "Attempts to write messages to a child output and if the write fails for any reason the message is retried either until success or, if the retries or max elapsed time fields are non-zero, either is reached.", + "type": "output" + }, + { + "categories": null, + "config": { + "children": [ { - "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"lz4\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"none\": true,\n \"zstd\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "compression", - "options": [ - "lz4", - "snappy", - "gzip", - "none", - "zstd" + "description": "Salesforce instance base URL for your org, protocol included and no trailing slash. Used as the base for both the OAuth token endpoint and REST queries. Production orgs use `https://{my-domain}.my.salesforce.com`; sandboxes use `https://{my-domain}.sandbox.my.salesforce.com`. Legacy instance URLs (`https://na123.salesforce.com`) still work but My Domain URLs are strongly recommended by Salesforce.", + "examples": [ + "https://acme.my.salesforce.com", + "https://acme--staging.sandbox.my.salesforce.com" ], - "type": "string" - }, - { - "default": true, - "description": "Enables topics to be auto created if they do not exist when fetching their metadata.", - "is_advanced": true, "kind": "scalar", - "name": "allow_auto_topic_creation", - "type": "bool" + "name": "org_url", + "type": "string" }, { - "default": "10s", - "description": "The maximum period of time to wait for message sends before abandoning the request and retrying", - "is_advanced": true, + "description": "Consumer Key of the Salesforce Connected App authorized for the OAuth Client Credentials flow. Create the Connected App under Setup → App Manager → New Connected App, enable OAuth settings, enable the Client Credentials Flow under `Flow Enablement`, then copy the Consumer Key from `Manage Consumer Details`.", "kind": "scalar", - "name": "timeout", + "name": "client_id", "type": "string" }, { - "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", - "examples": [ - "100MB", - "50mib" - ], - "is_advanced": true, + "description": "Consumer Secret of the Salesforce Connected App, paired with `client_id`. Sensitive — prefer environment variable interpolation (`${SALESFORCE_CLIENT_SECRET}`) over inlining.", + "is_secret": true, "kind": "scalar", - "name": "max_message_bytes", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": "100MiB", - "description": "The upper bound for the number of bytes written to a broker connection in a single write. This field corresponds to Kafka's `socket.request.max.bytes`.", + "default": "v65.0", + "description": "Salesforce REST API version to target, prefixed with `v`. Affects endpoint paths (`/services/data/{api_version}/...`) and available fields/objects. Must be supported by your org — check Setup → Company Information. Older versions may lack recent fields.", "examples": [ - "128MB", - "50mib" + "v65.0", + "v62.0" ], - "is_advanced": true, "kind": "scalar", - "name": "broker_write_max_bytes", + "name": "api_version", "type": "string" - } - ], - "kind": "scalar", - "linter": "root = match {\n this.partitioner == \"manual\" && this.partition.or(\"\") == \"\" => \"a partition must be specified when the partitioner is set to manual\"\n this.partitioner != \"manual\" && this.partition.or(\"\") != \"\" => \"a partition cannot be specified unless the partitioner is set to manual\"\n this.timestamp.or(\"\") != \"\" && this.timestamp_ms.or(\"\") != \"\" => \"both timestamp and timestamp_ms cannot be specified simultaneously\"\n}", - "name": "", - "type": "object" - }, - "description": "\nWrites a batch of messages to a Kafka broker and waits for acknowledgement before propagating it back to the input.\n\nThis output should be used in combination with a `redpanda_migrator` input identified by the label specified in\n`input_resource` which it can query for topic and ACL configurations. Once connected, the output will attempt to\ncreate all topics which the input consumes from along with their ACLs.\n\nIf the configured broker does not contain the current message topic, this output attempts to create it along with its\nACLs.\n\nACL migration adheres to the following principles:\n\n- `ALLOW WRITE` ACLs for topics are not migrated\n- `ALLOW ALL` ACLs for topics are downgraded to `ALLOW READ`\n- Only topic ACLs are migrated, group ACLs are not migrated\n", - "examples": [ - { - "config": "\noutput:\n redpanda_migrator:\n seed_brokers: [ \"127.0.0.1:9093\" ]\n topic: ${! metadata(\"kafka_topic\").or(throw(\"missing kafka_topic metadata\")) }\n key: ${! metadata(\"kafka_key\") }\n partitioner: manual\n partition: ${! metadata(\"kafka_partition\").or(throw(\"missing kafka_partition metadata\")) }\n timestamp_ms: ${! metadata(\"kafka_timestamp_ms\").or(timestamp_unix_milli()) }\n input_resource: redpanda_migrator_input\n max_in_flight: 1\n", - "summary": "Writes messages to the configured broker and creates topics and topic ACLs if they don't exist. It also ensures that the message order is preserved.", - "title": "Transfer data" - } - ], - "name": "redpanda_migrator", - "plugin": true, - "status": "beta", - "summary": "A Redpanda Migrator output using the https://github.com/twmb/franz-go[Franz Kafka client library^].", - "type": "output", - "version": "4.37.0" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "The `redpanda_migrator` output configuration.\n", - "kind": "map", - "name": "redpanda_migrator", - "type": "unknown" }, { - "description": "The `schema_registry` output configuration. The `subject` field must be left empty.\n", - "kind": "map", - "name": "schema_registry", - "type": "unknown" + "default": 1000, + "description": "Number of records per bulk job. Also controls the output batch size.", + "kind": "scalar", + "name": "bulk_batch_size", + "type": "int" }, { - "default": false, - "description": "Allow the target Schema Registry instance to allocate different schema IDs for migrated schemas. This is useful\nwhen it already contains some schemas which differ from the ones being migrated.\n", + "default": 10, + "description": "Maximum number of bulk jobs polling concurrently in the background. Each in-flight job buffers its CSV payload in memory; lower this value if memory usage is a concern.", "kind": "scalar", - "name": "translate_schema_ids", - "type": "bool" + "name": "max_concurrent_bulk_jobs", + "type": "int" }, { - "default": "", - "description": "Specify the redpanda_migrator_bundle input label if one is assigned to it.\n", + "default": "5s", + "description": "How often to poll Salesforce for bulk job completion status.", "kind": "scalar", - "name": "input_bundle_label", + "name": "bulk_poll_interval", "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "All-in-one output which writes messages and schemas to a Kafka or Redpanda cluster. This output is meant to be used\ntogether with the `redpanda_migrator_bundle` input.\n", - "name": "redpanda_migrator_bundle", - "plugin": true, - "status": "experimental", - "summary": "Redpanda Migrator bundle output", - "type": "output" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ + }, { - "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", - "examples": [ - [ - "localhost:9092" - ], - [ - "foo:9092", - "bar:9092" - ], - [ - "foo:9092,bar:9092" - ] - ], - "kind": "array", - "name": "seed_brokers", + "default": "5s", + "description": "Maximum period to wait before flushing an incomplete batch.", + "kind": "scalar", + "name": "batch_period", "type": "string" }, { - "default": "benthos", - "description": "An identifier for the client connection.", - "is_advanced": true, + "default": 1, + "description": "Maximum number of batches to send concurrently. Increasing this improves realtime write throughput.", "kind": "scalar", - "name": "client_id", - "type": "string" + "name": "max_in_flight", + "type": "int" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, + "description": "topic name to match against the message's 'topic' field", "kind": "scalar", - "name": "enabled", - "type": "bool" + "name": "topic", + "type": "string" }, { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, + "description": "Salesforce SObject API name (e.g., Account, Contact, MyObject__c)", "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" + "name": "sobject", + "type": "string" }, { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, + "default": "upsert", + "description": "Write operation: insert, update, upsert, or delete", "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "operation", + "type": "string" }, { "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, + "description": "External ID field name, required for upsert", "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "external_id_field", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "realtime", + "description": "Write mode: realtime (sObject Collections API) or bulk (Bulk API 2.0)", + "kind": "scalar", + "name": "mode", + "type": "string" + }, + { + "default": false, + "description": "Realtime only: roll back the entire batch if any record fails", + "kind": "scalar", + "name": "all_or_none", + "type": "bool" + } + ], + "description": "Per-topic Salesforce write configuration. Each entry maps a topic to an SObject and write settings.", + "kind": "array", + "name": "topic_mappings", + "type": "object" + }, + { + "children": [ + { + "default": "5s", + "description": "HTTP request timeout.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "cert", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "", - "description": "A plain text certificate key to use.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "default": "", - "description": "The path of a certificate to use.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { "default": "", - "description": "The path of a certificate key to use.", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "key_file", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", "examples": [ - "foo", - "${KEY_PASSWORD}" + "./root_cas.pem" ], "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "root_cas_file", "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "children": [ - { - "annotated_options": [ - [ - "AWS_MSK_IAM", - "AWS IAM based authentication as specified by the 'aws-msk-iam-auth' java library." - ], - [ - "OAUTHBEARER", - "OAuth Bearer based authentication." - ], - [ - "PLAIN", - "Plain text authentication." - ], - [ - "SCRAM-SHA-256", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "SCRAM-SHA-512", - "SCRAM based authentication as specified in RFC5802." - ], - [ - "none", - "Disable sasl authentication" - ] + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } ], - "description": "The SASL mechanism to use.", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "mechanism", - "type": "string" + "name": "tls", + "type": "object" }, { "default": "", - "description": "A username to provide for PLAIN or SCRAM-* authentication.", + "description": "HTTP proxy URL. Empty string disables proxying.", "is_advanced": true, "kind": "scalar", - "name": "username", + "name": "proxy_url", "type": "string" }, { - "default": "", - "description": "A password to provide for PLAIN or SCRAM-* authentication.", + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "disable_http2", + "type": "bool" }, { - "default": "", - "description": "The token to use for a single session's OAUTHBEARER authentication.", + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", "is_advanced": true, "kind": "scalar", - "name": "token", - "type": "string" + "name": "tps_limit", + "type": "float" }, { - "description": "Key/value pairs to add to OAUTHBEARER authentication requests.", + "default": 1, + "description": "Maximum burst size for rate limiting.", "is_advanced": true, - "is_optional": true, - "kind": "map", - "name": "extensions", - "type": "string" + "kind": "scalar", + "name": "tps_burst", + "type": "int" }, { "children": [ { - "description": "The AWS region to target.", + "default": "1s", + "description": "Initial interval between retries on 429 responses.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "region", + "name": "initial_interval", "type": "string" }, { - "description": "Allows you to specify a custom endpoint for the AWS API.", + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "endpoint", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", + "is_advanced": true, + "kind": "scalar", + "name": "backoff", + "type": "object" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "description": "A profile from `~/.aws/credentials` to use.", + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "profile", + "name": "idle", "type": "string" }, { - "description": "The ID of credentials to use.", + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "id", + "name": "interval", "type": "string" }, { - "description": "The secret for the credentials being used.", + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "is_optional": true, - "is_secret": true, "kind": "scalar", - "name": "secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" }, { - "description": "The token for the credentials being used, required when using short term credentials.", + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "token", - "type": "string" + "name": "max_decoder_header_table_size", + "type": "int" }, { - "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "from_ec2_role", - "type": "bool", - "version": "4.2.0" + "name": "max_encoder_header_table_size", + "type": "int" }, { - "description": "A role ARN to assume.", + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", "type": "string" }, { - "description": "An external ID to provide when assuming a role.", + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "role_external_id", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", "type": "string" } ], - "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "credentials", + "name": "h2", "type": "object" } ], - "description": "Contains AWS specific fields for when the `mechanism` is set to `AWS_MSK_IAM`.", + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "aws", + "name": "http", "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" } ], - "description": "Specify one or more methods of SASL authentication. SASL is tried in order; if the broker supports the first mechanism, all connections will use that mechanism. If the first mechanism fails, the client will pick the first supported mechanism. If the broker does not support any client mechanisms, connections will fail.", - "examples": [ - [ - { - "mechanism": "SCRAM-SHA-512", - "password": "bar", - "username": "foo" - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "sasl", - "type": "object" - }, - { - "default": "5m", - "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", - "is_advanced": true, - "kind": "scalar", - "name": "metadata_max_age", - "type": "string" - }, - { - "default": "10s", - "description": "The request time overhead. Uses the given time as overhead while deadlining requests. Roughly equivalent to request.timeout.ms, but grants additional time to requests that have timeout fields.", + "description": "HTTP client configuration for Salesforce REST calls (OAuth token endpoint and, where applicable, data queries).", "is_advanced": true, "kind": "scalar", - "name": "request_timeout_overhead", - "type": "string" - }, + "name": "http", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "Consumes batches of messages and writes them to Salesforce.\n\nEach message must have a `topic` field (set by the per-topic processor) and a `data` field\ncontaining the Salesforce record fields. The `topic` is used to look up the correct\n`topic_mappings` entry which defines the SObject, operation, and write mode.\n\n**Realtime mode** uses the sObject Collections REST API (synchronous, up to 200 records/call).\n**Bulk mode** uses the Bulk API 2.0 (asynchronous, polls until complete).\n\n```yaml\ninput:\n broker:\n inputs:\n - kafka_franz:\n seed_brokers: [localhost:9092]\n topics: [salesforce-account]\n consumer_group: sf-sink-account\n processors:\n - mapping: |\n root.topic = @kafka_topic\n root.data = this\n - kafka_franz:\n seed_brokers: [localhost:9092]\n topics: [salesforce-contact]\n consumer_group: sf-sink-contact\n processors:\n - mapping: |\n root.topic = @kafka_topic\n root.data = this\n\noutput:\n salesforce_sink:\n org_url: \"${SALESFORCE_ORG_URL}\"\n client_id: \"${SALESFORCE_CLIENT_ID}\"\n client_secret: \"${SALESFORCE_CLIENT_SECRET}\"\n topic_mappings:\n - topic: salesforce-account\n sobject: Account\n operation: upsert\n external_id_field: External_Id__c\n mode: realtime\n - topic: salesforce-contact\n sobject: Contact\n operation: upsert\n external_id_field: External_Id__c\n mode: bulk\n```", + "name": "salesforce_sink", + "plugin": true, + "status": "stable", + "summary": "Writes messages to Salesforce, routing each topic to its own SObject configuration.", + "type": "output" + }, + { + "categories": [ + "Integration" + ], + "config": { + "children": [ { - "default": "20s", - "description": "The rough amount of time to allow connections to idle before they are closed.", - "is_advanced": true, + "description": "The base URL of the schema registry service.", "kind": "scalar", - "name": "conn_idle_timeout", + "name": "url", "type": "string" }, { - "default": "${! @kafka_offset_topic }", - "description": "Kafka offset topic.", + "description": "Subject.", "interpolated": true, "kind": "scalar", - "name": "offset_topic", + "name": "subject", "type": "string" }, { - "default": "", - "description": "Kafka offset topic prefix.", + "description": "The compatibility level for the subject. Can be one of BACKWARD, BACKWARD_TRANSITIVE, FORWARD, FORWARD_TRANSITIVE, FULL, FULL_TRANSITIVE, NONE.", "interpolated": true, "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "offset_topic_prefix", - "type": "string" - }, - { - "default": "${! @kafka_offset_group }", - "description": "Kafka offset group.", - "interpolated": true, - "kind": "scalar", - "name": "offset_group", - "type": "string" - }, - { - "default": "${! @kafka_offset_partition }", - "description": "Kafka offset partition.", - "interpolated": true, - "kind": "scalar", - "name": "offset_partition", - "type": "string" - }, - { - "default": "${! @kafka_offset_commit_timestamp }", - "description": "Kafka offset commit timestamp.", - "interpolated": true, - "kind": "scalar", - "name": "offset_commit_timestamp", - "type": "string" - }, - { - "default": "${! @kafka_offset_metadata }", - "description": "Kafka offset metadata value.", - "interpolated": true, - "kind": "scalar", - "name": "offset_metadata", - "type": "string" - }, - { - "default": "${! @kafka_is_high_watermark }", - "description": "Indicates if the update represents the high watermark of the Kafka topic partition.", - "interpolated": true, - "kind": "scalar", - "name": "is_high_watermark", + "name": "subject_compatibility_level", "type": "string" }, { - "default": "${! @kafka_key }", - "description": "Kafka key.", - "interpolated": true, - "is_deprecated": true, + "default": true, + "description": "Backfill schema references and previous versions.", + "is_advanced": true, "kind": "scalar", - "name": "kafka_key", - "type": "string" + "name": "backfill_dependencies", + "type": "bool" }, { - "default": 1, - "description": "The maximum number of batches to be sending in parallel at any given time.", - "is_deprecated": true, + "default": false, + "description": "Translate schema IDs.", + "is_advanced": true, "kind": "scalar", - "name": "max_in_flight", - "type": "int" + "name": "translate_ids", + "type": "bool" }, { - "default": "10s", - "description": "The maximum period of time to wait for message sends before abandoning the request and retrying", + "default": true, + "description": "Normalize schemas.", "is_advanced": true, "kind": "scalar", - "name": "timeout", - "type": "string" + "name": "normalize", + "type": "bool" }, { - "default": "1MiB", - "description": "The maximum space in bytes than an individual message may take, messages larger than this value will be rejected. This field corresponds to Kafka's `max.message.bytes`.", - "examples": [ - "100MB", - "50mib" - ], + "default": true, + "description": "Remove metadata from schemas.", "is_advanced": true, "kind": "scalar", - "name": "max_message_bytes", - "type": "string" + "name": "remove_metadata", + "type": "bool" }, { - "default": "100MiB", - "description": "The upper bound for the number of bytes written to a broker connection in a single write. This field corresponds to Kafka's `socket.request.max.bytes`.", - "examples": [ - "128MB", - "50mib" - ], + "default": true, + "description": "Remove rule set from schemas.", "is_advanced": true, "kind": "scalar", - "name": "broker_write_max_bytes", - "type": "string" + "name": "remove_rule_set", + "type": "bool" }, { - "default": 0, - "description": "The maximum number of retries before giving up on the request. If set to zero there is no discrete limit.", + "default": "schema_registry_input", + "description": "The label of the schema_registry input from which to read source schemas.", "is_advanced": true, "kind": "scalar", - "name": "max_retries", - "type": "int" + "name": "input_resource", + "type": "string" }, { "children": [ { - "default": "1s", - "description": "The initial period to wait between retry attempts.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "initial_interval", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "5s", - "description": "The maximum period to wait between retry attempts.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, "kind": "scalar", - "name": "max_interval", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": "30s", - "description": "The maximum period to wait before retry attempts are abandoned. If zero then no limit is used.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], "is_advanced": true, "kind": "scalar", - "name": "max_elapsed_time", - "type": "string" - } - ], - "description": "Control time intervals between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "backoff", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "This output should be used in combination with the `redpanda_migrator_offsets` input", - "name": "redpanda_migrator_offsets", - "plugin": true, - "status": "beta", - "summary": "Redpanda Migrator consumer group offsets output using the https://github.com/twmb/franz-go[Franz Kafka client library^].", - "type": "output", - "version": "4.37.0" - }, - { - "categories": [ - "Utility" - ], - "config": { - "default": "", - "kind": "scalar", - "name": "", - "type": "string" - }, - "description": "\nThe routing of messages after this output depends on the type of input it came from. For inputs that support propagating nacks upstream such as AMQP or NATS the message will be nacked. However, for inputs that are sequential such as files or Kafka the messages will simply be reprocessed from scratch.\n\nTo learn when this output could be useful, see [the <>.", - "examples": [ - { - "config": "\noutput:\n switch:\n retry_until_success: false\n cases:\n - check: '!errored()'\n output:\n amqp_1:\n urls: [ amqps://guest:guest@localhost:5672/ ]\n target_address: queue:/the_foos\n\n - output:\n reject: \"processing failed due to: ${! error() }\"\n", - "summary": "\nThis input is particularly useful for routing messages that have failed during processing, where instead of routing them to some sort of dead letter queue we wish to push the error upstream. We can do this with a switch broker:", - "title": "Rejecting Failed Messages" - } - ], - "name": "reject", - "plugin": true, - "status": "stable", - "summary": "Rejects all messages, treating them as though the output destination failed to publish them.", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "kind": "scalar", - "name": "", - "type": "output" - }, - "description": "\nThe routing of messages rejected by this output depends on the type of input it came from. For inputs that support propagating nacks upstream such as AMQP or NATS the message will be nacked. However, for inputs that are sequential such as files or Kafka the messages will simply be reprocessed from scratch.", - "examples": [ - { - "config": "\ninput:\n nats_jetstream:\n urls: [ nats://127.0.0.1:4222 ]\n subject: foos.pending\n\npipeline:\n processors:\n - mutation: 'root.age = this.fuzzy.age.int64()'\n\noutput:\n reject_errored:\n nats_jetstream:\n urls: [ nats://127.0.0.1:4222 ]\n subject: foos.processed\n", - "summary": "\nThe most straight forward use case for this output type is to nack messages that have failed their processing steps. In this example our mapping might fail, in which case the messages that failed are rejected and will be nacked by our input:", - "title": "Rejecting Failed Messages" - }, - { - "config": "\npipeline:\n processors:\n - mutation: 'root.age = this.fuzzy.age.int64()'\n\noutput:\n fallback:\n - reject_errored:\n http_client:\n url: http://foo:4195/post/might/become/unreachable\n retries: 3\n retry_period: 1s\n - http_client:\n url: http://bar:4196/somewhere/else\n retries: 3\n retry_period: 1s\n", - "summary": "\nAnother use case for this output is to send failed messages straight into a dead-letter queue. You use it within a xref:components:outputs/fallback.adoc[fallback output] that allows you to specify where these failed messages should go to next.", - "title": "DLQing Failed Messages" - } - ], - "name": "reject_errored", - "plugin": true, - "status": "stable", - "summary": "Rejects messages that have failed their processing steps, resulting in nack behavior at the input level, otherwise sends them to a child output.", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "default": "", - "kind": "scalar", - "name": "", - "type": "string" - }, - "description": "Resources allow you to tidy up deeply nested configs. For example, the config:\n\n```yaml\noutput:\n broker:\n pattern: fan_out\n outputs:\n - kafka:\n addresses: [ TODO ]\n topic: foo\n - gcp_pubsub:\n project: bar\n topic: baz\n```\n\nCould also be expressed as:\n\n```yaml\noutput:\n broker:\n pattern: fan_out\n outputs:\n - resource: foo\n - resource: bar\n\noutput_resources:\n - label: foo\n kafka:\n addresses: [ TODO ]\n topic: foo\n\n - label: bar\n gcp_pubsub:\n project: bar\n topic: baz\n```\n\nYou can find out more about resources in xref:configuration:resources.adoc[]", - "name": "resource", - "plugin": true, - "status": "stable", - "summary": "Resource is an output type that channels messages to a resource output, identified by its name.", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "default": 0, - "description": "The maximum number of retries before giving up on the request. If set to zero there is no discrete limit.", - "is_advanced": true, - "kind": "scalar", - "name": "max_retries", - "type": "int" - }, - { - "children": [ - { - "default": "500ms", - "description": "The initial period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "initial_interval", - "type": "string" - }, - { - "default": "3s", - "description": "The maximum period to wait between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "max_interval", - "type": "string" - }, - { - "default": "0s", - "description": "The maximum period to wait before retry attempts are abandoned. If zero then no limit is used.", - "is_advanced": true, - "kind": "scalar", - "name": "max_elapsed_time", - "type": "string" - } - ], - "description": "Control time intervals between retry attempts.", - "is_advanced": true, - "kind": "scalar", - "name": "backoff", - "type": "object" - }, - { - "description": "A child output.", - "kind": "scalar", - "name": "output", - "type": "output" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nAll messages in Redpanda Connect are always retried on an output error, but this would usually involve propagating the error back to the source of the message, whereby it would be reprocessed before reaching the output layer once again.\n\nThis output type is useful whenever we wish to avoid reprocessing a message on the event of a failed send. We might, for example, have a deduplication processor that we want to avoid reapplying to the same message more than once in the pipeline.\n\nRather than retrying the same output you may wish to retry the send using a different output target (a dead letter queue). In which case you should instead use the xref:components:outputs/fallback.adoc[`fallback`] output type.", - "name": "retry", - "plugin": true, - "status": "stable", - "summary": "Attempts to write messages to a child output and if the write fails for any reason the message is retried either until success or, if the retries or max elapsed time fields are non-zero, either is reached.", - "type": "output" - }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ - { - "description": "The base URL of the schema registry service.", - "kind": "scalar", - "name": "url", - "type": "string" - }, - { - "description": "Subject.", - "interpolated": true, - "kind": "scalar", - "name": "subject", - "type": "string" - }, - { - "description": "The compatibility level for the subject. Can be one of BACKWARD, BACKWARD_TRANSITIVE, FORWARD, FORWARD_TRANSITIVE, FULL, FULL_TRANSITIVE, NONE.", - "interpolated": true, - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "subject_compatibility_level", - "type": "string" - }, - { - "default": true, - "description": "Backfill schema references and previous versions.", - "is_advanced": true, - "kind": "scalar", - "name": "backfill_dependencies", - "type": "bool" - }, - { - "default": false, - "description": "Translate schema IDs.", - "is_advanced": true, - "kind": "scalar", - "name": "translate_ids", - "type": "bool" - }, - { - "default": true, - "description": "Normalize schemas.", - "is_advanced": true, - "kind": "scalar", - "name": "normalize", - "type": "bool" - }, - { - "default": true, - "description": "Remove metadata from schemas.", - "is_advanced": true, - "kind": "scalar", - "name": "remove_metadata", - "type": "bool" - }, - { - "default": true, - "description": "Remove rule set from schemas.", - "is_advanced": true, - "kind": "scalar", - "name": "remove_rule_set", - "type": "bool" - }, - { - "default": "schema_registry_input", - "description": "The label of the schema_registry input from which to read source schemas.", - "is_advanced": true, - "kind": "scalar", - "name": "input_resource", - "type": "string" - }, - { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", + "name": "root_cas_file", "type": "string" }, { @@ -43893,7 +52627,7 @@ ], "name": "schema_registry", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Publishes schemas to SchemaRegistry.", "type": "output", "version": "4.32.2" @@ -44034,7 +52768,7 @@ "description": "In order to have a different path for each object you should use function interpolations described xref:configuration:interpolation.adoc#bloblang-queries[here].\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.", "name": "sftp", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Writes files to an SFTP server.", "type": "output", "version": "3.39.0" @@ -44124,7 +52858,7 @@ ], "name": "slack_post", "plugin": true, - "status": "experimental", + "status": "stable", "type": "output" }, { @@ -44186,7 +52920,7 @@ "description": "Add or remove an emoji reaction to a Slack message using https://api.slack.com/methods/reactions.add[^reactions.add] and https://api.slack.com/methods/reactions.remove[^reactions.remove]", "name": "slack_reaction", "plugin": true, - "status": "experimental", + "status": "stable", "type": "output" }, { @@ -44503,7 +53237,7 @@ "description": "\nIn order to use a different stage and / or Snowpipe for each message, you can use function interpolations as described in\nxref:configuration:interpolation.adoc#bloblang-queries[Bloblang queries]. When using batching, messages are grouped by the calculated\nstage and Snowpipe and are streamed to individual files in their corresponding stage and, optionally, a Snowpipe\n`insertFiles` REST API call will be made for each individual file.\n\n== Credentials\n\nTwo authentication mechanisms are supported:\n\n- User/password\n- Key Pair Authentication\n\n=== User/password\n\nThis is a basic authentication mechanism which allows you to PUT data into a stage. However, it is not compatible with\nSnowpipe.\n\n=== Key pair authentication\n\nThis authentication mechanism allows Snowpipe functionality, but it does require configuring an SSH Private Key\nbeforehand. Please consult the https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[documentation^]\nfor details on how to set it up and assign the Public Key to your user.\n\nNote that the Snowflake documentation https://twitter.com/felipehoffa/status/1560811785606684672[used to suggest^]\nusing this command:\n\n```bash\nopenssl genrsa 2048 | openssl pkcs8 -topk8 -inform PEM -out rsa_key.p8\n```\n\nto generate an encrypted SSH private key. However, in this case, it uses an encryption algorithm called\n`pbeWithMD5AndDES-CBC`, which is part of the PKCS#5 v1.5 and is considered insecure. Due to this, Redpanda Connect does not\nsupport it and, if you wish to use password-protected keys directly, you must use PKCS#5 v2.0 to encrypt them by using\nthe following command (as the current Snowflake docs suggest):\n\n```bash\nopenssl genrsa 2048 | openssl pkcs8 -topk8 -v2 des3 -inform PEM -out rsa_key.p8\n```\n\nIf you have an existing key encrypted with PKCS#5 v1.5, you can re-encrypt it with PKCS#5 v2.0 using this command:\n\n```bash\nopenssl pkcs8 -in rsa_key_original.p8 -topk8 -v2 des3 -out rsa_key.p8\n```\n\nPlease consult the https://linux.die.net/man/1/pkcs8[pkcs8 command documentation^] for details on PKCS#5 algorithms.\n\n== Batching\n\nIt's common to want to upload messages to Snowflake as batched archives. The easiest way to do this is to batch your\nmessages at the output level and join the batch of messages with an\nxref:components:processors/archive.adoc[`archive`] and/or xref:components:processors/compress.adoc[`compress`]\nprocessor.\n\nFor the optimal batch size, please consult the Snowflake https://docs.snowflake.com/en/user-guide/data-load-considerations-prepare.html[documentation^].\n\n== Snowpipe\n\nGiven a table called `BENTHOS_TBL` with one column of type `variant`:\n\n```sql\nCREATE OR REPLACE TABLE BENTHOS_DB.PUBLIC.BENTHOS_TBL(RECORD variant)\n```\n\nand the following `BENTHOS_PIPE` Snowpipe:\n\n```sql\nCREATE OR REPLACE PIPE BENTHOS_DB.PUBLIC.BENTHOS_PIPE AUTO_INGEST = FALSE AS COPY INTO BENTHOS_DB.PUBLIC.BENTHOS_TBL FROM (SELECT * FROM @%BENTHOS_TBL) FILE_FORMAT = (TYPE = JSON COMPRESSION = AUTO)\n```\n\nyou can configure Redpanda Connect to use the implicit table stage `@%BENTHOS_TBL` as the `stage` and\n`BENTHOS_PIPE` as the `snowpipe`. In this case, you must set `compression` to `AUTO` and, if\nusing message batching, you'll need to configure an xref:components:processors/archive.adoc[`archive`] processor\nwith the `concatenate` format. Since the `compression` is set to `AUTO`, the\nhttps://github.com/snowflakedb/gosnowflake[gosnowflake^] client library will compress the messages automatically so you\ndon't need to add a xref:components:processors/compress.adoc[`compress`] processor for message batches.\n\nIf you add `STRIP_OUTER_ARRAY = TRUE` in your Snowpipe `FILE_FORMAT`\ndefinition, then you must use `json_array` instead of `concatenate` as the archive processor format.\n\nNOTE: Only Snowpipes with `FILE_FORMAT` `TYPE` `JSON` are currently supported.\n\n== Snowpipe troubleshooting\n\nSnowpipe https://docs.snowflake.com/en/user-guide/data-load-snowpipe-rest-apis.html[provides^] the `insertReport`\nand `loadHistoryScan` REST API endpoints which can be used to get information about recent Snowpipe calls. In\norder to query them, you'll first need to generate a valid JWT token for your Snowflake account. There are two methods\nfor doing so:\n\n- Using the `snowsql` https://docs.snowflake.com/en/user-guide/snowsql.html[utility^]:\n\n```bash\nsnowsql --private-key-path rsa_key.p8 --generate-jwt -a -u \n```\n\n- Using the Python `sql-api-generate-jwt` https://docs.snowflake.com/en/developer-guide/sql-api/authenticating.html#generating-a-jwt-in-python[utility^]:\n\n```bash\npython3 sql-api-generate-jwt.py --private_key_file_path=rsa_key.p8 --account= --user=\n```\n\nOnce you successfully generate a JWT token and store it into the `JWT_TOKEN` environment variable, then you can,\nfor example, query the `insertReport` endpoint using `curl`:\n\n```bash\ncurl -H \"Authorization: Bearer ${JWT_TOKEN}\" \"https://.snowflakecomputing.com/v1/data/pipes/../insertReport\"\n```\n\nIf you need to pass in a valid `requestId` to any of these Snowpipe REST API endpoints, you can set a\nxref:guides:bloblang/functions.adoc#uuid_v4[uuid_v4()] string in a metadata field called\n`request_id`, log it via the xref:components:processors/log.adoc[`log`] processor and\nthen configure `request_id: ${ @request_id }` ). Alternatively, you can xref:components:logger/about.adoc[enable debug logging]\n and Redpanda Connect will print the Request IDs that it sends to Snowpipe.\n\n== General troubleshooting\n\nThe underlying https://github.com/snowflakedb/gosnowflake[`gosnowflake` driver^] requires write access to\nthe default directory to use for temporary files. Please consult the https://pkg.go.dev/os#TempDir[`os.TempDir`^]\ndocs for details on how to change this directory via environment variables.\n\nA silent failure can occur due to https://github.com/snowflakedb/gosnowflake/issues/701[this issue^], where the\nunderlying https://github.com/snowflakedb/gosnowflake[`gosnowflake` driver^] doesn't return an error and doesn't\nlog a failure if it can't figure out the current username. One way to trigger this behavior is by running Redpanda Connect in a\nDocker container with a non-existent user ID (such as `--user 1000:1000`).\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "examples": [ { - "config": "\ninput:\n kafka:\n addresses:\n - localhost:9092\n topics:\n - foo\n consumer_group: benthos\n batching:\n count: 10\n period: 3s\n processors:\n - mapping: |\n meta kafka_start_offset = meta(\"kafka_offset\").from(0)\n meta kafka_end_offset = meta(\"kafka_offset\").from(-1)\n meta batch_timestamp = if batch_index() == 0 { now() }\n - mapping: |\n meta batch_timestamp = if batch_index() != 0 { meta(\"batch_timestamp\").from(0) }\n\noutput:\n snowflake_put:\n account: benthos\n user: test@benthos.dev\n private_key_file: path_to_ssh_key.pem\n role: ACCOUNTADMIN\n database: BENTHOS_DB\n warehouse: COMPUTE_WH\n schema: PUBLIC\n stage: \"@%BENTHOS_TBL\"\n path: benthos/BENTHOS_TBL/${! @kafka_partition }\n file_name: ${! @kafka_start_offset }_${! @kafka_end_offset }_${! meta(\"batch_timestamp\") }\n upload_parallel_threads: 4\n compression: NONE\n snowpipe: BENTHOS_PIPE\n", + "config": "\ninput:\n redpanda:\n seed_brokers:\n - localhost:9092\n topics:\n - foo\n consumer_group: rpcn\n max_yield_batch_bytes: 8MB\n processors:\n - mapping: |\n meta kafka_start_offset = meta(\"kafka_offset\").from(0)\n meta kafka_end_offset = meta(\"kafka_offset\").from(-1)\n meta batch_timestamp = if batch_index() == 0 { now() }\n - mapping: |\n meta batch_timestamp = if batch_index() != 0 { meta(\"batch_timestamp\").from(0) }\n\noutput:\n snowflake_put:\n account: benthos\n user: test@benthos.dev\n private_key_file: path_to_ssh_key.pem\n role: ACCOUNTADMIN\n database: BENTHOS_DB\n warehouse: COMPUTE_WH\n schema: PUBLIC\n stage: \"@%BENTHOS_TBL\"\n path: benthos/BENTHOS_TBL/${! @kafka_partition }\n file_name: ${! @kafka_start_offset }_${! @kafka_end_offset }_${! meta(\"batch_timestamp\") }\n upload_parallel_threads: 4\n compression: NONE\n snowpipe: BENTHOS_PIPE\n", "summary": "Upload message batches from realtime brokers such as Kafka persisting the batch partition and offsets in the stage path and filename similarly to the https://docs.snowflake.com/en/user-guide/kafka-connector-ts.html#step-1-view-the-copy-history-for-the-table[Kafka Connector scheme^] and call Snowpipe to load them into a table. When batching is configured at the input level, it is done per-partition.", "title": "Kafka / realtime brokers" }, @@ -44535,7 +53269,7 @@ ], "name": "snowflake_put", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Sends messages to Snowflake stages and, optionally, calls Snowpipe to load this data into one or more tables.", "type": "output", "version": "4.0.0" @@ -44872,246 +53606,115 @@ "type": "string" }, { - "default": "60s", - "description": "The max duration to wait until the data has been asynchronously committed to Snowflake.", - "examples": [ - "10s", - "10m" - ], + "default": "", + "description": "Deprecated: use `commit_backoff.max_elapsed_time` instead.", "is_advanced": true, + "is_deprecated": true, "kind": "scalar", "name": "commit_timeout", "type": "string" - } - ], - "kind": "scalar", - "linter": "root = match {\n this.exists(\"channel_prefix\") && this.exists(\"channel_name\") => [ \"both `channel_prefix` and `channel_name` can't be set simultaneously\" ],\n}", - "name": "", - "type": "object" - }, - "description": "\nIngest data into Snowflake using Snowpipe Streaming.\n\n[%header,format=dsv]\n|===\nSnowflake column type:Allowed format in Redpanda Connect\nCHAR, VARCHAR:string\nBINARY:[]byte\nNUMBER:any numeric type, string\nFLOAT:any numeric type\nBOOLEAN:bool,any numeric type,string parsable according to `strconv.ParseBool`\nTIME,DATE,TIMESTAMP:unix or RFC 3339 with nanoseconds timestamps\nVARIANT,ARRAY,OBJECT:any data type is converted into JSON\nGEOGRAPHY,GEOMETRY: Not supported\n|===\n\nFor TIMESTAMP, TIME and DATE columns, you can parse different string formats using a bloblang `mapping`.\n\nAuthentication can be configured using a https://docs.snowflake.com/en/user-guide/key-pair-auth[RSA Key Pair^].\n\nThere are https://docs.snowflake.com/en/user-guide/data-load-snowpipe-streaming-overview#limitations[limitations^] of what data types can be loaded into Snowflake using this method.\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].\n\nIt is recommended that each batches results in at least 16MiB of compressed output being written to Snowflake.\nYou can monitor the output batch size using the `snowflake_compressed_output_size_bytes` metric.\n", - "examples": [ - { - "config": "\ninput:\n postgres_cdc:\n dsn: postgres://foouser:foopass@localhost:5432/foodb\n schema: \"public\"\n slot_name: \"my_repl_slot\"\n tables: [\"my_pg_table\"]\n # We want very large batches - each batch will be sent to Snowflake individually\n # so to optimize query performance we want as big of files as we have memory for\n batching:\n count: 50000\n period: 45s\n # Prevent multiple batches from being in flight at once, so that we never send\n # a batch while another batch is being retried, this is important to ensure that\n # the Snowflake Snowpipe Streaming channel does not see older data - as it will\n # assume that the older data is already committed.\n checkpoint_limit: 1\noutput:\n snowflake_streaming:\n # We use the log sequence number in the WAL from Postgres to ensure we\n # only upload data exactly once, these are already lexicographically\n # ordered.\n offset_token: \"${!@lsn}\"\n # Since we're sending a single ordered log, we can only send one thing\n # at a time to ensure that we're properly incrementing our offset_token\n # and only using a single channel at a time.\n max_in_flight: 1\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MY_PG_TABLE\"\n private_key_file: \"my/private/key.p8\"\n", - "summary": "How to send data from a PostgreSQL table into Snowflake exactly once using Postgres Logical Replication.\n\nNOTE: If attempting to do exactly-once it's important that rows are delivered in order to the output. Be sure to read the documentation for offset_token first.\nRemoving the offset_token is a safer option that will instruct Redpanda Connect to use its default at-least-once delivery model instead.", - "title": "Exactly once CDC into Snowflake" - }, - { - "config": "\ninput:\n redpanda_common:\n topics: [\"my_topic_going_to_snow\"]\n consumer_group: \"redpanda_connect_to_snowflake\"\n # We want very large batches - each batch will be sent to Snowflake individually\n # so to optimize query performance we want as big of files as we have memory for\n fetch_max_bytes: 100MiB\n fetch_min_bytes: 50MiB\n partition_buffer_bytes: 100MiB\npipeline:\n processors:\n - schema_registry_decode:\n url: \"redpanda.example.com:8081\"\n basic_auth:\n enabled: true\n username: MY_USER_NAME\n password: \"${TODO}\"\noutput:\n fallback:\n - snowflake_streaming:\n # To ensure that we write an ordered stream each partition in kafka gets its own\n # channel.\n channel_name: \"partition-${!@kafka_partition}\"\n # Ensure that our offsets are lexicographically sorted in string form by padding with\n # leading zeros\n offset_token: offset-${!\"%016X\".format(@kafka_offset)}\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MYTABLE\"\n private_key_file: \"my/private/key.p8\"\n schema_evolution:\n enabled: true\n # In order to prevent delivery orders from messing with the order of delivered records\n # it's important that failures are immediately sent to a dead letter queue and not retried\n # to Snowflake. See the ordering documentation for the \"redpanda\" input for more details.\n - retry:\n output:\n redpanda_common:\n topic: \"dead_letter_queue\"\n", - "summary": "How to ingest data from Redpanda with consumer groups, decode the schema using the schema registry, then write the corresponding data into Snowflake exactly once.\n\nNOTE: If attempting to do exactly-once its important that records are delivered in order to the output and correctly partitioned. Be sure to read the documentation for\nchannel_name and offset_token first. Removing the offset_token is a safer option that will instruct Redpanda Connect to use its default at-least-once delivery model instead.", - "title": "Ingesting data exactly once from Redpanda" - }, - { - "config": "\ninput:\n http_server:\n path: /snowflake\nbuffer:\n memory:\n # Max inflight data before applying backpressure\n limit: 524288000 # 50MiB\n # Batching policy, influences how large the generated files sent to Snowflake are\n batch_policy:\n enabled: true\n byte_size: 33554432 # 32MiB\n period: \"10s\"\noutput:\n snowflake_streaming:\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MYTABLE\"\n private_key_file: \"my/private/key.p8\"\n # By default there is only a single channel per output table allowed\n # if we want to have multiple Redpanda Connect streams writing data\n # then we need a unique channel prefix per stream. We'll use the host\n # name to get unique prefixes in this example.\n channel_prefix: \"snowflake-channel-for-${HOST}\"\n schema_evolution:\n enabled: true\n", - "summary": "This example demonstrates how to create an HTTP server input that can recieve HTTP PUT requests\nwith JSON payloads, that are buffered locally then written to Snowflake in batches.\n\nNOTE: This example uses a buffer to respond to the HTTP request immediately, so it's possible that failures to deliver data could result in data loss.\nSee the documentation about xref:components:buffers/memory.adoc[buffers] for more information, or remove the buffer entirely to respond to the HTTP request only once the data is written to Snowflake.", - "title": "HTTP Server to push data to Snowflake" - } - ], - "name": "snowflake_streaming", - "plugin": true, - "status": "experimental", - "summary": "Ingest data into Snowflake using Snowpipe Streaming.", - "type": "output", - "version": "4.39.0" - }, - { - "categories": [ - "Network" - ], - "config": { - "children": [ - { - "description": "A network type to connect as.", - "kind": "scalar", - "linter": "\nlet options = {\n \"unix\": true,\n \"tcp\": true,\n \"udp\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "network", - "options": [ - "unix", - "tcp", - "udp" - ], - "type": "string" - }, - { - "description": "The address to connect to.", - "examples": [ - "/tmp/benthos.sock", - "127.0.0.1:6000" - ], - "kind": "scalar", - "name": "address", - "type": "string" - }, - { - "annotated_options": [ - [ - "all-bytes", - "Only applicable to file based outputs. Writes each message to a file in full, if the file already exists the old content is deleted." - ], - [ - "append", - "Append each message to the output stream without any delimiter or special encoding." - ], - [ - "lines", - "Append each message to the output stream followed by a line break." - ], - [ - "delim:x", - "Append each message to the output stream followed by a custom delimiter." - ] - ], - "default": "lines", - "description": "The way in which the bytes of messages should be written out into the output data stream. It's possible to write lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar" - ], - "kind": "scalar", - "name": "codec", - "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "default": "32ms", + "description": "The initial period to wait between status polls.", "is_advanced": true, "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" + "name": "initial_interval", + "type": "string" }, { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], + "default": "512ms", + "description": "The maximum period to wait between status polls.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "max_interval", "type": "string" }, { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "60s", + "description": "The maximum total time to wait for data to be committed. If zero then no limit is used.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "max_elapsed_time", "type": "string" }, { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], + "default": 2, + "description": "The factor by which the poll interval grows on each attempt.", "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" + "kind": "scalar", + "name": "multiplier", + "type": "float" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "Control how frequently Snowflake is polled to check if data has been committed.", "is_advanced": true, "kind": "scalar", - "name": "tls", + "name": "commit_backoff", "type": "object" + }, + { + "annotated_options": [ + [ + "array", + "Messages are an array of values where the position in the array matches up the with ordinal of the column in snowflake" + ], + [ + "object", + "Messages are an object in JSON or bloblang where the key of the object is the column name in snowflake and the value is the value for the column" + ] + ], + "default": "object", + "description": "The format at which to expect incoming messages from the rest of the pipeline in.", + "examples": [ + "array" + ], + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"array\": true,\n \"object\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "message_format", + "type": "string" + }, + { + "default": "2006-01-02T15:04:05.999999999Z07:00", + "description": "The format to parse string values for TIMESTAMP, TIMESTAMP_LTZ and TIMESTAMP_NTZ columns. Should be a layout for https://pkg.go.dev/time#Parse[^time.Parse] in Golang.", + "is_advanced": true, + "kind": "scalar", + "name": "timestamp_format", + "type": "string" } ], "kind": "scalar", + "linter": "root = match {\n this.exists(\"channel_prefix\") && this.exists(\"channel_name\") => [ \"both `channel_prefix` and `channel_name` can't be set simultaneously\" ],\n}", "name": "", "type": "object" }, - "name": "socket", + "description": "\nIngest data into Snowflake using Snowpipe Streaming.\n\n[%header,format=dsv]\n|===\nSnowflake column type:Allowed format in Redpanda Connect\nCHAR, VARCHAR:string\nBINARY:[]byte\nNUMBER:any numeric type, string\nFLOAT:any numeric type\nBOOLEAN:bool,any numeric type,string parsable according to `strconv.ParseBool`\nTIME,DATE,TIMESTAMP:unix or RFC 3339 with nanoseconds timestamps\nVARIANT,ARRAY,OBJECT:any data type is converted into JSON\nGEOGRAPHY,GEOMETRY: Not supported\n|===\n\nFor TIMESTAMP, TIME and DATE columns, you can parse different string formats using a bloblang `mapping`.\n\nAuthentication can be configured using a https://docs.snowflake.com/en/user-guide/key-pair-auth[RSA Key Pair^].\n\nThere are https://docs.snowflake.com/en/user-guide/data-load-snowpipe-streaming-overview#limitations[limitations^] of what data types can be loaded into Snowflake using this method.\n\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].\n\nIt is recommended that each batches results in at least 16MiB of compressed output being written to Snowflake.\nYou can monitor the output batch size using the `snowflake_compressed_output_size_bytes` metric.\n", + "examples": [ + { + "config": "\ninput:\n postgres_cdc:\n dsn: postgres://foouser:foopass@localhost:5432/foodb\n schema: \"public\"\n slot_name: \"my_repl_slot\"\n tables: [\"my_pg_table\"]\n # We want very large batches - each batch will be sent to Snowflake individually\n # so to optimize query performance we want as big of files as we have memory for\n batching:\n count: 50000\n period: 45s\n # Prevent multiple batches from being in flight at once, so that we never send\n # a batch while another batch is being retried, this is important to ensure that\n # the Snowflake Snowpipe Streaming channel does not see older data - as it will\n # assume that the older data is already committed.\n checkpoint_limit: 1\noutput:\n snowflake_streaming:\n # We use the log sequence number in the WAL from Postgres to ensure we\n # only upload data exactly once, these are already lexicographically\n # ordered.\n offset_token: \"${!@lsn}\"\n # Since we're sending a single ordered log, we can only send one thing\n # at a time to ensure that we're properly incrementing our offset_token\n # and only using a single channel at a time.\n max_in_flight: 1\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MY_PG_TABLE\"\n private_key_file: \"my/private/key.p8\"\n", + "summary": "How to send data from a PostgreSQL table into Snowflake exactly once using Postgres Logical Replication.\n\nNOTE: If attempting to do exactly-once it's important that rows are delivered in order to the output. Be sure to read the documentation for offset_token first.\nRemoving the offset_token is a safer option that will instruct Redpanda Connect to use its default at-least-once delivery model instead.", + "title": "Exactly once CDC into Snowflake" + }, + { + "config": "\ninput:\n redpanda:\n topics: [\"my_topic_going_to_snow\"]\n consumer_group: \"redpanda_connect_to_snowflake\"\n # We want very large batches - each batch will be sent to Snowflake individually\n # so to optimize query performance we want as big of files as we have memory for\n fetch_max_bytes: 100MiB\n fetch_min_bytes: 50MiB\n partition_buffer_bytes: 100MiB\npipeline:\n processors:\n - schema_registry_decode:\n url: \"redpanda.example.com:8081\"\n basic_auth:\n enabled: true\n username: MY_USER_NAME\n password: \"${TODO}\"\noutput:\n fallback:\n - snowflake_streaming:\n # To ensure that we write an ordered stream each partition in kafka gets its own\n # channel.\n channel_name: \"partition-${!@kafka_partition}\"\n # Ensure that our offsets are lexicographically sorted in string form by padding with\n # leading zeros\n offset_token: offset-${!\"%016X\".format(@kafka_offset)}\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MYTABLE\"\n private_key_file: \"my/private/key.p8\"\n schema_evolution:\n enabled: true\n # In order to prevent delivery orders from messing with the order of delivered records\n # it's important that failures are immediately sent to a dead letter queue and not retried\n # to Snowflake. See the ordering documentation for the \"redpanda\" input for more details.\n - retry:\n output:\n redpanda:\n topic: \"dead_letter_queue\"\n", + "summary": "How to ingest data from Redpanda with consumer groups, decode the schema using the schema registry, then write the corresponding data into Snowflake exactly once.\n\nNOTE: If attempting to do exactly-once its important that records are delivered in order to the output and correctly partitioned. Be sure to read the documentation for\nchannel_name and offset_token first. Removing the offset_token is a safer option that will instruct Redpanda Connect to use its default at-least-once delivery model instead.", + "title": "Ingesting data exactly once from Redpanda" + }, + { + "config": "\ninput:\n http_server:\n path: /snowflake\nbuffer:\n memory:\n # Max inflight data before applying backpressure\n limit: 524288000 # 50MiB\n # Batching policy, influences how large the generated files sent to Snowflake are\n batch_policy:\n enabled: true\n byte_size: 33554432 # 32MiB\n period: \"10s\"\noutput:\n snowflake_streaming:\n account: \"MYSNOW-ACCOUNT\"\n user: MYUSER\n role: ACCOUNTADMIN\n database: \"MYDATABASE\"\n schema: \"PUBLIC\"\n table: \"MYTABLE\"\n private_key_file: \"my/private/key.p8\"\n # By default there is only a single channel per output table allowed\n # if we want to have multiple Redpanda Connect streams writing data\n # then we need a unique channel prefix per stream. We'll use the host\n # name to get unique prefixes in this example.\n channel_prefix: \"snowflake-channel-for-${HOST}\"\n schema_evolution:\n enabled: true\n", + "summary": "This example demonstrates how to create an HTTP server input that can receive HTTP PUT requests\nwith JSON payloads, that are buffered locally then written to Snowflake in batches.\n\nNOTE: This example uses a buffer to respond to the HTTP request immediately, so it's possible that failures to deliver data could result in data loss.\nSee the documentation about xref:components:buffers/memory.adoc[buffers] for more information, or remove the buffer entirely to respond to the HTTP request only once the data is written to Snowflake.", + "title": "HTTP Server to push data to Snowflake" + } + ], + "name": "snowflake_streaming", "plugin": true, "status": "stable", - "summary": "Connects to a (tcp/udp/unix) server and sends a continuous stream of data, dividing messages according to the specified codec.", - "type": "output" + "summary": "Ingest data into Snowflake using Snowpipe Streaming.", + "type": "output", + "version": "4.39.0" }, { "categories": [ @@ -45443,7 +54046,7 @@ "description": "\n\n== Performance\n\nThis output benefits from sending multiple messages in flight in parallel for improved performance. You can tune the max number of in flight messages (or message batches) with the field `max_in_flight`.\n\nThis output benefits from sending messages as a batch for improved performance. Batches can be formed at both the input and output level. You can find out more xref:configuration:batching.adoc[in this doc].", "name": "splunk_hec", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Publishes messages to a Splunk HTTP Endpoint Collector (HEC).", "type": "output", "version": "4.30.0" @@ -45457,11 +54060,12 @@ { "description": "A database <> to use.", "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "driver", "options": [ "mysql", "postgres", + "pgx", "clickhouse", "mssql", "sqlite", @@ -45469,320 +54073,158 @@ "snowflake", "trino", "gocosmos", - "spanner" + "spanner", + "databricks" ], "type": "string" }, { - "description": "Data source name.", + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" + ], "kind": "scalar", - "name": "data_source_name", + "name": "dsn", "type": "string" }, { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "description": "The table to insert to.", "examples": [ - "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);" + "foo" ], "kind": "scalar", - "name": "query", + "name": "table", + "type": "string" + }, + { + "description": "A list of columns to insert.", + "examples": [ + [ + "foo", + "bar", + "baz" + ] + ], + "kind": "array", + "name": "columns", "type": "string" }, { "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of columns specified.", "examples": [ "root = [ this.cat.meow, this.doc.woofs[0] ]", "root = [ meta(\"user.id\") ]" ], - "is_optional": true, "kind": "scalar", "name": "args_mapping", "type": "string" }, + { + "description": "An optional prefix to prepend to the insert query (before INSERT).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "prefix", + "type": "string" + }, + { + "description": "An optional suffix to append to the insert query.", + "examples": [ + "ON CONFLICT (name) DO NOTHING" + ], + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "suffix", + "type": "string" + }, + { + "description": "A list of keyword options to add before the INTO clause of the query.", + "examples": [ + [ + "DELAYED", + "IGNORE" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "options", + "type": "string" + }, { "default": 64, "description": "The maximum number of inserts to run in parallel.", "kind": "scalar", "name": "max_in_flight", - "type": "int" - }, - { - "children": [ - { - "default": 0, - "description": "A number of messages at which the batch should be flushed. If `0` disables count based batching.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": 0, - "description": "An amount of bytes at which the batch should be flushed. If `0` disables size based batching.", - "kind": "scalar", - "name": "byte_size", - "type": "int" - }, - { - "default": "", - "description": "A period in which an incomplete batch should be flushed regardless of its size.", - "examples": [ - "1s", - "1m", - "500ms" - ], - "kind": "scalar", - "name": "period", - "type": "string" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should end a batch.", - "examples": [ - "this.type == \"end_of_transaction\"" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of xref:components:processors/about.adoc[processors] to apply to a batch as it is flushed. This allows you to aggregate and archive the batch however you see fit. Please note that all resulting messages are flushed as a single batch, therefore splitting the batch into smaller batches using these processors is a no-op.", - "examples": [ - [ - { - "archive": { - "format": "concatenate" - } - } - ], - [ - { - "archive": { - "format": "lines" - } - } - ], - [ - { - "archive": { - "format": "json_array" - } - } - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "description": "\nAllows you to configure a xref:configuration:batching.adoc[batching policy].", - "examples": [ - { - "byte_size": 5000, - "count": 0, - "period": "1s" - }, - { - "count": 10, - "period": "1s" - }, - { - "check": "this.contains(\"END BATCH\")", - "count": 0, - "period": "1m" - } - ], - "kind": "", - "name": "batching", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Alternatives\n\nFor basic inserts use the xref:components:outputs/sql.adoc[`sql_insert`] output. For more complex queries use the xref:components:outputs/sql_raw.adoc[`sql_raw`] output.", - "name": "sql", - "plugin": true, - "status": "deprecated", - "summary": "Executes an arbitrary SQL query for each message.", - "type": "output", - "version": "3.65.0" - }, - { - "categories": [ - "Services" - ], - "config": { - "children": [ - { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "The table to insert to.", - "examples": [ - "foo" - ], - "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "description": "A list of columns to insert.", - "examples": [ - [ - "foo", - "bar", - "baz" - ] - ], - "kind": "array", - "name": "columns", - "type": "string" - }, - { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of columns specified.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], - "kind": "scalar", - "name": "args_mapping", - "type": "string" - }, - { - "description": "An optional prefix to prepend to the insert query (before INSERT).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "prefix", - "type": "string" - }, - { - "description": "An optional suffix to append to the insert query.", - "examples": [ - "ON CONFLICT (name) DO NOTHING" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "suffix", - "type": "string" - }, - { - "description": "A list of keyword options to add before the INTO clause of the query.", - "examples": [ - [ - "DELAYED", - "IGNORE" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "options", - "type": "string" - }, - { - "default": 64, - "description": "The maximum number of inserts to run in parallel.", - "kind": "scalar", - "name": "max_in_flight", - "type": "int" - }, - { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - [ - "./init/*.sql" - ], - [ - "./foo.sql", - "./bar.sql" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" - }, - { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" - }, - { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, - { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_life_time", - "type": "string" - }, - { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle", - "type": "int" - }, - { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_open", + "type": "int" + }, + { + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" + }, + { + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" + ], + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "init_statement", + "type": "string", + "version": "4.10.0" + }, + { + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle_time", + "type": "string" + }, + { + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_life_time", + "type": "string" + }, + { + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle", + "type": "int" + }, + { + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_open", "type": "int" }, { @@ -45905,11 +54347,12 @@ { "description": "A database <> to use.", "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", "name": "driver", "options": [ "mysql", "postgres", + "pgx", "clickhouse", "mssql", "sqlite", @@ -45917,24 +54360,26 @@ "snowflake", "trino", "gocosmos", - "spanner" + "spanner", + "databricks" ], "type": "string" }, { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", "examples": [ "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", "foouser:foopassword@tcp(localhost:3306)/foodb", "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" ], "kind": "scalar", "name": "dsn", "type": "string" }, { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `pgx` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", "examples": [ "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);" ], @@ -45966,7 +54411,7 @@ { "children": [ { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `pgx` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", "kind": "scalar", "name": "query", "type": "string" @@ -45982,9 +54427,21 @@ "kind": "scalar", "name": "args_mapping", "type": "string" + }, + { + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] that, when set, is evaluated for each message to determine whether this query should be executed. The mapping should return a boolean value. The first query in the list whose `when` condition evaluates to `true` (or that has no `when` condition) is the one that executes. This enables conditional query routing based on message content or metadata without requiring `unsafe_dynamic_query`.", + "examples": [ + "root = meta(\"kafka_tombstone_message\") == \"true\"", + "root = this.operation == \"delete\"" + ], + "is_optional": true, + "kind": "scalar", + "name": "when", + "type": "string" } ], - "description": "A list of statements to run in addition to `query`. When specifying multiple statements, they are all executed within a transaction.", + "description": "A list of query statements. When a `when` condition is specified on entries, the first query whose condition evaluates to `true` (or that has no condition) is executed for each message. When no `when` conditions are present, all queries execute for each message within a transaction. When specifying multiple statements without conditions, they are all executed within a transaction.", "is_optional": true, "kind": "array", "name": "queries", @@ -45992,7 +54449,7 @@ }, { "default": 64, - "description": "The maximum number of statements to execute in parallel.", + "description": "The maximum number of batches to be sending in parallel at any given time.", "kind": "scalar", "name": "max_in_flight", "type": "int" @@ -46154,10 +54611,11 @@ } ], "kind": "scalar", - "linter": "root = match {\n !this.exists(\"queries\") && !this.exists(\"query\") => [ \"either `query` or `queries` is required\" ],\n }", + "linter": "root = if this.exists(\"queries\") && this.queries.length() > 1 && this.queries.any(q -> q.exists(\"when\")) {\n this.queries.map_each(q -> if q.exists(\"when\") { \"has_when\" } else { \"no_when\" }).fold({\"idx\": 0, \"errs\": []}, item -> {\n \"idx\": item.tally.idx + 1,\n \"errs\": if item.value == \"no_when\" && item.tally.idx < this.queries.length() - 1 {\n item.tally.errs.append(\"query at index %d has no `when` condition and will always match, making subsequent queries unreachable\".format(item.tally.idx))\n } else {\n item.tally.errs\n },\n }).errs\n } else { [] }", "name": "", "type": "object" }, + "description": "When multiple queries are configured, all messages within a batch are executed in a single database transaction. Single-query configurations execute each message individually without a transaction, preserving per-message error granularity. When consuming from Kafka, messages are automatically ordered by partition within the transaction, allowing `max_in_flight` > 1 to parallelize across partitions while preserving consume order within each partition. Messages without `kafka_partition` metadata default to partition 0.", "examples": [ { "config": "\noutput:\n sql_raw:\n driver: mysql\n dsn: foouser:foopassword@tcp(localhost:3306)/foodb\n query: \"INSERT INTO footable (id, name, topic) VALUES (?, ?, ?);\"\n args_mapping: |\n root = [\n this.user.id,\n this.user.name,\n meta(\"kafka_topic\"),\n ]\n", @@ -46168,6 +54626,11 @@ "config": "\noutput:\n processors:\n - mapping: |\n root = this\n # Prevent SQL injection when using unsafe_dynamic_query\n meta table_name = \"\\\"\" + metadata(\"table_name\").replace_all(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n sql_raw:\n driver: postgres\n dsn: postgres://localhost/postgres\n unsafe_dynamic_query: true\n queries:\n - query: |\n CREATE TABLE IF NOT EXISTS ${!metadata(\"table_name\")} (id varchar primary key, document jsonb);\n - query: |\n INSERT INTO ${!metadata(\"table_name\")} (id, document) VALUES ($1, $2)\n ON CONFLICT (id) DO UPDATE SET document = EXCLUDED.document;\n args_mapping: |\n root = [ this.id, this.document.string() ]\n\n", "summary": "Here we dynamically create output tables transactionally with inserting a record into the newly created table.", "title": "Dynamically Creating Tables (PostgreSQL)" + }, + { + "config": "\noutput:\n sql_raw:\n driver: postgres\n dsn: postgres://localhost/postgres\n max_in_flight: 8\n batching:\n count: 100\n period: 100ms\n queries:\n - when: 'root = meta(\"kafka_tombstone_message\") == \"true\"'\n query: 'DELETE FROM users WHERE id = $1'\n args_mapping: 'root = [this.id]'\n - query: |\n INSERT INTO users (id, name, updated_at)\n VALUES ($1, $2, $3)\n ON CONFLICT (id) DO UPDATE SET\n name = EXCLUDED.name,\n updated_at = EXCLUDED.updated_at\n args_mapping: 'root = [this.id, this.name, this.updated_at]'\n", + "summary": "Route messages to different SQL operations based on message metadata. Tombstone messages trigger a DELETE, while all other messages perform an upsert. All operations within a batch execute in a single transaction, ordered by Kafka partition.", + "title": "Conditional CDC Queries (PostgreSQL)" } ], "name": "sql_raw", @@ -46177,96 +54640,6 @@ "type": "output", "version": "3.65.0" }, - { - "categories": [ - "Local" - ], - "config": { - "children": [ - { - "annotated_options": [ - [ - "all-bytes", - "Only applicable to file based outputs. Writes each message to a file in full, if the file already exists the old content is deleted." - ], - [ - "append", - "Append each message to the output stream without any delimiter or special encoding." - ], - [ - "lines", - "Append each message to the output stream followed by a line break." - ], - [ - "delim:x", - "Append each message to the output stream followed by a custom delimiter." - ] - ], - "default": "lines", - "description": "The way in which the bytes of messages should be written out into the output data stream. It's possible to write lines using a custom delimiter with the `delim:x` codec, where x is the character sequence custom delimiter.", - "examples": [ - "lines", - "delim:\t", - "delim:foobar" - ], - "kind": "scalar", - "name": "codec", - "type": "string", - "version": "3.46.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "stdout", - "plugin": true, - "status": "stable", - "summary": "Prints messages to stdout as a continuous stream of data.", - "type": "output" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "description": "The command to execute as a subprocess.", - "kind": "scalar", - "name": "name", - "type": "string" - }, - { - "default": [], - "description": "A list of arguments to provide the command.", - "kind": "array", - "name": "args", - "type": "string" - }, - { - "default": "lines", - "description": "The way in which messages should be written to the subprocess.", - "kind": "scalar", - "linter": "\nlet options = {\n \"lines\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "codec", - "options": [ - "lines" - ], - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nMessages are written according to a specified codec. The process is expected to terminate gracefully when stdin is closed.\n\nIf the subprocess exits unexpectedly then Redpanda Connect will log anything printed to stderr and will log the exit code, and will attempt to execute the command again until success.\n\nThe execution environment of the subprocess is the same as the Redpanda Connect instance, including environment variables and the current working directory.", - "name": "subprocess", - "plugin": true, - "status": "beta", - "summary": "Executes a command, runs it as a subprocess, and writes messages to it over stdin.", - "type": "output" - }, { "categories": [ "Utility" @@ -46385,311 +54758,51 @@ "status": "stable", "summary": "Returns the final message payload back to the input origin of the message, where it is dealt with according to that specific input type.", "type": "output" - }, + } + ], + "processors": [ { "categories": [ - "Network" + "AI" ], "config": { "children": [ { - "description": "The URL to connect to.", + "description": "URL for the A2A agent card. Can be either a base URL (e.g., `https://example.com`) or a full path to the agent card (e.g., `https://example.com/.well-known/agent.json`). If no path is provided, defaults to `/.well-known/agent.json`. Authentication uses OAuth2 from environment variables.", "kind": "scalar", - "name": "url", + "name": "agent_card_url", "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "description": "An optional HTTP proxy URL.", - "is_advanced": true, + "description": "The user prompt to send to the agent. By default, the processor submits the entire payload as a string.", + "interpolated": true, "is_optional": true, "kind": "scalar", - "name": "proxy_url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "prompt", "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", + "default": true, + "description": "If true, returns only the text from the final agent message (concatenated from all text parts). If false, returns the complete Message or Task object as structured data with full history, artifacts, and metadata.\n\nExample with final_message_only: true (default):\n```\nHere is the answer to your question...\n```\n\nExample with final_message_only: false:\n```json\n{\n \"id\": \"task-123\",\n \"contextId\": \"ctx-456\",\n \"status\": {\n \"state\": \"completed\"\n },\n \"history\": [\n {\"role\": \"user\", \"parts\": [{\"text\": \"Your question\"}]},\n {\"role\": \"agent\", \"parts\": [{\"text\": \"Here is the answer to your question...\"}]}\n ],\n \"artifacts\": []\n}\n```\n", "is_advanced": true, "kind": "scalar", - "name": "tls", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A value used to identify the client to the service provider.", - "is_advanced": true, - "kind": "scalar", - "name": "consumer_key", - "type": "string" - }, - { - "default": "", - "description": "A secret used to establish ownership of the consumer key.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "consumer_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", - "is_advanced": true, - "kind": "scalar", - "name": "access_token", - "type": "string" - }, - { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", - "is_advanced": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, - { - "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, - "kind": "scalar", - "name": "signing_method", - "type": "string" - }, - { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" - }, - { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" - } - ], - "description": "BETA: Allows you to specify JWT authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "jwt", - "type": "object" + "name": "final_message_only", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "name": "websocket", + "description": "\nThis processor enables Redpanda Connect pipelines to communicate with A2A protocol agents. Currently only JSON-RPC transport is supported.\n\nThe processor sends a message to the agent and polls for task completion. The agent's response\nis returned as the processor output.\n\nFor more information about the A2A protocol, see https://a2a-protocol.org/latest/specification", + "name": "a2a_message", "plugin": true, "status": "stable", - "summary": "Sends messages to an HTTP server via a websocket connection.", - "type": "output" - } - ], - "processors": [ + "summary": "Sends messages to an A2A (Agent-to-Agent) protocol agent and returns the response.", + "type": "processor", + "version": "4.40.0" + }, { "categories": [ "Parsing", @@ -46747,7 +54860,7 @@ "name": "", "type": "object" }, - "description": "\nSome archive formats (such as tar, zip) treat each archive item (message part) as a file with a path. Since message parts only contain raw data a unique path must be generated for each part. This can be done by using function interpolations on the 'path' field as described in xref:configuration:interpolation.adoc#bloblang-queries[Bloblang queries]. For types that aren't file based (such as binary) the file field is ignored.\n\nThe resulting archived message adopts the metadata of the _first_ message part of the batch.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].", + "description": "\nSome archive formats (such as tar, zip) treat each archive item (message part) as a file with a path. Since message parts only contain raw data a unique path must be generated for each part. This can be done by using function interpolations on the 'path' field as described in xref:configuration:interpolation.adoc#bloblang-queries[Bloblang queries]. For types that aren't file based (such as binary) the file field is ignored.\n\nThe resulting archived message adopts the metadata of the _first_ message part of the batch.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].\n\nTo reverse this process use the xref:components:processors/unarchive.adoc[`unarchive` processor] followed by a xref:components:processors/split.adoc[`split` processor] to process each message individually.", "examples": [ { "config": "\npipeline:\n processors:\n - archive:\n format: tar\n path: ${!json(\"doc.id\")}.json\n", @@ -46800,9 +54913,9 @@ }, { "default": "", - "description": "The path of a schema document to apply. Use either this or the `schema` field.", + "description": "The path of a schema document to apply. Use either this or the `schema` field. URLs must begin with `file://` or `http://`. Note that `file://` URLs must use absolute paths (e.g. `file:///absolute/path/to/spec.avsc`); relative paths are not supported.", "examples": [ - "file://path/to/spec.avsc", + "file:///path/to/spec.avsc", "http://localhost:8081/path/to/spec/versions/1" ], "kind": "scalar", @@ -46817,57 +54930,8 @@ "description": "\nWARNING: If you are consuming or generating messages using a schema registry service then it is likely this processor will fail as those services require messages to be prefixed with the identifier of the schema version being used. Instead, try the xref:components:processors/schema_registry_encode.adoc[`schema_registry_encode`] and xref:components:processors/schema_registry_decode.adoc[`schema_registry_decode`] processors.\n\n== Operators\n\n=== `to_json`\n\nConverts Avro documents into a JSON structure. This makes it easier to\nmanipulate the contents of the document within Benthos. The encoding field\nspecifies how the source documents are encoded.\n\n=== `from_json`\n\nAttempts to convert JSON documents into Avro documents according to the\nspecified encoding.", "name": "avro", "plugin": true, - "status": "beta", - "summary": "Performs Avro based operations on messages based on a schema.", - "type": "processor" - }, - { - "categories": [ - "Mapping" - ], - "config": { - "children": [ - { - "description": "A <> defines how messages should be inserted into the AWK program as variables. The codec does not change which <> are available. The `text` codec is the closest to a typical AWK use case.", - "kind": "scalar", - "linter": "\nlet options = {\n \"none\": true,\n \"text\": true,\n \"json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "codec", - "options": [ - "none", - "text", - "json" - ], - "type": "string" - }, - { - "description": "An AWK program to execute", - "kind": "scalar", - "name": "program", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nWorks by feeding message contents as the program input based on a chosen <> and replaces the contents of each message with the result. If the result is empty (nothing is printed by the program) then the original message contents remain unchanged.\n\nComes with a wide range of <> for accessing message metadata, json fields, printing logs, etc. These functions can be overridden by functions within the program.\n\nCheck out the <> in order to see how this processor can be used.\n\nThis processor uses https://github.com/benhoyt/goawk[GoAWK^], in order to understand the differences in how the program works you can read more about it in https://github.com/benhoyt/goawk#differences-from-awk[goawk.differences^].", - "examples": [ - { - "config": "\npipeline:\n processors:\n - awk:\n codec: none\n program: |\n function map_add_vals() {\n json_set_int(\"doc.result\", json_get(\"doc.val1\") + json_get(\"doc.val2\"));\n }\n function map_multiply_vals() {\n json_set_int(\"doc.result\", json_get(\"doc.val1\") * json_get(\"doc.val2\"));\n }\n function map_unknown(type) {\n json_set(\"error\",\"unknown document type\");\n print_log(\"Document type not recognised: \" type, \"ERROR\");\n }\n {\n type = json_get(\"type\");\n if (type == \"add\")\n map_add_vals();\n else if (type == \"multiply\")\n map_multiply_vals();\n else\n map_unknown(type);\n }\n", - "summary": "\nBecause AWK is a full programming language it's much easier to map documents and perform arithmetic with it than with other Redpanda Connect processors. For example, if we were expecting documents of the form:\n\n```json\n{\"doc\":{\"val1\":5,\"val2\":10},\"id\":\"1\",\"type\":\"add\"}\n{\"doc\":{\"val1\":5,\"val2\":10},\"id\":\"2\",\"type\":\"multiply\"}\n```\n\nAnd we wished to perform the arithmetic specified in the `type` field,\non the values `val1` and `val2` and, finally, map the result into the\ndocument, giving us the following resulting documents:\n\n```json\n{\"doc\":{\"result\":15,\"val1\":5,\"val2\":10},\"id\":\"1\",\"type\":\"add\"}\n{\"doc\":{\"result\":50,\"val1\":5,\"val2\":10},\"id\":\"2\",\"type\":\"multiply\"}\n```\n\nWe can do that with the following:", - "title": "JSON Mapping and Arithmetic" - }, - { - "config": "\npipeline:\n processors:\n - awk:\n codec: none\n program: |\n {\n array_path = \"path.to.foos\"\n array_len = json_length(array_path)\n\n for (i = 0; i < array_len; i++) {\n ele = json_get(array_path \".\" i)\n if ( ! ( ele in seen ) ) {\n json_append(array_path \"_unique\", ele)\n seen[ele] = 1\n }\n }\n }\n", - "summary": "\nIt's possible to iterate JSON arrays by appending an index value to the path, this can be used to do things like removing duplicates from arrays. For example, given the following input document:\n\n```json\n{\"path\":{\"to\":{\"foos\":[\"one\",\"two\",\"three\",\"two\",\"four\"]}}}\n```\n\nWe could create a new array `foos_unique` from `foos` giving us the result:\n\n```json\n{\"path\":{\"to\":{\"foos\":[\"one\",\"two\",\"three\",\"two\",\"four\"],\"foos_unique\":[\"one\",\"two\",\"three\",\"four\"]}}}\n```\n\nWith the following config:", - "title": "Stuff With Arrays" - } - ], - "footnotes": "\n== Codecs\n\nThe chosen codec determines how the contents of the message are fed into the\nprogram. Codecs only impact the input string and variables initialized for your\nprogram, they do not change the range of custom functions available.\n\n=== `none`\n\nAn empty string is fed into the program. Functions can still be used in order to\nextract and mutate metadata and message contents.\n\nThis is useful for when your program only uses functions and doesn't need the\nfull text of the message to be parsed by the program, as it is significantly\nfaster.\n\n=== `text`\n\nThe full contents of the message are fed into the program as a string, allowing\nyou to reference tokenized segments of the message with variables ($0, $1, etc).\nCustom functions can still be used with this codec.\n\nThis is the default codec as it behaves most similar to typical usage of the awk\ncommand line tool.\n\n=== `json`\n\nAn empty string is fed into the program, and variables are automatically\ninitialized before execution of your program by walking the flattened JSON\nstructure. Each value is converted into a variable by taking its full path,\ne.g. the object:\n\n```json\n{\n\t\"foo\": {\n\t\t\"bar\": {\n\t\t\t\"value\": 10\n\t\t},\n\t\t\"created_at\": \"2018-12-18T11:57:32\"\n\t}\n}\n```\n\nWould result in the following variable declarations:\n\n```\nfoo_bar_value = 10\nfoo_created_at = \"2018-12-18T11:57:32\"\n```\n\nCustom functions can also still be used with this codec.\n\n== AWK functions\n\n=== `json_get`\n\nSignature: `json_get(path)`\n\nAttempts to find a JSON value in the input message payload by a\nxref:configuration:field_paths.adoc[dot separated path] and returns it as a string.\n\n=== `json_set`\n\nSignature: `json_set(path, value)`\n\nAttempts to set a JSON value in the input message payload identified by a\nxref:configuration:field_paths.adoc[dot separated path], the value argument will be interpreted\nas a string.\n\nIn order to set non-string values use one of the following typed varieties:\n\n- `json_set_int(path, value)`\n- `json_set_float(path, value)`\n- `json_set_bool(path, value)`\n\n=== `json_append`\n\nSignature: `json_append(path, value)`\n\nAttempts to append a value to an array identified by a\nxref:configuration:field_paths.adoc[dot separated path]. If the target does not\nexist it will be created. If the target exists but is not already an array then\nit will be converted into one, with its original contents set to the first\nelement of the array.\n\nThe value argument will be interpreted as a string. In order to append\nnon-string values use one of the following typed varieties:\n\n- `json_append_int(path, value)`\n- `json_append_float(path, value)`\n- `json_append_bool(path, value)`\n\n=== `json_delete`\n\nSignature: `json_delete(path)`\n\nAttempts to delete a JSON field from the input message payload identified by a\nxref:configuration:field_paths.adoc[dot separated path].\n\n=== `json_length`\n\nSignature: `json_length(path)`\n\nReturns the size of the string or array value of JSON field from the input\nmessage payload identified by a xref:configuration:field_paths.adoc[dot separated path].\n\nIf the target field does not exist, or is not a string or array type, then zero\nis returned. In order to explicitly check the type of a field use `json_type`.\n\n=== `json_type`\n\nSignature: `json_type(path)`\n\nReturns the type of a JSON field from the input message payload identified by a\nxref:configuration:field_paths.adoc[dot separated path].\n\nPossible values are: \"string\", \"int\", \"float\", \"bool\", \"undefined\", \"null\",\n\"array\", \"object\".\n\n=== `create_json_object`\n\nSignature: `create_json_object(key1, val1, key2, val2, ...)`\n\nGenerates a valid JSON object of key value pair arguments. The arguments are\nvariadic, meaning any number of pairs can be listed. The value will always\nresolve to a string regardless of the value type. E.g. the following call:\n\n`create_json_object(\"a\", \"1\", \"b\", 2, \"c\", \"3\")`\n\nWould result in this string:\n\n`\\{\"a\":\"1\",\"b\":\"2\",\"c\":\"3\"}`\n\n=== `create_json_array`\n\nSignature: `create_json_array(val1, val2, ...)`\n\nGenerates a valid JSON array of value arguments. The arguments are variadic,\nmeaning any number of values can be listed. The value will always resolve to a\nstring regardless of the value type. E.g. the following call:\n\n`create_json_array(\"1\", 2, \"3\")`\n\nWould result in this string:\n\n`[\"1\",\"2\",\"3\"]`\n\n=== `metadata_set`\n\nSignature: `metadata_set(key, value)`\n\nSet a metadata key for the message to a value. The value will always resolve to\na string regardless of the value type.\n\n=== `metadata_get`\n\nSignature: `metadata_get(key) string`\n\nGet the value of a metadata key from the message.\n\n=== `timestamp_unix`\n\nSignature: `timestamp_unix() int`\n\nReturns the current unix timestamp (the number of seconds since 01-01-1970).\n\n=== `timestamp_unix`\n\nSignature: `timestamp_unix(date) int`\n\nAttempts to parse a date string by detecting its format and returns the\nequivalent unix timestamp (the number of seconds since 01-01-1970).\n\n=== `timestamp_unix`\n\nSignature: `timestamp_unix(date, format) int`\n\nAttempts to parse a date string according to a format and returns the equivalent\nunix timestamp (the number of seconds since 01-01-1970).\n\nThe format is defined by showing how the reference time, defined to be\n`Mon Jan 2 15:04:05 -0700 MST 2006` would be displayed if it were the value.\n\n=== `timestamp_unix_nano`\n\nSignature: `timestamp_unix_nano() int`\n\nReturns the current unix timestamp in nanoseconds (the number of nanoseconds\nsince 01-01-1970).\n\n=== `timestamp_unix_nano`\n\nSignature: `timestamp_unix_nano(date) int`\n\nAttempts to parse a date string by detecting its format and returns the\nequivalent unix timestamp in nanoseconds (the number of nanoseconds since\n01-01-1970).\n\n=== `timestamp_unix_nano`\n\nSignature: `timestamp_unix_nano(date, format) int`\n\nAttempts to parse a date string according to a format and returns the equivalent\nunix timestamp in nanoseconds (the number of nanoseconds since 01-01-1970).\n\nThe format is defined by showing how the reference time, defined to be\n`Mon Jan 2 15:04:05 -0700 MST 2006` would be displayed if it were the value.\n\n=== `timestamp_format`\n\nSignature: `timestamp_format(unix, format) string`\n\nFormats a unix timestamp. The format is defined by showing how the reference\ntime, defined to be `Mon Jan 2 15:04:05 -0700 MST 2006` would be displayed if it\nwere the value.\n\nThe format is optional, and if omitted RFC3339 (`2006-01-02T15:04:05Z07:00`)\nwill be used.\n\n=== `timestamp_format_nano`\n\nSignature: `timestamp_format_nano(unixNano, format) string`\n\nFormats a unix timestamp in nanoseconds. The format is defined by showing how\nthe reference time, defined to be `Mon Jan 2 15:04:05 -0700 MST 2006` would be\ndisplayed if it were the value.\n\nThe format is optional, and if omitted RFC3339 (`2006-01-02T15:04:05Z07:00`)\nwill be used.\n\n=== `print_log`\n\nSignature: `print_log(message, level)`\n\nPrints a Redpanda Connect log message at a particular log level. The log level is\noptional, and if omitted the level `INFO` will be used.\n\n=== `base64_encode`\n\nSignature: `base64_encode(data)`\n\nEncodes the input data to a base64 string.\n\n=== `base64_decode`\n\nSignature: `base64_decode(data)`\n\nAttempts to base64-decode the input data and returns the decoded string if\nsuccessful. It will emit an error otherwise.\n\n", - "name": "awk", - "plugin": true, "status": "stable", - "summary": "Executes an AWK program on messages. This processor is very powerful as it offers a range of <> for querying and mutating message contents and metadata.", + "summary": "Performs Avro based operations on messages based on a schema.", "type": "processor" }, { @@ -46892,6 +54956,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -46996,7 +55120,7 @@ "type": "int" }, { - "description": "The likelihood of the model selecting higher-probability options while generating a response. A lower value makes the model omre likely to choose higher-probability options, while a higher value makes the model more likely to choose lower-probability options.", + "description": "The likelihood of the model selecting higher-probability options while generating a response. A lower value makes the model more likely to choose higher-probability options, while a higher value makes the model more likely to choose lower-probability options.", "is_optional": true, "kind": "scalar", "linter": "root = if this < 0 || this > 1 { [\"field must be between 0.0-1.0\"] }", @@ -47028,7 +55152,7 @@ "description": "This processor sends prompts to your chosen large language model (LLM) and generates text from the responses, using the AWS Bedrock API.\nFor more information, see the https://docs.aws.amazon.com/bedrock/latest/userguide[AWS Bedrock documentation^].", "name": "aws_bedrock_chat", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates responses to messages in a chat conversation, using the AWS Bedrock API.", "type": "processor", "version": "4.34.0" @@ -47055,6 +55179,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -47130,7 +55314,8 @@ "amazon.titan-embed-text-v1", "amazon.titan-embed-text-v2:0", "cohere.embed-english-v3", - "cohere.embed-multilingual-v3" + "cohere.embed-multilingual-v3", + "cohere.embed-v4:0" ], "kind": "scalar", "name": "model", @@ -47142,6 +55327,32 @@ "kind": "scalar", "name": "text", "type": "string" + }, + { + "annotated_options": [ + [ + "classification", + "Used for embeddings passed through a text classifier." + ], + [ + "clustering", + "Used for the embeddings run through a clustering algorithm." + ], + [ + "search_document", + "Used for embeddings stored in a vector database for search use-cases." + ], + [ + "search_query", + "Used for embeddings of search queries run against a vector DB to find relevant documents." + ] + ], + "description": "Specifies the type of input passed to the model. Required by Cohere embedding models; ignored by Amazon Titan models.", + "is_optional": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"classification\": true,\n \"clustering\": true,\n \"search_document\": true,\n \"search_query\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "input_type", + "type": "string" } ], "kind": "scalar", @@ -47158,7 +55369,7 @@ ], "name": "aws_bedrock_embeddings", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Computes vector embeddings on text, using the AWS Bedrock API.", "type": "processor", "version": "4.37.0" @@ -47207,6 +55418,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -47291,7 +55562,7 @@ ], "name": "aws_dynamodb_partiql", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Executes a PartiQL expression against a DynamoDB table for each message.", "type": "processor", "version": "3.48.0" @@ -47339,6 +55610,66 @@ "name": "endpoint", "type": "string" }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, { "children": [ { @@ -47673,7 +56004,7 @@ "footnotes": "\n\n== CosmosDB emulator\n\nIf you wish to run the CosmosDB emulator that is referenced in the documentation https://learn.microsoft.com/en-us/azure/cosmos-db/linux-emulator[here^], the following Docker command should do the trick:\n\n```bash\n> docker run --rm -it -p 8081:8081 --name=cosmosdb -e AZURE_COSMOS_EMULATOR_PARTITION_COUNT=10 -e AZURE_COSMOS_EMULATOR_ENABLE_DATA_PERSISTENCE=false mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator\n```\n\nNote: `AZURE_COSMOS_EMULATOR_PARTITION_COUNT` controls the number of partitions that will be supported by the emulator. The bigger the value, the longer it takes for the container to start up.\n\nAdditionally, instead of installing the container self-signed certificate which is exposed via `https://localhost:8081/_explorer/emulator.pem`, you can run https://mitmproxy.org/[mitmproxy^] like so:\n\n```bash\n> mitmproxy -k --mode \"reverse:https://localhost:8081\"\n```\n\nThen you can access the CosmosDB UI via `http://localhost:8080/_explorer/index.html` and use `http://localhost:8080` as the CosmosDB endpoint.\n", "name": "azure_cosmosdb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Creates or updates messages as JSON documents in https://learn.microsoft.com/en-us/azure/cosmos-db/introduction[Azure CosmosDB^].", "type": "processor", "version": "v4.25.0" @@ -47706,7 +56037,7 @@ "description": "Logs messages per second and bytes per second of messages that are processed at a regular interval. A summary of the amount of messages processed over the entire lifetime of the processor will also be printed when the processor shuts down.\n\nThe following metrics are exposed:\n- benchmark_messages_per_second (gauge): The current throughput in messages per second\n- benchmark_messages_total (counter): The total number of messages processed\n- benchmark_bytes_per_second (gauge): The current throughput in bytes per second\n- benchmark_bytes_total (counter): The total number of bytes processed", "name": "benchmark", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Logs basic throughput statistics of messages that pass through this processor.", "type": "processor" }, @@ -48018,7 +56349,7 @@ ], "name": "cached", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Cache the result of applying one or more processors to messages identified by a key. If the key already exists within the cache the contents of the message will be replaced with the cached result instead of applying the processors. This component is therefore useful in situations where an expensive set of processors need only be executed periodically.", "type": "processor", "version": "4.3.0" @@ -48033,7 +56364,7 @@ "name": "", "type": "processor" }, - "description": "\nBehaves similarly to the xref:components:processors/for_each.adoc[`for_each`] processor, where a list of child processors are applied to individual messages of a batch. However, processors are only applied to messages that failed a processing step prior to the catch.\n\nFor example, with the following config:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - catch:\n - resource: bar\n - resource: baz\n```\n\nIf the processor `foo` fails for a particular message, that message will be fed into the processors `bar` and `baz`. Messages that do not fail for the processor `foo` will skip these processors.\n\nWhen messages leave the catch block their fail flags are cleared. This processor is useful for when it's possible to recover failed messages, or when special actions (such as logging/metrics) are required before dropping them.\n\nMore information about error handling can be found in xref:configuration:error_handling.adoc[].", + "description": "\nBehaves similarly to the xref:components:processors/for_each.adoc[`for_each`] processor, where a list of child processors are applied to individual messages of a batch. However, processors are only applied to messages that failed a processing step prior to the catch.\n\nFor example, with the following config:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - catch:\n - resource: bar\n - resource: baz\n```\n\nIf the processor `foo` fails for a particular message, that message will be fed into the processors `bar` and `baz`. Messages that do not fail for the processor `foo` will skip these processors.\n\nWhen messages leave the catch block their fail flags are cleared. This processor is useful for when it's possible to recover failed messages, or when special actions (such as logging/metrics) are required before dropping them.\n\nNOTE: When strict error handling (`error_handling.strict`) is enabled this processor does not recover anything, as a failed message short-circuits past it rather than reaching it. Use the xref:components:processors/try_catch.adoc[`try_catch`] processor instead, which contains both the fallible step and its recovery.\n\nMore information about error handling can be found in xref:configuration:error_handling.adoc[].", "name": "catch", "plugin": true, "status": "stable", @@ -48547,7 +56878,7 @@ "description": "\nThis processor sends the contents of user prompts to the Cohere API, which generates responses. By default, the processor submits the entire payload of each message as a string, unless you use the `prompt` configuration field to customize it.\n\nTo learn more about chat completion, see the https://docs.cohere.com/docs/chat-api[Cohere API documentation^].", "name": "cohere_chat", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates responses to messages in a chat conversation, using the Cohere API.", "type": "processor", "version": "4.37.0" @@ -48641,7 +56972,7 @@ ], "name": "cohere_embeddings", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates vector embeddings to represent input text, using the Cohere API.", "type": "processor", "version": "4.37.0" @@ -48720,65 +57051,11 @@ ], "name": "cohere_rerank", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates vector embeddings to represent input text, using the Cohere API.", "type": "processor", "version": "4.37.0" }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ - { - "description": "The name of the command to execute.", - "examples": [ - "bash", - "go", - "${! @command }" - ], - "interpolated": true, - "kind": "scalar", - "name": "name", - "type": "string" - }, - { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] that, when specified, should resolve into an array of arguments to pass to the command. Command arguments are expressed this way in order to support dynamic behavior.", - "examples": [ - "[ \"-c\", this.script_path ]" - ], - "is_optional": true, - "kind": "scalar", - "name": "args_mapping", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThe specified command is executed for each message processed, with the raw bytes of the message being fed into the stdin of the command process, and the resulting message having its contents replaced with the stdout of it.\n\n== Performance\n\nSince this processor executes a new process for each message performance will likely be an issue for high throughput streams. If this is the case then consider using the xref:components:processors/subprocess.adoc[`subprocess` processor] instead as it keeps the underlying process alive long term and uses codecs to insert and extract inputs and outputs to it via stdin/stdout.\n\n== Error handling\n\nIf a non-zero error code is returned by the command then an error containing the entirety of stderr (or a generic message if nothing is written) is set on the message. These failed messages will continue through the pipeline unchanged, but can be dropped or placed in a dead letter queue according to your config, you can read about xref:configuration:error_handling.adoc[these patterns].\n\nIf the command is successful but stderr is written to then a metadata field `command_stderr` is populated with its contents.\n", - "examples": [ - { - "config": "\ninput:\n generate:\n interval: '0,30 */2 * * * *'\n mapping: 'root = \"\"' # Empty string as we do not need to pipe anything to stdin\n processors:\n - command:\n name: df\n args_mapping: '[ \"-h\" ]'\n", - "summary": "This example uses a xref:components:inputs/generate.adoc[`generate` input] to trigger a command on a cron schedule:", - "title": "Cron Scheduled Command" - }, - { - "config": "\npipeline:\n processors:\n - command:\n name: ${! this.command }\n args_mapping: 'this.args'\n", - "summary": "This example config takes structured messages of the form `{\"command\":\"echo\",\"args\":[\"foo\"]}` and uses their contents to execute the contained command and arguments dynamically, replacing its contents with the command result printed to stdout:", - "title": "Dynamic Command Execution" - } - ], - "name": "command", - "plugin": true, - "status": "experimental", - "summary": "Executes a command for each message.", - "type": "processor", - "version": "4.21.0" - }, { "categories": [ "Parsing" @@ -48818,177 +57095,6 @@ "summary": "Compresses messages according to the selected algorithm. Supported compression algorithms are: [flate gzip lz4 pgzip snappy zlib]", "type": "processor" }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ - { - "description": "Couchbase connection string.", - "examples": [ - "couchbase://localhost:11210" - ], - "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "description": "Username to connect to the cluster.", - "is_optional": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "description": "Password to connect to the cluster.", - "is_optional": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "Couchbase bucket.", - "kind": "scalar", - "name": "bucket", - "type": "string" - }, - { - "description": "Bucket collection.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "collection", - "type": "string" - }, - { - "description": "Bucket scope.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "scope", - "type": "string" - }, - { - "annotated_options": [ - [ - "json", - "JSONTranscoder implements the default transcoding behavior and applies JSON transcoding to all values. This will apply the following behavior to the value: binary ([]byte) -> error. default -> JSON value, JSON Flags." - ], - [ - "legacy", - "LegacyTranscoder implements the behavior for a backward-compatible transcoder. This transcoder implements behavior matching that of gocb v1.This will apply the following behavior to the value: binary ([]byte) -> binary bytes, Binary expectedFlags. string -> string bytes, String expectedFlags. default -> JSON value, JSON expectedFlags." - ], - [ - "raw", - "RawBinaryTranscoder implements passthrough behavior of raw binary data. This transcoder does not apply any serialization. This will apply the following behavior to the value: binary ([]byte) -> binary bytes, binary expectedFlags. default -> error." - ], - [ - "rawjson", - "RawJSONTranscoder implements passthrough behavior of JSON data. This transcoder does not apply any serialization. It will forward data across the network without incurring unnecessary parsing costs. This will apply the following behavior to the value: binary ([]byte) -> JSON bytes, JSON expectedFlags. string -> JSON bytes, JSON expectedFlags. default -> error." - ], - [ - "rawstring", - "RawStringTranscoder implements passthrough behavior of raw string data. This transcoder does not apply any serialization. This will apply the following behavior to the value: string -> string bytes, string expectedFlags. default -> error." - ] - ], - "default": "legacy", - "description": "Couchbase transcoder to use.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"json\": true,\n \"legacy\": true,\n \"raw\": true,\n \"rawjson\": true,\n \"rawstring\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "transcoder", - "type": "string" - }, - { - "default": "15s", - "description": "Operation timeout.", - "is_advanced": true, - "kind": "scalar", - "name": "timeout", - "type": "string" - }, - { - "description": "Document id.", - "examples": [ - "${! json(\"id\") }" - ], - "interpolated": true, - "kind": "scalar", - "name": "id", - "type": "string" - }, - { - "bloblang": true, - "description": "Document content.", - "is_optional": true, - "kind": "scalar", - "name": "content", - "type": "string" - }, - { - "annotated_options": [ - [ - "get", - "fetch a document." - ], - [ - "insert", - "insert a new document." - ], - [ - "remove", - "delete a document." - ], - [ - "replace", - "replace the contents of a document." - ], - [ - "upsert", - "creates a new document if it does not exist, if it does exist then it updates it." - ] - ], - "default": "get", - "description": "Couchbase operation to perform.", - "kind": "scalar", - "linter": "\nlet options = {\n \"get\": true,\n \"insert\": true,\n \"remove\": true,\n \"replace\": true,\n \"upsert\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operation", - "type": "string" - } - ], - "kind": "scalar", - "linter": "root = if ((this.operation == \"insert\" || this.operation == \"replace\" || this.operation == \"upsert\") && !this.exists(\"content\")) { [ \"content must be set for insert, replace and upsert operations.\" ] }", - "name": "", - "type": "object" - }, - "description": "When inserting, replacing or upserting documents, each must have the `content` property set.", - "name": "couchbase", - "plugin": true, - "status": "experimental", - "summary": "Performs operations against Couchbase for each message, allowing you to store or retrieve data within message payloads.", - "type": "processor", - "version": "4.11.0" - }, - { - "categories": [ - "Utility" - ], - "config": { - "interpolated": true, - "kind": "scalar", - "name": "", - "type": "string" - }, - "name": "crash", - "plugin": true, - "status": "beta", - "summary": "Crashes the process using a fatal log message. The log message can be set using function interpolations described in xref:configuration:interpolation.adoc#bloblang-queries[Bloblang queries] which allows you to log the contents and metadata of messages.", - "type": "processor" - }, { "categories": [ "Parsing" @@ -49180,7 +57286,7 @@ ], "name": "gcp_bigquery_select", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Executes a `SELECT` query against BigQuery and replaces messages with the rows returned.", "type": "processor", "version": "3.64.0" @@ -49427,7 +57533,7 @@ ], "name": "gcp_vertex_ai_chat", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates responses to messages in a chat conversation, using the Vertex AI API.", "type": "processor", "version": "4.34.0" @@ -49531,7 +57637,7 @@ "description": "This processor sends text strings to the Vertex AI API, which generates vector embeddings. By default, the processor submits the entire payload of each message as a string, unless you use the `text` configuration field to customize it.\n\nFor more information, see the https://cloud.google.com/vertex-ai/generative-ai/docs/embeddings[Vertex AI documentation^].", "name": "gcp_vertex_ai_embeddings", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Generates vector embeddings to represent input text, using the Vertex AI API.", "type": "processor", "version": "4.37.0" @@ -49592,6 +57698,13 @@ "kind": "map", "name": "export_mime_types", "type": "string" + }, + { + "default": false, + "description": "Whether or not to include shared drives.", + "kind": "scalar", + "name": "shared_drives", + "type": "bool" } ], "kind": "scalar", @@ -49608,7 +57721,7 @@ ], "name": "google_drive_download", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Downloads files from Google Drive", "type": "processor" }, @@ -49635,7 +57748,7 @@ "description": "\nCan list all labels from Google Drive.\n\t\t== Authentication\nBy default, this connector will use Google Application Default Credentials (ADC) to authenticate with Google APIs.\n\nTo use this mechanism locally, the following gcloud commands can be used:\n\n\t# Login for the application default credentials and add scopes for readonly drive access\n\tgcloud auth application-default login --scopes='openid,https://www.googleapis.com/auth/userinfo.email,https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/drive.labels.readonly'\n\t# When logging in with a user account, you may need to set the quota project for the application default credentials\n\tgcloud auth application-default set-quota-project \n\nOtherwise if using a service account, you can create a JSON key for the service account and set it in the `credentials_json` field.\nIn order for a service account to access files in Google Drive either files need to be explicitly shared with the service account email, otherwise https://support.google.com/a/answer/162106[^domain wide delegation] can be used to share all files within a Google Workspace.\n", "name": "google_drive_list_labels", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Lists labels for a file in Google Drive", "type": "processor" }, @@ -49688,6 +57801,13 @@ "kind": "scalar", "name": "max_results", "type": "int" + }, + { + "default": false, + "description": "Whether or not to include shared drives in the result.", + "kind": "scalar", + "name": "shared_drives", + "type": "bool" } ], "kind": "scalar", @@ -49704,78 +57824,8 @@ ], "name": "google_drive_search", "plugin": true, - "status": "experimental", - "summary": "Searches Google Drive for files matching the provided query.", - "type": "processor" - }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ - { - "description": "One or more Grok expressions to attempt against incoming messages. The first expression to match at least one value will be used to form a result.", - "kind": "array", - "name": "expressions", - "type": "string" - }, - { - "default": {}, - "description": "A map of pattern definitions that can be referenced within `patterns`.", - "kind": "map", - "name": "pattern_definitions", - "type": "string" - }, - { - "default": [], - "description": "A list of paths to load Grok patterns from. This field supports wildcards, including super globs (double star).", - "kind": "array", - "name": "pattern_paths", - "type": "string" - }, - { - "default": true, - "description": "Whether to only capture values from named patterns.", - "is_advanced": true, - "kind": "scalar", - "name": "named_captures_only", - "type": "bool" - }, - { - "default": true, - "description": "Whether to use a <>.", - "is_advanced": true, - "kind": "scalar", - "name": "use_default_patterns", - "type": "bool" - }, - { - "default": true, - "description": "Whether to remove values that are empty from the resulting structure.", - "is_advanced": true, - "kind": "scalar", - "name": "remove_empty_values", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nType hints within patterns are respected, therefore with the pattern `%\\{WORD:first},%{INT:second:int}` and a payload of `foo,1` the resulting payload would be `\\{\"first\":\"foo\",\"second\":1}`.\n\n== Performance\n\nThis processor currently uses the https://golang.org/s/re2syntax[Go RE2^] regular expression engine, which is guaranteed to run in time linear to the size of the input. However, this property often makes it less performant than PCRE based implementations of grok. For more information, see https://swtch.com/~rsc/regexp/regexp1.html.", - "examples": [ - { - "config": "\npipeline:\n processors:\n - grok:\n expressions:\n - '%{VPCFLOWLOG}'\n pattern_definitions:\n VPCFLOWLOG: '%{NUMBER:version:int} %{NUMBER:accountid} %{NOTSPACE:interfaceid} %{NOTSPACE:srcaddr} %{NOTSPACE:dstaddr} %{NOTSPACE:srcport:int} %{NOTSPACE:dstport:int} %{NOTSPACE:protocol:int} %{NOTSPACE:packets:int} %{NOTSPACE:bytes:int} %{NUMBER:start:int} %{NUMBER:end:int} %{NOTSPACE:action} %{NOTSPACE:logstatus}'\n", - "summary": "\nGrok can be used to parse unstructured logs such as VPC flow logs that look like this:\n\n```text\n2 123456789010 eni-1235b8ca123456789 172.31.16.139 172.31.16.21 20641 22 6 20 4249 1418530010 1418530070 ACCEPT OK\n```\n\nInto structured objects that look like this:\n\n```json\n{\"accountid\":\"123456789010\",\"action\":\"ACCEPT\",\"bytes\":4249,\"dstaddr\":\"172.31.16.21\",\"dstport\":22,\"end\":1418530070,\"interfaceid\":\"eni-1235b8ca123456789\",\"logstatus\":\"OK\",\"packets\":20,\"protocol\":6,\"srcaddr\":\"172.31.16.139\",\"srcport\":20641,\"start\":1418530010,\"version\":2}\n```\n\nWith the following config:", - "title": "VPC Flow Logs" - } - ], - "footnotes": "\n== Default patterns\n\nFor summary of the default patterns on offer, see https://github.com/Jeffail/grok/blob/master/patterns.go#L5.", - "name": "grok", - "plugin": true, "status": "stable", - "summary": "Parses messages into a structured format by attempting to apply a list of Grok expressions, the first expression to result in at least one value replaces the original message with a JSON object containing the values.", + "summary": "Searches Google Drive for files matching the provided query.", "type": "processor" }, { @@ -49808,7 +57858,7 @@ "name": "", "type": "object" }, - "description": "\nOnce the groups are established a list of processors are applied to their respective grouped batch, which can be used to label the batch as per their grouping. Messages that do not pass the check of any specified group are placed in their own group.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].", + "description": "\nOnce the groups are established a list of processors are applied to their respective grouped batch, which can be used to label the batch as per their grouping. Messages that do not pass the check of any specified group are placed in their own group.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].\n\nTo further divide each group into individual messages, follow this processor with a xref:components:processors/split.adoc[`split` processor].", "examples": [ { "config": "\npipeline:\n processors:\n - group_by:\n - check: content().contains(\"this is a foo\")\n processors:\n - archive:\n format: tar\n - compress:\n algorithm: gzip\n - mapping: 'meta grouping = \"foo\"'\n\noutput:\n switch:\n cases:\n - check: meta(\"grouping\") == \"foo\"\n output:\n gcp_pubsub:\n project: foo_prod\n topic: only_the_foos\n - output:\n gcp_pubsub:\n project: somewhere_else\n topic: no_foos_here\n", @@ -49844,7 +57894,7 @@ "name": "", "type": "object" }, - "description": "\nThis allows you to group messages using arbitrary fields within their content or metadata, process them individually, and send them to unique locations as per their group.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].", + "description": "\nThis allows you to group messages using arbitrary fields within their content or metadata, process them individually, and send them to unique locations as per their group.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching xref:configuration:batching.adoc[in this doc].\n\nTo further divide each group into individual messages, follow this processor with a xref:components:processors/split.adoc[`split` processor].", "footnotes": "\n== Examples\n\nIf we were consuming Kafka messages and needed to group them by their key, archive the groups, and send them to S3 with the key as part of the path we could achieve that with the following:\n\n```yaml\npipeline:\n processors:\n - group_by_value:\n value: ${! meta(\"kafka_key\") }\n - archive:\n format: tar\n - compress:\n algorithm: gzip\noutput:\n aws_s3:\n bucket: TODO\n path: docs/${! meta(\"kafka_key\") }/${! count(\"files\") }-${! timestamp_unix_nano() }.tar.gz\n```", "name": "group_by_value", "plugin": true, @@ -50504,57 +58554,542 @@ }, { "categories": [ - "Mapping" + "Services" ], "config": { "children": [ { - "description": "An inline JavaScript program to run. One of `code` or `file` must be defined.", - "is_optional": true, + "description": "Jira instance account username/email", "kind": "scalar", - "name": "code", + "name": "username", "type": "string" }, { - "description": "A file containing a JavaScript program to run. One of `code` or `file` must be defined.", - "is_optional": true, + "description": "Jira API token for the specified account", + "is_secret": true, "kind": "scalar", - "name": "file", + "name": "api_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "default": [], - "description": "List of folders that will be used to load modules from if the requested JS module is not found elsewhere.", - "kind": "array", - "name": "global_folders", + "default": 50, + "description": "Maximum number of results to return per page when calling JIRA API", + "kind": "scalar", + "name": "max_results_per_page", + "type": "int" + }, + { + "description": "Base URL of the target service (e.g., https://api.example.com). TLS is enabled automatically for https URLs.", + "kind": "scalar", + "name": "base_url", "type": "string" + }, + { + "default": "5s", + "description": "HTTP request timeout.", + "kind": "scalar", + "name": "timeout", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "default": "", + "description": "HTTP proxy URL. Empty string disables proxying.", + "is_advanced": true, + "kind": "scalar", + "name": "proxy_url", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP/2 and force HTTP/1.1.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_http2", + "type": "bool" + }, + { + "default": 0, + "description": "Rate limit in requests per second. 0 disables rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_limit", + "type": "float" + }, + { + "default": 1, + "description": "Maximum burst size for rate limiting.", + "is_advanced": true, + "kind": "scalar", + "name": "tps_burst", + "type": "int" + }, + { + "children": [ + { + "default": "1s", + "description": "Initial interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "30s", + "description": "Maximum interval between retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": 3, + "description": "Maximum number of retries on 429 responses.", + "is_advanced": true, + "kind": "scalar", + "name": "max_retries", + "type": "int" + } + ], + "description": "Adaptive backoff configuration for 429 (Too Many Requests) responses. Always active.", + "is_advanced": true, + "kind": "scalar", + "name": "backoff", + "type": "object" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "default": 100, + "description": "Maximum total number of idle (keep-alive) connections across all hosts. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns", + "type": "int" + }, + { + "default": 0, + "description": "Maximum idle connections to keep per host. 0 (the default) uses GOMAXPROCS+1.", + "is_advanced": true, + "kind": "scalar", + "name": "max_idle_conns_per_host", + "type": "int" + }, + { + "default": 64, + "description": "Maximum total connections (active + idle) per host. 0 means unlimited.", + "is_advanced": true, + "kind": "scalar", + "name": "max_conns_per_host", + "type": "int" + }, + { + "default": "1m30s", + "description": "How long an idle connection remains in the pool before being closed. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "idle_conn_timeout", + "type": "string" + }, + { + "default": "10s", + "description": "Maximum time to wait for a TLS handshake to complete. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "tls_handshake_timeout", + "type": "string" + }, + { + "default": "1s", + "description": "Maximum time to wait for a server's 100-continue response before sending the body. 0 means the body is sent immediately.", + "is_advanced": true, + "kind": "scalar", + "name": "expect_continue_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Maximum time to wait for response headers after writing the full request. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "response_header_timeout", + "type": "string" + }, + { + "default": false, + "description": "Disable HTTP keep-alive connections; each request uses a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_keep_alives", + "type": "bool" + }, + { + "default": false, + "description": "Disable automatic decompression of gzip responses.", + "is_advanced": true, + "kind": "scalar", + "name": "disable_compression", + "type": "bool" + }, + { + "default": 1048576, + "description": "Maximum bytes of response headers to allow.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_header_bytes", + "type": "int" + }, + { + "default": 10485760, + "description": "Maximum bytes of response body the client will read. The response body is wrapped with a limit reader; reads beyond this cap return EOF. 0 disables the limit.", + "is_advanced": true, + "kind": "scalar", + "name": "max_response_body_bytes", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection write buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "write_buffer_size", + "type": "int" + }, + { + "default": 4096, + "description": "Size in bytes of the per-connection read buffer.", + "is_advanced": true, + "kind": "scalar", + "name": "read_buffer_size", + "type": "int" + }, + { + "children": [ + { + "default": false, + "description": "When true, new requests block when a connection's concurrency limit is reached instead of opening a new connection.", + "is_advanced": true, + "kind": "scalar", + "name": "strict_max_concurrent_requests", + "type": "bool" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to decode headers from the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_decoder_header_table_size", + "type": "int" + }, + { + "default": 4096, + "description": "Upper limit in bytes for the HPACK header table used to encode headers sent to the peer. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_encoder_header_table_size", + "type": "int" + }, + { + "default": 16384, + "description": "Largest HTTP/2 frame this endpoint will read. Valid range: 16 KiB to 16 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_read_frame_size", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a connection. Must be at least 64 KiB and less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_connection", + "type": "int" + }, + { + "default": 1048576, + "description": "Maximum flow-control window size in bytes for data received on a single stream. Must be less than 4 MiB.", + "is_advanced": true, + "kind": "scalar", + "name": "max_receive_buffer_per_stream", + "type": "int" + }, + { + "default": "0s", + "description": "Idle timeout after which a PING frame is sent to verify connection health. 0 disables health checks.", + "is_advanced": true, + "kind": "scalar", + "name": "send_ping_timeout", + "type": "string" + }, + { + "default": "15s", + "description": "Timeout waiting for a PING response before closing the connection.", + "is_advanced": true, + "kind": "scalar", + "name": "ping_timeout", + "type": "string" + }, + { + "default": "0s", + "description": "Timeout for writing data to a connection. The timer resets whenever bytes are written. 0 disables the timeout.", + "is_advanced": true, + "kind": "scalar", + "name": "write_byte_timeout", + "type": "string" + } + ], + "description": "HTTP/2-specific transport settings. Only applied when HTTP/2 is enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "h2", + "type": "object" + } + ], + "description": "HTTP transport settings controlling connection pooling, timeouts, and HTTP/2.", + "is_advanced": true, + "kind": "scalar", + "name": "http", + "type": "object" + }, + { + "default": "", + "description": "Log level for HTTP request/response logging. Empty disables logging.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"\": true,\n \"trace\": true,\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "access_log_level", + "options": [ + "", + "TRACE", + "DEBUG", + "INFO", + "WARN", + "ERROR" + ], + "type": "string" + }, + { + "default": 0, + "description": "Maximum bytes of request/response body to include in logs. 0 to skip body logging.", + "is_advanced": true, + "kind": "scalar", + "name": "access_log_body_limit", + "type": "int" } ], "kind": "scalar", - "linter": "\nlet codeLen = (this.code | \"\").length()\nlet fileLen = (this.file | \"\").length()\nroot = if $codeLen == 0 && $fileLen == 0 {\n \"either the code or file field must be specified\"\n} else if $codeLen > 0 && $fileLen > 0 {\n \"cannot specify both the code and file fields\"\n}", "name": "", "type": "object" }, - "description": "\nThe https://github.com/dop251/goja[execution engine^] behind this processor provides full ECMAScript 5.1 support (including regex and strict mode). Most of the ECMAScript 6 spec is implemented but this is a work in progress.\n\nImports via `require` should work similarly to NodeJS, and access to the console is supported which will print via the Redpanda Connect logger. More caveats can be found on https://github.com/dop251/goja#known-incompatibilities-and-caveats[GitHub^].\n\nThis processor is implemented using the https://github.com/dop251/goja[github.com/dop251/goja^] library.", + "description": "Executes Jira API queries based on input messages and returns structured results. The processor handles pagination, retries, and field expansion automatically.\n\nThis processor is deprecated in favour of the `jira` input, which streams issues, comments, and changelog entries with cursor-based incremental polling. The processor remains available for enrichment and lookup style operations.\n\nSupports querying the following Jira resources:\n- Issues (JQL queries)\n- Issue transitions\n- Users\n- Roles\n- Project versions\n- Project categories\n- Project types\n- Projects\n\nThe processor authenticates using basic authentication with username and API token. Input messages should contain valid Jira queries in JSON format.", "examples": [ { - "config": "\npipeline:\n processors:\n - javascript:\n code: 'benthos.v0_msg_set_string(benthos.v0_msg_as_string() + \"hello world\");'\n", - "summary": "In this example we define a simple function that performs a basic mutation against messages, treating their contents as raw strings.", - "title": "Simple mutation" + "config": "\npipeline:\n processors:\n - jira:\n base_url: \"https://your-domain.atlassian.net\"\n username: \"${JIRA_USERNAME}\"\n api_token: \"${JIRA_API_TOKEN}\"\n", + "summary": "Basic Jira processor setup with required fields only", + "title": "Minimal configuration" }, { - "config": "\npipeline:\n processors:\n - javascript:\n code: |\n (() => {\n let thing = benthos.v0_msg_as_structured();\n thing.num_keys = Object.keys(thing).length;\n delete thing[\"b\"];\n benthos.v0_msg_set_structured(thing);\n })();\n", - "summary": "In this example we define a function that performs basic mutations against a structured message. Note that we encapsulate the logic within an anonymous function that is called for each invocation, this is required in order to avoid duplicate variable declarations in the global state.", - "title": "Structured mutation" + "config": "\npipeline:\n processors:\n - jira:\n base_url: \"https://your-domain.atlassian.net\"\n username: \"${JIRA_USERNAME}\"\n api_token: \"${JIRA_API_TOKEN}\"\n max_results_per_page: 200\n timeout: \"30s\"\n", + "summary": "Complete configuration with pagination and timeout settings", + "title": "Full configuration with tuning" } ], - "footnotes": "\n== Runtime\n\nIn order to optimize code execution JS runtimes are created on demand (in order to support parallel execution) and are reused across invocations. Therefore, it is important to understand that global state created by your programs will outlive individual invocations. In order for your programs to avoid failing after the first invocation ensure that you do not define variables at the global scope.\n\nAlthough technically possible, it is recommended that you do not rely on the global state for maintaining state across invocations as the pooling nature of the runtimes will prevent deterministic behavior. We aim to support deterministic strategies for mutating global state in the future.\n\n== Functions\n\n### `benthos.v0_fetch`\n\nExecutes an HTTP request synchronously and returns the result as an object of the form `{\"status\":200,\"body\":\"foo\"}`.\n\n#### Parameters\n\n**`url`** <string> The URL to fetch \n**`headers`** <object(string,string)> An object of string/string key/value pairs to add the request as headers. \n**`method`** <string> The method of the request. \n**`body`** <(optional) string> A body to send. \n\n#### Examples\n\n```javascript\nlet result = benthos.v0_fetch(\"http://example.com\", {}, \"GET\", \"\")\nbenthos.v0_msg_set_structured(result);\n```\n\n### `benthos.v0_msg_as_string`\n\nObtain the raw contents of the processed message as a string.\n\n#### Examples\n\n```javascript\nlet contents = benthos.v0_msg_as_string();\n```\n\n### `benthos.v0_msg_as_structured`\n\nObtain the root of the processed message as a structured value. If the message is not valid JSON or has not already been expanded into a structured form this function will throw an error.\n\n#### Examples\n\n```javascript\nlet foo = benthos.v0_msg_as_structured().foo;\n```\n\n### `benthos.v0_msg_exists_meta`\n\nCheck that a metadata key exists.\n\n#### Parameters\n\n**`name`** <string> The metadata key to search for. \n\n#### Examples\n\n```javascript\nif (benthos.v0_msg_exists_meta(\"kafka_key\")) {}\n```\n\n### `benthos.v0_msg_get_meta`\n\nGet the value of a metadata key from the processed message.\n\n#### Parameters\n\n**`name`** <string> The metadata key to search for. \n\n#### Examples\n\n```javascript\nlet key = benthos.v0_msg_get_meta(\"kafka_key\");\n```\n\n### `benthos.v0_msg_set_meta`\n\nSet a metadata key on the processed message to a value.\n\n#### Parameters\n\n**`name`** <string> The metadata key to set. \n**`value`** <anything> The value to set it to. \n\n#### Examples\n\n```javascript\nbenthos.v0_msg_set_meta(\"thing\", \"hello world\");\n```\n\n### `benthos.v0_msg_set_string`\n\nSet the contents of the processed message to a given string.\n\n#### Parameters\n\n**`value`** <string> The value to set it to. \n\n#### Examples\n\n```javascript\nbenthos.v0_msg_set_string(\"hello world\");\n```\n\n### `benthos.v0_msg_set_structured`\n\nSet the root of the processed message to a given value of any type.\n\n#### Parameters\n\n**`value`** <anything> The value to set it to. \n\n#### Examples\n\n```javascript\nbenthos.v0_msg_set_structured({\n \"foo\": \"a thing\",\n \"bar\": \"something else\",\n \"baz\": 1234\n});\n```\n\n", - "name": "javascript", + "name": "jira", "plugin": true, - "status": "experimental", - "summary": "Executes a provided JavaScript code block or file for each message.", + "status": "deprecated", + "summary": "Queries Jira resources and returns structured data", "type": "processor", - "version": "4.14.0" + "version": "4.68.0" }, { "categories": [ @@ -51043,46 +59578,11 @@ }, "name": "mongodb", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Performs operations against MongoDB for each message, allowing you to store or retrieve data within message payloads.", "type": "processor", "version": "3.43.0" }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ - { - "annotated_options": [ - [ - "from_json", - "Convert JSON messages to MessagePack format" - ], - [ - "to_json", - "Convert MessagePack messages to JSON format" - ] - ], - "description": "The operation to perform on messages.", - "kind": "scalar", - "linter": "\nlet options = {\n \"from_json\": true,\n \"to_json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operator", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "msgpack", - "plugin": true, - "status": "beta", - "summary": "Converts messages to or from the https://msgpack.org/[MessagePack^] format.", - "type": "processor", - "version": "3.59.0" - }, { "categories": [ "Mapping", @@ -51427,6 +59927,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -51441,10 +59969,10 @@ "name": "", "type": "object" }, - "description": "\n== KV operations\n\nThe NATS KV processor supports a multitude of KV operations via the <> field. Along with `get`, `put`, and `delete`, this processor supports atomic operations like `update` and `create`, as well as utility operations like `purge`, `history`, and `keys`.\n\n== Metadata\n\nThis processor adds the following metadata fields to each message, depending on the chosen `operation`:\n\n=== get, get_revision\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_delta\n- nats_kv_operation\n- nats_kv_created\n```\n\n=== create, update, delete, purge\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_operation\n```\n\n=== keys\n``` text\n- nats_kv_bucket\n```\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\n== KV operations\n\nThe NATS KV processor supports a multitude of KV operations via the <> field. Along with `get`, `put`, and `delete`, this processor supports atomic operations like `update` and `create`, as well as utility operations like `purge`, `history`, and `keys`.\n\n== Metadata\n\nThis processor adds the following metadata fields to each message, depending on the chosen `operation`:\n\n=== get, get_revision\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_delta\n- nats_kv_operation\n- nats_kv_created\n```\n\n=== create, update, delete, purge\n``` text\n- nats_kv_key\n- nats_kv_bucket\n- nats_kv_revision\n- nats_kv_operation\n```\n\n=== keys\n``` text\n- nats_kv_bucket\n```\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_kv", "plugin": true, - "status": "beta", + "status": "stable", "summary": "Perform operations on a NATS key-value bucket.", "type": "processor", "version": "4.12.0" @@ -51760,6 +60288,34 @@ "name": "user_nkey_seed", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" + }, + { + "description": "An optional plain text user name (given along with the corresponding user password).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "user", + "type": "string" + }, + { + "description": "An optional plain text password (given along with the corresponding user name).", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "An optional plain text token.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "description": "Optional configuration of NATS authentication parameters.", @@ -51773,10 +60329,10 @@ "name": "", "type": "object" }, - "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_sequence_stream\n- nats_sequence_consumer\n- nats_num_delivered\n- nats_num_pending\n- nats_domain\n- nats_timestamp_unix_nano\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].", + "description": "\n== Metadata\n\nThis input adds the following metadata fields to each message:\n\n```text\n- nats_subject\n- nats_sequence_stream\n- nats_sequence_consumer\n- nats_num_delivered\n- nats_num_pending\n- nats_domain\n- nats_timestamp_unix_nano\n```\n\nYou can access these metadata fields using xref:configuration:interpolation.adoc#bloblang-queries[function interpolation].\n\n== Connection name\n\nWhen monitoring and managing a production NATS system, it is often useful to\nknow which connection a message was send/received from. This can be achieved by\nsetting the connection name option when creating a NATS connection.\n\nRedpanda Connect will automatically set the connection name based off the label of the given\nNATS component, so that monitoring tools between NATS and Redpanda Connect can stay in sync.\n\n\n== Authentication\n\nThere are several components within Redpanda Connect which uses NATS services. You will find that each of these components\nsupport optional advanced authentication parameters for https://docs.nats.io/nats-server/configuration/securing_nats/auth_intro/nkey_auth[NKeys^]\nand https://docs.nats.io/using-nats/developer/connecting/creds[User Credentials^].\n\nSee an https://docs.nats.io/running-a-nats-service/nats_admin/security/jwt[in-depth tutorial^].\n\n=== NKey file\n\nThe NATS server can use these NKeys in several ways for authentication. The simplest is for the server to be configured\nwith a list of known public keys and for the clients to respond to the challenge by signing it with its private NKey\nconfigured in the `nkey_file` or `nkey` field.\n\nhttps://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[More details^].\n\n=== User credentials\n\nNATS server supports decentralized authentication based on JSON Web Tokens (JWT). Clients need an https://docs.nats.io/nats-server/configuration/securing_nats/jwt#json-web-tokens[user JWT^]\nand a corresponding https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/nkey_auth[NKey secret^] when connecting to a server\nwhich is configured to use this authentication scheme.\n\nThe `user_credentials_file` field should point to a file containing both the private key and the JWT and can be\ngenerated with the https://docs.nats.io/nats-tools/nsc[nsc tool^].\n\nAlternatively, the `user_jwt` field can contain a plain text JWT and the `user_nkey_seed`can contain\nthe plain text NKey Seed.\n\nhttps://docs.nats.io/using-nats/developer/connecting/creds[More details^].\n\n=== Token\n\nThe `token` field can contain a plain text token string for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/tokens[token-based authentication^].\n\n=== User and password\n\nThe `user` and `password` fields can be used for https://docs.nats.io/running-a-nats-service/configuration/securing_nats/auth_intro/username_password[username/password authentication^].", "name": "nats_request_reply", "plugin": true, - "status": "experimental", + "status": "stable", "summary": "Sends a message to a NATS subject and expects a reply, from a NATS subscriber acting as a responder, back.", "type": "processor", "version": "4.27.0" @@ -51802,19 +60358,34 @@ "config": { "children": [ { - "description": "The name of the Ollama LLM to use. For a full list of models, see the https://ollama.com/models[Ollama website].", + "default": "https://api.openai.com/v1", + "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", + "kind": "scalar", + "name": "server_address", + "type": "string" + }, + { + "description": "The API key for OpenAI API.", + "is_secret": true, + "kind": "scalar", + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The name of the OpenAI model to use.", "examples": [ - "llama3.1", - "gemma2", - "qwen2", - "phi3" + "gpt-4o", + "gpt-4o-mini", + "gpt-4", + "gpt4-turbo" ], "kind": "scalar", "name": "model", "type": "string" }, { - "description": "The prompt you want to generate a response for. By default, the processor submits the entire payload as a string.", + "description": "The user prompt you want to generate a response for. By default, the processor submits the entire payload as a string.", "interpolated": true, "is_optional": true, "kind": "scalar", @@ -51822,9 +60393,8 @@ "type": "string" }, { - "description": "The system prompt to submit to the Ollama LLM.", + "description": "The system prompt to submit along with the user prompt.", "interpolated": true, - "is_advanced": true, "is_optional": true, "kind": "scalar", "name": "system_prompt", @@ -51832,7 +60402,15 @@ }, { "bloblang": true, - "description": "The image to submit along with the prompt to the model. The result should be a byte array.", + "description": "The history of the prior conversation. A bloblang query that should result in an array of objects of the form: [{\"role\": \"user\", \"content\": \"\"}, {\"role\":\"assistant\", \"content\":\"\"}]", + "is_optional": true, + "kind": "scalar", + "name": "history", + "type": "string" + }, + { + "bloblang": true, + "description": "An image to send along with the prompt. The mapping result must be a byte array.", "examples": [ "root = this.image.decode(\"base64\") # decode base64 encoded image" ], @@ -51843,127 +60421,415 @@ "version": "4.38.0" }, { - "default": "text", - "description": "The format of the response that the Ollama model generates. If specifying JSON output, then the `prompt` should specify that the output should be in JSON as well.", - "kind": "scalar", - "linter": "\nlet options = {\n \"text\": true,\n \"json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "response_format", - "options": [ - "text", - "json" - ], - "type": "string" - }, - { - "description": "The maximum number of tokens to predict and output. Limiting the amount of output means that requests are processed faster and have a fixed limit on the cost.", + "description": "The maximum number of tokens that can be generated in the chat completion.", "is_optional": true, "kind": "scalar", "name": "max_tokens", "type": "int" }, { - "description": "The temperature of the model. Increasing the temperature makes the model answer more creatively.", + "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or top_p but not both.", "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < 0 { [ \"field must be between 0.0 and 2.0\" ] }", + "linter": "root = if this > 2 || this < 0 { [ \"field must be between 0 and 2\" ] }", "name": "temperature", - "type": "int" + "type": "float" }, { - "description": "Specify the number of tokens from the initial prompt to retain when the model resets its internal context. By default, this value is set to `4`. Use `-1` to retain all tokens from the initial prompt.", - "is_advanced": true, + "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.", + "interpolated": true, "is_optional": true, "kind": "scalar", - "name": "num_keep", - "type": "int" + "name": "user", + "type": "string" }, { - "description": "Sets the random number seed to use for generation. Setting this to a specific number will make the model generate the same text for the same prompt.", - "examples": [ - 42 + "default": "text", + "description": "Specify the model's output format. If `json_schema` is specified, then additionally a `json_schema` or `schema_registry` must be configured.", + "kind": "scalar", + "linter": "\nlet options = {\n \"text\": true,\n \"json\": true,\n \"json_schema\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "response_format", + "options": [ + "text", + "json", + "json_schema" ], - "is_advanced": true, + "type": "string" + }, + { + "children": [ + { + "description": "The name of the schema.", + "kind": "scalar", + "name": "name", + "type": "string" + }, + { + "description": "Additional description of the schema for the LLM.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "description", + "type": "string" + }, + { + "description": "The JSON schema for the LLM to use when generating the output.", + "kind": "scalar", + "name": "schema", + "type": "string" + } + ], + "description": "The JSON schema to use when responding in `json_schema` format. To learn more about what JSON schema is supported see the https://platform.openai.com/docs/guides/structured-outputs/supported-schemas[OpenAI documentation^].", "is_optional": true, "kind": "scalar", - "name": "seed", - "type": "int" + "name": "json_schema", + "type": "object" }, { - "description": "Reduces the probability of generating nonsense. A higher value, for example `100`, will give more diverse answers. A lower value, for example `10`, will be more conservative.", + "children": [ + { + "description": "The base URL of the schema registry service.", + "is_advanced": true, + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "schema_registry_id_", + "description": "The prefix of the name for this schema, the schema ID is used as a suffix.", + "is_advanced": true, + "kind": "scalar", + "name": "name_prefix", + "type": "string" + }, + { + "description": "The subject name to fetch the schema for.", + "is_advanced": true, + "kind": "scalar", + "name": "subject", + "type": "string" + }, + { + "description": "The refresh rate for getting the latest schema. If not specified the schema does not refresh.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "refresh_interval", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "oauth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "basic_auth", + "type": "object" + }, + { + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object" + } + ], + "description": "The schema registry to dynamically load schemas from when responding in `json_schema` format. Schemas themselves must be in JSON format. To learn more about what JSON schema is supported see the https://platform.openai.com/docs/guides/structured-outputs/supported-schemas[OpenAI documentation^].", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "top_k", - "type": "int" + "name": "schema_registry", + "type": "object" }, { - "description": "Works together with `top-k`. A higher value, for example 0.95, will lead to more diverse text. A lower value, for example 0.5, will generate more focused and conservative text.", + "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "linter": "root = if this > 1 || this < 0 { [ \"field must be between 0.0 and 1.0\" ] }", + "linter": "root = if this > 1 || this < 0 { [ \"field must be between 0 and 1\" ] }", "name": "top_p", "type": "float" }, { - "description": "Sets how strongly to penalize repetitions. A higher value, for example 1.5, will penalize repetitions more strongly. A lower value, for example 0.9, will be more lenient.", + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < -2 { [ \"field must be between -2.0 and 2.0\" ] }", - "name": "repeat_penalty", + "linter": "root = if this > 2 || this < -2 { [ \"field must be less than 2 and greater than -2\" ] }", + "name": "frequency_penalty", "type": "float" }, { - "description": "Positive values penalize new tokens if they have appeared in the text so far. This increases the model's likelihood to talk about new topics.", + "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < -2 { [ \"field must be between -2.0 and 2.0\" ] }", + "linter": "root = if this > 2 || this < -2 { [ \"field must be less than 2 and greater than -2\" ] }", "name": "presence_penalty", "type": "float" }, { - "description": "Positive values penalize new tokens based on the frequency of their appearance in the text so far. This decreases the model's likelihood to repeat the same line verbatim.", + "description": "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same seed and parameters should return the same result. Determinism is not guaranteed.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < -2 { [ \"field must be between -2.0 and 2.0\" ] }", - "name": "frequency_penalty", - "type": "float" + "name": "seed", + "type": "int" }, { - "description": "Sets the stop sequences to use. When this pattern is encountered the LLM stops generating text and returns the final response.", + "description": "Up to 4 sequences where the API will stop generating further tokens.", "is_advanced": true, "is_optional": true, "kind": "array", "name": "stop", "type": "string" }, - { - "default": false, - "description": "If enabled the prompt is saved as @prompt metadata on the output message. If system_prompt is used it's also saved as @system_prompt", - "kind": "scalar", - "name": "save_prompt_metadata", - "type": "bool" - }, - { - "bloblang": true, - "description": "Historical messages to include in the chat request. The result of the bloblang query should be an array of objects of the form of [{\"role\": \"\", \"content\":\"\"}].", - "is_optional": true, - "kind": "scalar", - "name": "history", - "type": "string" - }, - { - "default": 3, - "description": "The maximum number of sequential tool calls.", - "is_advanced": true, - "kind": "scalar", - "linter": "root = if this <= 0 { [\"field must be greater than zero\"] }", - "name": "max_tool_calls", - "type": "int" - }, { "children": [ { @@ -52015,6 +60881,7 @@ "type": "object" } ], + "default": [], "description": "The parameters the LLM needs to provide to invoke this tool.", "kind": "scalar", "name": "parameters", @@ -52028,110 +60895,111 @@ "type": "processor" } ], - "default": [], "description": "The tools to allow the LLM to invoke. This allows building subpipelines that the LLM can choose to invoke to execute agentic-like actions.", "kind": "array", "name": "tools", "type": "object" + } + ], + "kind": "scalar", + "linter": "\n root = match {\n this.exists(\"json_schema\") && this.exists(\"schema_registry\") => [\"cannot set both `json_schema` and `schema_registry`\"]\n this.response_format == \"json_schema\" && !this.exists(\"json_schema\") && !this.exists(\"schema_registry\") => [\"schema must be specified using either `json_schema` or `schema_registry`\"]\n }\n ", + "name": "", + "type": "object" + }, + "description": "\nThis processor sends the contents of user prompts to the OpenAI API, which generates responses. By default, the processor submits the entire payload of each message as a string, unless you use the `prompt` configuration field to customize it.\n\nTo learn more about chat completion, see the https://platform.openai.com/docs/guides/chat-completions[OpenAI API documentation^].", + "examples": [ + { + "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - http:\n verb: GET\n url: \"${!content().string()}\"\n - openai_chat_completion:\n model: gpt-4o\n api_key: TODO\n prompt: \"Describe the following image\"\n image: \"root = content()\"\noutput:\n stdout:\n codec: lines\n", + "summary": "This example fetches image URLs from stdin and has GPT-4o describe the image.", + "title": "Use GPT-4o analyze an image" + }, + { + "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - mapping: |\n root.prompt = content().string()\n - branch:\n processors:\n - cache:\n resource: mem\n operator: get\n key: history\n - catch:\n - mapping: 'root = []'\n result_map: 'root.history = this'\n - branch:\n processors:\n - openai_chat_completion:\n model: gpt-4o\n api_key: TODO\n prompt: \"${!this.prompt}\"\n history: 'root = this.history'\n result_map: 'root.response = content().string()'\n - mutation: |\n root.history = this.history.concat([\n {\"role\": \"user\", \"content\": this.prompt},\n {\"role\": \"assistant\", \"content\": this.response},\n ])\n - cache:\n resource: mem\n operator: set\n key: history\n value: '${!this.history}'\n - mapping: |\n root = this.response\noutput:\n stdout:\n codec: lines\n\ncache_resources:\n - label: mem \n memory: {}\n", + "summary": "This pipeline provides a historical chat history to GPT-4o using a cache.", + "title": "Provide historical chat history" + }, + { + "config": "\ninput:\n generate:\n count: 1\n mapping: |\n root = \"What is the weather like in Chicago?\"\npipeline:\n processors:\n - openai_chat_completion:\n model: gpt-4o\n api_key: \"${OPENAI_API_KEY}\"\n prompt: \"${!content().string()}\"\n tools:\n - name: GetWeather\n description: \"Retrieve the weather for a specific city\"\n parameters:\n required: [\"city\"]\n properties:\n city:\n type: string\n description: the city to look up the weather for\n processors:\n - http:\n verb: GET\n url: 'https://wttr.in/${!this.city}?T'\n headers:\n User-Agent: curl/8.11.1 # Returns a text string from the weather website\noutput:\n stdout: {}\n", + "summary": "This example asks GPT-4o to respond with the weather by invoking an HTTP processor to get the forecast.", + "title": "Use GPT-4o to call a tool" + } + ], + "name": "openai_chat_completion", + "plugin": true, + "status": "stable", + "summary": "Generates responses to messages in a chat conversation, using the OpenAI API.", + "type": "processor", + "version": "4.32.0" + }, + { + "categories": [ + "AI" + ], + "config": { + "children": [ + { + "default": "https://api.openai.com/v1", + "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", + "kind": "scalar", + "name": "server_address", + "type": "string" }, { - "children": [ - { - "description": "Sets the size of the context window used to generate the next token. Using a larger context window uses more memory and takes longer to processor.", - "is_optional": true, - "kind": "scalar", - "name": "context_size", - "type": "int" - }, - { - "description": "The maximum number of requests to process in parallel.", - "is_optional": true, - "kind": "scalar", - "name": "batch_size", - "type": "int" - }, - { - "description": "This option allows offloading some layers to the GPU for computation. This generally results in increased performance. By default, the runtime decides the number of layers dynamically.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "gpu_layers", - "type": "int" - }, - { - "description": "Set the number of threads to use during generation. For optimal performance, it is recommended to set this value to the number of physical CPU cores your system has. By default, the runtime decides the optimal number of threads.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "threads", - "type": "int" - }, - { - "description": "Map the model into memory. This is only support on unix systems and allows loading only the necessary parts of the model as needed.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "use_mmap", - "type": "bool" - } - ], - "description": "Options for the model runner that are used when the model is first loaded into memory.", - "is_optional": true, + "description": "The API key for OpenAI API.", + "is_secret": true, "kind": "scalar", - "name": "runner", - "type": "object" + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" }, { - "description": "The address of the Ollama server to use. Leave the field blank and the processor starts and runs a local Ollama server or specify the address of your own local or remote server.", + "description": "The name of the OpenAI model to use.", "examples": [ - "http://127.0.0.1:11434" + "text-embedding-3-large", + "text-embedding-3-small", + "text-embedding-ada-002" ], - "is_optional": true, "kind": "scalar", - "name": "server_address", + "name": "model", "type": "string" }, { - "description": "If `server_address` is not set - the directory to download the ollama binary and use as a model cache.", - "examples": [ - "/opt/cache/connect/ollama" - ], - "is_advanced": true, + "bloblang": true, + "description": "The text you want to generate a vector embedding for. By default, the processor submits the entire payload as a string.", "is_optional": true, "kind": "scalar", - "name": "cache_directory", + "name": "text_mapping", "type": "string" }, { - "description": "If `server_address` is not set - the URL to download the ollama binary from. Defaults to the offical Ollama GitHub release for this platform.", - "is_advanced": true, + "description": "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.", "is_optional": true, "kind": "scalar", - "name": "download_url", - "type": "string" + "name": "dimensions", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "This processor sends prompts to your chosen Ollama large language model (LLM) and generates text from the responses, using the Ollama API.\n\nBy default, the processor starts and runs a locally installed Ollama server. Alternatively, to use an already running Ollama server, add your server details to the `server_address` field. You can https://ollama.com/download[download and install Ollama from the Ollama website^].\n\nFor more information, see the https://github.com/ollama/ollama/tree/main/docs[Ollama documentation^].", + "description": "\nThis processor sends text strings to the OpenAI API, which generates vector embeddings. By default, the processor submits the entire payload of each message as a string, unless you use the `text_mapping` configuration field to customize it.\n\nTo learn more about vector embeddings, see the https://platform.openai.com/docs/guides/embeddings[OpenAI API documentation^].", "examples": [ { - "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - http:\n verb: GET\n url: \"${!content().string()}\"\n - ollama_chat:\n model: llava\n prompt: \"Describe the following image\"\n image: \"root = content()\"\noutput:\n stdout:\n codec: lines\n", - "summary": "This example fetches image URLs from stdin and has a multimodal LLM describe the image.", - "title": "Use Llava to analyze an image" + "config": "input:\n generate:\n interval: 1s\n mapping: |\n root = {\"text\": fake(\"paragraph\")}\npipeline:\n processors:\n - openai_embeddings:\n model: text-embedding-3-large\n api_key: \"${OPENAI_API_KEY}\"\n text_mapping: \"root = this.text\"\noutput:\n pinecone:\n host: \"${PINECONE_HOST}\"\n api_key: \"${PINECONE_API_KEY}\"\n id: \"root = uuid_v4()\"\n vector_mapping: \"root = this\"", + "summary": "Compute embeddings for some generated data and store it within xrefs:component:outputs/pinecone.adoc[Pinecone]", + "title": "Store embedding vectors in Pinecone" }, { - "config": "\ninput:\n generate:\n count: 1\n mapping: |\n root = \"What is the weather like in Chicago?\"\npipeline:\n processors:\n - ollama_chat:\n model: llama3.2\n prompt: \"${!content().string()}\"\n tools:\n - name: GetWeather\n description: \"Retrieve the weather for a specific city\"\n parameters:\n required: [\"city\"]\n properties:\n city:\n type: string\n description: the city to lookup the weather for\n processors:\n - http:\n verb: GET\n url: 'https://wttr.in/${!this.city}?T'\n headers:\n # Spoof curl user-ageent to get a plaintext text\n User-Agent: curl/8.11.1\noutput:\n stdout: {}\n", - "summary": "This example allows llama3.2 to execute a subpipeline as a tool call to get more data.", - "title": "Use subpipelines as tool calls" + "config": "input:\n generate:\n interval: 1s\n mapping: |\n root = {\"text\": fake(\"paragraph\")}\npipeline:\n processors:\n - openai_embeddings:\n model: text-embedding-3-large\n api_key: \"${OPENAI_API_KEY}\"\n text_mapping: \"root = this.text\"\noutput:\n cyborgdb:\n host: \"${CYBORGDB_HOST}\"\n api_key: \"${CYBORGDB_API_KEY}\"\n index_key: \"${CYBORGDB_INDEX_KEY}\"\n index_name: \"my_encrypted_index\"\n operation: \"upsert\"\n id: \"root = uuid_v4()\"\n vector_mapping: \"root = this\"", + "summary": "Compute embeddings for some generated data and store it within xrefs:component:outputs/cyborgdb.adoc[CyborgDB]", + "title": "Store embedding vectors in CyborgDB" } ], - "name": "ollama_chat", + "name": "openai_embeddings", "plugin": true, - "status": "experimental", - "summary": "Generates responses to messages in a chat conversation, using the Ollama API.", + "status": "stable", + "summary": "Generates vector embeddings to represent input text, using the OpenAI API.", "type": "processor", "version": "4.32.0" }, @@ -52142,99 +61010,77 @@ "config": { "children": [ { - "description": "The name of the Ollama LLM to use. For a full list of models, see the https://ollama.com/models[Ollama website].", - "examples": [ - "nomic-embed-text", - "mxbai-embed-large", - "snowflake-artic-embed", - "all-minilm" - ], + "default": "https://api.openai.com/v1", + "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", "kind": "scalar", - "name": "model", + "name": "server_address", "type": "string" }, { - "description": "The text you want to create vector embeddings for. By default, the processor submits the entire payload as a string.", - "interpolated": true, - "is_optional": true, + "description": "The API key for OpenAI API.", + "is_secret": true, "kind": "scalar", - "name": "text", + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "children": [ - { - "description": "Sets the size of the context window used to generate the next token. Using a larger context window uses more memory and takes longer to processor.", - "is_optional": true, - "kind": "scalar", - "name": "context_size", - "type": "int" - }, - { - "description": "The maximum number of requests to process in parallel.", - "is_optional": true, - "kind": "scalar", - "name": "batch_size", - "type": "int" - }, - { - "description": "This option allows offloading some layers to the GPU for computation. This generally results in increased performance. By default, the runtime decides the number of layers dynamically.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "gpu_layers", - "type": "int" - }, - { - "description": "Set the number of threads to use during generation. For optimal performance, it is recommended to set this value to the number of physical CPU cores your system has. By default, the runtime decides the optimal number of threads.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "threads", - "type": "int" - }, - { - "description": "Map the model into memory. This is only support on unix systems and allows loading only the necessary parts of the model as needed.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "use_mmap", - "type": "bool" - } + "description": "The name of the OpenAI model to use.", + "examples": [ + "dall-e-3", + "dall-e-2" ], - "description": "Options for the model runner that are used when the model is first loaded into memory.", + "kind": "scalar", + "name": "model", + "type": "string" + }, + { + "bloblang": true, + "description": "A text description of the image you want to generate. The `prompt` field accepts a maximum of 1000 characters for `dall-e-2` and 4000 characters for `dall-e-3`.", "is_optional": true, "kind": "scalar", - "name": "runner", - "type": "object" + "name": "prompt", + "type": "string" }, { - "description": "The address of the Ollama server to use. Leave the field blank and the processor starts and runs a local Ollama server or specify the address of your own local or remote server.", + "description": "The quality of the image to generate. Use `hd` to create images with finer details and greater consistency across the image. This parameter is only supported for `dall-e-3` models.", "examples": [ - "http://127.0.0.1:11434" + "standard", + "hd" ], + "interpolated": true, + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "server_address", + "name": "quality", "type": "string" }, { - "description": "If `server_address` is not set - the directory to download the ollama binary and use as a model cache.", + "description": "The size of the generated image. Choose from `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Choose from `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3` models.", "examples": [ - "/opt/cache/connect/ollama" + "1024x1024", + "512x512", + "1792x1024", + "1024x1792" ], + "interpolated": true, "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "cache_directory", + "name": "size", "type": "string" }, { - "description": "If `server_address` is not set - the URL to download the ollama binary from. Defaults to the offical Ollama GitHub release for this platform.", + "description": "The style of the generated image. Choose from `vivid` or `natural`. Vivid causes the model to lean towards generating hyperreal and dramatic images. Natural causes the model to produce more natural, less hyperreal looking images. This parameter is only supported for `dall-e-3`.", + "examples": [ + "vivid", + "natural" + ], + "interpolated": true, "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "download_url", + "name": "style", "type": "string" } ], @@ -52242,23 +61088,11 @@ "name": "", "type": "object" }, - "description": "This processor sends text to your chosen Ollama large language model (LLM) and creates vector embeddings, using the Ollama API. Vector embeddings are long arrays of numbers that represent values or objects, in this case text. \n\nBy default, the processor starts and runs a locally installed Ollama server. Alternatively, to use an already running Ollama server, add your server details to the `server_address` field. You can https://ollama.com/download[download and install Ollama from the Ollama website^].\n\nFor more information, see the https://github.com/ollama/ollama/tree/main/docs[Ollama documentation^].", - "examples": [ - { - "config": "input:\n generate:\n interval: 1s\n mapping: |\n root = {\"text\": fake(\"paragraph\")}\npipeline:\n processors:\n - ollama_embeddings:\n model: snowflake-artic-embed\n text: \"${!this.text}\"\noutput:\n qdrant:\n grpc_host: localhost:6334\n collection_name: \"example_collection\"\n id: \"root = uuid_v4()\"\n vector_mapping: \"root = this\"\n", - "summary": "Compute embeddings for some generated data and store it within xrefs:component:outputs/qdrant.adoc[Qdrant]", - "title": "Store embedding vectors in Qdrant" - }, - { - "config": "input:\n generate:\n interval: 1s\n mapping: |\n root = {\"text\": fake(\"paragraph\")}\npipeline:\n processors:\n - branch:\n processors:\n - ollama_embeddings:\n model: snowflake-artic-embed\n text: \"${!this.text}\"\n result_map: |\n root.embeddings = this\noutput:\n sql_insert:\n driver: clickhouse\n dsn: \"clickhouse://localhost:9000\"\n table: searchable_text\n columns: [\"id\", \"text\", \"vector\"]\n args_mapping: \"root = [uuid_v4(), this.text, this.embeddings]\"\n", - "summary": "Compute embeddings for some generated data and store it within https://clickhouse.com/[Clickhouse^]", - "title": "Store embedding vectors in Clickhouse" - } - ], - "name": "ollama_embeddings", + "description": "\nThis processor sends an image description and other attributes, such as image size and quality to the OpenAI API, which generates an image. By default, the processor submits the entire payload of each message as a string, unless you use the `prompt` configuration field to customize it.\n\nTo learn more about image generation, see the https://platform.openai.com/docs/guides/images[OpenAI API documentation^].", + "name": "openai_image_generation", "plugin": true, - "status": "experimental", - "summary": "Generates vector embeddings from text, using the Ollama API.", + "status": "stable", + "summary": "Generates an image from a text description and other attributes, using OpenAI API.", "type": "processor", "version": "4.32.0" }, @@ -52269,114 +61103,68 @@ "config": { "children": [ { - "annotated_options": [ - [ - "llama-guard3", - "When using llama-guard3, two pieces of metadata is added: @safe with the value of `yes` or `no` and the second being @category for the safety category violation. For more information see the https://ollama.com/library/llama-guard3[Llama Guard 3 Model Card]." - ], - [ - "shieldgemma", - "When using shieldgemma, the model output is a single piece of metadata of @safe with a value of `yes` or `no` if the response is not in violation of its defined safety policies." - ] - ], - "description": "The name of the Ollama LLM to use.", - "examples": [ - "llama-guard3", - "shieldgemma" - ], + "default": "https://api.openai.com/v1", + "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", "kind": "scalar", - "linter": "\nlet options = {\n \"llama-guard3\": true,\n \"shieldgemma\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "model", + "name": "server_address", "type": "string" }, { - "description": "The input prompt that was used with the LLM. If using `ollama_chat` the you can use `save_prompt_metadata` to safe the prompt as metadata.", - "interpolated": true, + "description": "The API key for OpenAI API.", + "is_secret": true, "kind": "scalar", - "name": "prompt", + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "The LLM's response to classify if it contains safe or unsafe content.", - "interpolated": true, + "description": "The name of the OpenAI model to use.", + "examples": [ + "tts-1", + "tts-1-hd" + ], "kind": "scalar", - "name": "response", + "name": "model", "type": "string" }, { - "children": [ - { - "description": "Sets the size of the context window used to generate the next token. Using a larger context window uses more memory and takes longer to processor.", - "is_optional": true, - "kind": "scalar", - "name": "context_size", - "type": "int" - }, - { - "description": "The maximum number of requests to process in parallel.", - "is_optional": true, - "kind": "scalar", - "name": "batch_size", - "type": "int" - }, - { - "description": "This option allows offloading some layers to the GPU for computation. This generally results in increased performance. By default, the runtime decides the number of layers dynamically.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "gpu_layers", - "type": "int" - }, - { - "description": "Set the number of threads to use during generation. For optimal performance, it is recommended to set this value to the number of physical CPU cores your system has. By default, the runtime decides the optimal number of threads.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "threads", - "type": "int" - }, - { - "description": "Map the model into memory. This is only support on unix systems and allows loading only the necessary parts of the model as needed.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "use_mmap", - "type": "bool" - } - ], - "description": "Options for the model runner that are used when the model is first loaded into memory.", + "bloblang": true, + "description": "A text description of the audio you want to generate. The `input` field accepts a maximum of 4096 characters.", "is_optional": true, "kind": "scalar", - "name": "runner", - "type": "object" + "name": "input", + "type": "string" }, { - "description": "The address of the Ollama server to use. Leave the field blank and the processor starts and runs a local Ollama server or specify the address of your own local or remote server.", + "description": "The type of voice to use when generating the audio.", "examples": [ - "http://127.0.0.1:11434" + "alloy", + "echo", + "fable", + "onyx", + "nova", + "shimmer" ], - "is_optional": true, + "interpolated": true, "kind": "scalar", - "name": "server_address", + "name": "voice", "type": "string" }, { - "description": "If `server_address` is not set - the directory to download the ollama binary and use as a model cache.", + "description": "The format to generate audio in. Default is `mp3`.", "examples": [ - "/opt/cache/connect/ollama" + "mp3", + "opus", + "aac", + "flac", + "wav", + "pcm" ], + "interpolated": true, "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "cache_directory", - "type": "string" - }, - { - "description": "If `server_address` is not set - the URL to download the ollama binary from. Defaults to the offical Ollama GitHub release for this platform.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "download_url", + "name": "response_format", "type": "string" } ], @@ -52384,20 +61172,13 @@ "name": "", "type": "object" }, - "description": "This processor checks LLM response safety using either `llama-guard3` or `shieldgemma`. If you want to check if a given prompt is safe, then that can be done with the `ollama_chat` processor - this processor is for response classification only.\n\nBy default, the processor starts and runs a locally installed Ollama server. Alternatively, to use an already running Ollama server, add your server details to the `server_address` field. You can https://ollama.com/download[download and install Ollama from the Ollama website^].\n\nFor more information, see the https://github.com/ollama/ollama/tree/main/docs[Ollama documentation^].", - "examples": [ - { - "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - ollama_chat:\n model: llava\n prompt: \"${!content().string()}\"\n save_prompt_metadata: true\n - ollama_moderation:\n model: llama-guard3\n prompt: \"${!@prompt}\"\n response: \"${!content().string()}\"\n - mapping: |\n root.response = content().string()\n root.is_safe = @safe\noutput:\n stdout:\n codec: lines\n", - "summary": "This example uses Llama Guard 3 to check if another model responded with a safe or unsafe content.", - "title": "Use Llama Guard 3 classify a LLM response" - } - ], - "name": "ollama_moderation", + "description": "\nThis processor sends a text description and other attributes, such as a voice type and format to the OpenAI API, which generates audio. By default, the processor submits the entire payload of each message as a string, unless you use the `input` configuration field to customize it.\n\nTo learn more about turning text into spoken audio, see the https://platform.openai.com/docs/guides/text-to-speech[OpenAI API documentation^].", + "name": "openai_speech", "plugin": true, - "status": "experimental", - "summary": "Generates responses to messages in a chat conversation, using the Ollama API.", + "status": "stable", + "summary": "Generates audio from a text description and other attributes, using OpenAI API.", "type": "processor", - "version": "4.42.0" + "version": "4.32.0" }, { "categories": [ @@ -52423,346 +61204,561 @@ { "description": "The name of the OpenAI model to use.", "examples": [ - "gpt-4o", - "gpt-4o-mini", - "gpt-4", - "gpt4-turbo" + "whisper-1" ], "kind": "scalar", "name": "model", "type": "string" }, { - "description": "The user prompt you want to generate a response for. By default, the processor submits the entire payload as a string.", - "interpolated": true, - "is_optional": true, + "bloblang": true, + "description": "The audio file object (not file name) to transcribe, in one of the following formats: `flac`, `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `ogg`, `wav`, or `webm`.", "kind": "scalar", - "name": "prompt", + "name": "file", "type": "string" }, { - "description": "The system prompt to submit along with the user prompt.", + "description": "The language of the input audio. Supplying the input language in ISO-639-1 format improves accuracy and latency.", + "examples": [ + "en", + "fr", + "de", + "zh" + ], "interpolated": true, + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "system_prompt", + "name": "language", "type": "string" }, { - "bloblang": true, - "description": "The history of the prior conversation. A bloblang query that should result in an array of objects of the form: [{\"role\": \"user\", \"content\": \"\"}, {\"role\":\"assistant\", \"content\":\"\"}]", + "description": "Optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.", + "interpolated": true, + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "history", + "name": "prompt", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis processor sends an audio file object along with the input language to OpenAI API to generate a transcription. By default, the processor submits the entire payload of each message as a string, unless you use the `file` configuration field to customize it.\n\nTo learn more about audio transcription, see the: https://platform.openai.com/docs/guides/speech-to-text[OpenAI API documentation^].", + "name": "openai_transcription", + "plugin": true, + "status": "stable", + "summary": "Generates a transcription of spoken audio in the input language, using the OpenAI API.", + "type": "processor", + "version": "4.32.0" + }, + { + "categories": [ + "AI" + ], + "config": { + "children": [ + { + "default": "https://api.openai.com/v1", + "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", + "kind": "scalar", + "name": "server_address", "type": "string" }, { - "bloblang": true, - "description": "An image to send along with the prompt. The mapping result must be a byte array.", - "examples": [ - "root = this.image.decode(\"base64\") # decode base64 encoded image" - ], - "is_optional": true, + "description": "The API key for OpenAI API.", + "is_secret": true, "kind": "scalar", - "name": "image", - "type": "string", - "version": "4.38.0" + "name": "api_key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" }, { - "description": "The maximum number of tokens that can be generated in the chat completion.", - "is_optional": true, + "description": "The name of the OpenAI model to use.", + "examples": [ + "whisper-1" + ], "kind": "scalar", - "name": "max_tokens", - "type": "int" + "name": "model", + "type": "string" }, { - "description": "What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic.\n\nWe generally recommend altering this or top_p but not both.", + "bloblang": true, + "description": "The audio file object (not file name) to translate, in one of the following formats: `flac`, `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `ogg`, `wav`, or `webm`.", "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < 0 { [ \"field must be between 0 and 2\" ] }", - "name": "temperature", - "type": "float" + "name": "file", + "type": "string" }, { - "description": "A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse.", + "description": "Optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.", "interpolated": true, + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "user", + "name": "prompt", "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis processor sends an audio file object to OpenAI API to generate a translation. By default, the processor submits the entire payload of each message as a string, unless you use the `file` configuration field to customize it.\n\nTo learn more about translation, see the https://platform.openai.com/docs/guides/speech-to-text[OpenAI API documentation^].", + "name": "openai_translation", + "plugin": true, + "status": "stable", + "summary": "Translates spoken audio into English, using the OpenAI API.", + "type": "processor", + "version": "4.32.0" + }, + { + "categories": [ + "Composition" + ], + "config": { + "children": [ + { + "default": 0, + "description": "The maximum number of messages to have processing at a given time.", + "kind": "scalar", + "name": "cap", + "type": "int" }, { - "default": "text", - "description": "Specify the model's output format. If `json_schema` is specified, then additionally a `json_schema` or `schema_registry` must be configured.", + "description": "A list of child processors to apply.", + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThe field `cap`, if greater than zero, caps the maximum number of parallel processing threads.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching in xref:configuration:batching.adoc[].", + "name": "parallel", + "plugin": true, + "status": "stable", + "summary": "A processor that applies a list of child processors to messages of a batch as though they were each a batch of one message (similar to the xref:components:processors/for_each.adoc[`for_each`] processor), but where each message is processed in parallel.", + "type": "processor" + }, + { + "categories": [ + "Parsing" + ], + "config": { + "children": [ + { + "default": false, + "description": "Whether to extract BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY values as strings rather than byte slices in all cases. Values with a logical type of UTF8 will automatically be extracted as strings irrespective of this field. Enabling this field makes serializing the data as JSON more intuitive as `[]byte` values are serialized as base64 encoded strings by default.", + "is_deprecated": true, "kind": "scalar", - "linter": "\nlet options = {\n \"text\": true,\n \"json\": true,\n \"json_schema\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "response_format", - "options": [ - "text", - "json", - "json_schema" + "name": "byte_array_as_string", + "type": "bool" + }, + { + "annotated_options": [ + [ + "v1", + "No special handling of logical types" + ], + [ + "v2", + "\n- TIMESTAMP - decodes as an RFC3339 string describing the time. If the `isAdjustedToUTC` flag is set to true in the parquet file, the time zone will be set to UTC. If it is set to false the time zone will be set to local time.\n- UUID - decodes as a string, i.e. `00112233-4455-6677-8899-aabbccddeeff`." + ] + ], + "default": "v1", + "description": "Whether to be smart about decoding logical types. In the Parquet format, logical types are stored as one of the standard physical types with some additional metadata describing the logical type. For example, UUIDs are stored in a FIXED_LEN_BYTE_ARRAY physical type, but there is metadata in the schema denoting that it is a UUID. By default, this logical type metadata will be ignored and values will be decoded directly from the physical type, which isn't always desirable. By enabling this option, logical types will be given special treatment and will decode into more useful values. The value for this field specifies a version, i.e. v0, v1... Any given version enables the logical type handling for that version and all versions below it, which allows the handling of new logical types to be introduced without breaking existing pipelines. We recommend enabling the newest version available of this feature when creating new pipelines.", + "examples": [ + "v2" ], + "kind": "scalar", + "linter": "\nlet options = {\n \"v1\": true,\n \"v2\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "handle_logical_types", "type": "string" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis processor uses https://github.com/parquet-go/parquet-go[https://github.com/parquet-go/parquet-go^], which is itself experimental. Therefore changes could be made into how this processor functions outside of major version releases.", + "examples": [ + { + "config": "\ninput:\n aws_s3:\n bucket: TODO\n prefix: foos/\n scanner:\n to_the_end: {}\n sqs:\n url: TODO\n processors:\n - parquet_decode: {}\n\noutput:\n file:\n codec: lines\n path: './foos/${! meta(\"s3_key\") }.jsonl'\n", + "summary": "In this example we consume files from AWS S3 as they're written by listening onto an SQS queue for upload events. We make sure to use the `to_the_end` scanner which means files are read into memory in full, which then allows us to use a `parquet_decode` processor to expand each file into a batch of messages. Finally, we write the data out to local files as newline delimited JSON.", + "title": "Reading Parquet Files from AWS S3" + } + ], + "name": "parquet_decode", + "plugin": true, + "status": "stable", + "summary": "Decodes https://parquet.apache.org/docs/[Parquet files^] into a batch of structured messages.", + "type": "processor", + "version": "4.4.0" + }, + { + "categories": [ + "Parsing" + ], + "config": { + "children": [ { "children": [ { - "description": "The name of the schema.", + "description": "The name of the column.", "kind": "scalar", "name": "name", "type": "string" }, { - "description": "Additional description of the schema for the LLM.", - "is_advanced": true, + "description": "The type of the column, only applicable for leaf columns with no child fields. Some logical types can be specified here such as UTF8.", "is_optional": true, "kind": "scalar", - "name": "description", - "type": "string" - }, - { - "description": "The JSON schema for the LLM to use when generating the output.", - "kind": "scalar", - "name": "schema", - "type": "string" - } - ], - "description": "The JSON schema to use when responding in `json_schema` format. To learn more about what JSON schema is supported see the https://platform.openai.com/docs/guides/structured-outputs/supported-schemas[OpenAI documentation^].", - "is_optional": true, - "kind": "scalar", - "name": "json_schema", + "linter": "\nlet options = {\n \"boolean\": true,\n \"int32\": true,\n \"int64\": true,\n \"float\": true,\n \"double\": true,\n \"byte_array\": true,\n \"utf8\": true,\n \"timestamp\": true,\n \"bson\": true,\n \"enum\": true,\n \"json\": true,\n \"uuid\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "type", + "options": [ + "BOOLEAN", + "INT32", + "INT64", + "FLOAT", + "DOUBLE", + "BYTE_ARRAY", + "UTF8", + "TIMESTAMP", + "BSON", + "ENUM", + "JSON", + "UUID" + ], + "type": "string" + }, + { + "default": false, + "description": "Whether the field is repeated.", + "kind": "scalar", + "name": "repeated", + "type": "bool" + }, + { + "default": false, + "description": "Whether the field is optional.", + "kind": "scalar", + "name": "optional", + "type": "bool" + }, + { + "description": "A list of child fields.", + "examples": [ + [ + { + "name": "foo", + "type": "INT64" + }, + { + "name": "bar", + "type": "BYTE_ARRAY" + } + ] + ], + "is_optional": true, + "kind": "array", + "name": "fields", + "type": "unknown" + } + ], + "description": "Parquet schema.", + "is_optional": true, + "kind": "array", + "name": "schema", "type": "object" }, + { + "default": "", + "description": "Optionally specify a metadata field containing a schema definition to use for encoding instead of a statically defined schema. For batches of messages, the first message's schema will be applied to all subsequent messages of the batch.", + "kind": "scalar", + "name": "schema_metadata", + "type": "string" + }, + { + "default": "uncompressed", + "description": "The default compression type to use for fields.", + "kind": "scalar", + "linter": "\nlet options = {\n \"uncompressed\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"brotli\": true,\n \"zstd\": true,\n \"lz4raw\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "default_compression", + "options": [ + "uncompressed", + "snappy", + "gzip", + "brotli", + "zstd", + "lz4raw" + ], + "type": "string" + }, + { + "default": "DELTA_LENGTH_BYTE_ARRAY", + "description": "The default encoding type to use for fields. A custom default encoding is only necessary when consuming data with libraries that do not support `DELTA_LENGTH_BYTE_ARRAY` and is therefore best left unset where possible.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"delta_length_byte_array\": true,\n \"plain\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "default_encoding", + "options": [ + "DELTA_LENGTH_BYTE_ARRAY", + "PLAIN" + ], + "type": "string", + "version": "4.11.0" + }, + { + "default": "NANOSECOND", + "description": "The precision used when encoding TIMESTAMP logical types. The default `NANOSECOND` matches historical behaviour, but `TIMESTAMP(NANOS)` is not readable by Apache Spark (Databricks), AWS Athena or DuckDB; set this to `MICROSECOND` (or `MILLISECOND`) when writing Parquet files intended for consumption by those engines.", + "is_advanced": true, + "kind": "scalar", + "linter": "\nlet options = {\n \"nanosecond\": true,\n \"microsecond\": true,\n \"millisecond\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "default_timestamp_unit", + "options": [ + "NANOSECOND", + "MICROSECOND", + "MILLISECOND" + ], + "type": "string", + "version": "4.89.0" + } + ], + "kind": "scalar", + "linter": "root = if this.schema.or([]).length() == 0 && this.schema_metadata.or(\"\") == \"\" { \"either a schema or schema_metadata must be specified\" }", + "name": "", + "type": "object" + }, + "description": "\nThis processor uses https://github.com/parquet-go/parquet-go[https://github.com/parquet-go/parquet-go^], which is itself experimental. Therefore changes could be made into how this processor functions outside of major version releases.\n", + "examples": [ + { + "config": "\noutput:\n aws_s3:\n bucket: TODO\n path: 'stuff/${! timestamp_unix() }-${! uuid_v4() }.parquet'\n batching:\n count: 1000\n period: 10s\n processors:\n - parquet_encode:\n schema:\n - name: id\n type: INT64\n - name: weight\n type: DOUBLE\n - name: content\n type: BYTE_ARRAY\n default_compression: zstd\n", + "summary": "In this example we use the batching mechanism of an `aws_s3` output to collect a batch of messages in memory, which then converts it to a parquet file and uploads it.", + "title": "Writing Parquet Files to AWS S3" + } + ], + "name": "parquet_encode", + "plugin": true, + "status": "stable", + "summary": "Encodes https://parquet.apache.org/docs/[Parquet files^] from a batch of structured messages.", + "type": "processor", + "version": "4.4.0" + }, + { + "categories": [ + "Parsing" + ], + "config": { + "children": [ + { + "description": "A common log <> to parse.", + "kind": "scalar", + "linter": "\nlet options = {\n \"syslog_rfc5424\": true,\n \"syslog_rfc3164\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "format", + "options": [ + "syslog_rfc5424", + "syslog_rfc3164" + ], + "type": "string" + }, + { + "default": true, + "description": "Still returns partially parsed messages even if an error occurs.", + "is_advanced": true, + "kind": "scalar", + "name": "best_effort", + "type": "bool" + }, + { + "default": true, + "description": "Also accept timestamps in rfc3339 format while parsing. Applicable to format `syslog_rfc3164`.", + "is_advanced": true, + "kind": "scalar", + "name": "allow_rfc3339", + "type": "bool" + }, + { + "default": "current", + "description": "Sets the strategy used to set the year for rfc3164 timestamps. Applicable to format `syslog_rfc3164`. When set to `current` the current year will be set, when set to an integer that value will be used. Leave this field empty to not set a default year at all.", + "is_advanced": true, + "kind": "scalar", + "name": "default_year", + "type": "string" + }, + { + "default": "UTC", + "description": "Sets the strategy to decide the timezone for rfc3164 timestamps. Applicable to format `syslog_rfc3164`. This value should follow the https://golang.org/pkg/time/#LoadLocation[time.LoadLocation^] format.", + "is_advanced": true, + "kind": "scalar", + "name": "default_timezone", + "type": "string" + }, + { + "is_deprecated": true, + "kind": "scalar", + "name": "codec", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "footnotes": "\n== Codecs\n\nCurrently the only supported structured data codec is `json`.\n\n== Formats\n\n=== `syslog_rfc5424`\n\nAttempts to parse a log following the https://tools.ietf.org/html/rfc5424[Syslog RFC5424^] spec. The resulting structured document may contain any of the following fields:\n\n- `message` (string)\n- `timestamp` (string, RFC3339)\n- `facility` (int)\n- `severity` (int)\n- `priority` (int)\n- `version` (int)\n- `hostname` (string)\n- `procid` (string)\n- `appname` (string)\n- `msgid` (string)\n- `structureddata` (object)\n\n=== `syslog_rfc3164`\n\nAttempts to parse a log following the https://tools.ietf.org/html/rfc3164[Syslog rfc3164] spec. The resulting structured document may contain any of the following fields:\n\n- `message` (string)\n- `timestamp` (string, RFC3339)\n- `facility` (int)\n- `severity` (int)\n- `priority` (int)\n- `hostname` (string)\n- `procid` (string)\n- `appname` (string)\n- `msgid` (string)\n", + "name": "parse_log", + "plugin": true, + "status": "stable", + "summary": "Parses common log <> into <>. This is easier and often much faster than xref:components:processors/grok.adoc[`grok`].", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "default": [], + "kind": "array", + "name": "", + "type": "processor" + }, + "description": "This processor is useful in situations where you want to collect several processors under a single resource identifier, whether it is for making your configuration easier to read and navigate, or for improving the testability of your configuration. The behavior of child processors will match exactly the behavior they would have under any other processors block.", + "examples": [ + { + "config": "\npipeline:\n processors:\n - label: my_super_feature\n processors:\n - log:\n message: \"Let's do something cool\"\n - archive:\n format: json_array\n - mapping: root.items = this\n", + "summary": "Imagine we have a collection of processors who cover a specific functionality. We could use this processor to group them together and make it easier to read and mock during testing by giving the whole block a label:", + "title": "Grouped Processing" + } + ], + "name": "processors", + "plugin": true, + "status": "stable", + "summary": "A processor grouping several sub-processors.", + "type": "processor" + }, + { + "categories": [ + "AI" + ], + "config": { + "children": [ + { + "description": "The gRPC host of the Qdrant server.", + "examples": [ + "localhost:6334", + "xyz-example.eu-central.aws.cloud.qdrant.io:6334" + ], + "kind": "scalar", + "name": "grpc_host", + "type": "string" + }, + { + "default": "", + "description": "The Qdrant API token for authentication. Defaults to an empty string.", + "is_secret": true, + "kind": "scalar", + "name": "api_token", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, { "children": [ { - "description": "The base URL of the schema registry service.", + "default": false, + "description": "Whether custom TLS settings are enabled.", "is_advanced": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "enabled", + "type": "bool" }, { - "default": "schema_registry_id_", - "description": "The prefix of the name for this schema, the schema ID is used as a suffix.", + "default": false, + "description": "Whether to skip server side certificate verification.", "is_advanced": true, "kind": "scalar", - "name": "name_prefix", - "type": "string" + "name": "skip_cert_verify", + "type": "bool" }, { - "description": "The subject name to fetch the schema for.", + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", "is_advanced": true, "kind": "scalar", - "name": "subject", - "type": "string" + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" }, { - "description": "The refresh rate for getting the latest schema. If not specified the schema does not refresh.", + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], "is_advanced": true, - "is_optional": true, + "is_secret": true, "kind": "scalar", - "name": "refresh_interval", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" ], - "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", - "name": "tls", - "type": "object" + "name": "root_cas_file", + "type": "string" }, { "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, { "default": "", - "description": "A value used to identify the client to the service provider.", + "description": "A plain text certificate to use.", "is_advanced": true, "kind": "scalar", - "name": "consumer_key", + "name": "cert", "type": "string" }, { "default": "", - "description": "A secret used to establish ownership of the consumer key.", + "description": "A plain text certificate key to use.", "is_advanced": true, "is_secret": true, "kind": "scalar", - "name": "consumer_secret", + "name": "key", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", - "is_advanced": true, - "kind": "scalar", - "name": "access_token", - "type": "string" - }, - { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", + "description": "The path of a certificate to use.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "name": "cert_file", "type": "string" - } - ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" }, { "default": "", - "description": "A username to authenticate as.", + "description": "The path of a certificate key to use.", "is_advanced": true, "kind": "scalar", - "name": "username", + "name": "key_file", "type": "string" }, { "default": "", - "description": "A password to authenticate with.", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], "is_advanced": true, "is_secret": true, "kind": "scalar", @@ -52771,577 +61767,652 @@ "type": "string" } ], - "description": "Allows you to specify basic authentication.", + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", - "is_advanced": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, - { - "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, - "kind": "scalar", - "name": "signing_method", - "type": "string" - }, - { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" - }, - { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" - } - ], - "description": "BETA: Allows you to specify JWT authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "jwt", + "kind": "array", + "name": "client_certs", "type": "object" } ], - "description": "The schema registry to dynamically load schemas from when responding in `json_schema` format. Schemas themselves must be in JSON format. To learn more about what JSON schema is supported see the https://platform.openai.com/docs/guides/structured-outputs/supported-schemas[OpenAI documentation^].", + "description": "TLS(HTTPS) config to use when connecting", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "schema_registry", + "name": "tls", "type": "object" }, { - "description": "An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered.\n\nWe generally recommend altering this or temperature but not both.", - "is_advanced": true, - "is_optional": true, + "description": "The name of the collection in Qdrant.", + "interpolated": true, "kind": "scalar", - "linter": "root = if this > 1 || this < 0 { [ \"field must be between 0 and 1\" ] }", - "name": "top_p", - "type": "float" + "name": "collection_name", + "type": "string" }, { - "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim.", - "is_advanced": true, - "is_optional": true, + "bloblang": true, + "description": "The mapping to extract the search vector from the document.", + "examples": [ + "root = [1.2, 0.5, 0.76]", + "root = this.vector", + "root = [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]", + "root = {\"some_sparse\": {\"indices\":[23,325,532],\"values\":[0.352,0.532,0.532]}}", + "root = {\"some_multi\": [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]}", + "root = {\"some_dense\": [0.352,0.532,0.532,0.234]}" + ], "kind": "scalar", - "linter": "root = if this > 2 || this < -2 { [ \"field must be less than 2 and greater than -2\" ] }", - "name": "frequency_penalty", - "type": "float" + "name": "vector_mapping", + "type": "string" }, { - "description": "Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics.", - "is_advanced": true, + "bloblang": true, + "description": "Additional filtering to perform on the results. The mapping should return a valid filter (using the proto3 encoded form) in qdrant. See the https://qdrant.tech/documentation/concepts/filtering/[^Qdrant documentation] for examples.", + "examples": [ + "\nroot.must = [\n\t{\"has_id\":{\"has_id\":[{\"num\": 8}, { \"uuid\":\"1234-5678-90ab-cdef\" }]}},\n\t{\"field\":{\"key\": \"city\", \"match\": {\"text\": \"London\"}}},\n]\n", + "\nroot.must = [\n\t{\"field\":{\"key\": \"city\", \"match\": {\"text\": \"London\"}}},\n]\nroot.must_not = [\n\t{\"field\":{\"color\": \"city\", \"match\": {\"text\": \"red\"}}},\n]\n" + ], "is_optional": true, "kind": "scalar", - "linter": "root = if this > 2 || this < -2 { [ \"field must be less than 2 and greater than -2\" ] }", - "name": "presence_penalty", - "type": "float" + "name": "filter", + "type": "string" }, { - "description": "If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same seed and parameters should return the same result. Determinism is not guaranteed.", - "is_advanced": true, - "is_optional": true, + "default": [], + "description": "The fields to include or exclude in returned result based on the `payload_filter`.", + "kind": "array", + "name": "payload_fields", + "type": "string" + }, + { + "annotated_options": [ + [ + "exclude", + "Exclude the payload fields specified in `payload_fields`." + ], + [ + "include", + "Include the payload fields specified in `payload_fields`." + ] + ], + "default": "include", + "description": "The way the fields in `payload_fields` are filtered in the result.", "kind": "scalar", - "name": "seed", + "linter": "\nlet options = {\n \"exclude\": true,\n \"include\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "payload_filter", + "type": "string" + }, + { + "default": 10, + "description": "The maximum number of points to return.", + "kind": "scalar", + "name": "limit", "type": "int" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "qdrant", + "plugin": true, + "status": "stable", + "summary": "Query items within a https://qdrant.tech/[Qdrant^] collection.", + "type": "processor" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ + { + "description": "The target xref:components:rate_limits/about.adoc[`rate_limit` resource].", + "kind": "scalar", + "name": "resource", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "rate_limit", + "plugin": true, + "status": "stable", + "summary": "Throttles the throughput of a pipeline according to a specified xref:components:rate_limits/about.adoc[`rate_limit`] resource. Rate limits are shared across components and therefore apply globally to all processing pipelines.", + "type": "processor" + }, + { + "categories": [ + "Integration" + ], + "config": { + "children": [ + { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" }, { - "description": "Up to 4 sequences where the API will stop generating further tokens.", + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "stop", + "kind": "scalar", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], + "type": "string" + }, + { + "default": "", + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, + "kind": "scalar", + "name": "master", "type": "string" }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" + }, { "children": [ { - "description": "The name of this tool.", + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, "kind": "scalar", - "name": "name", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { - "description": "A description of this tool, the LLM uses this to decide if the tool should be used.", + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, "kind": "scalar", - "name": "description", + "name": "root_cas_file", "type": "string" }, { "children": [ { - "default": [], - "description": "The required parameters for this pipeline.", - "kind": "array", - "name": "required", + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", "type": "string" }, { - "children": [ - { - "description": "The type of this parameter.", - "kind": "scalar", - "name": "type", - "type": "string" - }, - { - "description": "A description of this parameter.", - "kind": "scalar", - "name": "description", - "type": "string" - }, - { - "default": [], - "description": "Specifies that this parameter is an enum and only these specific values should be used.", - "kind": "array", - "name": "enum", - "type": "string" - } + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" ], - "description": "The properties for the processor's input data", - "kind": "map", - "name": "properties", - "type": "object" + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" } ], "default": [], - "description": "The parameters the LLM needs to provide to invoke this tool.", - "kind": "scalar", - "name": "parameters", - "type": "object" - }, - { - "description": "The pipeline to execute when the LLM uses this tool.", - "is_optional": true, + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, "kind": "array", - "name": "processors", - "type": "processor" + "name": "client_certs", + "type": "object" } ], - "description": "The tools to allow the LLM to invoke. This allows building subpipelines that the LLM can choose to invoke to execute agentic-like actions.", - "kind": "array", - "name": "tools", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "is_advanced": true, + "kind": "scalar", + "name": "tls", "type": "object" - } - ], - "kind": "scalar", - "linter": "\n root = match {\n this.exists(\"json_schema\") && this.exists(\"schema_registry\") => [\"cannot set both `json_schema` and `schema_registry`\"]\n this.response_format == \"json_schema\" && !this.exists(\"json_schema\") && !this.exists(\"schema_registry\") => [\"schema must be specified using either `json_schema` or `schema_registry`\"]\n }\n ", - "name": "", - "type": "object" - }, - "description": "\nThis processor sends the contents of user prompts to the OpenAI API, which generates responses. By default, the processor submits the entire payload of each message as a string, unless you use the `prompt` configuration field to customize it.\n\nTo learn more about chat completion, see the https://platform.openai.com/docs/guides/chat-completions[OpenAI API documentation^].", - "examples": [ - { - "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - http:\n verb: GET\n url: \"${!content().string()}\"\n - openai_chat_completion:\n model: gpt-4o\n api_key: TODO\n prompt: \"Describe the following image\"\n image: \"root = content()\"\noutput:\n stdout:\n codec: lines\n", - "summary": "This example fetches image URLs from stdin and has GPT-4o describe the image.", - "title": "Use GPT-4o analyze an image" - }, - { - "config": "\ninput:\n stdin:\n scanner:\n lines: {}\npipeline:\n processors:\n - mapping: |\n root.prompt = content().string()\n - branch:\n processors:\n - cache:\n resource: mem\n operator: get\n key: history\n - catch:\n - mapping: 'root = []'\n result_map: 'root.history = this'\n - branch:\n processors:\n - openai_chat_completion:\n model: gpt-4o\n api_key: TODO\n prompt: \"${!this.prompt}\"\n history: 'root = this.history'\n result_map: 'root.response = content().string()'\n - mutation: |\n root.history = this.history.concat([\n {\"role\": \"user\", \"content\": this.prompt},\n {\"role\": \"assistant\", \"content\": this.response},\n ])\n - cache:\n resource: mem\n operator: set\n key: history\n value: '${!this.history}'\n - mapping: |\n root = this.response\noutput:\n stdout:\n codec: lines\n\ncache_resources:\n - label: mem \n memory: {}\n", - "summary": "This pipeline provides a historical chat history to GPT-4o using a cache.", - "title": "Provide historical chat history" - }, - { - "config": "\ninput:\n generate:\n count: 1\n mapping: |\n root = \"What is the weather like in Chicago?\"\npipeline:\n processors:\n - openai_chat_completion:\n model: gpt-4o\n api_key: \"${OPENAI_API_KEY}\"\n prompt: \"${!content().string()}\"\n tools:\n - name: GetWeather\n description: \"Retrieve the weather for a specific city\"\n parameters:\n required: [\"city\"]\n properties:\n city:\n type: string\n description: the city to look up the weather for\n processors:\n - http:\n verb: GET\n url: 'https://wttr.in/${!this.city}?T'\n headers:\n User-Agent: curl/8.11.1 # Returns a text string from the weather website\noutput:\n stdout: {}\n", - "summary": "This example asks GPT-4o to respond with the weather by invoking an HTTP processor to get the forecast.", - "title": "Use GPT-4o to call a tool" - } - ], - "name": "openai_chat_completion", - "plugin": true, - "status": "experimental", - "summary": "Generates responses to messages in a chat conversation, using the OpenAI API.", - "type": "processor", - "version": "4.32.0" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ + }, { - "default": "https://api.openai.com/v1", - "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", + "description": "The command to execute.", + "examples": [ + "scard", + "incrby", + "${! meta(\"command\") }" + ], + "interpolated": true, + "is_optional": true, "kind": "scalar", - "name": "server_address", - "type": "string" + "name": "command", + "type": "string", + "version": "4.3.0" }, { - "description": "The API key for OpenAI API.", - "is_secret": true, + "bloblang": true, + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of arguments required for the specified Redis command.", + "examples": [ + "root = [ this.key ]", + "root = [ meta(\"kafka_key\"), this.count ]" + ], + "is_optional": true, "kind": "scalar", - "name": "api_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "args_mapping", + "type": "string", + "version": "4.3.0" }, { - "description": "The name of the OpenAI model to use.", - "examples": [ - "text-embedding-3-large", - "text-embedding-3-small", - "text-embedding-ada-002" + "annotated_options": [ + [ + "incrby", + "Increments the number stored at `key` by the message content. If the key does not exist, it is set to `0` before performing the operation. Returns the value of `key` after the increment." + ], + [ + "keys", + "Returns an array of strings containing all the keys that match the pattern specified by the `key` field." + ], + [ + "sadd", + "Adds a new member to a set. Returns `1` if the member was added." + ], + [ + "scard", + "Returns the cardinality of a set, or `0` if the key does not exist." + ] ], + "description": "The operator to apply.", + "is_deprecated": true, + "is_optional": true, "kind": "scalar", - "name": "model", + "linter": "\nlet options = {\n \"incrby\": true,\n \"keys\": true,\n \"sadd\": true,\n \"scard\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "operator", "type": "string" }, { - "bloblang": true, - "description": "The text you want to generate a vector embedding for. By default, the processor submits the entire payload as a string.", + "description": "A key to use for the target operator.", + "interpolated": true, + "is_deprecated": true, "is_optional": true, "kind": "scalar", - "name": "text_mapping", + "name": "key", "type": "string" }, { - "description": "The number of dimensions the resulting output embeddings should have. Only supported in `text-embedding-3` and later models.", - "is_optional": true, + "default": 3, + "description": "The maximum number of retries before abandoning a request.", + "is_advanced": true, "kind": "scalar", - "name": "dimensions", + "name": "retries", "type": "int" + }, + { + "default": "500ms", + "description": "The time to wait before consecutive retry attempts.", + "is_advanced": true, + "kind": "scalar", + "name": "retry_period", + "type": "string" } ], "kind": "scalar", + "linter": "root = match {\n this.exists(\"operator\") == this.exists(\"command\") => [ \"one of 'operator' (old style) or 'command' (new style) fields must be specified\" ]\n this.exists(\"args_mapping\") && this.exists(\"operator\") => [ \"field args_mapping is invalid with an operator set\" ],\n}", "name": "", "type": "object" }, - "description": "\nThis processor sends text strings to the OpenAI API, which generates vector embeddings. By default, the processor submits the entire payload of each message as a string, unless you use the `text_mapping` configuration field to customize it.\n\nTo learn more about vector embeddings, see the https://platform.openai.com/docs/guides/embeddings[OpenAI API documentation^].", "examples": [ { - "config": "input:\n generate:\n interval: 1s\n mapping: |\n root = {\"text\": fake(\"paragraph\")}\npipeline:\n processors:\n - openai_embeddings:\n model: text-embedding-3-large\n api_key: \"${OPENAI_API_KEY}\"\n text_mapping: \"root = this.text\"\noutput:\n pinecone:\n host: \"${PINECONE_HOST}\"\n api_key: \"${PINECONE_API_KEY}\"\n id: \"root = uuid_v4()\"\n vector_mapping: \"root = this\"", - "summary": "Compute embeddings for some generated data and store it within xrefs:component:outputs/pinecone.adoc[Pinecone]", - "title": "Store embedding vectors in Pinecone" + "config": "\npipeline:\n processors:\n - branch:\n processors:\n - redis:\n url: TODO\n command: scard\n args_mapping: 'root = [ meta(\"set_key\") ]'\n result_map: 'root.cardinality = this'\n", + "summary": "If given payloads containing a metadata field `set_key` it's possible to query and store the cardinality of the set for each message using a xref:components:processors/branch.adoc[`branch` processor] in order to augment rather than replace the message contents:", + "title": "Querying Cardinality" + }, + { + "config": "\npipeline:\n processors:\n - branch:\n processors:\n - redis:\n url: TODO\n command: incrby\n args_mapping: 'root = [ this.name, this.friends_visited ]'\n result_map: 'root.total = this'\n", + "summary": "If we have JSON data containing number of friends visited during covid 19:\n\n```json\n{\"name\":\"ash\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":10}\n{\"name\":\"ash\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":-2}\n{\"name\":\"bob\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":3}\n{\"name\":\"bob\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":1}\n```\n\nWe can add a field that contains the running total number of friends visited:\n\n```json\n{\"name\":\"ash\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":10,\"total\":10}\n{\"name\":\"ash\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":-2,\"total\":8}\n{\"name\":\"bob\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":3,\"total\":3}\n{\"name\":\"bob\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":1,\"total\":4}\n```\n\nUsing the `incrby` command:", + "title": "Running Total" } ], - "name": "openai_embeddings", + "name": "redis", "plugin": true, - "status": "experimental", - "summary": "Generates vector embeddings to represent input text, using the OpenAI API.", - "type": "processor", - "version": "4.32.0" + "status": "stable", + "summary": "Performs actions against Redis that aren't possible using a xref:components:processors/cache.adoc[`cache`] processor. Actions are\nperformed for each message and the message contents are replaced with the result. In order to merge the result into the original message compose this processor within a xref:components:processors/branch.adoc[`branch` processor].", + "type": "processor" }, { "categories": [ - "AI" + "Integration" ], "config": { "children": [ { - "default": "https://api.openai.com/v1", - "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], "kind": "scalar", - "name": "server_address", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "description": "The API key for OpenAI API.", - "is_secret": true, + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "is_advanced": true, "kind": "scalar", - "name": "api_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], "type": "string" }, { - "description": "The name of the OpenAI model to use.", + "default": "", + "description": "Name of the redis master when `kind` is `failover`", "examples": [ - "dall-e-3", - "dall-e-2" + "mymaster" ], + "is_advanced": true, "kind": "scalar", - "name": "model", + "name": "master", "type": "string" }, { - "bloblang": true, - "description": "A text description of the image you want to generate. The `prompt` field accepts a maximum of 1000 characters for `dall-e-2` and 4000 characters for `dall-e-3`.", - "is_optional": true, + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, "kind": "scalar", - "name": "prompt", - "type": "string" + "name": "client_name", + "type": "string", + "version": "4.82.0" }, { - "description": "The quality of the image to generate. Use `hd` to create images with finer details and greater consistency across the image. This parameter is only supported for `dall-e-3` models.", - "examples": [ - "standard", - "hd" - ], - "interpolated": true, - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "quality", - "type": "string" - }, - { - "description": "The size of the generated image. Choose from `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Choose from `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3` models.", - "examples": [ - "1024x1024", - "512x512", - "1792x1024", - "1024x1792" - ], - "interpolated": true, - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "size", - "type": "string" - }, - { - "description": "The style of the generated image. Choose from `vivid` or `natural`. Vivid causes the model to lean towards generating hyperreal and dramatic images. Natural causes the model to produce more natural, less hyperreal looking images. This parameter is only supported for `dall-e-3`.", - "examples": [ - "vivid", - "natural" + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } ], - "interpolated": true, + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "style", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis processor sends an image description and other attributes, such as image size and quality to the OpenAI API, which generates an image. By default, the processor submits the entire payload of each message as a string, unless you use the `prompt` configuration field to customize it.\n\nTo learn more about image generation, see the https://platform.openai.com/docs/guides/images[OpenAI API documentation^].", - "name": "openai_image_generation", - "plugin": true, - "status": "experimental", - "summary": "Generates an image from a text description and other attributes, using OpenAI API.", - "type": "processor", - "version": "4.32.0" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "default": "https://api.openai.com/v1", - "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", - "kind": "scalar", - "name": "server_address", - "type": "string" - }, - { - "description": "The API key for OpenAI API.", - "is_secret": true, "kind": "scalar", - "name": "api_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "tls", + "type": "object" }, { - "description": "The name of the OpenAI model to use.", + "description": "A script to use for the target operator. It has precedence over the 'command' field.", "examples": [ - "tts-1", - "tts-1-hd" + "return redis.call('set', KEYS[1], ARGV[1])" ], "kind": "scalar", - "name": "model", + "name": "script", "type": "string" }, { "bloblang": true, - "description": "A text description of the audio you want to generate. The `input` field accepts a maximum of 4096 characters.", - "is_optional": true, - "kind": "scalar", - "name": "input", - "type": "string" - }, - { - "description": "The type of voice to use when generating the audio.", - "examples": [ - "alloy", - "echo", - "fable", - "onyx", - "nova", - "shimmer" - ], - "interpolated": true, - "kind": "scalar", - "name": "voice", - "type": "string" - }, - { - "description": "The format to generate audio in. Default is `mp3`.", - "examples": [ - "mp3", - "opus", - "aac", - "flac", - "wav", - "pcm" - ], - "interpolated": true, - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "response_format", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis processor sends a text description and other attributes, such as a voice type and format to the OpenAI API, which generates audio. By default, the processor submits the entire payload of each message as a string, unless you use the `input` configuration field to customize it.\n\nTo learn more about turning text into spoken audio, see the https://platform.openai.com/docs/guides/text-to-speech[OpenAI API documentation^].", - "name": "openai_speech", - "plugin": true, - "status": "experimental", - "summary": "Generates audio from a text description and other attributes, using OpenAI API.", - "type": "processor", - "version": "4.32.0" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "default": "https://api.openai.com/v1", - "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", - "kind": "scalar", - "name": "server_address", - "type": "string" - }, - { - "description": "The API key for OpenAI API.", - "is_secret": true, - "kind": "scalar", - "name": "api_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "The name of the OpenAI model to use.", + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of arguments required for the specified Redis script.", "examples": [ - "whisper-1" + "root = [ this.key ]", + "root = [ meta(\"kafka_key\"), \"hardcoded_value\" ]" ], "kind": "scalar", - "name": "model", + "name": "args_mapping", "type": "string" }, { "bloblang": true, - "description": "The audio file object (not file name) to transcribe, in one of the following formats: `flac`, `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `ogg`, `wav`, or `webm`.", - "kind": "scalar", - "name": "file", - "type": "string" - }, - { - "description": "The language of the input audio. Supplying the input language in ISO-639-1 format improves accuracy and latency.", + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of keys matching in size to the number of arguments required for the specified Redis script.", "examples": [ - "en", - "fr", - "de", - "zh" + "root = [ this.key ]", + "root = [ meta(\"kafka_key\"), this.count ]" ], - "interpolated": true, - "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "language", + "name": "keys_mapping", "type": "string" }, { - "description": "Optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.", - "interpolated": true, + "default": 3, + "description": "The maximum number of retries before abandoning a request.", "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "prompt", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis processor sends an audio file object along with the input language to OpenAI API to generate a transcription. By default, the processor submits the entire payload of each message as a string, unless you use the `file` configuration field to customize it.\n\nTo learn more about audio transcription, see the: https://platform.openai.com/docs/guides/speech-to-text[OpenAI API documentation^].", - "name": "openai_transcription", - "plugin": true, - "status": "experimental", - "summary": "Generates a transcription of spoken audio in the input language, using the OpenAI API.", - "type": "processor", - "version": "4.32.0" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "default": "https://api.openai.com/v1", - "description": "The Open API endpoint that the processor sends requests to. Update the default value to use another OpenAI compatible service.", - "kind": "scalar", - "name": "server_address", - "type": "string" - }, - { - "description": "The API key for OpenAI API.", - "is_secret": true, - "kind": "scalar", - "name": "api_key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "description": "The name of the OpenAI model to use.", - "examples": [ - "whisper-1" - ], - "kind": "scalar", - "name": "model", - "type": "string" - }, - { - "bloblang": true, - "description": "The audio file object (not file name) to translate, in one of the following formats: `flac`, `mp3`, `mp4`, `mpeg`, `mpga`, `m4a`, `ogg`, `wav`, or `webm`.", - "is_optional": true, "kind": "scalar", - "name": "file", - "type": "string" + "name": "retries", + "type": "int" }, { - "description": "Optional text to guide the model's style or continue a previous audio segment. The prompt should match the audio language.", - "interpolated": true, + "default": "500ms", + "description": "The time to wait before consecutive retry attempts.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "prompt", + "name": "retry_period", "type": "string" } ], @@ -53349,558 +62420,418 @@ "name": "", "type": "object" }, - "description": "\nThis processor sends an audio file object to OpenAI API to generate a translation. By default, the processor submits the entire payload of each message as a string, unless you use the `file` configuration field to customize it.\n\nTo learn more about translation, see the https://platform.openai.com/docs/guides/speech-to-text[OpenAI API documentation^].", - "name": "openai_translation", + "description": "Actions are performed for each message and the message contents are replaced with the result.\n\nIn order to merge the result into the original message compose this processor within a xref:components:processors/branch.adoc[`branch` processor].", + "examples": [ + { + "config": "\npipeline:\n processors:\n - redis_script:\n url: TODO\n script: |\n local value = redis.call(\"ZRANGE\", KEYS[1], '0', '0')\n\n if next(elements) == nil then\n return ''\n end\n\n redis.call(\"ZADD\", \"XX\", KEYS[1], ARGV[1], value)\n\n return value\n keys_mapping: 'root = [ meta(\"key\") ]'\n args_mapping: 'root = [ timestamp_unix_nano() ]'\n", + "summary": "The following example will use a script execution to get next element from a sorted set and set its score with timestamp unix nano value.", + "title": "Running a script" + } + ], + "name": "redis_script", "plugin": true, - "status": "experimental", - "summary": "Translates spoken audio into English, using the OpenAI API.", + "status": "stable", + "summary": "Performs actions against Redis using https://redis.io/docs/manual/programmability/eval-intro/[LUA scripts^].", "type": "processor", - "version": "4.32.0" + "version": "4.11.0" }, { "categories": [ - "Composition" + "Utility" ], "config": { - "children": [ - { - "default": 0, - "description": "The maximum number of messages to have processing at a given time.", - "kind": "scalar", - "name": "cap", - "type": "int" - }, - { - "description": "A list of child processors to apply.", - "kind": "array", - "name": "processors", - "type": "processor" - } - ], + "default": "", "kind": "scalar", "name": "", - "type": "object" + "type": "string" }, - "description": "\nThe field `cap`, if greater than zero, caps the maximum number of parallel processing threads.\n\nThe functionality of this processor depends on being applied across messages that are batched. You can find out more about batching in xref:configuration:batching.adoc[].", - "name": "parallel", + "description": "\nThis processor allows you to reference the same configured processor resource in multiple places, and can also tidy up large nested configs. For example, the config:\n\n```yaml\npipeline:\n processors:\n - mapping: |\n root.message = this\n root.meta.link_count = this.links.length()\n root.user.age = this.user.age.number()\n```\n\nIs equivalent to:\n\n```yaml\npipeline:\n processors:\n - resource: foo_proc\n\nprocessor_resources:\n - label: foo_proc\n mapping: |\n root.message = this\n root.meta.link_count = this.links.length()\n root.user.age = this.user.age.number()\n```\n\nYou can find out more about resources in xref:configuration:resources.adoc[]", + "name": "resource", "plugin": true, "status": "stable", - "summary": "A processor that applies a list of child processors to messages of a batch as though they were each a batch of one message (similar to the xref:components:processors/for_each.adoc[`for_each`] processor), but where each message is processed in parallel.", + "summary": "Resource is a processor type that runs a processor resource identified by its label.", "type": "processor" }, { "categories": [ - "Parsing" + "Composition" ], "config": { "children": [ { - "annotated_options": [ - [ - "from_json", - "Compress a batch of JSON documents into a file." - ], - [ - "to_json", - "Expand a file into one or more JSON messages." - ] + "children": [ + { + "default": "500ms", + "description": "The initial period to wait between retry attempts.", + "examples": [ + "50ms", + "1s" + ], + "kind": "scalar", + "name": "initial_interval", + "type": "string" + }, + { + "default": "10s", + "description": "The maximum period to wait between retry attempts", + "examples": [ + "5s", + "1m" + ], + "kind": "scalar", + "name": "max_interval", + "type": "string" + }, + { + "default": "1m", + "description": "The maximum overall period of time to spend on retry attempts before the request is aborted. Setting this value to a zeroed duration (such as `0s`) will result in unbounded retries.", + "examples": [ + "1m", + "1h" + ], + "kind": "scalar", + "name": "max_elapsed_time", + "type": "string" + } ], - "description": "Determines whether the processor converts messages into a parquet file or expands parquet files into messages. Converting into JSON allows subsequent processors and mappings to convert the data into any other format.", + "description": "Determine time intervals and cut offs for retry attempts.", "kind": "scalar", - "linter": "\nlet options = {\n \"from_json\": true,\n \"to_json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operator", - "type": "string" + "name": "backoff", + "type": "object" }, { - "default": "snappy", - "description": "The type of compression to use when writing parquet files, this field is ignored when consuming parquet files.", - "kind": "scalar", - "linter": "\nlet options = {\n \"uncompressed\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"lz4\": true,\n \"zstd\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "compression", - "options": [ - "uncompressed", - "snappy", - "gzip", - "lz4", - "zstd" - ], - "type": "string" + "description": "A list of xref:components:processors/about.adoc[processors] to execute on each message.", + "kind": "array", + "name": "processors", + "type": "processor" }, { - "description": "A file path containing a schema used to describe the parquet files being generated or consumed, the format of the schema is a JSON document detailing the tag and fields of documents. The schema can be found at: https://pkg.go.dev/github.com/xitongsys/parquet-go#readme-json. Either a `schema_file` or `schema` field must be specified when creating Parquet files via the `from_json` operator.", - "examples": [ - "schemas/foo.json" - ], - "is_optional": true, + "default": false, + "description": "When processing batches of messages these batches are ignored and the processors apply to each message sequentially. However, when this field is set to `true` each message will be processed in parallel. Caution should be made to ensure that batch sizes do not surpass a point where this would cause resource (CPU, memory, API limits) contention.", "kind": "scalar", - "name": "schema_file", - "type": "string" + "name": "parallel", + "type": "bool" }, { - "description": "A schema used to describe the parquet files being generated or consumed, the format of the schema is a JSON document detailing the tag and fields of documents. The schema can be found at: https://pkg.go.dev/github.com/xitongsys/parquet-go#readme-json. Either a `schema_file` or `schema` field must be specified when creating Parquet files via the `from_json` operator.", - "examples": [ - "{\n \"Tag\": \"name=root, repetitiontype=REQUIRED\",\n \"Fields\": [\n {\"Tag\":\"name=name,inname=NameIn,type=BYTE_ARRAY,convertedtype=UTF8, repetitiontype=REQUIRED\"},\n {\"Tag\":\"name=age,inname=Age,type=INT32,repetitiontype=REQUIRED\"}\n ]\n}" - ], - "is_optional": true, + "default": 0, + "description": "The maximum number of retry attempts before the request is aborted. Setting this value to `0` will result in unbounded number of retries.", "kind": "scalar", - "name": "schema", - "type": "string" + "name": "max_retries", + "type": "int" } ], "kind": "scalar", - "linter": "\nroot = if this.operator == \"from_json\" && (this.schema | this.schema_file | \"\") == \"\" {\n\t\"a schema or schema_file must be specified when the operator is set to from_json\"\n}", "name": "", "type": "object" }, - "description": "\n== Alternatives\n\nThis processor is now deprecated, it's recommended that you use the new xref:components:processors/parquet_decode.adoc[`parquet_decode`] and xref:components:processors/parquet_encode.adoc[`parquet_encode`] processors as they provide a number of advantages, the most important of which is better error messages for when schemas are mismatched or files could not be consumed.\n\n== Troubleshooting\n\nThis processor is experimental and the error messages that it provides are often vague and unhelpful. An error message of the form `interface \\{} is nil, not ` implies that a field of the given type was expected but not found in the processed message when writing parquet files.\n\nUnfortunately the name of the field will sometimes be missing from the error, in which case it's worth double checking the schema you provided to make sure that there are no typos in the field names, and if that doesn't reveal the issue it can help to mark fields as OPTIONAL in the schema and gradually change them back to REQUIRED until the error returns.\n\n== Define the schema\n\nThe schema must be specified as a JSON string, containing an object that describes the fields expected at the root of each document. Each field can itself have more fields defined, allowing for nested structures:\n\n```json\n{\n \"Tag\": \"name=root, repetitiontype=REQUIRED\",\n \"Fields\": [\n {\"Tag\": \"name=name, inname=NameIn, type=BYTE_ARRAY, convertedtype=UTF8, repetitiontype=REQUIRED\"},\n {\"Tag\": \"name=age, inname=Age, type=INT32, repetitiontype=REQUIRED\"},\n {\"Tag\": \"name=id, inname=Id, type=INT64, repetitiontype=REQUIRED\"},\n {\"Tag\": \"name=weight, inname=Weight, type=FLOAT, repetitiontype=REQUIRED\"},\n {\n \"Tag\": \"name=favPokemon, inname=FavPokemon, type=LIST, repetitiontype=OPTIONAL\",\n \"Fields\": [\n {\"Tag\": \"name=name, inname=PokeName, type=BYTE_ARRAY, convertedtype=UTF8, repetitiontype=REQUIRED\"},\n {\"Tag\": \"name=coolness, inname=Coolness, type=FLOAT, repetitiontype=REQUIRED\"}\n ]\n }\n ]\n}\n```\n\nA schema can be derived from a source file using https://github.com/xitongsys/parquet-go/tree/master/tool/parquet-tools:\n\n```sh\n./parquet-tools -cmd schema -file foo.parquet\n```", - "name": "parquet", + "description": "\nExecutes child processors and if a resulting message is errored then, after a specified backoff period, the same original message will be attempted again through those same processors. If the child processors result in more than one message then the retry mechanism will kick in if _any_ of the resulting messages are errored.\n\nIt is important to note that any mutations performed on the message during these child processors will be discarded for the next retry, and therefore it is safe to assume that each execution of the child processors will always be performed on the data as it was when it first reached the retry processor.\n\nBy default the retry backoff has a specified <>, if this time period is reached during retries and an error still occurs these errored messages will proceed through to the next processor after the retry (or your outputs). Normal xref:configuration:error_handling.adoc[error handling patterns] can be used on these messages.\n\nIn order to avoid permanent loops any error associated with messages as they first enter a retry processor will be cleared.\n\n== Metadata\n\nThis processor adds the following metadata fields to each message:\n\n```text\n- retry_count - The number of retry attempts.\n- backoff_duration - The total time (in nanoseconds) elapsed while performing retries.\n```\n\n[CAUTION]\n.Batching\n====\nIf you wish to wrap a batch-aware series of processors then take a look at the <>.\n====\n", + "examples": [ + { + "config": "\ninput:\n generate:\n interval: 1s\n mapping: 'root.noise = [ \"woof\", \"meow\", \"moo\", \"quack\" ].index(random_int(min: 0, max: 3))'\n\npipeline:\n processors:\n - retry:\n backoff:\n initial_interval: 100ms\n max_interval: 5s\n max_elapsed_time: 0s\n processors:\n - http:\n url: 'http://example.com/try/not/to/dox/taz'\n verb: POST\n\noutput:\n # Drop everything because it's junk data, I don't want it lol\n drop: {}\n", + "summary": "\nHere we have a config where I generate animal noises and send them to Taz via HTTP. Taz has a tendency to stop his servers whenever I dispatch my animals upon him, and therefore these HTTP requests sometimes fail. However, I have the retry processor and with this super power I can specify a back off policy and it will ensure that for each animal noise the HTTP processor is attempted until either it succeeds or my Redpanda Connect instance is stopped.\n\nI even go as far as to zero-out the maximum elapsed time field, which means that for each animal noise I will wait indefinitely, because I really really want Taz to receive every single animal noise that he is entitled to.", + "title": "Stop ignoring me Taz" + } + ], + "footnotes": "\n== Batching\n\nWhen messages are batched the child processors of a retry are executed for each individual message in isolation, performed serially by default but in parallel when the field <> is set to `true`. This is an intentional limitation of the retry processor and is done in order to ensure that errors are correctly associated with a given input message. Otherwise, the archiving, expansion, grouping, filtering and so on of the child processors could obfuscate this relationship.\n\nIf the target behavior of your retried processors is \"batch aware\", in that you wish to perform some processing across the entire batch of messages and repeat it in the event of errors, you can use an xref:components:processors/archive.adoc[`archive` processor] to collapse the batch into an individual message. Then, within these child processors either perform your batch aware processing on the archive, or use an xref:components:processors/unarchive.adoc[`unarchive` processor] in order to expand the single message back out into a batch.\n\nFor example, if the retry processor were being used to wrap an HTTP request where the payload data is a batch archived into a JSON array it should look something like this:\n\n```yaml\npipeline:\n processors:\n - archive:\n format: json_array\n - retry:\n processors:\n - http:\n url: example.com/nope\n verb: POST\n - unarchive:\n format: json_array\n```\n", + "name": "retry", "plugin": true, - "status": "deprecated", - "summary": "Converts batches of documents to or from https://parquet.apache.org/docs/[Parquet files^].", + "status": "beta", + "summary": "Attempts to execute a series of child processors until success.", "type": "processor", - "version": "3.62.0" + "version": "4.27.0" }, { "categories": [ - "Parsing" + "Parsing", + "Integration" ], "config": { "children": [ { "default": false, - "description": "Whether to extract BYTE_ARRAY and FIXED_LEN_BYTE_ARRAY values as strings rather than byte slices in all cases. Values with a logical type of UTF8 will automatically be extracted as strings irrespective of this field. Enabling this field makes serializing the data as JSON more intuitive as `[]byte` values are serialized as base64 encoded strings by default.", + "description": "Whether Avro messages should be decoded into normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^]. When true, union values are unwrapped (bare values instead of {\"type\": value} wrappers).", + "is_advanced": true, "is_deprecated": true, "kind": "scalar", - "name": "byte_array_as_string", + "name": "avro_raw_json", "type": "bool" }, - { - "annotated_options": [ - [ - "v1", - "No special handling of logical types" - ], - [ - "v2", - "\n- TIMESTAMP - decodes as an RFC3339 string describing the time. If the `isAdjustedToUTC` flag is set to true in the parquet file, the time zone will be set to UTC. If it is set to false the time zone will be set to local time.\n- UUID - decodes as a string, i.e. `00112233-4455-6677-8899-aabbccddeeff`." - ] - ], - "default": "v1", - "description": "Whether to be smart about decoding logical types. In the Parquet format, logical types are stored as one of the standard physical types with some additional metadata describing the logical type. For example, UUIDs are stored in a FIXED_LEN_BYTE_ARRAY physical type, but there is metadata in the schema denoting that it is a UUID. By default, this logical type metadata will be ignored and values will be decoded directly from the physical type, which isn't always desirable. By enabling this option, logical types will be given special treatment and will decode into more useful values. The value for this field specifies a version, i.e. v0, v1... Any given version enables the logical type handling for that version and all versions below it, which allows the handling of new logical types to be introduced without breaking existing pipelines. We recommend enabling the newest version available of this feature when creating new pipelines.", - "examples": [ - "v2" - ], - "kind": "scalar", - "linter": "\nlet options = {\n \"v1\": true,\n \"v2\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "handle_logical_types", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis processor uses https://github.com/parquet-go/parquet-go[https://github.com/parquet-go/parquet-go^], which is itself experimental. Therefore changes could be made into how this processor functions outside of major version releases.", - "examples": [ - { - "config": "\ninput:\n aws_s3:\n bucket: TODO\n prefix: foos/\n scanner:\n to_the_end: {}\n sqs:\n url: TODO\n processors:\n - parquet_decode: {}\n\noutput:\n file:\n codec: lines\n path: './foos/${! meta(\"s3_key\") }.jsonl'\n", - "summary": "In this example we consume files from AWS S3 as they're written by listening onto an SQS queue for upload events. We make sure to use the `to_the_end` scanner which means files are read into memory in full, which then allows us to use a `parquet_decode` processor to expand each file into a batch of messages. Finally, we write the data out to local files as newline delimited JSON.", - "title": "Reading Parquet Files from AWS S3" - } - ], - "name": "parquet_decode", - "plugin": true, - "status": "experimental", - "summary": "Decodes https://parquet.apache.org/docs/[Parquet files^] into a batch of structured messages.", - "type": "processor", - "version": "4.4.0" - }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ { "children": [ { - "description": "The name of the column.", - "kind": "scalar", - "name": "name", - "type": "string" - }, - { - "description": "The type of the column, only applicable for leaf columns with no child fields. Some logical types can be specified here such as UTF8.", + "description": "Whether avro messages should be decoded into normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[JSON as specified in the Avro Spec^].\n\nFor example, if there is a union schema `[\"null\", \"string\", \"Foo\"]` where `Foo` is a record name, with raw_unions as false (the default) you get:\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nWhen raw_unions is set to true then the above union schema is decoded as the following:\n- `null` as `null`;\n- the string `\"a\"` as `\"a\"`; and\n- a `Foo` instance as `{...}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n", "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"boolean\": true,\n \"int32\": true,\n \"int64\": true,\n \"float\": true,\n \"double\": true,\n \"byte_array\": true,\n \"utf8\": true,\n \"timestamp\": true,\n \"bson\": true,\n \"enum\": true,\n \"json\": true,\n \"uuid\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "type", - "options": [ - "BOOLEAN", - "INT32", - "INT64", - "FLOAT", - "DOUBLE", - "BYTE_ARRAY", - "UTF8", - "TIMESTAMP", - "BSON", - "ENUM", - "JSON", - "UUID" - ], - "type": "string" + "name": "raw_unions", + "type": "bool" }, { "default": false, - "description": "Whether the field is repeated.", + "description": "Whether logical types should be preserved or transformed back into their primitive type. By default, decimals are decoded as raw bytes and timestamps are decoded as plain integers. Setting this field to true keeps decimal types as numbers in bloblang and timestamps as time values.", "kind": "scalar", - "name": "repeated", + "name": "preserve_logical_types", "type": "bool" }, { "default": false, - "description": "Whether the field is optional.", + "description": "Only valid if preserve_logical_types is true. This decodes various Kafka Connect types into their bloblang equivalents when not representable by standard logical types according to the Avro standard.\n\nTypes that are currently translated:\n\n.Debezium Custom Temporal Types\n|===\n|Type Name |Bloblang Type |Description\n\n|io.debezium.time.Date\n|timestamp\n|Date without time (days since epoch)\n\n|io.debezium.time.Timestamp\n|timestamp\n|Timestamp without timezone (milliseconds since epoch)\n\n|io.debezium.time.MicroTimestamp\n|timestamp\n|Timestamp with microsecond precision\n\n|io.debezium.time.NanoTimestamp\n|timestamp\n|Timestamp with nanosecond precision\n\n|io.debezium.time.ZonedTimestamp\n|timestamp\n|Timestamp with timezone (ISO-8601 format)\n\n|io.debezium.time.Year\n|timestamp at January 1st at 00:00:00\n|Year value\n\n|io.debezium.time.Time\n|timestamp at the unix epoch\n|Time without date (milliseconds past midnight)\n\n|io.debezium.time.MicroTime\n|timestamp at the unix epoch\n|Time with microsecond precision\n\n|io.debezium.time.NanoTime\n|timestamp at the unix epoch\n|Time with nanosecond precision\n\n|===\n\n", "kind": "scalar", - "name": "optional", + "name": "translate_kafka_connect_types", "type": "bool" }, { - "description": "A list of child fields.", + "bloblang": true, + "description": "A custom mapping to apply to Avro schemas JSON representation. This is useful to transform custom types emitted by other tools into standard avro.", "examples": [ - [ - { - "name": "foo", - "type": "INT64" - }, - { - "name": "bar", - "type": "BYTE_ARRAY" - } - ] + "\nmap isDebeziumTimestampType {\n root = this.type == \"long\" && this.\"connect.name\" == \"io.debezium.time.Timestamp\" && !this.exists(\"logicalType\")\n}\nmap debeziumTimestampToAvroTimestamp {\n let mapped_fields = this.fields.or([]).map_each(item -> item.apply(\"debeziumTimestampToAvroTimestamp\"))\n root = match {\n this.type == \"record\" => this.assign({\"fields\": $mapped_fields})\n this.type.type() == \"array\" => this.assign({\"type\": this.type.map_each(item -> item.apply(\"debeziumTimestampToAvroTimestamp\"))})\n # Add a logical type so that it's decoded as a timestamp instead of a long.\n this.type.type() == \"object\" && this.type.apply(\"isDebeziumTimestampType\") => this.merge({\"type\":{\"logicalType\": \"timestamp-millis\"}})\n _ => this\n }\n}\nroot = this.apply(\"debeziumTimestampToAvroTimestamp\")\n" ], + "is_advanced": true, "is_optional": true, - "kind": "array", - "name": "fields", - "type": "unknown" + "kind": "scalar", + "name": "mapping", + "type": "string" + }, + { + "description": "Optionally store the schema used to decode messages as a metadata field under the given name. This field can later be referenced in other components such as a `parquet_encode` processor in order to automatically infer their schema.", + "is_optional": true, + "kind": "scalar", + "name": "store_schema_metadata", + "type": "string" } ], - "description": "Parquet schema.", - "is_optional": true, - "kind": "array", - "name": "schema", - "type": "object" - }, - { - "default": "", - "description": "Optionally specify a metadata field containing a schema definition to use for encoding instead of a statically defined schema. For batches of messages, the first message's schema will be applied to all subsequent messages of the batch.", - "kind": "scalar", - "name": "schema_metadata", - "type": "string" - }, - { - "default": "uncompressed", - "description": "The default compression type to use for fields.", + "description": "Configuration for how to decode schemas that are of type AVRO.", "kind": "scalar", - "linter": "\nlet options = {\n \"uncompressed\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"brotli\": true,\n \"zstd\": true,\n \"lz4raw\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "default_compression", - "options": [ - "uncompressed", - "snappy", - "gzip", - "brotli", - "zstd", - "lz4raw" - ], - "type": "string" + "name": "avro", + "type": "object" }, { - "default": "DELTA_LENGTH_BYTE_ARRAY", - "description": "The default encoding type to use for fields. A custom default encoding is only necessary when consuming data with libraries that do not support `DELTA_LENGTH_BYTE_ARRAY` and is therefore best left unset where possible.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"delta_length_byte_array\": true,\n \"plain\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "default_encoding", - "options": [ - "DELTA_LENGTH_BYTE_ARRAY", - "PLAIN" - ], - "type": "string", - "version": "4.11.0" - } - ], - "kind": "scalar", - "linter": "root = if this.schema.or([]).length() == 0 && this.schema_metadata.or(\"\") == \"\" { \"either a schema or schema_metadata must be specified\" }", - "name": "", - "type": "object" - }, - "description": "\nThis processor uses https://github.com/parquet-go/parquet-go[https://github.com/parquet-go/parquet-go^], which is itself experimental. Therefore changes could be made into how this processor functions outside of major version releases.\n", - "examples": [ - { - "config": "\noutput:\n aws_s3:\n bucket: TODO\n path: 'stuff/${! timestamp_unix() }-${! uuid_v4() }.parquet'\n batching:\n count: 1000\n period: 10s\n processors:\n - parquet_encode:\n schema:\n - name: id\n type: INT64\n - name: weight\n type: DOUBLE\n - name: content\n type: BYTE_ARRAY\n default_compression: zstd\n", - "summary": "In this example we use the batching mechanism of an `aws_s3` output to collect a batch of messages in memory, which then converts it to a parquet file and uploads it.", - "title": "Writing Parquet Files to AWS S3" - } - ], - "name": "parquet_encode", - "plugin": true, - "status": "experimental", - "summary": "Encodes https://parquet.apache.org/docs/[Parquet files^] from a batch of structured messages.", - "type": "processor", - "version": "4.4.0" - }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ - { - "description": "A common log <> to parse.", - "kind": "scalar", - "linter": "\nlet options = {\n \"syslog_rfc5424\": true,\n \"syslog_rfc3164\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "format", - "options": [ - "syslog_rfc5424", - "syslog_rfc3164" + "children": [ + { + "default": false, + "description": "Use proto field name instead of lowerCamelCase name.", + "kind": "scalar", + "name": "use_proto_names", + "type": "bool" + }, + { + "default": false, + "description": "Emits enum values as numbers.", + "kind": "scalar", + "name": "use_enum_numbers", + "type": "bool" + }, + { + "default": false, + "description": "Whether to emit unpopulated fields. It does not emit unpopulated oneof fields or unpopulated extension fields.", + "kind": "scalar", + "name": "emit_unpopulated", + "type": "bool" + }, + { + "default": false, + "description": "Whether to emit default-valued primitive fields, empty lists, and empty maps. emit_unpopulated takes precedence over emit_default_values ", + "kind": "scalar", + "name": "emit_default_values", + "type": "bool" + }, + { + "default": true, + "description": "If messages should be serialized to JSON bytes. If false then the message is kept in decoded form, which means that 64 bit integers are not converted to strings and types for bytes and google.protobuf.Timestamp are preserved (as they are not serialized to JSON strings).", + "kind": "scalar", + "name": "serialize_to_json", + "type": "bool" + } ], - "type": "string" - }, - { - "default": true, - "description": "Still returns partially parsed messages even if an error occurs.", - "is_advanced": true, - "kind": "scalar", - "name": "best_effort", - "type": "bool" - }, - { - "default": true, - "description": "Also accept timestamps in rfc3339 format while parsing. Applicable to format `syslog_rfc3164`.", - "is_advanced": true, - "kind": "scalar", - "name": "allow_rfc3339", - "type": "bool" - }, - { - "default": "current", - "description": "Sets the strategy used to set the year for rfc3164 timestamps. Applicable to format `syslog_rfc3164`. When set to `current` the current year will be set, when set to an integer that value will be used. Leave this field empty to not set a default year at all.", - "is_advanced": true, + "description": "Configuration for how to decode schemas that are of type PROTOBUF.", "kind": "scalar", - "name": "default_year", - "type": "string" + "name": "protobuf", + "type": "object" }, { - "default": "UTC", - "description": "Sets the strategy to decide the timezone for rfc3164 timestamps. Applicable to format `syslog_rfc3164`. This value should follow the https://golang.org/pkg/time/#LoadLocation[time.LoadLocation^] format.", - "is_advanced": true, + "children": [ + { + "default": false, + "description": "Whether decoded values should be coerced to match the types declared in the JSON Schema. By default JSON Schema decoding only validates the message and leaves it untouched, which means numbers are later interpreted as floating point (`double`) and date-time values as strings. When set to `true` the decoder rebuilds the message so that values match the schema: `integer` fields become 64-bit integers, `number` fields stay floating point, `string` fields with `format: date-time` become timestamps, and any `default` values declared in the schema are applied to absent fields. This is useful for downstream components that infer their schema from the decoded values, such as the `iceberg` outputs, which will then create `bigint` columns for integer fields rather than `double`. Note that, unlike the default behaviour, this is no longer a read-only operation: the message contents are transformed. Because coercion is stricter than validation, a message that passes validation may still fail coercion (for example an integer that overflows a 64-bit value, or a `date-time` string that is not valid RFC 3339), in which case the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", + "kind": "scalar", + "name": "coerce_data", + "type": "bool", + "version": "4.97.0" + } + ], + "description": "Configuration for how to decode schemas that are of type JSON.", "kind": "scalar", - "name": "default_timezone", - "type": "string" + "name": "json", + "type": "object" }, { - "is_deprecated": true, - "kind": "scalar", - "name": "codec", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "footnotes": "\n== Codecs\n\nCurrently the only supported structured data codec is `json`.\n\n== Formats\n\n=== `syslog_rfc5424`\n\nAttempts to parse a log following the https://tools.ietf.org/html/rfc5424[Syslog RFC5424^] spec. The resulting structured document may contain any of the following fields:\n\n- `message` (string)\n- `timestamp` (string, RFC3339)\n- `facility` (int)\n- `severity` (int)\n- `priority` (int)\n- `version` (int)\n- `hostname` (string)\n- `procid` (string)\n- `appname` (string)\n- `msgid` (string)\n- `structureddata` (object)\n\n=== `syslog_rfc3164`\n\nAttempts to parse a log following the https://tools.ietf.org/html/rfc3164[Syslog rfc3164] spec. The resulting structured document may contain any of the following fields:\n\n- `message` (string)\n- `timestamp` (string, RFC3339)\n- `facility` (int)\n- `severity` (int)\n- `priority` (int)\n- `hostname` (string)\n- `procid` (string)\n- `appname` (string)\n- `msgid` (string)\n", - "name": "parse_log", - "plugin": true, - "status": "stable", - "summary": "Parses common log <> into <>. This is easier and often much faster than xref:components:processors/grok.adoc[`grok`].", - "type": "processor" - }, - { - "categories": [ - "Composition" - ], - "config": { - "default": [], - "kind": "array", - "name": "", - "type": "processor" - }, - "description": "This processor is useful in situations where you want to collect several processors under a single resource identifier, whether it is for making your configuration easier to read and navigate, or for improving the testability of your configuration. The behavior of child processors will match exactly the behavior they would have under any other processors block.", - "examples": [ - { - "config": "\npipeline:\n processors:\n - label: my_super_feature\n processors:\n - log:\n message: \"Let's do something cool\"\n - archive:\n format: json_array\n - mapping: root.items = this\n", - "summary": "Imagine we have a collection of processors who cover a specific functionality. We could use this processor to group them together and make it easier to read and mock during testing by giving the whole block a label:", - "title": "Grouped Processing" - } - ], - "name": "processors", - "plugin": true, - "status": "stable", - "summary": "A processor grouping several sub-processors.", - "type": "processor" - }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ - { - "description": "The [operator](#operators) to execute", - "kind": "scalar", - "linter": "\nlet options = {\n \"to_json\": true,\n \"from_json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operator", - "options": [ - "to_json", - "from_json" + "default": "10m", + "description": "The duration after which a schema is considered stale and will be removed from the cache.", + "examples": [ + "1h", + "5m" ], - "type": "string" - }, - { - "description": "The fully qualified name of the protobuf message to convert to/from.", "kind": "scalar", - "name": "message", + "name": "cache_duration", "type": "string" }, { - "default": false, - "description": "If `true`, the `from_json` operator discards fields that are unknown to the schema.", - "kind": "scalar", - "name": "discard_unknown", - "type": "bool" - }, - { - "default": false, - "description": "If `true`, the `to_json` operator deserializes fields exactly as named in schema file.", + "description": "The base URL of the schema registry service.", "kind": "scalar", - "name": "use_proto_names", - "type": "bool" - }, - { - "default": [], - "description": "A list of directories containing .proto files, including all definitions required for parsing the target message. If left empty the current directory is used. Each directory listed will be walked with all found .proto files imported. Either this field or `bsr` must be populated.", - "kind": "array", - "name": "import_paths", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "default": false, - "description": "If `true`, the `to_json` operator deserializes enums as numerical values instead of string names.", + "description": "If set, this schema ID will be used when a message's schema header cannot be read (ErrBadHeader). If not set, schema header errors will be returned. WARNING: This is configuration does not work with PROTOBUF schemas. You may also use `with_schema_registry_header` bloblang function to add a schema ID to messages.", + "is_optional": true, "kind": "scalar", - "name": "use_enum_numbers", - "type": "bool" + "name": "default_schema_id", + "type": "int" }, { "children": [ { - "description": "Module to fetch from a Buf Schema Registry e.g. 'buf.build/exampleco/mymodule'.", + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, "kind": "scalar", - "name": "module", - "type": "string" + "name": "enabled", + "type": "bool" }, { "default": "", - "description": "Buf Schema Registry URL, leave blank to extract from module.", + "description": "A value used to identify the client to the service provider.", "is_advanced": true, "kind": "scalar", - "name": "url", + "name": "consumer_key", "type": "string" }, { "default": "", - "description": "Buf Schema Registry server API key, can be left blank for a public registry.", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, "is_secret": true, "kind": "scalar", - "name": "api_key", + "name": "consumer_secret", "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" }, { "default": "", - "description": "Version to retrieve from the Buf Schema Registry, leave blank for latest.", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "version", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", "type": "string" } ], - "default": [], - "description": "Buf Schema Registry configuration. Either this field or `import_paths` must be populated. Note that this field is an array, and multiple BSR configurations can be provided.", - "kind": "array", - "name": "bsr", - "type": "object" - } - ], - "kind": "scalar", - "linter": "\nroot = match {\nthis.import_paths.type() == \"unknown\" && this.bsr.length() == 0 => [ \"at least one of `import_paths`and `bsr` must be set\" ],\nthis.import_paths.type() == \"array\" && this.import_paths.length() > 0 && this.bsr.length() > 0 => [ \"both `import_paths` and `bsr` can't be set simultaneously\" ],\n}", - "name": "", - "type": "object" - }, - "description": "\nThe main functionality of this processor is to map to and from JSON documents, you can read more about JSON mapping of protobuf messages here: [https://developers.google.com/protocol-buffers/docs/proto3#json](https://developers.google.com/protocol-buffers/docs/proto3#json)\n\nUsing reflection for processing protobuf messages in this way is less performant than generating and using native code. Therefore when performance is critical it is recommended that you use Redpanda Connect plugins instead for processing protobuf messages natively, you can find an example of Redpanda Connect plugins at [https://github.com/redpanda-data/redpanda-connect-plugin-example](https://github.com/redpanda-data/redpanda-connect-plugin-example)\n\nThe processor will ignore any files that begin with a dot (\".\"g), a convention for hidden files, when loading protocol buffer definitions.\n== Operators\n\n=== `to_json`\n\nConverts protobuf messages into a generic JSON structure. This makes it easier to manipulate the contents of the document within Redpanda Connect.\n\n=== `from_json`\n\nAttempts to create a target protobuf message from a generic JSON structure.\n", - "examples": [ - { - "config": "\npipeline:\n processors:\n - protobuf:\n operator: from_json\n message: testing.Person\n import_paths: [ testing/schema ]\n", - "summary": "\nIf we have the following protobuf definition within a directory called `testing/schema`:\n\n```protobuf\nsyntax = \"proto3\";\npackage testing;\n\nimport \"google/protobuf/timestamp.proto\";\n\nmessage Person {\n string first_name = 1;\n string last_name = 2;\n string full_name = 3;\n int32 age = 4;\n int32 id = 5; // Unique ID number for this person.\n string email = 6;\n\n google.protobuf.Timestamp last_updated = 7;\n}\n```\n\nAnd a stream of JSON documents of the form:\n\n```json\n{\n\t\"firstName\": \"caleb\",\n\t\"lastName\": \"quaye\",\n\t\"email\": \"caleb@myspace.com\"\n}\n```\n\nWe can convert the documents into protobuf messages with the following config:", - "title": "JSON to Protobuf using Schema from Disk" - }, - { - "config": "\npipeline:\n processors:\n - protobuf:\n operator: to_json\n message: testing.Person\n import_paths: [ testing/schema ]\n", - "summary": "\nIf we have the following protobuf definition within a directory called `testing/schema`:\n\n```protobuf\nsyntax = \"proto3\";\npackage testing;\n\nimport \"google/protobuf/timestamp.proto\";\n\nmessage Person {\n string first_name = 1;\n string last_name = 2;\n string full_name = 3;\n int32 age = 4;\n int32 id = 5; // Unique ID number for this person.\n string email = 6;\n\n google.protobuf.Timestamp last_updated = 7;\n}\n```\n\nAnd a stream of protobuf messages of the type `Person`, we could convert them into JSON documents of the format:\n\n```json\n{\n\t\"firstName\": \"caleb\",\n\t\"lastName\": \"quaye\",\n\t\"email\": \"caleb@myspace.com\"\n}\n```\n\nWith the following config:", - "title": "Protobuf to JSON using Schema from Disk" - }, - { - "config": "\npipeline:\n processors:\n - protobuf:\n operator: from_json\n message: testing.Person\n bsr:\n - module: buf.build/exampleco/mymodule\n api_key: xxx\n", - "summary": "\nIf we have the following protobuf definition within a BSR module hosted at `buf.build/exampleco/mymodule`:\n\n```protobuf\nsyntax = \"proto3\";\npackage testing;\n\nimport \"google/protobuf/timestamp.proto\";\n\nmessage Person {\n string first_name = 1;\n string last_name = 2;\n string full_name = 3;\n int32 age = 4;\n int32 id = 5; // Unique ID number for this person.\n string email = 6;\n\n google.protobuf.Timestamp last_updated = 7;\n}\n```\n\nAnd a stream of JSON documents of the form:\n\n```json\n{\n\t\"firstName\": \"caleb\",\n\t\"lastName\": \"quaye\",\n\t\"email\": \"caleb@myspace.com\"\n}\n```\n\nWe can convert the documents into protobuf messages with the following config:", - "title": "JSON to Protobuf using Buf Schema Registry" - }, - { - "config": "\npipeline:\n processors:\n - protobuf:\n operator: to_json\n message: testing.Person\n bsr:\n - module: buf.build/exampleco/mymodule\n api_key: xxxx\n", - "summary": "\nIf we have the following protobuf definition within a BSR module hosted at `buf.build/exampleco/mymodule`:\n```protobuf\nsyntax = \"proto3\";\npackage testing;\n\nimport \"google/protobuf/timestamp.proto\";\n\nmessage Person {\n string first_name = 1;\n string last_name = 2;\n string full_name = 3;\n int32 age = 4;\n int32 id = 5; // Unique ID number for this person.\n string email = 6;\n\n google.protobuf.Timestamp last_updated = 7;\n}\n```\n\nAnd a stream of protobuf messages of the type `Person`, we could convert them into JSON documents of the format:\n\n```json\n{\n\t\"firstName\": \"caleb\",\n\t\"lastName\": \"quaye\",\n\t\"email\": \"caleb@myspace.com\"\n}\n```\n\nWith the following config:", - "title": "Protobuf to JSON using Buf Schema Registry" - } - ], - "name": "protobuf", - "plugin": true, - "status": "stable", - "summary": "\nPerforms conversions to or from a protobuf message. This processor uses reflection, meaning conversions can be made directly from the target .proto files.\n", - "type": "processor" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "description": "The gRPC host of the Qdrant server.", - "examples": [ - "localhost:6334", - "xyz-example.eu-central.aws.cloud.qdrant.io:6334" - ], + "description": "Allows you to specify open authentication via OAuth version 1.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "grpc_host", - "type": "string" + "name": "oauth", + "type": "object", + "version": "4.7.0" }, { - "default": "", - "description": "The Qdrant API token for authentication. Defaults to an empty string.", - "is_secret": true, + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "api_token", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "basic_auth", + "type": "object", + "version": "4.7.0" }, { "children": [ { "default": false, - "description": "Whether custom TLS settings are enabled.", + "description": "Whether to use JWT authentication in requests.", "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object", + "version": "4.7.0" + }, + { + "children": [ { "default": false, "description": "Whether to skip server side certificate verification.", @@ -54015,168 +62946,272 @@ "type": "object" } ], - "description": "TLS(HTTPS) config to use when connecting", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nDecodes messages automatically from a schema stored within a https://docs.confluent.io/platform/current/schema-registry/index.html[Confluent Schema Registry service^] by extracting a schema ID from the message and obtaining the associated schema from the registry. If a message fails to match against the schema then it will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].\n\nAvro, Protobuf and Json schemas are supported, all are capable of expanding from schema references as of v4.22.0.\n\n== Avro JSON format\n\nThis processor creates documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when decoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead create documents in standard/raw JSON format by setting the field <> to `true`.\n\n== Protobuf format\n\nThis processor decodes protobuf messages to JSON documents, you can read more about JSON mapping of protobuf messages here: https://developers.google.com/protocol-buffers/docs/proto3#json\n\n== Metadata\n\nThis processor also adds the following metadata to each outgoing message:\n\nschema_id: the ID of the schema in the schema registry that was associated with the message.\n", + "name": "schema_registry_decode", + "plugin": true, + "status": "stable", + "summary": "Automatically decodes and validates messages with schemas from a Confluent Schema Registry service.", + "type": "processor" + }, + { + "categories": [ + "Parsing", + "Integration" + ], + "config": { + "children": [ { - "description": "The name of the collection in Qdrant.", - "interpolated": true, + "description": "The base URL of the schema registry service.", "kind": "scalar", - "name": "collection_name", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "bloblang": true, - "description": "The mapping to extract the search vector from the document.", + "description": "The schema subject to derive schemas from.", "examples": [ - "root = [1.2, 0.5, 0.76]", - "root = this.vector", - "root = [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]", - "root = {\"some_sparse\": {\"indices\":[23,325,532],\"values\":[0.352,0.532,0.532]}}", - "root = {\"some_multi\": [[0.352,0.532,0.532,0.234],[0.352,0.532,0.532,0.234]]}", - "root = {\"some_dense\": [0.352,0.532,0.532,0.234]}" + "foo", + "${! meta(\"kafka_topic\") }" ], + "interpolated": true, "kind": "scalar", - "name": "vector_mapping", + "name": "subject", "type": "string" }, { - "bloblang": true, - "description": "Additional filtering to perform on the results. The mapping should return a valid filter (using the proto3 encoded form) in qdrant. See the https://qdrant.tech/documentation/concepts/filtering/[^Qdrant documentation] for examples.", + "default": "10m", + "description": "The period after which a schema is refreshed for each subject, this is done by polling the schema registry service.", "examples": [ - "\nroot.must = [\n\t{\"has_id\":{\"has_id\":[{\"num\": 8}, { \"uuid\":\"1234-5678-90ab-cdef\" }]}},\n\t{\"field\":{\"key\": \"city\", \"match\": {\"text\": \"London\"}}},\n]\n", - "\nroot.must = [\n\t{\"field\":{\"key\": \"city\", \"match\": {\"text\": \"London\"}}},\n]\nroot.must_not = [\n\t{\"field\":{\"color\": \"city\", \"match\": {\"text\": \"red\"}}},\n]\n" + "60s", + "1h" ], - "is_optional": true, "kind": "scalar", - "name": "filter", + "name": "refresh_period", "type": "string" }, { - "default": [], - "description": "The fields to include or exclude in returned result based on the `payload_filter`.", - "kind": "array", - "name": "payload_fields", - "type": "string" + "default": false, + "description": "DEPRECATED: Use avro.raw_json instead.", + "is_advanced": true, + "is_deprecated": true, + "kind": "scalar", + "name": "avro_raw_json", + "type": "bool", + "version": "3.59.0" }, { - "annotated_options": [ - [ - "exclude", - "Exclude the payload fields specified in `payload_fields`." - ], - [ - "include", - "Include the payload fields specified in `payload_fields`." - ] - ], - "default": "include", - "description": "The way the fields in `payload_fields` are filtered in the result.", + "default": "", + "description": "When set, the processor reads a schema in benthos common schema format from this metadata key on each message, converts it to the format specified by `format`, registers it with the schema registry under the configured subject, and encodes the message. When empty (the default), the processor pulls the latest schema from the registry instead.", "kind": "scalar", - "linter": "\nlet options = {\n \"exclude\": true,\n \"include\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "payload_filter", + "name": "schema_metadata", "type": "string" }, { - "default": 10, - "description": "The maximum number of points to return.", + "description": "The encoding format to use when converting a common schema from metadata. Required when `schema_metadata` is set.", + "is_optional": true, "kind": "scalar", - "name": "limit", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "qdrant", - "plugin": true, - "status": "experimental", - "summary": "Query items within a https://qdrant.tech/[Qdrant^] collection.", - "type": "processor" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ + "linter": "\nlet options = {\n \"avro\": true,\n \"json_schema\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "format", + "options": [ + "avro", + "json_schema" + ], + "type": "string" + }, { - "description": "The target xref:components:rate_limits/about.adoc[`rate_limit` resource].", + "default": true, + "description": "Whether to normalize the schema before registering with the schema registry (schema_metadata mode only).", + "is_advanced": true, "kind": "scalar", - "name": "resource", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "rate_limit", - "plugin": true, - "status": "stable", - "summary": "Throttles the throughput of a pipeline according to a specified xref:components:rate_limits/about.adoc[`rate_limit`] resource. Rate limits are shared across components and therefore apply globally to all processing pipelines.", - "type": "processor" - }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ + "name": "normalize", + "type": "bool" + }, { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", - "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" + "children": [ + { + "description": "Whether messages encoded in Avro format should be parsed as normal JSON rather than Avro JSON. Overrides the deprecated top-level `avro_raw_json` when set.", + "is_optional": true, + "kind": "scalar", + "name": "raw_json", + "type": "bool" + }, + { + "default": "", + "description": "The name to use for the root Avro record type when encoding from a common schema (schema_metadata mode). If empty, derived from the subject.", + "is_optional": true, + "kind": "scalar", + "name": "record_name", + "type": "string" + }, + { + "default": "", + "description": "The Avro namespace for the root record type when encoding from a common schema (schema_metadata mode).", + "is_optional": true, + "kind": "scalar", + "name": "namespace", + "type": "string" + } ], + "description": "Configuration for Avro encoding.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "avro", + "type": "object" }, { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify open authentication via OAuth version 1.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" - ], - "type": "string" + "name": "oauth", + "type": "object", + "version": "4.7.0" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], + "description": "Allows you to specify basic authentication.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "master", - "type": "string" + "name": "basic_auth", + "type": "object", + "version": "4.7.0" }, { "children": [ { "default": false, - "description": "Whether custom TLS settings are enabled.", + "description": "Whether to use JWT authentication in requests.", "is_advanced": true, "kind": "scalar", "name": "enabled", "type": "bool" }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "jwt", + "type": "object", + "version": "4.7.0" + }, + { + "children": [ { "default": false, "description": "Whether to skip server side certificate verification.", @@ -54291,113 +63326,140 @@ "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nEncodes messages automatically from schemas obtained from a https://docs.confluent.io/platform/current/schema-registry/index.html[Confluent Schema Registry service^] by polling the service for the latest schema version for target subjects.\n\nAlternatively, when `schema_metadata` is set, the processor reads a schema in benthos common schema format from message metadata (as produced by CDC inputs such as `postgresql`, `mysql_cdc`, and `microsoft_sql_server_cdc`), converts it to the target `format` (Avro or JSON Schema), registers it with the schema registry, and encodes the message. This is useful when the schema is not pre-registered in the registry and instead travels with the data.\n\nIf a message fails to encode under the schema then it will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].\n\nAvro, Protobuf and JSON Schema formats are supported. In registry-pull mode all three are auto-detected from the registry. In metadata mode Avro and JSON Schema are supported, with the target format selected via the `format` field. Schema references are supported in registry-pull mode as of v4.22.0.\n\n== Avro JSON format\n\nBy default this processor expects documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when encoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `\\{\"string\": \"a\"}`; and\n- a `Foo` instance as `\\{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead consume documents in standard/raw JSON format by setting `avro.raw_json` to `true`. This is strongly recommended when using `schema_metadata` mode, as CDC sources emit standard JSON rather than Avro JSON.\n\nNOTE: The top-level `avro_raw_json` field is deprecated in favor of `avro.raw_json`.\n\n== Protobuf format\n\nThis processor encodes protobuf messages either from any format parsed within Redpanda Connect (encoded as JSON by default), or from raw JSON documents, you can read more about JSON mapping of protobuf messages here: https://developers.google.com/protocol-buffers/docs/proto3#json\n\n=== Multiple message support\n\nWhen a target subject presents a protobuf schema that contains multiple messages it becomes ambiguous which message definition a given input data should be encoded against. In such scenarios Redpanda Connect will attempt to encode the data against each of them and select the first to successfully match against the data, this process currently *ignores all nested message definitions*. In order to speed up this exhaustive search the last known successful message will be attempted first for each subsequent input.\n\nWe will be considering alternative approaches in future so please https://redpanda.com/slack[get in touch^] with thoughts and feedback.\n", + "name": "schema_registry_encode", + "plugin": true, + "status": "stable", + "summary": "Automatically encodes and validates messages with schemas from a Confluent Schema Registry service.", + "type": "processor", + "version": "3.58.0" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ { - "description": "The command to execute.", - "examples": [ - "scard", - "incrby", - "${! meta(\"command\") }" - ], - "interpolated": true, - "is_optional": true, + "default": [], + "description": "An array of message indexes of a batch. Indexes can be negative, and if so the part will be selected from the end counting backwards starting from -1.", + "kind": "array", + "name": "parts", + "type": "int" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThe selected parts are added to the new message batch in the same order as the selection array. E.g. with 'parts' set to [ 2, 0, 1 ] and the message parts [ '0', '1', '2', '3' ], the output will be [ '2', '0', '1' ].\n\nIf none of the selected parts exist in the input batch (resulting in an empty output message) the batch is dropped entirely.\n\nMessage indexes can be negative, and if so the part will be selected from the end counting backwards starting from -1. E.g. if index = -1 then the selected part will be the last part of the message, if index = -2 then the part before the last element with be selected, and so on.\n\nThis processor is only applicable to xref:configuration:batching.adoc[batched messages].", + "name": "select_parts", + "plugin": true, + "status": "stable", + "summary": "Cherry pick a set of messages from a batch by their index. Indexes larger than the number of messages are simply ignored.", + "type": "processor" + }, + { + "categories": null, + "config": { + "children": [ + { + "description": "The Slack Bot User OAuth token to use.", "kind": "scalar", - "name": "command", - "type": "string", - "version": "4.3.0" + "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", + "name": "bot_token", + "type": "string" }, { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of arguments required for the specified Redis command.", - "examples": [ - "root = [ this.key ]", - "root = [ meta(\"kafka_key\"), this.count ]" - ], - "is_optional": true, + "description": "The channel ID to read messages from.", + "interpolated": true, "kind": "scalar", - "name": "args_mapping", - "type": "string", - "version": "4.3.0" + "name": "channel_id", + "type": "string" }, { - "annotated_options": [ - [ - "incrby", - "Increments the number stored at `key` by the message content. If the key does not exist, it is set to `0` before performing the operation. Returns the value of `key` after the increment." - ], - [ - "keys", - "Returns an array of strings containing all the keys that match the pattern specified by the `key` field." - ], - [ - "sadd", - "Adds a new member to a set. Returns `1` if the member was added." - ], - [ - "scard", - "Returns the cardinality of a set, or `0` if the key does not exist." - ] - ], - "description": "The operator to apply.", - "is_deprecated": true, - "is_optional": true, + "description": "The thread timestamp to read the full thread of.", + "interpolated": true, "kind": "scalar", - "linter": "\nlet options = {\n \"incrby\": true,\n \"keys\": true,\n \"sadd\": true,\n \"scard\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operator", + "name": "thread_ts", "type": "string" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "Read a thread using the https://api.slack.com/methods/conversations.replies[^Slack API]", + "name": "slack_thread", + "plugin": true, + "status": "stable", + "type": "processor" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ { - "description": "A key to use for the target operator.", + "description": "The duration of time to sleep for each execution.", "interpolated": true, - "is_deprecated": true, - "is_optional": true, "kind": "scalar", - "name": "key", + "name": "duration", "type": "string" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "sleep", + "plugin": true, + "status": "stable", + "summary": "Sleep for a period of time specified as a duration string for each message. This processor will interpolate functions within the `duration` field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].", + "type": "processor" + }, + { + "categories": [ + "Utility" + ], + "config": { + "children": [ { - "default": 3, - "description": "The maximum number of retries before abandoning a request.", - "is_advanced": true, + "default": 1, + "description": "The target number of messages.", "kind": "scalar", - "name": "retries", + "name": "size", "type": "int" }, { - "default": "500ms", - "description": "The time to wait before consecutive retry attempts.", - "is_advanced": true, + "default": 0, + "description": "An optional target of total message bytes.", "kind": "scalar", - "name": "retry_period", - "type": "string" + "name": "byte_size", + "type": "int" } ], "kind": "scalar", - "linter": "root = match {\n this.exists(\"operator\") == this.exists(\"command\") => [ \"one of 'operator' (old style) or 'command' (new style) fields must be specified\" ]\n this.exists(\"args_mapping\") && this.exists(\"operator\") => [ \"field args_mapping is invalid with an operator set\" ],\n}", "name": "", "type": "object" }, - "examples": [ - { - "config": "\npipeline:\n processors:\n - branch:\n processors:\n - redis:\n url: TODO\n command: scard\n args_mapping: 'root = [ meta(\"set_key\") ]'\n result_map: 'root.cardinality = this'\n", - "summary": "If given payloads containing a metadata field `set_key` it's possible to query and store the cardinality of the set for each message using a xref:components:processors/branch.adoc[`branch` processor] in order to augment rather than replace the message contents:", - "title": "Querying Cardinality" - }, - { - "config": "\npipeline:\n processors:\n - branch:\n processors:\n - redis:\n url: TODO\n command: incrby\n args_mapping: 'root = [ this.name, this.friends_visited ]'\n result_map: 'root.total = this'\n", - "summary": "If we have JSON data containing number of friends visited during covid 19:\n\n```json\n{\"name\":\"ash\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":10}\n{\"name\":\"ash\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":-2}\n{\"name\":\"bob\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":3}\n{\"name\":\"bob\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":1}\n```\n\nWe can add a field that contains the running total number of friends visited:\n\n```json\n{\"name\":\"ash\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":10,\"total\":10}\n{\"name\":\"ash\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":-2,\"total\":8}\n{\"name\":\"bob\",\"month\":\"feb\",\"year\":2019,\"friends_visited\":3,\"total\":3}\n{\"name\":\"bob\",\"month\":\"apr\",\"year\":2019,\"friends_visited\":1,\"total\":4}\n```\n\nUsing the `incrby` command:", - "title": "Running Total" - } - ], - "name": "redis", + "description": "\nThis processor is for breaking batches down into smaller ones. In order to break a single message out into multiple messages use the xref:components:processors/unarchive.adoc[`unarchive` processor].\n\nIf there is a remainder of messages after splitting a batch the remainder is also sent as a single batch. For example, if your target size was 10, and the processor received a batch of 95 message parts, the result would be 9 batches of 10 messages followed by a batch of 5 messages.", + "name": "split", "plugin": true, "status": "stable", - "summary": "Performs actions against Redis that aren't possible using a xref:components:processors/cache.adoc[`cache`] processor. Actions are\nperformed for each message and the message contents are replaced with the result. In order to merge the result into the original message compose this processor within a xref:components:processors/branch.adoc[`branch` processor].", + "summary": "Breaks message batches (synonymous with multiple part messages) into smaller batches. The size of the resulting batches are determined either by a discrete size or, if the field `byte_size` is non-zero, then by total size in bytes (which ever limit is reached first).", "type": "processor" }, { @@ -54407,479 +63469,559 @@ "config": { "children": [ { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", - "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" - ], + "description": "A database <> to use.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "driver", + "options": [ + "mysql", + "postgres", + "pgx", + "clickhouse", + "mssql", + "sqlite", + "oracle", + "snowflake", + "trino", + "gocosmos", + "spanner", + "databricks" + ], "type": "string" }, { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" ], + "kind": "scalar", + "name": "dsn", "type": "string" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", + "description": "The table to insert to.", "examples": [ - "mymaster" + "foo" ], - "is_advanced": true, "kind": "scalar", - "name": "master", + "name": "table", "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } + "description": "A list of columns to insert.", + "examples": [ + [ + "foo", + "bar", + "baz" + ] ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "kind": "array", + "name": "columns", + "type": "string" + }, + { + "bloblang": true, + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of columns specified.", + "examples": [ + "root = [ this.cat.meow, this.doc.woofs[0] ]", + "root = [ meta(\"user.id\") ]" + ], + "kind": "scalar", + "name": "args_mapping", + "type": "string" + }, + { + "description": "An optional prefix to prepend to the insert query (before INSERT).", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls", - "type": "object" + "name": "prefix", + "type": "string" }, { - "description": "A script to use for the target operator. It has precedence over the 'command' field.", + "description": "An optional suffix to append to the insert query.", "examples": [ - "return redis.call('set', KEYS[1], ARGV[1])" + "ON CONFLICT (name) DO NOTHING" ], + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "script", + "name": "suffix", "type": "string" }, { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of arguments required for the specified Redis script.", + "description": "A list of keyword options to add before the INTO clause of the query.", "examples": [ - "root = [ this.key ]", - "root = [ meta(\"kafka_key\"), \"hardcoded_value\" ]" + [ + "DELAYED", + "IGNORE" + ] ], - "kind": "scalar", - "name": "args_mapping", + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "options", "type": "string" }, { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of keys matching in size to the number of arguments required for the specified Redis script.", + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", "examples": [ - "root = [ this.key ]", - "root = [ meta(\"kafka_key\"), this.count ]" + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" + }, + { + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" ], + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "keys_mapping", + "name": "init_statement", + "type": "string", + "version": "4.10.0" + }, + { + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle_time", "type": "string" }, { - "default": 3, - "description": "The maximum number of retries before abandoning a request.", + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "retries", + "name": "conn_max_life_time", + "type": "string" + }, + { + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle", "type": "int" }, { - "default": "500ms", - "description": "The time to wait before consecutive retry attempts.", + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "retry_period", - "type": "string" + "name": "conn_max_open", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "Actions are performed for each message and the message contents are replaced with the result.\n\nIn order to merge the result into the original message compose this processor within a xref:components:processors/branch.adoc[`branch` processor].", + "description": "\nIf the insert fails to execute then the message will still remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", "examples": [ { - "config": "\npipeline:\n processors:\n - redis_script:\n url: TODO\n script: |\n local value = redis.call(\"ZRANGE\", KEYS[1], '0', '0')\n\n if next(elements) == nil then\n return ''\n end\n\n redis.call(\"ZADD\", \"XX\", KEYS[1], ARGV[1], value)\n\n return value\n keys_mapping: 'root = [ meta(\"key\") ]'\n args_mapping: 'root = [ timestamp_unix_nano() ]'\n", - "summary": "The following example will use a script execution to get next element from a sorted set and set its score with timestamp unix nano value.", - "title": "Running a script" + "config": "\npipeline:\n processors:\n - sql_insert:\n driver: mysql\n dsn: foouser:foopassword@tcp(localhost:3306)/foodb\n table: footable\n columns: [ id, name, topic ]\n args_mapping: |\n root = [\n this.user.id,\n this.user.name,\n meta(\"kafka_topic\"),\n ]\n", + "summary": "\nHere we insert rows into a database by populating the columns id, name and topic with values extracted from messages and metadata:", + "title": "Table Insert (MySQL)" } ], - "name": "redis_script", + "name": "sql_insert", "plugin": true, - "status": "beta", - "summary": "Performs actions against Redis using https://redis.io/docs/manual/programmability/eval-intro/[LUA scripts^].", + "status": "stable", + "summary": "Inserts rows into an SQL database for each message, and leaves the message unchanged.", "type": "processor", - "version": "4.11.0" + "version": "3.59.0" }, { "categories": [ - "Utility" + "Integration" ], "config": { "children": [ { - "description": "The path of the target WASM module to execute.", + "description": "A database <> to use.", + "kind": "scalar", + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "driver", + "options": [ + "mysql", + "postgres", + "pgx", + "clickhouse", + "mssql", + "sqlite", + "oracle", + "snowflake", + "trino", + "gocosmos", + "spanner", + "databricks" + ], + "type": "string" + }, + { + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" + ], "kind": "scalar", - "name": "module_path", + "name": "dsn", "type": "string" }, { - "description": "An optional key to populate for each message.", - "interpolated": true, + "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `pgx` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "examples": [ + "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);", + "SELECT * FROM footable WHERE user_id = $1;" + ], "is_optional": true, "kind": "scalar", - "name": "input_key", + "name": "query", "type": "string" }, { - "description": "An optional name of metadata for an output message key.", + "default": false, + "description": "Whether to enable xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions] in the query. Great care should be made to ensure your queries are defended against injection attacks.", + "is_advanced": true, + "kind": "scalar", + "name": "unsafe_dynamic_query", + "type": "bool" + }, + { + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", + "examples": [ + "root = [ this.cat.meow, this.doc.woofs[0] ]", + "root = [ meta(\"user.id\") ]" + ], "is_optional": true, "kind": "scalar", - "name": "output_key", + "name": "args_mapping", "type": "string" }, { - "children": [ - { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "kind": "array", - "name": "include_prefixes", - "type": "string" - }, - { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", - "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] - ], - "kind": "array", - "name": "include_patterns", - "type": "string" - } - ], - "description": "Determine which (if any) metadata values should be added to messages as headers.", + "description": "Whether the query result should be discarded. When set to `true` the message contents will remain unchanged, which is useful in cases where you are executing inserts, updates, etc. By default this is true for the last query, and previous queries don't change the results. If set to true for any query but the last one, the subsequent `args_mappings` input is overwritten.", "is_optional": true, "kind": "scalar", - "name": "input_headers", - "type": "object" + "name": "exec_only", + "type": "bool" }, { "children": [ { - "default": [], - "description": "Provide a list of explicit metadata key prefixes to match against.", - "examples": [ - [ - "foo_", - "bar_" - ], - [ - "kafka_" - ], - [ - "content-" - ] - ], - "kind": "array", - "name": "include_prefixes", + "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `pgx` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "kind": "scalar", + "name": "query", "type": "string" }, { - "default": [], - "description": "Provide a list of explicit metadata key regular expression (re2) patterns to match against.", + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", "examples": [ - [ - ".*" - ], - [ - "_timestamp_unix$" - ] + "root = [ this.cat.meow, this.doc.woofs[0] ]", + "root = [ meta(\"user.id\") ]" ], - "kind": "array", - "name": "include_patterns", + "is_optional": true, + "kind": "scalar", + "name": "args_mapping", "type": "string" + }, + { + "description": "Whether the query result should be discarded. When set to `true` the message contents will remain unchanged, which is useful in cases where you are executing inserts, updates, etc. By default this is true for the last query, and previous queries don't change the results. If set to true for any query but the last one, the subsequent `args_mappings` input is overwritten.", + "is_optional": true, + "kind": "scalar", + "name": "exec_only", + "type": "bool" } ], - "description": "Determine which (if any) message headers should be added to the output as metadata.", + "description": "A list of statements to run in addition to `query`. When specifying multiple statements, they are all executed within a transaction. The output of the processor is always the last query that runs, unless `exec_only` is used.", "is_optional": true, - "kind": "scalar", - "name": "output_metadata", + "kind": "array", + "name": "queries", "type": "object" }, { - "description": "An optional timestamp to set for each message. When left empty, the current timestamp is used.", + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", "examples": [ - "${! timestamp_unix() }", - "${! metadata(\"kafka_timestamp_ms\") }" + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" + }, + { + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" ], - "interpolated": true, "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "timestamp", + "name": "init_statement", + "type": "string", + "version": "4.10.0" + }, + { + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle_time", "type": "string" }, { - "default": "10s", - "description": "The maximum period of time for a message to be processed", + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "timeout", + "name": "conn_max_life_time", "type": "string" }, { - "default": 1600, - "description": "The maximum amount of wasm memory pages (64KiB) that an individual wasm module instance can use", + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle", + "type": "int" + }, + { + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "max_memory_pages", + "name": "conn_max_open", "type": "int" } ], "kind": "scalar", + "linter": "root = match {\n !this.exists(\"queries\") && !this.exists(\"query\") => [ \"either `query` or `queries` is required\" ],\n }", "name": "", "type": "object" }, - "description": "\nThis processor executes a Redpanda Data Transform WebAssembly module, calling OnRecordWritten for each message being processed.\n\nYou can find out about how transforms work here: https://docs.redpanda.com/current/develop/data-transforms/how-transforms-work/[https://docs.redpanda.com/current/develop/data-transforms/how-transforms-work/^]\n", - "name": "redpanda_data_transform", - "plugin": true, - "status": "experimental", - "summary": "Executes a Redpanda Data Transform as a processor", - "type": "processor", - "version": "4.31.0" - }, - { - "categories": [ - "Utility" + "description": "\nIf the query fails to execute then the message will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", + "examples": [ + { + "config": "\npipeline:\n processors:\n - sql_raw:\n driver: mysql\n dsn: foouser:foopassword@tcp(localhost:3306)/foodb\n query: \"INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);\"\n args_mapping: '[ document.foo, document.bar, meta(\"kafka_topic\") ]'\n exec_only: true\n", + "summary": "The following example inserts rows into the table footable with the columns foo, bar and baz populated with values extracted from messages.", + "title": "Table Insert (MySQL)" + }, + { + "config": "\npipeline:\n processors:\n - branch:\n processors:\n - sql_raw:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n query: \"SELECT * FROM footable WHERE user_id = $1;\"\n args_mapping: '[ this.user.id ]'\n result_map: 'root.foo_rows = this'\n", + "summary": "Here we query a database for columns of footable that share a `user_id` with the message field `user.id`. A xref:components:processors/branch.adoc[`branch` processor] is used in order to insert the resulting array into the original message at the path `foo_rows`.", + "title": "Table Query (PostgreSQL)" + }, + { + "config": "\npipeline:\n processors:\n - mapping: |\n root = this\n # Prevent SQL injection when using unsafe_dynamic_query\n meta table_name = \"\\\"\" + metadata(\"table_name\").replace_all(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n - sql_raw:\n driver: postgres\n dsn: postgres://localhost/postgres\n unsafe_dynamic_query: true\n queries:\n - query: |\n CREATE TABLE IF NOT EXISTS ${!metadata(\"table_name\")} (id varchar primary key, document jsonb);\n - query: |\n INSERT INTO ${!metadata(\"table_name\")} (id, document) VALUES ($1, $2)\n ON CONFLICT (id) DO UPDATE SET document = EXCLUDED.document;\n args_mapping: |\n root = [ this.id, this.document.string() ]\n", + "summary": "Here we query a database for columns of footable that share a `user_id` with the message field `user.id`. A xref:components:processors/branch.adoc[`branch` processor] is used in order to insert the resulting array into the original message at the path `foo_rows`.", + "title": "Dynamically Creating Tables (PostgreSQL)" + } ], - "config": { - "default": "", - "kind": "scalar", - "name": "", - "type": "string" - }, - "description": "\nThis processor allows you to reference the same configured processor resource in multiple places, and can also tidy up large nested configs. For example, the config:\n\n```yaml\npipeline:\n processors:\n - mapping: |\n root.message = this\n root.meta.link_count = this.links.length()\n root.user.age = this.user.age.number()\n```\n\nIs equivalent to:\n\n```yaml\npipeline:\n processors:\n - resource: foo_proc\n\nprocessor_resources:\n - label: foo_proc\n mapping: |\n root.message = this\n root.meta.link_count = this.links.length()\n root.user.age = this.user.age.number()\n```\n\nYou can find out more about resources in xref:configuration:resources.adoc[]", - "name": "resource", + "name": "sql_raw", "plugin": true, "status": "stable", - "summary": "Resource is a processor type that runs a processor resource identified by its label.", - "type": "processor" + "summary": "Runs an arbitrary SQL query against a database and (optionally) returns the result as an array of objects, one for each row returned.", + "type": "processor", + "version": "3.65.0" }, { "categories": [ - "Composition" + "Integration" ], "config": { "children": [ { - "children": [ - { - "default": "500ms", - "description": "The initial period to wait between retry attempts.", - "examples": [ - "50ms", - "1s" - ], - "kind": "scalar", - "name": "initial_interval", - "type": "string" - }, - { - "default": "10s", - "description": "The maximum period to wait between retry attempts", - "examples": [ - "5s", - "1m" - ], - "kind": "scalar", - "name": "max_interval", - "type": "string" - }, - { - "default": "1m", - "description": "The maximum overall period of time to spend on retry attempts before the request is aborted. Setting this value to a zeroed duration (such as `0s`) will result in unbounded retries.", - "examples": [ - "1m", - "1h" - ], - "kind": "scalar", - "name": "max_elapsed_time", - "type": "string" - } - ], - "description": "Determine time intervals and cut offs for retry attempts.", + "description": "A database <> to use.", "kind": "scalar", - "name": "backoff", - "type": "object" + "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"pgx\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n \"databricks\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "driver", + "options": [ + "mysql", + "postgres", + "pgx", + "clickhouse", + "mssql", + "sqlite", + "oracle", + "snowflake", + "trino", + "gocosmos", + "spanner", + "databricks" + ], + "type": "string" }, { - "description": "A list of xref:components:processors/about.adoc[processors] to execute on each message.", + "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, pgx=community, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` and `pgx` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n\n| `databricks` \n| `token:@:/` \n|===\n\nPlease note that the `postgres` and `pgx` drivers enforce SSL by default, you can override this with the parameter `sslmode=disable` if required.\nThe `pgx` driver is an alternative to the standard `postgres` (pq) driver and comes with extra functionality such as support for array insertion.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` instead of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", + "examples": [ + "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", + "foouser:foopassword@tcp(localhost:3306)/foodb", + "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", + "oracle://foouser:foopass@localhost:1521/service_name", + "token:dapi1234567890ab@dbc-a1b2345c-d6e7.cloud.databricks.com:443/sql/1.0/warehouses/abc123def456" + ], + "kind": "scalar", + "name": "dsn", + "type": "string" + }, + { + "description": "The table to query.", + "examples": [ + "foo" + ], + "kind": "scalar", + "name": "table", + "type": "string" + }, + { + "description": "A list of columns to query.", + "examples": [ + [ + "*" + ], + [ + "foo", + "bar", + "baz" + ] + ], "kind": "array", - "name": "processors", - "type": "processor" + "name": "columns", + "type": "string" }, { - "default": false, - "description": "When processing batches of messages these batches are ignored and the processors apply to each message sequentially. However, when this field is set to `true` each message will be processed in parallel. Caution should be made to ensure that batch sizes do not surpass a point where this would cause resource (CPU, memory, API limits) contention.", + "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks, and will automatically be converted to dollar syntax when the postgres or clickhouse drivers are used.", + "examples": [ + "meow = ? and woof = ?", + "user_id = ?" + ], + "is_optional": true, "kind": "scalar", - "name": "parallel", - "type": "bool" + "name": "where", + "type": "string" }, { - "default": 0, - "description": "The maximum number of retry attempts before the request is aborted. Setting this value to `0` will result in unbounded number of retries.", + "bloblang": true, + "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", + "examples": [ + "root = [ this.cat.meow, this.doc.woofs[0] ]", + "root = [ meta(\"user.id\") ]" + ], + "is_optional": true, "kind": "scalar", - "name": "max_retries", + "name": "args_mapping", + "type": "string" + }, + { + "description": "An optional prefix to prepend to the query (before SELECT).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "prefix", + "type": "string" + }, + { + "description": "An optional suffix to append to the select query.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "suffix", + "type": "string" + }, + { + "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + [ + "./init/*.sql" + ], + [ + "./foo.sql", + "./bar.sql" + ] + ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "init_files", + "type": "string", + "version": "4.10.0" + }, + { + "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "examples": [ + "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" + ], + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "init_statement", + "type": "string", + "version": "4.10.0" + }, + { + "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle_time", + "type": "string" + }, + { + "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_life_time", + "type": "string" + }, + { + "default": 2, + "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_idle", + "type": "int" + }, + { + "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "conn_max_open", "type": "int" } ], @@ -54887,291 +64029,695 @@ "name": "", "type": "object" }, - "description": "\nExecutes child processors and if a resulting message is errored then, after a specified backoff period, the same original message will be attempted again through those same processors. If the child processors result in more than one message then the retry mechanism will kick in if _any_ of the resulting messages are errored.\n\nIt is important to note that any mutations performed on the message during these child processors will be discarded for the next retry, and therefore it is safe to assume that each execution of the child processors will always be performed on the data as it was when it first reached the retry processor.\n\nBy default the retry backoff has a specified <>, if this time period is reached during retries and an error still occurs these errored messages will proceed through to the next processor after the retry (or your outputs). Normal xref:configuration:error_handling.adoc[error handling patterns] can be used on these messages.\n\nIn order to avoid permanent loops any error associated with messages as they first enter a retry processor will be cleared.\n\n== Metadata\n\nThis processor adds the following metadata fields to each message:\n\n```text\n- retry_count - The number of retry attempts.\n- backoff_duration - The total time (in nanoseconds) elapsed while performing retries.\n```\n\n[CAUTION]\n.Batching\n====\nIf you wish to wrap a batch-aware series of processors then take a look at the <>.\n====\n", + "description": "\nIf the query fails to execute then the message will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", "examples": [ { - "config": "\ninput:\n generate:\n interval: 1s\n mapping: 'root.noise = [ \"woof\", \"meow\", \"moo\", \"quack\" ].index(random_int(min: 0, max: 3))'\n\npipeline:\n processors:\n - retry:\n backoff:\n initial_interval: 100ms\n max_interval: 5s\n max_elapsed_time: 0s\n processors:\n - http:\n url: 'http://example.com/try/not/to/dox/taz'\n verb: POST\n\noutput:\n # Drop everything because it's junk data, I don't want it lol\n drop: {}\n", - "summary": "\nHere we have a config where I generate animal noises and send them to Taz via HTTP. Taz has a tendency to stop his servers whenever I dispatch my animals upon him, and therefore these HTTP requests sometimes fail. However, I have the retry processor and with this super power I can specify a back off policy and it will ensure that for each animal noise the HTTP processor is attempted until either it succeeds or my Redpanda Connect instance is stopped.\n\nI even go as far as to zero-out the maximum elapsed time field, which means that for each animal noise I will wait indefinitely, because I really really want Taz to receive every single animal noise that he is entitled to.", - "title": "Stop ignoring me Taz" + "config": "\npipeline:\n processors:\n - branch:\n processors:\n - sql_select:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n table: footable\n columns: [ '*' ]\n where: user_id = ?\n args_mapping: '[ this.user.id ]'\n result_map: 'root.foo_rows = this'\n", + "summary": "\nHere we query a database for columns of footable that share a `user_id`\nwith the message `user.id`. A xref:components:processors/branch.adoc[`branch` processor]\nis used in order to insert the resulting array into the original message at the\npath `foo_rows`:", + "title": "Table Query (PostgreSQL)" } ], - "footnotes": "\n== Batching\n\nWhen messages are batched the child processors of a retry are executed for each individual message in isolation, performed serially by default but in parallel when the field <> is set to `true`. This is an intentional limitation of the retry processor and is done in order to ensure that errors are correctly associated with a given input message. Otherwise, the archiving, expansion, grouping, filtering and so on of the child processors could obfuscate this relationship.\n\nIf the target behavior of your retried processors is \"batch aware\", in that you wish to perform some processing across the entire batch of messages and repeat it in the event of errors, you can use an xref:components:processors/archive.adoc[`archive` processor] to collapse the batch into an individual message. Then, within these child processors either perform your batch aware processing on the archive, or use an xref:components:processors/unarchive.adoc[`unarchive` processor] in order to expand the single message back out into a batch.\n\nFor example, if the retry processor were being used to wrap an HTTP request where the payload data is a batch archived into a JSON array it should look something like this:\n\n```yaml\npipeline:\n processors:\n - archive:\n format: json_array\n - retry:\n processors:\n - http:\n url: example.com/nope\n verb: POST\n - unarchive:\n format: json_array\n```\n", - "name": "retry", + "name": "sql_select", "plugin": true, - "status": "beta", - "summary": "Attempts to execute a series of child processors until success.", + "status": "stable", + "summary": "Runs an SQL select query against a database and returns the result as an array of objects, one for each row returned, containing a key for each column queried and its value.", "type": "processor", - "version": "4.27.0" + "version": "3.59.0" }, { "categories": [ - "Parsing", - "Integration" + "Parsing" ], "config": { "children": [ + { + "default": "\n", + "description": "The delimiter to split the string by.", + "kind": "scalar", + "name": "delimiter", + "type": "string" + }, { "default": false, - "description": "Whether Avro messages should be decoded into normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^]. If `true` the schema returned from the subject should be decoded as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard json^] instead of as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodec[avro json^]. There is a https://github.com/linkedin/goavro/blob/5ec5a5ee7ec82e16e6e2b438d610e1cab2588393/union.go#L224-L249[comment in goavro^], the https://github.com/linkedin/goavro[underlining library used for avro serialization^], that explains in more detail the difference between the standard json and avro json.", + "description": "When true, the output will be bloblang bytes instead of strings.", "is_advanced": true, - "is_deprecated": true, "kind": "scalar", - "name": "avro_raw_json", + "name": "emit_bytes", "type": "bool" }, { - "children": [ - { - "description": "Whether avro messages should be decoded into normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[JSON as specified in the Avro Spec^].\n\nFor example, if there is a union schema `[\"null\", \"string\", \"Foo\"]` where `Foo` is a record name, with raw_unions as false (the default) you get:\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nWhen raw_unions is set to true then the above union schema is decoded as the following:\n- `null` as `null`;\n- the string `\"a\"` as `\"a\"`; and\n- a `Foo` instance as `{...}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n", - "is_optional": true, - "kind": "scalar", - "name": "raw_unions", - "type": "bool" - }, - { - "default": false, - "description": "Whether logical types should be preserved or transformed back into their primitive type. By default, decimals are decoded as raw bytes and timestamps are decoded as plain integers. Setting this field to true keeps decimal types as numbers in bloblang and timestamps as time values.", - "kind": "scalar", - "name": "preserve_logical_types", - "type": "bool" - }, - { - "default": false, - "description": "Only valid if preserve_logical_types is true. This decodes various Kafka Connect types into their bloblang equivalents when not representable by standard logical types according to the Avro standard.\n\nTypes that are currently translated:\n\n.Debezium Custom Temporal Types\n|===\n|Type Name |Bloblang Type |Description\n\n|io.debezium.time.Date\n|timestamp\n|Date without time (days since epoch)\n\n|io.debezium.time.Timestamp\n|timestamp\n|Timestamp without timezone (milliseconds since epoch)\n\n|io.debezium.time.MicroTimestamp\n|timestamp\n|Timestamp with microsecond precision\n\n|io.debezium.time.NanoTimestamp\n|timestamp\n|Timestamp with nanosecond precision\n\n|io.debezium.time.ZonedTimestamp\n|timestamp\n|Timestamp with timezone (ISO-8601 format)\n\n|io.debezium.time.Year\n|timestamp at January 1st at 00:00:00\n|Year value\n\n|io.debezium.time.Time\n|timestamp at the unix epoch\n|Time without date (milliseconds past midnight)\n\n|io.debezium.time.MicroTime\n|timestamp at the unix epoch\n|Time with microsecond precision\n\n|io.debezium.time.NanoTime\n|timestamp at the unix epoch\n|Time with nanosecond precision\n\n|===\n\n", - "kind": "scalar", - "name": "translate_kafka_connect_types", - "type": "bool" - }, - { - "bloblang": true, - "description": "A custom mapping to apply to Avro schemas JSON representation. This is useful to transform custom types emitted by other tools into standard avro.", - "examples": [ - "\nmap isDebeziumTimestampType {\n root = this.type == \"long\" && this.\"connect.name\" == \"io.debezium.time.Timestamp\" && !this.exists(\"logicalType\")\n}\nmap debeziumTimestampToAvroTimestamp {\n let mapped_fields = this.fields.or([]).map_each(item -> item.apply(\"debeziumTimestampToAvroTimestamp\"))\n root = match {\n this.type == \"record\" => this.assign({\"fields\": $mapped_fields})\n this.type.type() == \"array\" => this.assign({\"type\": this.type.map_each(item -> item.apply(\"debeziumTimestampToAvroTimestamp\"))})\n # Add a logical type so that it's decoded as a timestamp instead of a long.\n this.type.type() == \"object\" && this.type.apply(\"isDebeziumTimestampType\") => this.merge({\"type\":{\"logicalType\": \"timestamp-millis\"}})\n _ => this\n }\n}\nroot = this.apply(\"debeziumTimestampToAvroTimestamp\")\n" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "mapping", - "type": "string" - }, - { - "description": "Optionally store the schema used to decode messages as a metadata field under the given name. This field can later be referenced in other components such as a `parquet_encode` processor in order to automatically infer their schema.", - "is_optional": true, - "kind": "scalar", - "name": "store_schema_metadata", - "type": "string" - } + "default": false, + "description": "When true, empty strings resulting from the split are converted to null.", + "kind": "scalar", + "name": "empty_as_null", + "type": "bool" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "string_split", + "plugin": true, + "status": "stable", + "summary": "Splits a string by a delimiter into an array. Generally, using bloblang's `split` method is preferred. In some high performance use cases this processor can be faster than the equivalent bloblang if there is no additional logic.", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "children": [ + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should have the processors of this case executed on it. If left empty the case always passes. If the check mapping throws an error the message will be flagged xref:configuration:error_handling.adoc[as having failed] and will not be tested against any other cases.", + "examples": [ + "this.type == \"foo\"", + "this.contents.urls.contains(\"https://benthos.dev/\")" ], - "description": "Configuration for how to decode schemas that are of type AVRO.", "kind": "scalar", - "name": "avro", - "type": "object" + "name": "check", + "type": "string" }, { - "children": [ - { - "default": false, - "description": "Use proto field name instead of lowerCamelCase name.", - "kind": "scalar", - "name": "use_proto_names", - "type": "bool" - }, - { - "default": false, - "description": "Emits enum values as numbers.", - "kind": "scalar", - "name": "use_enum_numbers", - "type": "bool" - }, - { - "default": false, - "description": "Whether to emit unpopulated fields. It does not emit unpopulated oneof fields or unpopulated extension fields.", - "kind": "scalar", - "name": "emit_unpopulated", - "type": "bool" - }, - { - "default": false, - "description": "Whether to emit default-valued primitive fields, empty lists, and empty maps. emit_unpopulated takes precedence over emit_default_values ", - "kind": "scalar", - "name": "emit_default_values", - "type": "bool" - } - ], - "description": "Configuration for how to decode schemas that are of type PROTOBUF.", + "default": [], + "description": "A list of xref:components:processors/about.adoc[processors] to execute on a message.", + "kind": "array", + "name": "processors", + "type": "processor" + }, + { + "default": false, + "description": "Indicates whether, if this case passes for a message, the next case should also be executed without checking its condition.", + "is_advanced": true, "kind": "scalar", - "name": "protobuf", - "type": "object" + "name": "fallthrough", + "type": "bool" }, { - "default": "10m", - "description": "The duration after which a schema is considered stale and will be removed from the cache.", - "examples": [ - "1h", - "5m" + "default": false, + "description": "Indicates whether, if this case passes for a message, the next case should also be tested. Unlike `fallthrough`, which skips the next case's check, `continue` will evaluate the next case's condition before executing.", + "is_advanced": true, + "kind": "scalar", + "name": "continue", + "type": "bool" + } + ], + "kind": "array", + "name": "", + "type": "object" + }, + "description": "For each switch case a xref:guides:bloblang/about.adoc[Bloblang query] is checked and, if the result is true (or the check is empty) the child processors are executed on the message.", + "examples": [ + { + "config": "\npipeline:\n processors:\n - switch:\n - check: this.user.name.first != \"George\"\n processors:\n - metric:\n type: counter\n name: MessagesWeCareAbout\n\n - processors:\n - metric:\n type: gauge\n name: GeorgesAnger\n value: ${! json(\"user.anger\") }\n - mapping: root = deleted()\n", + "summary": "\nWe have a system where we're counting a metric for all messages that pass through our system. However, occasionally we get messages from George that we don't care about.\n\nFor George's messages we want to instead emit a metric that gauges how angry he is about being ignored and then we drop it.", + "title": "Ignore George" + } + ], + "footnotes": "\n== Batching\n\nWhen a switch processor executes on a xref:configuration:batching.adoc[batch of messages] they are checked individually and can be matched independently against cases. During processing the messages matched against a case are processed as a batch, although the ordering of messages during case processing cannot be guaranteed to match the order as received.\n\nAt the end of switch processing the resulting batch will follow the same ordering as the batch was received. If any child processors have split or otherwise grouped messages this grouping will be lost as the result of a switch is always a single batch. In order to perform conditional grouping and/or splitting use the xref:components:processors/group_by.adoc[`group_by` processor].", + "name": "switch", + "plugin": true, + "status": "stable", + "summary": "Conditionally processes messages based on their contents.", + "type": "processor" + }, + { + "categories": [ + "Utility" + ], + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nFor most inputs this mechanism is ignored entirely, in which case the sync response is dropped without penalty. It is therefore safe to use this processor even when combining input types that might not have support for sync responses. An example of an input able to utilize this is the `http_server`.\n\nFor more information please read xref:guides:sync_responses.adoc[synchronous responses].", + "name": "sync_response", + "plugin": true, + "status": "stable", + "summary": "Adds the payload in its current state as a synchronous response to the input source, where it is dealt with according to that specific input type.", + "type": "processor" + }, + { + "categories": [ + "AI" + ], + "config": { + "children": [ + { + "annotated_options": [ + [ + "markdown", + "Split text by markdown headers." + ], + [ + "recursive_character", + "Split text recursively by characters (defined in `separators`)." + ], + [ + "token", + "Split text by tokens." + ] ], "kind": "scalar", - "name": "cache_duration", + "linter": "\nlet options = {\n \"markdown\": true,\n \"recursive_character\": true,\n \"token\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "strategy", "type": "string" }, { - "description": "The base URL of the schema registry service.", + "default": 512, + "description": "The maximum size of each chunk.", "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "chunk_size", + "type": "int" }, { - "description": "If set, this schema ID will be used when a message's schema header cannot be read (ErrBadHeader). If not set, schema header errors will be returned.", - "is_optional": true, + "default": 100, + "description": "The number of characters to overlap between chunks.", "kind": "scalar", - "name": "default_schema_id", + "name": "chunk_overlap", "type": "int" }, { - "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A value used to identify the client to the service provider.", - "is_advanced": true, - "kind": "scalar", - "name": "consumer_key", - "type": "string" - }, - { - "default": "", - "description": "A secret used to establish ownership of the consumer key.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "consumer_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", - "is_advanced": true, - "kind": "scalar", - "name": "access_token", - "type": "string" - }, - { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } + "default": [ + "\n\n", + "\n", + " ", + "" ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, + "description": "A list of strings that should be considered as separators between chunks.", + "kind": "array", + "name": "separators", + "type": "string" + }, + { + "annotated_options": [ + [ + "graphemes", + "Use unicode graphemes to determine the length of a string." + ], + [ + "runes", + "Use the number of codepoints to determine the length of a string." + ], + [ + "token", + "Use the number of tokens (using the `token_encoding` tokenizer) to determine the length of a string." + ], + [ + "utf8", + "Determine the length of text using the number of utf8 bytes." + ] + ], + "default": "runes", + "description": "The method for measuring the length of a string.", "kind": "scalar", - "name": "oauth", - "type": "object", - "version": "4.7.0" + "linter": "\nlet options = {\n \"graphemes\": true,\n \"runes\": true,\n \"token\": true,\n \"utf8\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "length_measure", + "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } + "description": "The encoding to use for tokenization.", + "examples": [ + "cl100k_base", + "r50k_base" ], - "description": "Allows you to specify basic authentication.", "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "basic_auth", - "type": "object", - "version": "4.7.0" + "name": "token_encoding", + "type": "string" }, { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", - "is_advanced": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, + "default": [], + "description": "A list of special tokens that are allowed in the output.", + "is_advanced": true, + "kind": "array", + "name": "allowed_special", + "type": "string" + }, + { + "default": [ + "all" + ], + "description": "A list of special tokens that are disallowed in the output.", + "is_advanced": true, + "kind": "array", + "name": "disallowed_special", + "type": "string" + }, + { + "default": false, + "description": "Whether to include code blocks in the output.", + "kind": "scalar", + "name": "include_code_blocks", + "type": "bool" + }, + { + "default": false, + "description": "Whether to keep reference links in the output.", + "kind": "scalar", + "name": "keep_reference_links", + "type": "bool" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "A processor allowing splitting text into chunks based on several different strategies.", + "name": "text_chunker", + "plugin": true, + "status": "stable", + "summary": "A processor that allows chunking and splitting text based on some strategy. Usually used for creating vector embeddings of large documents.", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "default": [], + "kind": "array", + "name": "", + "type": "processor" + }, + "description": "\nThis processor behaves similarly to the xref:components:processors/for_each.adoc[`for_each`] processor, where a list of child processors are applied to individual messages of a batch. However, if a message has failed any prior processor (before or during the try block) then that message will skip all following processors.\n\nFor example, with the following config:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - try:\n - resource: bar\n - resource: baz\n - resource: buz\n```\n\nIf the processor `bar` fails for a particular message, that message will skip the processors `baz` and `buz`. Similarly, if `bar` succeeds but `baz` does not then `buz` will be skipped. If the processor `foo` fails for a message then none of `bar`, `baz` or `buz` are executed on that message.\n\nThis processor is useful for when child processors depend on the successful output of previous processors. This processor can be followed with a xref:components:processors/catch.adoc[catch] processor for defining child processors to be applied only to failed messages.\n\nMore information about error handing can be found in xref:configuration:error_handling.adoc[].\n\n== Nest within a catch block\n\nIn some cases it might be useful to nest a try block within a catch block, since the xref:components:processors/catch.adoc[`catch` processor] only clears errors _after_ executing its child processors this means a nested try processor will not execute unless the errors are explicitly cleared beforehand.\n\nThis can be done by inserting an empty catch block before the try block like as follows:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - catch:\n - log:\n level: ERROR\n message: \"Foo failed due to: ${! error() }\"\n - catch: [] # Clear prior error\n - try:\n - resource: bar\n - resource: baz\n```", + "name": "try", + "plugin": true, + "status": "stable", + "summary": "Executes a list of child processors on messages only if no prior processors have failed (or the errors have been cleared).", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "children": [ + { + "default": [], + "description": "A list of processors to execute on each message. If a processor fails for a given message the remaining processors in this list are skipped for that message, and the message is routed to the `catch` processors.", + "kind": "array", + "name": "processors", + "type": "processor" + }, + { + "default": [], + "description": "A list of processors to execute on each message that failed one of the `processors` above. The message is no longer flagged as failed when these run; the error is available as an object in the metadata field named by `error_metadata` (e.g. `@error.what`). When omitted or empty the error is recorded in metadata and the flag is cleared (the failure is swallowed).", + "kind": "array", + "name": "catch", + "type": "processor" + }, + { + "default": "error", + "description": "The metadata key under which the caught error is stored, as an object with a `what` field (the error message) plus `name`, `label` and `path` fields describing the component that failed, before the `catch` processors are executed.", + "kind": "scalar", + "name": "error_metadata", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThis processor combines the behaviour of the xref:components:processors/try.adoc[`try`] and xref:components:processors/catch.adoc[`catch`] processors into a single block with an explicit recovery path. Because it contains both the fallible step and its recovery within a single processor, it is the recommended way to handle expected errors when strict error handling (`error_handling.strict`) is enabled.\n\nEach message of a batch is processed individually. The `processors` field is executed with \"try\" semantics: as soon as a processor fails for a given message the remaining `processors` are skipped for that message.\n\nAny message that failed is then routed to the `catch` processors. Before they run, the failure is moved off the message: it is stored as a structured object in a metadata field (see `error_metadata`, `error` by default) and the message's failure flag is **cleared**. The error is therefore available to recovery logic as an ordinary variable rather than as a message property. The object contains:\n\n- `what`: the error message.\n- `name`: the name of the component that failed (when known).\n- `label`: the label of the component that failed (when set).\n- `path`: the dot-path of the component that failed (when known).\n\nSo a recovery mapping reads the failure with, for example, `@error.what` (equivalent to `meta(\"error\").what`). Because the flag is cleared, the `catch` processors run under the normal error semantics — including strict — so a _new_ failure raised while recovering is treated as a fresh error and is not silently tolerated.\n\nNote that because the failure flag is cleared before the `catch` processors run, the xref:guides:bloblang/functions.adoc#error[`error`] and `error_source_*` functions do not report the original failure within the `catch` block; use the metadata object instead. An empty or omitted `catch` simply records the error in metadata and clears the flag (the failure is swallowed).\n\n```yaml\npipeline:\n processors:\n - try_catch:\n processors:\n - resource: foo\n - resource: bar\n catch:\n - mutation: 'root = \"failed to process: \" + @error.what'\n```\n\nIn the example above, if either `foo` or `bar` fails for a message then the `mutation` is applied to that message, replacing its contents with a description of the error (read from the metadata object), and the message continues downstream without a failure flag.\n\nMore information about error handling can be found in xref:configuration:error_handling.adoc[].", + "name": "try_catch", + "plugin": true, + "status": "beta", + "summary": "Executes a list of child `processors` on each message and, if any of them fail, executes a separate list of `catch` processors to recover from or react to the error.", + "type": "processor" + }, + { + "categories": [ + "Parsing", + "Utility" + ], + "config": { + "children": [ + { + "annotated_options": [ + [ + "binary", + "Extract messages from a https://github.com/redpanda-data/benthos/blob/main/internal/message/message.go#L96[binary blob format^]." + ], + [ + "csv", + "Attempt to parse the message as a csv file (header required) and for each row in the file expands its contents into a json object in a new message." + ], + [ + "csv:x", + "Attempt to parse the message as a csv file (header required) and for each row in the file expands its contents into a json object in a new message using a custom delimiter. The custom delimiter must be a single character, e.g. the format \"csv:\\t\" would consume a tab delimited file." + ], + [ + "json_array", + "Attempt to parse a message as a JSON array, and extract each element into its own message." + ], + [ + "json_documents", + "Attempt to parse a message as a stream of concatenated JSON documents. Each parsed document is expanded into a new message." + ], + [ + "json_map", + "Attempt to parse the message as a JSON map and for each element of the map expands its contents into a new message. A metadata field is added to each message called `archive_key` with the relevant key from the top-level map." + ], + [ + "lines", + "Extract the lines of a message each into their own message." + ], + [ + "tar", + "Extract messages from a unix standard tape archive." + ], + [ + "zip", + "Extract messages from a zip file." + ] + ], + "description": "The unarchiving format to apply.", + "kind": "scalar", + "name": "format", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nWhen a message is unarchived the new messages replace the original message in the batch. Messages that are selected but fail to unarchive (invalid format) will remain unchanged in the message batch but will be flagged as having failed, allowing you to xref:configuration:error_handling.adoc[error handle them].\n\nUnarchived messages are kept in the same batch. To process each unarchived message individually, follow this processor with a xref:components:processors/split.adoc[`split` processor].\n\n== Metadata\n\nThe metadata found on the messages handled by this processor will be copied into the resulting messages. For the unarchive formats that contain file information (tar, zip), a metadata field is also added to each message called `archive_filename` with the extracted filename.\n", + "name": "unarchive", + "plugin": true, + "status": "stable", + "summary": "Unarchives messages according to the selected archive format into multiple messages within a xref:configuration:batching.adoc[batch].", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "children": [ + { + "default": false, + "description": "Whether to always run the child processors at least one time.", + "kind": "scalar", + "name": "at_least_once", + "type": "bool" + }, + { + "default": 0, + "description": "An optional maximum number of loops to execute. Helps protect against accidentally creating infinite loops.", + "is_advanced": true, + "kind": "scalar", + "name": "max_loops", + "type": "int" + }, + { + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether the while loop should execute again.", + "examples": [ + "errored()", + "this.urls.unprocessed.length() > 0" + ], + "kind": "scalar", + "name": "check", + "type": "string" + }, + { + "description": "A list of child processors to execute on each loop.", + "kind": "array", + "name": "processors", + "type": "processor" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\nThe field `at_least_once`, if true, ensures that the child processors are always executed at least one time (like a do .. while loop.)\n\nThe field `max_loops`, if greater than zero, caps the number of loops for a message batch to this value.\n\nIf following a loop execution the number of messages in a batch is reduced to zero the loop is exited regardless of the condition result. If following a loop execution there are more than 1 message batches the query is checked against the first batch only.\n\nThe conditions of this processor are applied across entire message batches. You can find out more about batching xref:configuration:batching.adoc[in this doc].", + "name": "while", + "plugin": true, + "status": "stable", + "summary": "A processor that checks a xref:guides:bloblang/about.adoc[Bloblang query] against each batch of messages and executes child processors on them for as long as the query resolves to true.", + "type": "processor" + }, + { + "categories": [ + "Composition" + ], + "config": { + "children": [ + { + "default": "meta.workflow", + "description": "A xref:configuration:field_paths.adoc[dot path] indicating where to store and reference <> about the workflow execution.", + "kind": "scalar", + "name": "meta_path", + "type": "string" + }, + { + "default": [], + "description": "An explicit declaration of branch ordered tiers, which describes the order in which parallel tiers of branches should be executed. Branches should be identified by the name as they are configured in the field `branches`. It's also possible to specify branch processors configured <>.", + "examples": [ + [ + [ + "foo", + "bar" + ], + [ + "baz" + ] + ], + [ + [ + "foo" + ], + [ + "bar" + ], + [ + "baz" + ] + ] + ], + "kind": "2darray", + "name": "order", + "type": "string" + }, + { + "default": [], + "description": "An optional list of xref:components:processors/branch.adoc[`branch` processor] names that are configured as <>. These resources will be included in the workflow with any branches configured inline within the <> field. The order and parallelism in which branches are executed is automatically resolved based on the mappings of each branch. When using resources with an explicit order it is not necessary to list resources in this field.", + "is_advanced": true, + "kind": "array", + "name": "branch_resources", + "type": "string", + "version": "3.38.0" + }, + { + "children": [ { + "bloblang": true, "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] that describes how to create a request payload suitable for the child processors of this branch. If left empty then the branch will begin with an exact copy of the origin message (including metadata).", + "examples": [ + "root = {\n\t\"id\": this.doc.id,\n\t\"content\": this.doc.body.text\n}", + "root = if this.type == \"foo\" {\n\tthis.foo.request\n} else {\n\tdeleted()\n}" + ], "kind": "scalar", - "name": "signing_method", + "name": "request_map", "type": "string" }, { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" + "description": "A list of processors to apply to mapped requests. When processing message batches the resulting batch must match the size and ordering of the input batch, therefore filtering, grouping should not be performed within these processors.", + "kind": "array", + "name": "processors", + "type": "processor" }, { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" + "bloblang": true, + "default": "", + "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] that describes how the resulting messages from branched processing should be mapped back into the original payload. If left empty the origin message will remain unchanged (including metadata).", + "examples": [ + "meta foo_code = metadata(\"code\")\nroot.foo_result = this", + "meta = metadata()\nroot.bar.body = this.body\nroot.bar.id = this.user.id", + "root.raw_result = content().string()", + "root.enrichments.foo = if metadata(\"request_failed\") != null {\n throw(metadata(\"request_failed\"))\n} else {\n this\n}", + "# Retain only the updated metadata fields which were present in the origin message\nmeta = metadata().filter(v -> @.get(v.key) != null)" + ], + "kind": "scalar", + "name": "result_map", + "type": "string" } ], - "description": "BETA: Allows you to specify JWT authentication.", + "default": {}, + "description": "An object of named xref:components:processors/branch.adoc[`branch` processors] that make up the workflow. The order and parallelism in which branches are executed can either be made explicit with the field `order`, or if omitted an attempt is made to automatically resolve an ordering based on the mappings of each branch.", + "kind": "map", + "name": "branches", + "type": "object" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\n== Why use a workflow\n\n=== Performance\n\nMost of the time the best way to compose processors is also the simplest, just configure them in series. This is because processors are often CPU bound, low-latency, and you can gain vertical scaling by increasing the number of processor pipeline threads, allowing Redpanda Connect to process xref:configuration:processing_pipelines.adoc[multiple messages in parallel].\n\nHowever, some processors such as xref:components:processors/http.adoc[`http`], xref:components:processors/aws_lambda.adoc[`aws_lambda`] or xref:components:processors/cache.adoc[`cache`] interact with external services and therefore spend most of their time waiting for a response. These processors tend to be high-latency and low CPU activity, which causes messages to process slowly.\n\nWhen a processing pipeline contains multiple network processors that aren't dependent on each other we can benefit from performing these processors in parallel for each individual message, reducing the overall message processing latency.\n\n=== Simplifying processor topology\n\nA workflow is often expressed as a https://en.wikipedia.org/wiki/Directed_acyclic_graph[DAG^] of processing stages, where each stage can result in N possible next stages, until finally the flow ends at an exit node.\n\nFor example, if we had processing stages A, B, C and D, where stage A could result in either stage B or C being next, always followed by D, it might look something like this:\n\n```text\n /--> B --\\\nA --| |--> D\n \\--> C --/\n```\n\nThis flow would be easy to express in a standard Redpanda Connect config, we could simply use a xref:components:processors/switch.adoc[`switch` processor] to route to either B or C depending on a condition on the result of A. However, this method of flow control quickly becomes unfeasible as the DAG gets more complicated, imagine expressing this flow using switch processors:\n\n```text\n /--> B -------------|--> D\n / /\nA --| /--> E --|\n \\--> C --| \\\n \\----------|--> F\n```\n\nAnd imagine doing so knowing that the diagram is subject to change over time. Yikes! Instead, with a workflow we can either trust it to automatically resolve the DAG or express it manually as simply as `order: [ [ A ], [ B, C ], [ E ], [ D, F ] ]`, and the conditional logic for determining if a stage is executed is defined as part of the branch itself.", + "examples": [ + { + "config": "\npipeline:\n processors:\n - workflow:\n meta_path: meta.workflow\n branches:\n foo:\n request_map: 'root = \"\"'\n processors:\n - http:\n url: TODO\n result_map: 'root.foo = this'\n\n bar:\n request_map: 'root = this.body'\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.bar = this'\n\n baz:\n request_map: |\n root.fooid = this.foo.id\n root.barstuff = this.bar.content\n processors:\n - cache:\n resource: TODO\n operator: set\n key: ${! json(\"fooid\") }\n value: ${! json(\"barstuff\") }\n", + "summary": "\nWhen the field `order` is omitted a best attempt is made to determine a dependency tree between branches based on their request and result mappings. In the following example the branches foo and bar will be executed first in parallel, and afterwards the branch baz will be executed.", + "title": "Automatic Ordering" + }, + { + "config": "\npipeline:\n processors:\n - workflow:\n branches:\n A:\n request_map: |\n root = if this.document.type != \"foo\" {\n deleted()\n }\n processors:\n - http:\n url: TODO\n result_map: 'root.tmp.result = this'\n\n B:\n request_map: |\n root = if this.document.type == \"foo\" {\n deleted()\n }\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.tmp.result = this'\n\n C:\n request_map: |\n root = if this.tmp.result != null {\n deleted()\n }\n processors:\n - http:\n url: TODO_SOMEWHERE_ELSE\n result_map: 'root.tmp.result = this'\n", + "summary": "\nBranches of a workflow are skipped when the `request_map` assigns `deleted()` to the root. In this example the branch A is executed when the document type is \"foo\", and branch B otherwise. Branch C is executed afterwards and is skipped unless either A or B successfully provided a result at `tmp.result`.", + "title": "Conditional Branches" + }, + { + "config": "\npipeline:\n processors:\n - workflow:\n order: [ [ foo, bar ], [ baz ] ]\n branches:\n bar:\n request_map: 'root = this.body'\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.bar = this'\n\nprocessor_resources:\n - label: foo\n branch:\n request_map: 'root = \"\"'\n processors:\n - http:\n url: TODO\n result_map: 'root.foo = this'\n\n - label: baz\n branch:\n request_map: |\n root.fooid = this.foo.id\n root.barstuff = this.bar.content\n processors:\n - cache:\n resource: TODO\n operator: set\n key: ${! json(\"fooid\") }\n value: ${! json(\"barstuff\") }\n", + "summary": "\nThe `order` field can be used in order to refer to <>, this can sometimes make your pipeline configuration cleaner, as well as allowing you to reuse branch configurations in order places. It's also possible to mix and match branches configured within the workflow and configured as resources.", + "title": "Resources" + } + ], + "footnotes": "\n== Structured metadata\n\nWhen the field `meta_path` is non-empty the workflow processor creates an object describing which workflows were successful, skipped or failed for each message and stores the object within the message at the end.\n\nThe object is of the following form:\n\n```json\n{\n\t\"succeeded\": [ \"foo\" ],\n\t\"skipped\": [ \"bar\" ],\n\t\"failed\": {\n\t\t\"baz\": \"the error message from the branch\"\n\t}\n}\n```\n\nIf a message already has a meta object at the given path when it is processed then the object is used in order to determine which branches have already been performed on the message (or skipped) and can therefore be skipped on this run.\n\nThis is a useful pattern when replaying messages that have failed some branches previously. For example, given the above example object the branches foo and bar would automatically be skipped, and baz would be reattempted.\n\nThe previous meta object will also be preserved in the field `.previous` when the new meta object is written, preserving a full record of all workflow executions.\n\nIf a field `.apply` exists in the meta object for a message and is an array then it will be used as an explicit list of stages to apply, all other stages will be skipped.\n\n== Resources\n\nIt's common to configure processors (and other components) xref:configuration:resources.adoc[as resources] in order to keep the pipeline configuration cleaner. With the workflow processor you can include branch processors configured as resources within your workflow either by specifying them by name in the field `order`, if Redpanda Connect doesn't find a branch within the workflow configuration of that name it'll refer to the resources.\n\nAlternatively, if you do not wish to have an explicit ordering, you can add resource names to the field `branch_resources` and they will be included in the workflow with automatic DAG resolution along with any branches configured in the `branches` field.\n\n=== Resource error conditions\n\nThere are two error conditions that could potentially occur when resources included in your workflow are mutated, and if you are planning to mutate resources in your workflow it is important that you understand them.\n\nThe first error case is that a resource in the workflow is removed and not replaced, when this happens the workflow will still be executed but the individual branch will fail. This should only happen if you explicitly delete a branch resource, as any mutation operation will create the new resource before removing the old one.\n\nThe second error case is when automatic DAG resolution is being used and a resource in the workflow is changed in a way that breaks the DAG (circular dependencies, etc). When this happens it is impossible to execute the workflow and therefore the processor will fail, which is possible to capture and handle using xref:configuration:error_handling.adoc[standard error handling patterns].\n\n== Error handling\n\nThe recommended approach to handle failures within a workflow is to query against the <> it provides, as it provides granular information about exactly which branches failed and which ones succeeded and therefore aren't necessary to perform again.\n\nFor example, if our meta object is stored at the path `meta.workflow` and we wanted to check whether a message has failed for any branch we can do that using a xref:guides:bloblang/about.adoc[Bloblang query] like `this.meta.workflow.failed.length() | 0 > 0`, or to check whether a specific branch failed we can use `this.exists(\"meta.workflow.failed.foo\")`.\n\nHowever, if structured metadata is disabled by setting the field `meta_path` to empty then the workflow processor instead adds a general error flag to messages when any executed branch fails. In this case it's possible to handle failures using xref:configuration:error_handling.adoc[standard error handling patterns].\n\n", + "name": "workflow", + "plugin": true, + "status": "stable", + "summary": "Executes a topology of xref:components:processors/branch.adoc[`branch` processors], performing them in parallel where possible.", + "type": "processor" + }, + { + "categories": [ + "Parsing" + ], + "config": { + "children": [ + { + "default": "", + "description": "An XML <> to apply to messages.", + "kind": "scalar", + "linter": "\nlet options = {\n \"to_json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "operator", + "options": [ + "to_json" + ], + "type": "string" + }, + { + "default": false, + "description": "Whether to try to cast values that are numbers and booleans to the right type. Default: all values are strings.", + "kind": "scalar", + "name": "cast", + "type": "bool" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\n== Operators\n\n=== `to_json`\n\nConverts an XML document into a JSON structure, where elements appear as keys of an object according to the following rules:\n\n- If an element contains attributes they are parsed by prefixing a hyphen, `-`, to the attribute label.\n- If the element is a simple element and has attributes, the element value is given the key `#text`.\n- XML comments, directives, and process instructions are ignored.\n- When elements are repeated the resulting JSON value is an array.\n\nFor example, given the following XML:\n\n```xml\n\n This is a title\n This is a description\n foo1\n foo2\n foo3\n\n```\n\nThe resulting JSON structure would look like this:\n\n```json\n{\n \"root\":{\n \"title\":\"This is a title\",\n \"description\":{\n \"#text\":\"This is a description\",\n \"-tone\":\"boring\"\n },\n \"elements\":[\n {\"#text\":\"foo1\",\"-id\":\"1\"},\n {\"#text\":\"foo2\",\"-id\":\"2\"},\n \"foo3\"\n ]\n }\n}\n```\n\nWith cast set to true, the resulting JSON structure would look like this:\n\n```json\n{\n \"root\":{\n \"title\":\"This is a title\",\n \"description\":{\n \"#text\":\"This is a description\",\n \"-tone\":\"boring\"\n },\n \"elements\":[\n {\"#text\":\"foo1\",\"-id\":1},\n {\"#text\":\"foo2\",\"-id\":2},\n \"foo3\"\n ]\n }\n}\n```", + "name": "xml", + "plugin": true, + "status": "stable", + "summary": "Parses messages as an XML document, performs a mutation on the data, and then overwrites the previous contents with the new value.", + "type": "processor" + } + ], + "rate-limits": [ + { + "categories": null, + "config": { + "children": [ + { + "default": 1000, + "description": "The maximum number of requests to allow for a given period of time.", + "kind": "scalar", + "name": "count", + "type": "int" + }, + { + "default": "1s", + "description": "The time window to limit requests by.", + "kind": "scalar", + "name": "interval", + "type": "string" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "local", + "plugin": true, + "status": "stable", + "summary": "The local rate limit is a simple X every Y type rate limit that can be shared across any number of components within the pipeline but does not support distributed rate limits across multiple running instances of Benthos.", + "type": "rate_limit" + }, + { + "categories": null, + "config": { + "children": [ + { + "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "examples": [ + "redis://:6379", + "redis://localhost:6379", + "redis://foousername:foopassword@redisplace:6379", + "redis://:foopassword@redisplace:6379", + "redis://localhost:6379/1", + "redis://localhost:6379/1,redis://localhost:6380/1" + ], + "kind": "scalar", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": "simple", + "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", "is_advanced": true, "kind": "scalar", - "name": "jwt", - "type": "object", - "version": "4.7.0" + "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "kind", + "options": [ + "simple", + "cluster", + "failover" + ], + "type": "string" + }, + { + "default": "", + "description": "Name of the redis master when `kind` is `failover`", + "examples": [ + "mymaster" + ], + "is_advanced": true, + "kind": "scalar", + "name": "master", + "type": "string" + }, + { + "default": "redpanda-connect", + "description": "Set the client name for the Redis connection.", + "is_advanced": true, + "kind": "scalar", + "name": "client_name", + "type": "string", + "version": "4.82.0" }, { "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, { "default": false, "description": "Whether to skip server side certificate verification.", @@ -55286,357 +64832,79 @@ "type": "object" } ], - "description": "Custom TLS settings can be used to override system defaults.", + "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", "is_advanced": true, "kind": "scalar", "name": "tls", "type": "object" + }, + { + "default": 1000, + "description": "The maximum number of messages to allow for a given period of time.", + "kind": "scalar", + "linter": "root = if this <= 0 { [ \"count must be larger than zero\" ] }", + "name": "count", + "type": "int" + }, + { + "default": "1s", + "description": "The time window to limit requests by.", + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "description": "The key to use for the rate limit.", + "kind": "scalar", + "name": "key", + "type": "string" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nDecodes messages automatically from a schema stored within a https://docs.confluent.io/platform/current/schema-registry/index.html[Confluent Schema Registry service^] by extracting a schema ID from the message and obtaining the associated schema from the registry. If a message fails to match against the schema then it will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].\n\nAvro, Protobuf and Json schemas are supported, all are capable of expanding from schema references as of v4.22.0.\n\n== Avro JSON format\n\nThis processor creates documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when decoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead create documents in https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard/raw JSON format^] by setting the field <> to `true`.\n\n== Protobuf format\n\nThis processor decodes protobuf messages to JSON documents, you can read more about JSON mapping of protobuf messages here: https://developers.google.com/protocol-buffers/docs/proto3#json\n\n== Metadata\n\nThis processor also adds the following metadata to each outgoing message:\n\nschema_id: the ID of the schema in the schema registry that was associated with the message.\n", - "name": "schema_registry_decode", + "name": "redis", "plugin": true, - "status": "beta", - "summary": "Automatically decodes and validates messages with schemas from a Confluent Schema Registry service.", - "type": "processor" - }, + "status": "stable", + "summary": "A rate limit implementation using Redis. It works by using a simple token bucket algorithm to limit the number of requests to a given count within a given time period. The rate limit is shared across all instances of Redpanda Connect that use the same Redis instance, which must all have a consistent count and interval.", + "type": "rate_limit", + "version": "4.12.0" + } + ], + "scanners": [ { - "categories": [ - "Parsing", - "Integration" - ], + "categories": null, "config": { "children": [ { - "description": "The base URL of the schema registry service.", + "default": false, + "description": "Whether messages should be decoded into normal JSON rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^]. When true, union values are unwrapped (bare values instead of {\"type\": value} wrappers).", + "is_advanced": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" - }, - { - "description": "The schema subject to derive schemas from.", - "examples": [ - "foo", - "${! meta(\"kafka_topic\") }" - ], - "interpolated": true, - "kind": "scalar", - "name": "subject", - "type": "string" - }, - { - "default": "10m", - "description": "The period after which a schema is refreshed for each subject, this is done by polling the schema registry service.", - "examples": [ - "60s", - "1h" - ], - "kind": "scalar", - "name": "refresh_period", - "type": "string" - }, - { - "default": false, - "description": "Whether messages encoded in Avro format should be parsed as normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^]. If `true` the schema returned from the subject should be parsed as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard json^] instead of as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodec[avro json^]. There is a https://github.com/linkedin/goavro/blob/5ec5a5ee7ec82e16e6e2b438d610e1cab2588393/union.go#L224-L249[comment in goavro^], the https://github.com/linkedin/goavro[underlining library used for avro serialization^], that explains in more detail the difference between standard json and avro json.", - "is_advanced": true, - "kind": "scalar", - "name": "avro_raw_json", - "type": "bool", - "version": "3.59.0" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use OAuth version 1 in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A value used to identify the client to the service provider.", - "is_advanced": true, - "kind": "scalar", - "name": "consumer_key", - "type": "string" - }, - { - "default": "", - "description": "A secret used to establish ownership of the consumer key.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "consumer_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "A value used to gain access to the protected resources on behalf of the user.", - "is_advanced": true, - "kind": "scalar", - "name": "access_token", - "type": "string" - }, - { - "default": "", - "description": "A secret provided in order to establish ownership of a given access token.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "access_token_secret", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify open authentication via OAuth version 1.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "oauth", - "type": "object", - "version": "4.7.0" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use basic authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A username to authenticate as.", - "is_advanced": true, - "kind": "scalar", - "name": "username", - "type": "string" - }, - { - "default": "", - "description": "A password to authenticate with.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "description": "Allows you to specify basic authentication.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "basic_auth", - "type": "object", - "version": "4.7.0" - }, - { - "children": [ - { - "default": false, - "description": "Whether to use JWT authentication in requests.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": "", - "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", - "is_advanced": true, - "kind": "scalar", - "name": "private_key_file", - "type": "string" - }, - { - "default": "", - "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", - "is_advanced": true, - "kind": "scalar", - "name": "signing_method", - "type": "string" - }, - { - "default": {}, - "description": "A value used to identify the claims that issued the JWT.", - "is_advanced": true, - "kind": "map", - "name": "claims", - "type": "unknown" - }, - { - "default": {}, - "description": "Add optional key/value headers to the JWT.", - "is_advanced": true, - "kind": "map", - "name": "headers", - "type": "unknown" - } - ], - "description": "BETA: Allows you to specify JWT authentication.", - "is_advanced": true, - "kind": "scalar", - "name": "jwt", - "type": "object", - "version": "4.7.0" - }, - { - "children": [ - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], - "is_advanced": true, - "kind": "scalar", - "name": "root_cas_file", - "type": "string" - }, - { - "children": [ - { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert_file", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate key to use.", - "is_advanced": true, - "kind": "scalar", - "name": "key_file", - "type": "string" - }, - { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - } - ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], - "is_advanced": true, - "kind": "array", - "name": "client_certs", - "type": "object" - } - ], - "description": "Custom TLS settings can be used to override system defaults.", - "is_advanced": true, - "kind": "scalar", - "name": "tls", - "type": "object" + "name": "raw_json", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nEncodes messages automatically from schemas obtains from a https://docs.confluent.io/platform/current/schema-registry/index.html[Confluent Schema Registry service^] by polling the service for the latest schema version for target subjects.\n\nIf a message fails to encode under the schema then it will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].\n\nAvro, Protobuf and Json schemas are supported, all are capable of expanding from schema references as of v4.22.0.\n\n== Avro JSON format\n\nBy default this processor expects documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when encoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `\\{\"string\": \"a\"}`; and\n- a `Foo` instance as `\\{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead consume documents in https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard/raw JSON format^] by setting the field <> to `true`.\n\n=== Known issues\n\nImportant! There is an outstanding issue in the https://github.com/linkedin/goavro[avro serializing library^] that Redpanda Connect uses which means it https://github.com/linkedin/goavro/issues/252[doesn't encode logical types correctly^]. It's still possible to encode logical types that are in-line with the spec if `avro_raw_json` is set to true, though now of course non-logical types will not be in-line with the spec.\n\n== Protobuf format\n\nThis processor encodes protobuf messages either from any format parsed within Redpanda Connect (encoded as JSON by default), or from raw JSON documents, you can read more about JSON mapping of protobuf messages here: https://developers.google.com/protocol-buffers/docs/proto3#json\n\n=== Multiple message support\n\nWhen a target subject presents a protobuf schema that contains multiple messages it becomes ambiguous which message definition a given input data should be encoded against. In such scenarios Redpanda Connect will attempt to encode the data against each of them and select the first to successfully match against the data, this process currently *ignores all nested message definitions*. In order to speed up this exhaustive search the last known successful message will be attempted first for each subsequent input.\n\nWe will be considering alternative approaches in future so please https://redpanda.com/slack[get in touch^] with thoughts and feedback.\n", - "name": "schema_registry_encode", + "description": "\n== Avro JSON format\n\nThis scanner yields documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when decoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead create documents in standard/raw JSON format by setting the field <> to `true`.\n\nThis scanner also emits the canonical Avro schema as `@avro_schema` metadata, along with the schema's fingerprint available via `@avro_schema_fingerprint`.\n", + "name": "avro", "plugin": true, - "status": "beta", - "summary": "Automatically encodes and validates messages with schemas from a Confluent Schema Registry service.", - "type": "processor", - "version": "3.58.0" + "status": "stable", + "summary": "Consume a stream of Avro OCF datum.", + "type": "scanner" }, { - "categories": [ - "Utility" - ], + "categories": null, "config": { "children": [ { - "default": [], - "description": "An array of message indexes of a batch. Indexes can be negative, and if so the part will be selected from the end counting backwards starting from -1.", - "kind": "array", - "name": "parts", + "description": "The size of each chunk in bytes.", + "kind": "scalar", + "name": "size", "type": "int" } ], @@ -55644,2240 +64912,1498 @@ "name": "", "type": "object" }, - "description": "\nThe selected parts are added to the new message batch in the same order as the selection array. E.g. with 'parts' set to [ 2, 0, 1 ] and the message parts [ '0', '1', '2', '3' ], the output will be [ '2', '0', '1' ].\n\nIf none of the selected parts exist in the input batch (resulting in an empty output message) the batch is dropped entirely.\n\nMessage indexes can be negative, and if so the part will be selected from the end counting backwards starting from -1. E.g. if index = -1 then the selected part will be the last part of the message, if index = -2 then the part before the last element with be selected, and so on.\n\nThis processor is only applicable to xref:configuration:batching.adoc[batched messages].", - "name": "select_parts", + "name": "chunker", "plugin": true, "status": "stable", - "summary": "Cherry pick a set of messages from a batch by their index. Indexes larger than the number of messages are simply ignored.", - "type": "processor" + "summary": "Split an input stream into chunks of a given number of bytes.", + "type": "scanner" }, { "categories": null, "config": { "children": [ { - "default": "", - "description": "The DSN address to send sentry events to. If left empty, then SENTRY_DSN is used.", - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "A message to set on the sentry event", - "examples": [ - "webhook event received", - "failed to find product in database: ${! error() }" - ], - "interpolated": true, - "kind": "scalar", - "name": "message", - "type": "string" - }, - { - "bloblang": true, - "description": "A mapping that must evaluate to an object-of-objects or `deleted()`. If this mapping produces a value, then it is set on a sentry event as additional context.", - "examples": [ - "root = {\"order\": {\"product_id\": \"P93174\", \"quantity\": 5}}", - "root = deleted()" - ], - "is_optional": true, - "kind": "scalar", - "name": "context", - "type": "string" - }, - { - "bloblang": true, - "description": "A mapping that must evaluate to an object. If this mapping produces a value, then it is set on a sentry event as extras.", - "examples": [ - "root.foo = \"bar\"", - "root = this.without(\"password\")" - ], - "is_optional": true, - "kind": "scalar", - "name": "extras", - "type": "string" - }, - { - "description": "Sets key/value string tags on an event. Unlike context, these are indexed and searchable on Sentry but have length limitations.", - "interpolated": true, + "description": "Use a provided custom delimiter instead of the default comma.", "is_optional": true, - "kind": "map", - "name": "tags", - "type": "string" - }, - { - "default": "", - "description": "The environment to be sent with events. If left empty, then SENTRY_ENVIRONMENT is used.", - "kind": "scalar", - "name": "environment", - "type": "string" - }, - { - "default": "", - "description": "The version of the code deployed to an environment. If left empty, then the Sentry client will attempt to detect the release from the environment.", - "kind": "scalar", - "name": "release", - "type": "string" - }, - { - "default": "INFO", - "description": "Sets the level on sentry events similar to logging levels.", "kind": "scalar", - "linter": "\nlet options = {\n \"debug\": true,\n \"info\": true,\n \"warn\": true,\n \"error\": true,\n \"fatal\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "level", - "options": [ - "DEBUG", - "INFO", - "WARN", - "ERROR", - "FATAL" - ], + "name": "custom_delimiter", "type": "string" }, { - "default": "async", - "description": "Determines how events are sent. A sync transport will block when sending each event until a response is received from the Sentry server. The recommended async transport will enqueue events in a buffer and send them in the background.", + "default": true, + "description": "Whether to reference the first row as a header row. If set to true the output structure for messages will be an object where field keys are determined by the header row. Otherwise, each message will consist of an array of values from the corresponding CSV row.", "kind": "scalar", - "linter": "\nlet options = {\n \"async\": true,\n \"sync\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "transport_mode", - "options": [ - "async", - "sync" - ], - "type": "string" + "name": "parse_header_row", + "type": "bool" }, { - "default": "5s", - "description": "The duration to wait when closing the processor to flush any remaining enqueued events.", + "default": false, + "description": "If set to `true`, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.", "kind": "scalar", - "name": "flush_timeout", - "type": "string" + "name": "lazy_quotes", + "type": "bool" }, { - "default": 1, - "description": "The rate at which events are sent to the server. A value of 0 disables capturing sentry events entirely. A value of 1 results in sending all events to Sentry. Any value in between results sending some percentage of events.", + "default": false, + "description": "If a row fails to parse due to any error emit an empty message marked with the error and then continue consuming subsequent rows when possible. This can sometimes be useful in situations where input data contains individual rows which are malformed. However, when a row encounters a parsing error it is impossible to guarantee that following rows are valid, as this indicates that the input data is unreliable and could potentially emit misaligned rows.", "kind": "scalar", - "linter": "root = if this < 0 || this > 1 { [\"sampling rate must be between 0.0 and 1.0\" ] }", - "name": "sampling_rate", - "type": "float" + "name": "continue_on_error", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "name": "sentry_capture", + "description": "\n== Metadata\n\nThis scanner adds the following metadata to each message:\n\n- `csv_row` The index of each row, beginning at 0.\n\n", + "name": "csv", "plugin": true, - "status": "experimental", - "summary": "Captures log events from messages and submits them to https://sentry.io/[Sentry^].", - "type": "processor", - "version": "4.16.0" + "status": "stable", + "summary": "Consume comma-separated values row by row, including support for custom delimiters.", + "type": "scanner" }, { "categories": null, "config": { "children": [ { - "description": "The Slack Bot User OAuth token to use.", - "kind": "scalar", - "linter": "\n root = if !this.has_prefix(\"xoxb-\") { [ \"field must start with xoxb-\" ] }\n ", - "name": "bot_token", - "type": "string" - }, - { - "description": "The channel ID to read messages from.", - "interpolated": true, + "description": "One of `gzip`, `pgzip`, `zlib`, `bzip2`, `flate`, `snappy`, `lz4`, `zstd`.", "kind": "scalar", - "name": "channel_id", + "name": "algorithm", "type": "string" }, { - "description": "The thread timestamp to read the full thread of.", - "interpolated": true, + "default": { + "to_the_end": {} + }, + "description": "The child scanner to feed the decompressed stream into.", "kind": "scalar", - "name": "thread_ts", - "type": "string" + "name": "into", + "type": "scanner" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "Read a thread using the https://api.slack.com/methods/conversations.replies[^Slack API]", - "name": "slack_thread", + "name": "decompress", "plugin": true, - "status": "experimental", - "type": "processor" + "status": "stable", + "summary": "Decompress the stream of bytes according to an algorithm, before feeding it into a child scanner.", + "type": "scanner" }, { - "categories": [ - "Utility" - ], + "categories": null, "config": { - "children": [ - { - "description": "The duration of time to sleep for each execution.", - "interpolated": true, - "kind": "scalar", - "name": "duration", - "type": "string" - } - ], + "default": {}, "kind": "scalar", "name": "", "type": "object" }, - "name": "sleep", + "name": "json_array", "plugin": true, "status": "stable", - "summary": "Sleep for a period of time specified as a duration string for each message. This processor will interpolate functions within the `duration` field, you can find a list of functions xref:configuration:interpolation.adoc#bloblang-queries[here].", - "type": "processor" + "summary": "Consumes a stream of one or more JSON elements within a top level array.", + "type": "scanner" }, { - "categories": [ - "Utility" - ], + "categories": null, + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "json_documents", + "plugin": true, + "status": "stable", + "summary": "Consumes a stream of one or more JSON documents.", + "type": "scanner", + "version": "4.27.0" + }, + { + "categories": null, "config": { "children": [ { - "default": 1, - "description": "The target number of messages.", + "description": "Use a provided custom delimiter for detecting the end of a line rather than a single line break.", + "is_optional": true, "kind": "scalar", - "name": "size", - "type": "int" + "name": "custom_delimiter", + "type": "string" }, { - "default": 0, - "description": "An optional target of total message bytes.", + "default": 65536, + "description": "Set the maximum buffer size for storing line data, this limits the maximum size that a line can be without causing an error.", "kind": "scalar", - "name": "byte_size", + "name": "max_buffer_size", "type": "int" + }, + { + "default": false, + "description": "Omit empty lines.", + "kind": "scalar", + "name": "omit_empty", + "type": "bool" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nThis processor is for breaking batches down into smaller ones. In order to break a single message out into multiple messages use the xref:components:processors/unarchive.adoc[`unarchive` processor].\n\nIf there is a remainder of messages after splitting a batch the remainder is also sent as a single batch. For example, if your target size was 10, and the processor received a batch of 95 message parts, the result would be 9 batches of 10 messages followed by a batch of 5 messages.", - "name": "split", + "name": "lines", "plugin": true, "status": "stable", - "summary": "Breaks message batches (synonymous with multiple part messages) into smaller batches. The size of the resulting batches are determined either by a discrete size or, if the field `byte_size` is non-zero, then by total size in bytes (which ever limit is reached first).", - "type": "processor" + "summary": "Split an input stream into a message per line of data.", + "type": "scanner" }, { - "categories": [ - "Integration" - ], + "categories": null, "config": { "children": [ { - "description": "A database <> to use.", + "description": "The pattern to match against.", + "examples": [ + "(?m)^\\d\\d:\\d\\d:\\d\\d" + ], "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "Data source name.", - "kind": "scalar", - "name": "data_source_name", - "type": "string" - }, - { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", - "examples": [ - "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);" - ], - "kind": "scalar", - "name": "query", - "type": "string" - }, - { - "default": false, - "description": "Whether to enable xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions] in the query. Great care should be made to ensure your queries are defended against injection attacks.", - "is_advanced": true, - "kind": "scalar", - "name": "unsafe_dynamic_query", - "type": "bool" - }, - { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], - "is_optional": true, - "kind": "scalar", - "name": "args_mapping", + "name": "pattern", "type": "string" }, { - "default": "none", - "description": "Result codec.", + "default": 65536, + "description": "Set the maximum buffer size for storing line data, this limits the maximum size that a message can be without causing an error.", "kind": "scalar", - "name": "result_codec", - "type": "string" + "name": "max_buffer_size", + "type": "int" } ], "kind": "scalar", "name": "", "type": "object" }, - "description": "\nIf the query fails to execute then the message will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].\n\n== Alternatives\n\nFor basic inserts or select queries use either the xref:components:processors/sql_insert.adoc[`sql_insert`] or the xref:components:processors/sql_select.adoc[`sql_select`] processor. For more complex queries use the xref:components:processors/sql_raw.adoc[`sql_raw`] processor.", - "name": "sql", + "name": "re_match", "plugin": true, - "status": "deprecated", - "summary": "Runs an arbitrary SQL query against a database and (optionally) returns the result as an array of objects, one for each row returned.", - "type": "processor", - "version": "3.65.0" + "status": "stable", + "summary": "Split an input stream into segments matching against a regular expression.", + "type": "scanner" }, { - "categories": [ - "Integration" - ], + "categories": null, "config": { "children": [ { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "The table to insert to.", - "examples": [ - "foo" - ], - "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "description": "A list of columns to insert.", - "examples": [ - [ - "foo", - "bar", - "baz" - ] - ], - "kind": "array", - "name": "columns", - "type": "string" - }, - { - "bloblang": true, - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of columns specified.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], - "kind": "scalar", - "name": "args_mapping", - "type": "string" - }, - { - "description": "An optional prefix to prepend to the insert query (before INSERT).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "prefix", - "type": "string" - }, - { - "description": "An optional suffix to append to the insert query.", - "examples": [ - "ON CONFLICT (name) DO NOTHING" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "suffix", - "type": "string" - }, - { - "description": "A list of keyword options to add before the INTO clause of the query.", - "examples": [ - [ - "DELAYED", - "IGNORE" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "options", - "type": "string" - }, - { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - [ - "./init/*.sql" - ], - [ - "./foo.sql", - "./bar.sql" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" - }, - { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" - }, - { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, + "default": { + "to_the_end": {} + }, + "description": "The child scanner to feed the resulting stream into.", "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, + "name": "into", + "type": "scanner" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "skip_bom", + "plugin": true, + "status": "stable", + "summary": "Skip one or more byte order marks for each opened child scanner.", + "type": "scanner" + }, + { + "categories": null, + "config": { + "children": [ { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, + "description": "A regular expression to test against the name of each source of data fed into the scanner (filename or equivalent). If this pattern matches the child scanner is selected.", "is_optional": true, "kind": "scalar", - "name": "conn_max_life_time", + "name": "re_match_name", "type": "string" }, { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle", - "type": "int" - }, - { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", - "is_advanced": true, - "is_optional": true, + "description": "The scanner to activate if this candidate passes.", "kind": "scalar", - "name": "conn_max_open", - "type": "int" + "name": "scanner", + "type": "scanner" } ], - "kind": "scalar", + "kind": "array", "name": "", "type": "object" }, - "description": "\nIf the insert fails to execute then the message will still remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", + "description": "This scanner outlines a list of potential child scanner candidates to be chosen, and for each source of data the first candidate to pass will be selected. A candidate without any conditions acts as a catch-all and will pass for every source, it is recommended to always have a catch-all scanner at the end of your list. If a given source of data does not pass a candidate an error is returned and the data is rejected.", "examples": [ { - "config": "\npipeline:\n processors:\n - sql_insert:\n driver: mysql\n dsn: foouser:foopassword@tcp(localhost:3306)/foodb\n table: footable\n columns: [ id, name, topic ]\n args_mapping: |\n root = [\n this.user.id,\n this.user.name,\n meta(\"kafka_topic\"),\n ]\n", - "summary": "\nHere we insert rows into a database by populating the columns id, name and topic with values extracted from messages and metadata:", - "title": "Table Insert (MySQL)" + "config": "\ninput:\n file:\n paths: [ ./data/* ]\n scanner:\n switch:\n - re_match_name: '\\.avro$'\n scanner: { avro: {} }\n\n - re_match_name: '\\.csv$'\n scanner: { csv: {} }\n\n - re_match_name: '\\.csv.gz$'\n scanner:\n decompress:\n algorithm: gzip\n into:\n csv: {}\n\n - re_match_name: '\\.tar$'\n scanner: { tar: {} }\n\n - re_match_name: '\\.tar.gz$'\n scanner:\n decompress:\n algorithm: gzip\n into:\n tar: {}\n\n - scanner: { to_the_end: {} }\n", + "summary": "In this example a file input chooses a scanner based on the extension of each file", + "title": "Switch based on file name" } ], - "name": "sql_insert", + "name": "switch", "plugin": true, "status": "stable", - "summary": "Inserts rows into an SQL database for each message, and leaves the message unchanged.", - "type": "processor", - "version": "3.59.0" + "summary": "Select a child scanner dynamically for source data based on factors such as the filename.", + "type": "scanner" }, { - "categories": [ - "Integration" - ], + "categories": null, + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\n== Metadata\n\nThis scanner adds the following metadata to each message:\n\n- `tar_name`\n\n", + "name": "tar", + "plugin": true, + "status": "stable", + "summary": "Consume a tar archive file by file.", + "type": "scanner" + }, + { + "categories": null, + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "description": "\n[CAUTION]\n====\nSome sources of data may not have a logical end, therefore caution should be made to exclusively use this scanner when the end of an input stream is clearly defined (and well within memory).\n====\n", + "name": "to_the_end", + "plugin": true, + "status": "stable", + "summary": "Read the input stream all the way until the end and deliver it as a single message.", + "type": "scanner" + } + ], + "tracers": [ + { + "categories": null, "config": { "children": [ { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], + "description": "The google project with Cloud Trace API enabled. If this is omitted then the Google Cloud SDK will attempt auto-detect it from the environment.", "kind": "scalar", - "name": "dsn", + "name": "project", "type": "string" }, { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "default": 1, + "description": "Sets the ratio of traces to sample. Tuning the sampling ratio is recommended for high-volume production workloads.", "examples": [ - "INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);", - "SELECT * FROM footable WHERE user_id = $1;" + 1 ], - "is_optional": true, "kind": "scalar", - "name": "query", - "type": "string" + "name": "sampling_ratio", + "type": "float" }, { - "default": false, - "description": "Whether to enable xref:configuration:interpolation.adoc#bloblang-queries[interpolation functions] in the query. Great care should be made to ensure your queries are defended against injection attacks.", + "default": {}, + "description": "A map of tags to add to tracing spans.", "is_advanced": true, - "kind": "scalar", - "name": "unsafe_dynamic_query", - "type": "bool" + "kind": "map", + "name": "tags", + "type": "string" }, { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], + "description": "The period of time between each flush of tracing spans.", "is_optional": true, "kind": "scalar", - "name": "args_mapping", + "name": "flush_interval", "type": "string" - }, + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "gcp_cloudtrace", + "plugin": true, + "status": "stable", + "summary": "Send tracing events to a https://cloud.google.com/trace[Google Cloud Trace^].", + "type": "tracer", + "version": "4.2.0" + }, + { + "categories": null, + "config": { + "default": {}, + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "none", + "plugin": true, + "status": "stable", + "summary": "Do not send tracing events anywhere.", + "type": "tracer" + }, + { + "categories": null, + "config": { + "children": [ { - "description": "Whether the query result should be discarded. When set to `true` the message contents will remain unchanged, which is useful in cases where you are executing inserts, updates, etc. By default this is true for the last query, and previous queries don't change the results. If set to true for any query but the last one, the subsequent `args_mappings` input is overwritten.", - "is_optional": true, + "default": "benthos", + "description": "The name of the service in traces.", "kind": "scalar", - "name": "exec_only", - "type": "bool" + "name": "service", + "type": "string" }, { "children": [ { - "description": "The query to execute. The style of placeholder to use depends on the driver, some drivers require question marks (`?`) whereas others expect incrementing dollar signs (`$1`, `$2`, and so on) or colons (`:1`, `:2` and so on). The style to use is outlined in this table:\n\n| Driver | Placeholder Style |\n|---|---|\n| `clickhouse` | Dollar sign |\n| `mysql` | Question mark |\n| `postgres` | Dollar sign |\n| `mssql` | Question mark |\n| `sqlite` | Question mark |\n| `oracle` | Colon |\n| `snowflake` | Question mark |\n| `trino` | Question mark |\n| `gocosmos` | Colon |\n", + "description": "The endpoint of a collector to send events to.", + "examples": [ + "localhost:4318" + ], + "is_optional": true, "kind": "scalar", - "name": "query", + "name": "address", "type": "string" }, { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `query`.", + "default": "localhost:4318", + "description": "The URL of a collector to send events to.", + "is_deprecated": true, + "kind": "scalar", + "name": "url", + "type": "string" + }, + { + "default": false, + "description": "Connect to the collector over HTTPS", + "kind": "scalar", + "name": "secure", + "type": "bool" + } + ], + "description": "A list of http collectors.", + "kind": "array", + "name": "http", + "type": "object" + }, + { + "children": [ + { + "description": "The endpoint of a collector to send events to.", "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" + "localhost:4317" ], "is_optional": true, "kind": "scalar", - "name": "args_mapping", + "name": "address", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "description": "Whether the query result should be discarded. When set to `true` the message contents will remain unchanged, which is useful in cases where you are executing inserts, updates, etc. By default this is true for the last query, and previous queries don't change the results. If set to true for any query but the last one, the subsequent `args_mappings` input is overwritten.", - "is_optional": true, + "default": "localhost:4317", + "description": "The URL of a collector to send events to.", + "is_deprecated": true, "kind": "scalar", - "name": "exec_only", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": false, + "description": "Connect to the collector with client transport security", + "kind": "scalar", + "name": "secure", "type": "bool" } ], - "description": "A list of statements to run in addition to `query`. When specifying multiple statements, they are all executed within a transaction. The output of the processor is always the last query that runs, unless `exec_only` is used.", - "is_optional": true, + "description": "A list of grpc collectors.", "kind": "array", - "name": "queries", + "name": "grpc", "type": "object" }, { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", + "default": {}, + "description": "A map of tags to add to all exported spans and metrics.", + "is_advanced": true, + "kind": "map", + "name": "tags", + "type": "string" + }, + { + "children": [ + { + "default": false, + "description": "Whether to enable sampling.", + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "description": "Sets the ratio of traces to sample.", + "examples": [ + 0.85, + 0.5 + ], + "is_optional": true, + "kind": "scalar", + "name": "ratio", + "type": "float" + } + ], + "description": "Settings for trace sampling. Sampling is recommended for high-volume production workloads.", + "kind": "scalar", + "name": "sampling", + "type": "object", + "version": "4.25.0" + } + ], + "kind": "scalar", + "name": "", + "type": "object" + }, + "name": "open_telemetry_collector", + "plugin": true, + "status": "stable", + "summary": "Send tracing events to an https://opentelemetry.io/docs/collector/[Open Telemetry collector^].", + "type": "tracer" + }, + { + "categories": null, + "config": { + "children": [ + { + "description": "A list of broker addresses to connect to in order to establish connections. If an item of the list contains commas it will be expanded into multiple addresses.", "examples": [ [ - "./init/*.sql" + "localhost:9092" ], [ - "./foo.sql", - "./bar.sql" + "foo:9092", + "bar:9092" + ], + [ + "foo:9092,bar:9092" ] ], - "is_advanced": true, - "is_optional": true, "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" + "name": "seed_brokers", + "type": "string" }, { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" - ], + "default": "redpanda-connect", + "description": "An identifier for the client connection.", "is_advanced": true, - "is_optional": true, "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" + "name": "client_id", + "type": "string" }, { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, - { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_life_time", - "type": "string" - }, - { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle", - "type": "int" - }, - { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_open", - "type": "int" - } - ], - "kind": "scalar", - "linter": "root = match {\n !this.exists(\"queries\") && !this.exists(\"query\") => [ \"either `query` or `queries` is required\" ],\n }", - "name": "", - "type": "object" - }, - "description": "\nIf the query fails to execute then the message will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", - "examples": [ - { - "config": "\npipeline:\n processors:\n - sql_raw:\n driver: mysql\n dsn: foouser:foopassword@tcp(localhost:3306)/foodb\n query: \"INSERT INTO footable (foo, bar, baz) VALUES (?, ?, ?);\"\n args_mapping: '[ document.foo, document.bar, meta(\"kafka_topic\") ]'\n exec_only: true\n", - "summary": "The following example inserts rows into the table footable with the columns foo, bar and baz populated with values extracted from messages.", - "title": "Table Insert (MySQL)" - }, - { - "config": "\npipeline:\n processors:\n - branch:\n processors:\n - sql_raw:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n query: \"SELECT * FROM footable WHERE user_id = $1;\"\n args_mapping: '[ this.user.id ]'\n result_map: 'root.foo_rows = this'\n", - "summary": "Here we query a database for columns of footable that share a `user_id` with the message field `user.id`. A xref:components:processors/branch.adoc[`branch` processor] is used in order to insert the resulting array into the original message at the path `foo_rows`.", - "title": "Table Query (PostgreSQL)" - }, - { - "config": "\npipeline:\n processors:\n - mapping: |\n root = this\n # Prevent SQL injection when using unsafe_dynamic_query\n meta table_name = \"\\\"\" + metadata(\"table_name\").replace_all(\"\\\"\", \"\\\"\\\"\") + \"\\\"\"\n - sql_raw:\n driver: postgres\n dsn: postgres://localhost/postgres\n unsafe_dynamic_query: true\n queries:\n - query: |\n CREATE TABLE IF NOT EXISTS ${!metadata(\"table_name\")} (id varchar primary key, document jsonb);\n - query: |\n INSERT INTO ${!metadata(\"table_name\")} (id, document) VALUES ($1, $2)\n ON CONFLICT (id) DO UPDATE SET document = EXCLUDED.document;\n args_mapping: |\n root = [ this.id, this.document.string() ]\n", - "summary": "Here we query a database for columns of footable that share a `user_id` with the message field `user.id`. A xref:components:processors/branch.adoc[`branch` processor] is used in order to insert the resulting array into the original message at the path `foo_rows`.", - "title": "Dynamically Creating Tables (PostgreSQL)" - } - ], - "name": "sql_raw", - "plugin": true, - "status": "stable", - "summary": "Runs an arbitrary SQL query against a database and (optionally) returns the result as an array of objects, one for each row returned.", - "type": "processor", - "version": "3.65.0" - }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ - { - "description": "A database <> to use.", - "kind": "scalar", - "linter": "\nlet options = {\n \"mysql\": true,\n \"postgres\": true,\n \"clickhouse\": true,\n \"mssql\": true,\n \"sqlite\": true,\n \"oracle\": true,\n \"snowflake\": true,\n \"trino\": true,\n \"gocosmos\": true,\n \"spanner\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "driver", - "options": [ - "mysql", - "postgres", - "clickhouse", - "mssql", - "sqlite", - "oracle", - "snowflake", - "trino", - "gocosmos", - "spanner" - ], - "type": "string" - }, - { - "description": "A Data Source Name to identify the target database.\n\n==== Drivers\n\n:driver-support: mysql=certified, postgres=certified, clickhouse=community, mssql=community, sqlite=certified, oracle=certified, snowflake=community, trino=community, gocosmos=community, spanner=community\n\nThe following is a list of supported drivers, their placeholder style, and their respective DSN formats:\n\n|===\n| Driver | Data Source Name Format\n\n| `clickhouse` \n| https://github.com/ClickHouse/clickhouse-go#dsn[`clickhouse://[username[:password\\]@\\][netloc\\][:port\\]/dbname[?param1=value1&...¶mN=valueN\\]`^] \n\n| `mysql` \n| `[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]` \n\n| `postgres` \n| `postgres://[user[:password]@][netloc][:port][/dbname][?param1=value1&...]` \n\n| `mssql` \n| `sqlserver://[user[:password]@][netloc][:port][?database=dbname¶m1=value1&...]` \n\n| `sqlite` \n| `file:/path/to/filename.db[?param&=value1&...]` \n\n| `oracle` \n| `oracle://[username[:password]@][netloc][:port]/service_name?server=server2&server=server3` \n\n| `snowflake` \n| `username[:password]@account_identifier/dbname/schemaname[?param1=value&...¶mN=valueN]` \n\n| `trino` \n| https://github.com/trinodb/trino-go-client#dsn-data-source-name[`http[s\\]://user[:pass\\]@host[:port\\][?parameters\\]`^] \n\n| `gocosmos` \n| https://pkg.go.dev/github.com/microsoft/gocosmos#readme-example-usage[`AccountEndpoint=;AccountKey=[;TimeoutMs=\\][;Version=\\][;DefaultDb/Db=\\][;AutoId=\\][;InsecureSkipVerify=\\]`^] \n\n| `spanner` \n| projects/[PROJECT]/instances/[INSTANCE]/databases/[DATABASE] \n|===\n\nPlease note that the `postgres` driver enforces SSL by default, you can override this with the parameter `sslmode=disable` if required.\n\nThe `snowflake` driver supports multiple DSN formats. Please consult https://pkg.go.dev/github.com/snowflakedb/gosnowflake#hdr-Connection_String[the docs^] for more details. For https://docs.snowflake.com/en/user-guide/key-pair-auth.html#configuring-key-pair-authentication[key pair authentication^], the DSN has the following format: `@//?warehouse=&role=&authenticator=snowflake_jwt&privateKey=`, where the value for the `privateKey` parameter can be constructed from an unencrypted RSA private key file `rsa_key.p8` using `openssl enc -d -base64 -in rsa_key.p8 | basenc --base64url -w0` (you can use `gbasenc` insted of `basenc` on OSX if you install `coreutils` via Homebrew). If you have a password-encrypted private key, you can decrypt it using `openssl pkcs8 -in rsa_key_encrypted.p8 -out rsa_key.p8`. Also, make sure fields such as the username are URL-encoded.\n\nThe https://pkg.go.dev/github.com/microsoft/gocosmos[`gocosmos`^] driver is still experimental, but it has support for https://learn.microsoft.com/en-us/azure/cosmos-db/hierarchical-partition-keys[hierarchical partition keys^] as well as https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/how-to-query-container#cross-partition-query[cross-partition queries^]. Please refer to the https://github.com/microsoft/gocosmos/blob/main/SQL.md[SQL notes^] for details.", - "examples": [ - "clickhouse://username:password@host1:9000,host2:9000/database?dial_timeout=200ms&max_execution_time=60", - "foouser:foopassword@tcp(localhost:3306)/foodb", - "postgres://foouser:foopass@localhost:5432/foodb?sslmode=disable", - "oracle://foouser:foopass@localhost:1521/service_name" - ], - "kind": "scalar", - "name": "dsn", - "type": "string" - }, - { - "description": "The table to query.", - "examples": [ - "foo" - ], - "kind": "scalar", - "name": "table", - "type": "string" - }, - { - "description": "A list of columns to query.", - "examples": [ - [ - "*" - ], - [ - "foo", - "bar", - "baz" - ] - ], - "kind": "array", - "name": "columns", - "type": "string" - }, - { - "description": "An optional where clause to add. Placeholder arguments are populated with the `args_mapping` field. Placeholders should always be question marks, and will automatically be converted to dollar syntax when the postgres or clickhouse drivers are used.", - "examples": [ - "meow = ? and woof = ?", - "user_id = ?" - ], - "is_optional": true, - "kind": "scalar", - "name": "where", - "type": "string" - }, - { - "bloblang": true, - "description": "An optional xref:guides:bloblang/about.adoc[Bloblang mapping] which should evaluate to an array of values matching in size to the number of placeholder arguments in the field `where`.", - "examples": [ - "root = [ this.cat.meow, this.doc.woofs[0] ]", - "root = [ meta(\"user.id\") ]" - ], - "is_optional": true, - "kind": "scalar", - "name": "args_mapping", - "type": "string" - }, - { - "description": "An optional prefix to prepend to the query (before SELECT).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "prefix", - "type": "string" - }, - { - "description": "An optional suffix to append to the select query.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "suffix", - "type": "string" - }, - { - "description": "\nAn optional list of file paths containing SQL statements to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Glob patterns are supported, including super globs (double star).\n\nCare should be taken to ensure that the statements are idempotent, and therefore would not cause issues when run multiple times after service restarts. If both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf a statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - [ - "./init/*.sql" - ], - [ - "./foo.sql", - "./bar.sql" - ] - ], - "is_advanced": true, - "is_optional": true, - "kind": "array", - "name": "init_files", - "type": "string", - "version": "4.10.0" - }, - { - "description": "\nAn optional SQL statement to execute immediately upon the first connection to the target database. This is a useful way to initialise tables before processing data. Care should be taken to ensure that the statement is idempotent, and therefore would not cause issues when run multiple times after service restarts.\n\nIf both `init_statement` and `init_files` are specified the `init_statement` is executed _after_ the `init_files`.\n\nIf the statement fails for any reason a warning log will be emitted but the operation of this component will not be stopped.\n", - "examples": [ - "\nCREATE TABLE IF NOT EXISTS some_table (\n foo varchar(50) not null,\n bar integer,\n baz varchar(50),\n primary key (foo)\n) WITHOUT ROWID;\n" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "init_statement", - "type": "string", - "version": "4.10.0" - }, - { - "description": "An optional maximum amount of time a connection may be idle. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections idle time.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle_time", - "type": "string" - }, - { - "description": "An optional maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If `value <= 0`, connections are not closed due to a connections age.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_life_time", - "type": "string" - }, - { - "default": 2, - "description": "An optional maximum number of connections in the idle connection pool. If conn_max_open is greater than 0 but less than the new conn_max_idle, then the new conn_max_idle will be reduced to match the conn_max_open limit. If `value <= 0`, no idle connections are retained. The default max idle connections is currently 2. This may change in a future release.", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_idle", - "type": "int" - }, - { - "description": "An optional maximum number of open connections to the database. If conn_max_idle is greater than 0 and the new conn_max_open is less than conn_max_idle, then conn_max_idle will be reduced to match the new conn_max_open limit. If `value <= 0`, then there is no limit on the number of open connections. The default is 0 (unlimited).", - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "conn_max_open", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nIf the query fails to execute then the message will remain unchanged and the error can be caught using xref:configuration:error_handling.adoc[error handling methods].", - "examples": [ - { - "config": "\npipeline:\n processors:\n - branch:\n processors:\n - sql_select:\n driver: postgres\n dsn: postgres://foouser:foopass@localhost:5432/testdb?sslmode=disable\n table: footable\n columns: [ '*' ]\n where: user_id = ?\n args_mapping: '[ this.user.id ]'\n result_map: 'root.foo_rows = this'\n", - "summary": "\nHere we query a database for columns of footable that share a `user_id`\nwith the message `user.id`. A xref:components:processors/branch.adoc[`branch` processor]\nis used in order to insert the resulting array into the original message at the\npath `foo_rows`:", - "title": "Table Query (PostgreSQL)" - } - ], - "name": "sql_select", - "plugin": true, - "status": "stable", - "summary": "Runs an SQL select query against a database and returns the result as an array of objects, one for each row returned, containing a key for each column queried and its value.", - "type": "processor", - "version": "3.59.0" - }, - { - "categories": [ - "Integration" - ], - "config": { - "children": [ - { - "description": "The command to execute as a subprocess.", - "examples": [ - "cat", - "sed", - "awk" - ], - "kind": "scalar", - "name": "name", - "type": "string" - }, - { - "default": [], - "description": "A list of arguments to provide the command.", - "kind": "array", - "name": "args", - "type": "string" - }, - { - "default": 65536, - "description": "The maximum expected response size.", - "is_advanced": true, - "kind": "scalar", - "name": "max_buffer", - "type": "int" - }, - { - "default": "lines", - "description": "Determines how messages written to the subprocess are encoded, which allows them to be logically separated.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"lines\": true,\n \"length_prefixed_uint32_be\": true,\n \"netstring\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "codec_send", - "options": [ - "lines", - "length_prefixed_uint32_be", - "netstring" - ], - "type": "string", - "version": "3.37.0" - }, - { - "default": "lines", - "description": "Determines how messages read from the subprocess are decoded, which allows them to be logically separated.", - "is_advanced": true, - "kind": "scalar", - "linter": "\nlet options = {\n \"lines\": true,\n \"length_prefixed_uint32_be\": true,\n \"netstring\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "codec_recv", - "options": [ - "lines", - "length_prefixed_uint32_be", - "netstring" - ], - "type": "string", - "version": "3.37.0" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n[NOTE]\n====\nThis processor keeps the subprocess alive and requires very specific behavior from the command executed. If you wish to simply execute a command for each message take a look at the xref:components:processors/command.adoc[`command` processor] instead.\n====\n\nThe subprocess must then either return a line over stdout or stderr. If a response is returned over stdout then its contents will replace the message. If a response is instead returned from stderr it will be logged and the message will continue unchanged and will be xref:configuration:error_handling.adoc[marked as failed].\n\nRather than separating data by a newline it's possible to specify alternative <> and <> values, which allow binary messages to be encoded for logical separation.\n\nThe execution environment of the subprocess is the same as the Redpanda Connect instance, including environment variables and the current working directory.\n\nThe field `max_buffer` defines the maximum response size able to be read from the subprocess. This value should be set significantly above the real expected maximum response size.\n\n== Subprocess requirements\n\nIt is required that subprocesses flush their stdout and stderr pipes for each line. Redpanda Connect will attempt to keep the process alive for as long as the pipeline is running. If the process exits early it will be restarted.\n\n== Messages containing line breaks\n\nIf a message contains line breaks each line of the message is piped to the subprocess and flushed, and a response is expected from the subprocess before another line is fed in.", - "name": "subprocess", - "plugin": true, - "status": "stable", - "summary": "Executes a command as a subprocess and, for each message, will pipe its contents to the stdin stream of the process followed by a newline.", - "type": "processor" - }, - { - "categories": [ - "Composition" - ], - "config": { - "children": [ - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether a message should have the processors of this case executed on it. If left empty the case always passes. If the check mapping throws an error the message will be flagged xref:configuration:error_handling.adoc[as having failed] and will not be tested against any other cases.", - "examples": [ - "this.type == \"foo\"", - "this.contents.urls.contains(\"https://benthos.dev/\")" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "default": [], - "description": "A list of xref:components:processors/about.adoc[processors] to execute on a message.", - "kind": "array", - "name": "processors", - "type": "processor" - }, - { - "default": false, - "description": "Indicates whether, if this case passes for a message, the next case should also be executed.", - "is_advanced": true, - "kind": "scalar", - "name": "fallthrough", - "type": "bool" - } - ], - "kind": "array", - "name": "", - "type": "object" - }, - "description": "For each switch case a xref:guides:bloblang/about.adoc[Bloblang query] is checked and, if the result is true (or the check is empty) the child processors are executed on the message.", - "examples": [ - { - "config": "\npipeline:\n processors:\n - switch:\n - check: this.user.name.first != \"George\"\n processors:\n - metric:\n type: counter\n name: MessagesWeCareAbout\n\n - processors:\n - metric:\n type: gauge\n name: GeorgesAnger\n value: ${! json(\"user.anger\") }\n - mapping: root = deleted()\n", - "summary": "\nWe have a system where we're counting a metric for all messages that pass through our system. However, occasionally we get messages from George that we don't care about.\n\nFor George's messages we want to instead emit a metric that gauges how angry he is about being ignored and then we drop it.", - "title": "Ignore George" - } - ], - "footnotes": "\n== Batching\n\nWhen a switch processor executes on a xref:configuration:batching.adoc[batch of messages] they are checked individually and can be matched independently against cases. During processing the messages matched against a case are processed as a batch, although the ordering of messages during case processing cannot be guaranteed to match the order as received.\n\nAt the end of switch processing the resulting batch will follow the same ordering as the batch was received. If any child processors have split or otherwise grouped messages this grouping will be lost as the result of a switch is always a single batch. In order to perform conditional grouping and/or splitting use the xref:components:processors/group_by.adoc[`group_by` processor].", - "name": "switch", - "plugin": true, - "status": "stable", - "summary": "Conditionally processes messages based on their contents.", - "type": "processor" - }, - { - "categories": [ - "Utility" - ], - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nFor most inputs this mechanism is ignored entirely, in which case the sync response is dropped without penalty. It is therefore safe to use this processor even when combining input types that might not have support for sync responses. An example of an input able to utilize this is the `http_server`.\n\nFor more information please read xref:guides:sync_responses.adoc[synchronous responses].", - "name": "sync_response", - "plugin": true, - "status": "stable", - "summary": "Adds the payload in its current state as a synchronous response to the input source, where it is dealt with according to that specific input type.", - "type": "processor" - }, - { - "categories": [ - "AI" - ], - "config": { - "children": [ - { - "annotated_options": [ - [ - "markdown", - "Split text by markdown headers." - ], - [ - "recursive_character", - "Split text recursively by characters (defined in `separators`)." - ], - [ - "token", - "Split text by tokens." - ] - ], - "kind": "scalar", - "linter": "\nlet options = {\n \"markdown\": true,\n \"recursive_character\": true,\n \"token\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "strategy", - "type": "string" - }, - { - "default": 512, - "description": "The maximum size of each chunk.", - "kind": "scalar", - "name": "chunk_size", - "type": "int" - }, - { - "default": 100, - "description": "The number of characters to overlap between chunks.", - "kind": "scalar", - "name": "chunk_overlap", - "type": "int" - }, - { - "default": [ - "\n\n", - "\n", - " ", - "" - ], - "description": "A list of strings that should be considered as separators between chunks.", - "kind": "array", - "name": "separators", - "type": "string" - }, - { - "annotated_options": [ - [ - "graphemes", - "Use unicode graphemes to determine the length of a string." - ], - [ - "runes", - "Use the number of codepoints to determine the length of a string." - ], - [ - "token", - "Use the number of tokens (using the `token_encoding` tokenizer) to determine the length of a string." - ], - [ - "utf8", - "Determine the length of text using the number of utf8 bytes." - ] - ], - "default": "runes", - "description": "The method for measuring the length of a string.", - "kind": "scalar", - "linter": "\nlet options = {\n \"graphemes\": true,\n \"runes\": true,\n \"token\": true,\n \"utf8\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "length_measure", - "type": "string" - }, - { - "description": "The encoding to use for tokenization.", - "examples": [ - "cl100k_base", - "r50k_base" - ], - "is_advanced": true, - "is_optional": true, - "kind": "scalar", - "name": "token_encoding", - "type": "string" - }, - { - "default": [], - "description": "A list of special tokens that are allowed in the output.", - "is_advanced": true, - "kind": "array", - "name": "allowed_special", - "type": "string" - }, - { - "default": [ - "all" - ], - "description": "A list of special tokens that are disallowed in the output.", - "is_advanced": true, - "kind": "array", - "name": "disallowed_special", - "type": "string" - }, - { - "default": false, - "description": "Whether to include code blocks in the output.", - "kind": "scalar", - "name": "include_code_blocks", - "type": "bool" - }, - { - "default": false, - "description": "Whether to keep reference links in the output.", - "kind": "scalar", - "name": "keep_reference_links", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "A processor allowing splitting text into chunks based on several different strategies.", - "name": "text_chunker", - "plugin": true, - "status": "experimental", - "summary": "A processor that allows chunking and splitting text based on some strategy. Usually used for creating vector embeddings of large documents.", - "type": "processor" - }, - { - "categories": [ - "Composition" - ], - "config": { - "default": [], - "kind": "array", - "name": "", - "type": "processor" - }, - "description": "\nThis processor behaves similarly to the xref:components:processors/for_each.adoc[`for_each`] processor, where a list of child processors are applied to individual messages of a batch. However, if a message has failed any prior processor (before or during the try block) then that message will skip all following processors.\n\nFor example, with the following config:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - try:\n - resource: bar\n - resource: baz\n - resource: buz\n```\n\nIf the processor `bar` fails for a particular message, that message will skip the processors `baz` and `buz`. Similarly, if `bar` succeeds but `baz` does not then `buz` will be skipped. If the processor `foo` fails for a message then none of `bar`, `baz` or `buz` are executed on that message.\n\nThis processor is useful for when child processors depend on the successful output of previous processors. This processor can be followed with a xref:components:processors/catch.adoc[catch] processor for defining child processors to be applied only to failed messages.\n\nMore information about error handing can be found in xref:configuration:error_handling.adoc[].\n\n== Nest within a catch block\n\nIn some cases it might be useful to nest a try block within a catch block, since the xref:components:processors/catch.adoc[`catch` processor] only clears errors _after_ executing its child processors this means a nested try processor will not execute unless the errors are explicitly cleared beforehand.\n\nThis can be done by inserting an empty catch block before the try block like as follows:\n\n```yaml\npipeline:\n processors:\n - resource: foo\n - catch:\n - log:\n level: ERROR\n message: \"Foo failed due to: ${! error() }\"\n - catch: [] # Clear prior error\n - try:\n - resource: bar\n - resource: baz\n```", - "name": "try", - "plugin": true, - "status": "stable", - "summary": "Executes a list of child processors on messages only if no prior processors have failed (or the errors have been cleared).", - "type": "processor" - }, - { - "categories": [ - "Parsing", - "Utility" - ], - "config": { - "children": [ - { - "annotated_options": [ - [ - "binary", - "Extract messages from a https://github.com/redpanda-data/benthos/blob/main/internal/message/message.go#L96[binary blob format^]." - ], - [ - "csv", - "Attempt to parse the message as a csv file (header required) and for each row in the file expands its contents into a json object in a new message." - ], - [ - "csv:x", - "Attempt to parse the message as a csv file (header required) and for each row in the file expands its contents into a json object in a new message using a custom delimiter. The custom delimiter must be a single character, e.g. the format \"csv:\\t\" would consume a tab delimited file." - ], - [ - "json_array", - "Attempt to parse a message as a JSON array, and extract each element into its own message." - ], - [ - "json_documents", - "Attempt to parse a message as a stream of concatenated JSON documents. Each parsed document is expanded into a new message." - ], - [ - "json_map", - "Attempt to parse the message as a JSON map and for each element of the map expands its contents into a new message. A metadata field is added to each message called `archive_key` with the relevant key from the top-level map." - ], - [ - "lines", - "Extract the lines of a message each into their own message." - ], - [ - "tar", - "Extract messages from a unix standard tape archive." - ], - [ - "zip", - "Extract messages from a zip file." - ] - ], - "description": "The unarchiving format to apply.", - "kind": "scalar", - "name": "format", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nWhen a message is unarchived the new messages replace the original message in the batch. Messages that are selected but fail to unarchive (invalid format) will remain unchanged in the message batch but will be flagged as having failed, allowing you to xref:configuration:error_handling.adoc[error handle them].\n\n== Metadata\n\nThe metadata found on the messages handled by this processor will be copied into the resulting messages. For the unarchive formats that contain file information (tar, zip), a metadata field is also added to each message called `archive_filename` with the extracted filename.\n", - "name": "unarchive", - "plugin": true, - "status": "stable", - "summary": "Unarchives messages according to the selected archive format into multiple messages within a xref:configuration:batching.adoc[batch].", - "type": "processor" - }, - { - "categories": [ - "Utility" - ], - "config": { - "children": [ - { - "description": "The path of the target WASM module to execute.", - "kind": "scalar", - "name": "module_path", - "type": "string" - }, - { - "default": "process", - "description": "The name of the function exported by the target WASM module to run for each message.", - "kind": "scalar", - "name": "function", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThis processor uses https://github.com/tetratelabs/wazero[Wazero^] to execute a WASM module (with support for WASI), calling a specific function for each message being processed. From within the WASM module it is possible to query and mutate the message being processed via a suite of functions exported to the module.\n\nThis ecosystem is delicate as WASM doesn't have a single clearly defined way to pass strings back and forth between the host and the module. In order to remedy this we're gradually working on introducing libraries and examples for multiple languages which can be found in https://github.com/redpanda-data/benthos/tree/main/public/wasm/README.md[the codebase^].\n\nThese examples, as well as the processor itself, is a work in progress.\n\n== Parallelism\n\nIt's not currently possible to execute a single WASM runtime across parallel threads with this processor. Therefore, in order to support parallel processing this processor implements pooling of module runtimes. Ideally your WASM module shouldn't depend on any global state, but if it does then you need to ensure the processor xref:configuration:processing_pipelines.adoc[is only run on a single thread].\n", - "name": "wasm", - "plugin": true, - "status": "experimental", - "summary": "Executes a function exported by a WASM module for each message.", - "type": "processor", - "version": "4.11.0" - }, - { - "categories": [ - "Composition" - ], - "config": { - "children": [ - { - "default": false, - "description": "Whether to always run the child processors at least one time.", - "kind": "scalar", - "name": "at_least_once", - "type": "bool" - }, - { - "default": 0, - "description": "An optional maximum number of loops to execute. Helps protect against accidentally creating infinite loops.", - "is_advanced": true, - "kind": "scalar", - "name": "max_loops", - "type": "int" - }, - { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang query] that should return a boolean value indicating whether the while loop should execute again.", - "examples": [ - "errored()", - "this.urls.unprocessed.length() > 0" - ], - "kind": "scalar", - "name": "check", - "type": "string" - }, - { - "description": "A list of child processors to execute on each loop.", - "kind": "array", - "name": "processors", - "type": "processor" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\nThe field `at_least_once`, if true, ensures that the child processors are always executed at least one time (like a do .. while loop.)\n\nThe field `max_loops`, if greater than zero, caps the number of loops for a message batch to this value.\n\nIf following a loop execution the number of messages in a batch is reduced to zero the loop is exited regardless of the condition result. If following a loop execution there are more than 1 message batches the query is checked against the first batch only.\n\nThe conditions of this processor are applied across entire message batches. You can find out more about batching xref:configuration:batching.adoc[in this doc].", - "name": "while", - "plugin": true, - "status": "stable", - "summary": "A processor that checks a xref:guides:bloblang/about.adoc[Bloblang query] against each batch of messages and executes child processors on them for as long as the query resolves to true.", - "type": "processor" - }, - { - "categories": [ - "Composition" - ], - "config": { - "children": [ - { - "default": "meta.workflow", - "description": "A xref:configuration:field_paths.adoc[dot path] indicating where to store and reference <> about the workflow execution.", - "kind": "scalar", - "name": "meta_path", - "type": "string" - }, - { - "default": [], - "description": "An explicit declaration of branch ordered tiers, which describes the order in which parallel tiers of branches should be executed. Branches should be identified by the name as they are configured in the field `branches`. It's also possible to specify branch processors configured <>.", - "examples": [ - [ - [ - "foo", - "bar" + "children": [ + { + "default": false, + "description": "Whether custom TLS settings are enabled.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" ], - [ - "baz" - ] - ], - [ - [ - "foo" + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" ], - [ - "bar" + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], - [ - "baz" - ] - ] + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } ], - "kind": "2darray", - "name": "order", - "type": "string" - }, - { - "default": [], - "description": "An optional list of xref:components:processors/branch.adoc[`branch` processor] names that are configured as <>. These resources will be included in the workflow with any branches configured inline within the <> field. The order and parallelism in which branches are executed is automatically resolved based on the mappings of each branch. When using resources with an explicit order it is not necessary to list resources in this field.", + "description": "Custom TLS settings can be used to override system defaults.", "is_advanced": true, - "kind": "array", - "name": "branch_resources", - "type": "string", - "version": "3.38.0" + "kind": "scalar", + "name": "tls", + "type": "object" }, { "children": [ { - "bloblang": true, - "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] that describes how to create a request payload suitable for the child processors of this branch. If left empty then the branch will begin with an exact copy of the origin message (including metadata).", - "examples": [ - "root = {\n\t\"id\": this.doc.id,\n\t\"content\": this.doc.body.text\n}", - "root = if this.type == \"foo\" {\n\tthis.foo.request\n} else {\n\tdeleted()\n}" + "annotated_options": [ + [ + "AWS_MSK_IAM", + "AWS IAM based authentication as specified by the 'aws-msk-iam-auth' java library." + ], + [ + "OAUTHBEARER", + "OAuth Bearer based authentication." + ], + [ + "PLAIN", + "Plain text authentication." + ], + [ + "REDPANDA_CLOUD_SERVICE_ACCOUNT", + "Redpanda Cloud Service Account authentication when running in Redpanda Cloud." + ], + [ + "SCRAM-SHA-256", + "SCRAM based authentication as specified in RFC5802." + ], + [ + "SCRAM-SHA-512", + "SCRAM based authentication as specified in RFC5802." + ], + [ + "none", + "Disable sasl authentication" + ] ], + "description": "The SASL mechanism to use.", + "is_advanced": true, "kind": "scalar", - "name": "request_map", + "linter": "\nlet options = {\n \"aws_msk_iam\": true,\n \"oauthbearer\": true,\n \"plain\": true,\n \"redpanda_cloud_service_account\": true,\n \"scram-sha-256\": true,\n \"scram-sha-512\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "mechanism", "type": "string" }, { - "description": "A list of processors to apply to mapped requests. When processing message batches the resulting batch must match the size and ordering of the input batch, therefore filtering, grouping should not be performed within these processors.", - "kind": "array", - "name": "processors", - "type": "processor" + "default": "", + "description": "A username to provide for PLAIN or SCRAM-* authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" }, { - "bloblang": true, "default": "", - "description": "A xref:guides:bloblang/about.adoc[Bloblang mapping] that describes how the resulting messages from branched processing should be mapped back into the original payload. If left empty the origin message will remain unchanged (including metadata).", - "examples": [ - "meta foo_code = metadata(\"code\")\nroot.foo_result = this", - "meta = metadata()\nroot.bar.body = this.body\nroot.bar.id = this.user.id", - "root.raw_result = content().string()", - "root.enrichments.foo = if metadata(\"request_failed\") != null {\n throw(metadata(\"request_failed\"))\n} else {\n this\n}", - "# Retain only the updated metadata fields which were present in the origin message\nmeta = metadata().filter(v -> @.get(v.key) != null)" - ], + "description": "A password to provide for PLAIN or SCRAM-* authentication.", + "is_advanced": true, + "is_secret": true, "kind": "scalar", - "name": "result_map", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The token to use for a single session's OAUTHBEARER authentication.", + "is_advanced": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Key/value pairs to add to OAUTHBEARER authentication requests.", + "is_advanced": true, + "is_optional": true, + "kind": "map", + "name": "extensions", "type": "string" + }, + { + "children": [ + { + "description": "The AWS region to target.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "region", + "type": "string" + }, + { + "description": "Allows you to specify a custom endpoint for the AWS API.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "endpoint", + "type": "string" + }, + { + "children": [ + { + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "connect_timeout", + "type": "string" + }, + { + "children": [ + { + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", + "is_advanced": true, + "kind": "scalar", + "name": "idle", + "type": "string" + }, + { + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", + "is_advanced": true, + "kind": "scalar", + "name": "interval", + "type": "string" + }, + { + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", + "is_advanced": true, + "kind": "scalar", + "name": "count", + "type": "int" + } + ], + "description": "TCP keep-alive probe configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", + "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" + } + ], + "description": "TCP socket configuration.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "tcp", + "type": "object" + }, + { + "children": [ + { + "description": "A profile from `~/.aws/credentials` to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "profile", + "type": "string" + }, + { + "description": "The ID of credentials to use.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "id", + "type": "string" + }, + { + "description": "The secret for the credentials being used.", + "is_advanced": true, + "is_optional": true, + "is_secret": true, + "kind": "scalar", + "name": "secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "description": "The token for the credentials being used, required when using short term credentials.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "token", + "type": "string" + }, + { + "description": "Use the credentials of a host EC2 machine configured to assume https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_use_switch-role-ec2.html[an IAM role associated with the instance^].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "from_ec2_role", + "type": "bool", + "version": "4.2.0" + }, + { + "description": "A role ARN to assume.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role", + "type": "string" + }, + { + "description": "An external ID to provide when assuming a role.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "role_external_id", + "type": "string" + } + ], + "description": "Optional manual configuration of AWS credentials to use. More information can be found in xref:guides:cloud/aws.adoc[].", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "credentials", + "type": "object" + } + ], + "description": "Contains AWS specific fields for when the `mechanism` is set to `AWS_MSK_IAM`.", + "is_advanced": true, + "is_optional": true, + "kind": "scalar", + "name": "aws", + "type": "object" } ], - "default": {}, - "description": "An object of named xref:components:processors/branch.adoc[`branch` processors] that make up the workflow. The order and parallelism in which branches are executed can either be made explicit with the field `order`, or if omitted an attempt is made to automatically resolve an ordering based on the mappings of each branch.", - "kind": "map", - "name": "branches", - "type": "object" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Why use a workflow\n\n=== Performance\n\nMost of the time the best way to compose processors is also the simplest, just configure them in series. This is because processors are often CPU bound, low-latency, and you can gain vertical scaling by increasing the number of processor pipeline threads, allowing Redpanda Connect to process xref:configuration:processing_pipelines.adoc[multiple messages in parallel].\n\nHowever, some processors such as xref:components:processors/http.adoc[`http`], xref:components:processors/aws_lambda.adoc[`aws_lambda`] or xref:components:processors/cache.adoc[`cache`] interact with external services and therefore spend most of their time waiting for a response. These processors tend to be high-latency and low CPU activity, which causes messages to process slowly.\n\nWhen a processing pipeline contains multiple network processors that aren't dependent on each other we can benefit from performing these processors in parallel for each individual message, reducing the overall message processing latency.\n\n=== Simplifying processor topology\n\nA workflow is often expressed as a https://en.wikipedia.org/wiki/Directed_acyclic_graph[DAG^] of processing stages, where each stage can result in N possible next stages, until finally the flow ends at an exit node.\n\nFor example, if we had processing stages A, B, C and D, where stage A could result in either stage B or C being next, always followed by D, it might look something like this:\n\n```text\n /--> B --\\\nA --| |--> D\n \\--> C --/\n```\n\nThis flow would be easy to express in a standard Redpanda Connect config, we could simply use a xref:components:processors/switch.adoc[`switch` processor] to route to either B or C depending on a condition on the result of A. However, this method of flow control quickly becomes unfeasible as the DAG gets more complicated, imagine expressing this flow using switch processors:\n\n```text\n /--> B -------------|--> D\n / /\nA --| /--> E --|\n \\--> C --| \\\n \\----------|--> F\n```\n\nAnd imagine doing so knowing that the diagram is subject to change over time. Yikes! Instead, with a workflow we can either trust it to automatically resolve the DAG or express it manually as simply as `order: [ [ A ], [ B, C ], [ E ], [ D, F ] ]`, and the conditional logic for determining if a stage is executed is defined as part of the branch itself.", - "examples": [ - { - "config": "\npipeline:\n processors:\n - workflow:\n meta_path: meta.workflow\n branches:\n foo:\n request_map: 'root = \"\"'\n processors:\n - http:\n url: TODO\n result_map: 'root.foo = this'\n\n bar:\n request_map: 'root = this.body'\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.bar = this'\n\n baz:\n request_map: |\n root.fooid = this.foo.id\n root.barstuff = this.bar.content\n processors:\n - cache:\n resource: TODO\n operator: set\n key: ${! json(\"fooid\") }\n value: ${! json(\"barstuff\") }\n", - "summary": "\nWhen the field `order` is omitted a best attempt is made to determine a dependency tree between branches based on their request and result mappings. In the following example the branches foo and bar will be executed first in parallel, and afterwards the branch baz will be executed.", - "title": "Automatic Ordering" - }, - { - "config": "\npipeline:\n processors:\n - workflow:\n branches:\n A:\n request_map: |\n root = if this.document.type != \"foo\" {\n deleted()\n }\n processors:\n - http:\n url: TODO\n result_map: 'root.tmp.result = this'\n\n B:\n request_map: |\n root = if this.document.type == \"foo\" {\n deleted()\n }\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.tmp.result = this'\n\n C:\n request_map: |\n root = if this.tmp.result != null {\n deleted()\n }\n processors:\n - http:\n url: TODO_SOMEWHERE_ELSE\n result_map: 'root.tmp.result = this'\n", - "summary": "\nBranches of a workflow are skipped when the `request_map` assigns `deleted()` to the root. In this example the branch A is executed when the document type is \"foo\", and branch B otherwise. Branch C is executed afterwards and is skipped unless either A or B successfully provided a result at `tmp.result`.", - "title": "Conditional Branches" - }, - { - "config": "\npipeline:\n processors:\n - workflow:\n order: [ [ foo, bar ], [ baz ] ]\n branches:\n bar:\n request_map: 'root = this.body'\n processors:\n - aws_lambda:\n function: TODO\n result_map: 'root.bar = this'\n\nprocessor_resources:\n - label: foo\n branch:\n request_map: 'root = \"\"'\n processors:\n - http:\n url: TODO\n result_map: 'root.foo = this'\n\n - label: baz\n branch:\n request_map: |\n root.fooid = this.foo.id\n root.barstuff = this.bar.content\n processors:\n - cache:\n resource: TODO\n operator: set\n key: ${! json(\"fooid\") }\n value: ${! json(\"barstuff\") }\n", - "summary": "\nThe `order` field can be used in order to refer to <>, this can sometimes make your pipeline configuration cleaner, as well as allowing you to reuse branch configurations in order places. It's also possible to mix and match branches configured within the workflow and configured as resources.", - "title": "Resources" - } - ], - "footnotes": "\n== Structured metadata\n\nWhen the field `meta_path` is non-empty the workflow processor creates an object describing which workflows were successful, skipped or failed for each message and stores the object within the message at the end.\n\nThe object is of the following form:\n\n```json\n{\n\t\"succeeded\": [ \"foo\" ],\n\t\"skipped\": [ \"bar\" ],\n\t\"failed\": {\n\t\t\"baz\": \"the error message from the branch\"\n\t}\n}\n```\n\nIf a message already has a meta object at the given path when it is processed then the object is used in order to determine which branches have already been performed on the message (or skipped) and can therefore be skipped on this run.\n\nThis is a useful pattern when replaying messages that have failed some branches previously. For example, given the above example object the branches foo and bar would automatically be skipped, and baz would be reattempted.\n\nThe previous meta object will also be preserved in the field `.previous` when the new meta object is written, preserving a full record of all workflow executions.\n\nIf a field `.apply` exists in the meta object for a message and is an array then it will be used as an explicit list of stages to apply, all other stages will be skipped.\n\n== Resources\n\nIt's common to configure processors (and other components) xref:configuration:resources.adoc[as resources] in order to keep the pipeline configuration cleaner. With the workflow processor you can include branch processors configured as resources within your workflow either by specifying them by name in the field `order`, if Redpanda Connect doesn't find a branch within the workflow configuration of that name it'll refer to the resources.\n\nAlternatively, if you do not wish to have an explicit ordering, you can add resource names to the field `branch_resources` and they will be included in the workflow with automatic DAG resolution along with any branches configured in the `branches` field.\n\n=== Resource error conditions\n\nThere are two error conditions that could potentially occur when resources included in your workflow are mutated, and if you are planning to mutate resources in your workflow it is important that you understand them.\n\nThe first error case is that a resource in the workflow is removed and not replaced, when this happens the workflow will still be executed but the individual branch will fail. This should only happen if you explicitly delete a branch resource, as any mutation operation will create the new resource before removing the old one.\n\nThe second error case is when automatic DAG resolution is being used and a resource in the workflow is changed in a way that breaks the DAG (circular dependencies, etc). When this happens it is impossible to execute the workflow and therefore the processor will fail, which is possible to capture and handle using xref:configuration:error_handling.adoc[standard error handling patterns].\n\n== Error handling\n\nThe recommended approach to handle failures within a workflow is to query against the <> it provides, as it provides granular information about exactly which branches failed and which ones succeeded and therefore aren't necessary to perform again.\n\nFor example, if our meta object is stored at the path `meta.workflow` and we wanted to check whether a message has failed for any branch we can do that using a xref:guides:bloblang/about.adoc[Bloblang query] like `this.meta.workflow.failed.length() | 0 > 0`, or to check whether a specific branch failed we can use `this.exists(\"meta.workflow.failed.foo\")`.\n\nHowever, if structured metadata is disabled by setting the field `meta_path` to empty then the workflow processor instead adds a general error flag to messages when any executed branch fails. In this case it's possible to handle failures using xref:configuration:error_handling.adoc[standard error handling patterns].\n\n", - "name": "workflow", - "plugin": true, - "status": "stable", - "summary": "Executes a topology of xref:components:processors/branch.adoc[`branch` processors], performing them in parallel where possible.", - "type": "processor" - }, - { - "categories": [ - "Parsing" - ], - "config": { - "children": [ - { - "default": "", - "description": "An XML <> to apply to messages.", - "kind": "scalar", - "linter": "\nlet options = {\n \"to_json\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "operator", - "options": [ - "to_json" - ], - "type": "string" - }, - { - "default": false, - "description": "Whether to try to cast values that are numbers and booleans to the right type. Default: all values are strings.", - "kind": "scalar", - "name": "cast", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Operators\n\n=== `to_json`\n\nConverts an XML document into a JSON structure, where elements appear as keys of an object according to the following rules:\n\n- If an element contains attributes they are parsed by prefixing a hyphen, `-`, to the attribute label.\n- If the element is a simple element and has attributes, the element value is given the key `#text`.\n- XML comments, directives, and process instructions are ignored.\n- When elements are repeated the resulting JSON value is an array.\n\nFor example, given the following XML:\n\n```xml\n\n This is a title\n This is a description\n foo1\n foo2\n foo3\n\n```\n\nThe resulting JSON structure would look like this:\n\n```json\n{\n \"root\":{\n \"title\":\"This is a title\",\n \"description\":{\n \"#text\":\"This is a description\",\n \"-tone\":\"boring\"\n },\n \"elements\":[\n {\"#text\":\"foo1\",\"-id\":\"1\"},\n {\"#text\":\"foo2\",\"-id\":\"2\"},\n \"foo3\"\n ]\n }\n}\n```\n\nWith cast set to true, the resulting JSON structure would look like this:\n\n```json\n{\n \"root\":{\n \"title\":\"This is a title\",\n \"description\":{\n \"#text\":\"This is a description\",\n \"-tone\":\"boring\"\n },\n \"elements\":[\n {\"#text\":\"foo1\",\"-id\":1},\n {\"#text\":\"foo2\",\"-id\":2},\n \"foo3\"\n ]\n }\n}\n```", - "name": "xml", - "plugin": true, - "status": "beta", - "summary": "Parses messages as an XML document, performs a mutation on the data, and then overwrites the previous contents with the new value.", - "type": "processor" - } - ], - "rate-limits": [ - { - "categories": null, - "config": { - "children": [ - { - "default": 1000, - "description": "The maximum number of requests to allow for a given period of time.", - "kind": "scalar", - "name": "count", - "type": "int" - }, - { - "default": "1s", - "description": "The time window to limit requests by.", - "kind": "scalar", - "name": "interval", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "local", - "plugin": true, - "status": "stable", - "summary": "The local rate limit is a simple X every Y type rate limit that can be shared across any number of components within the pipeline but does not support distributed rate limits across multiple running instances of Benthos.", - "type": "rate_limit" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The URL of the target Redis server. Database is optional and is supplied as the URL path.", + "description": "Specify one or more methods of SASL authentication. SASL is tried in order; if the broker supports the first mechanism, all connections will use that mechanism. If the first mechanism fails, the client will pick the first supported mechanism. If the broker does not support any client mechanisms, connections will fail.", "examples": [ - "redis://:6379", - "redis://localhost:6379", - "redis://foousername:foopassword@redisplace:6379", - "redis://:foopassword@redisplace:6379", - "redis://localhost:6379/1", - "redis://localhost:6379/1,redis://localhost:6380/1" + [ + { + "mechanism": "SCRAM-SHA-512", + "password": "bar", + "username": "foo" + } + ] ], + "is_advanced": true, + "is_optional": true, + "kind": "array", + "name": "sasl", + "type": "object" + }, + { + "default": "1m", + "description": "The maximum age of metadata before it is refreshed. This interval also controls how frequently regex topic patterns are re-evaluated to discover new matching topics.", + "is_advanced": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "name": "metadata_max_age", "type": "string" }, { - "default": "simple", - "description": "Specifies a simple, cluster-aware, or failover-aware redis client.", + "default": "10s", + "description": "The request time overhead. Uses the given time as overhead while deadlining requests. Roughly equivalent to request.timeout.ms, but grants additional time to requests that have timeout fields.", "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"simple\": true,\n \"cluster\": true,\n \"failover\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "kind", - "options": [ - "simple", - "cluster", - "failover" - ], + "name": "request_timeout_overhead", "type": "string" }, { - "default": "", - "description": "Name of the redis master when `kind` is `failover`", - "examples": [ - "mymaster" - ], + "default": "20s", + "description": "The rough amount of time to allow connections to idle before they are closed.", "is_advanced": true, "kind": "scalar", - "name": "master", + "name": "conn_idle_timeout", "type": "string" }, { "children": [ { - "default": false, - "description": "Whether custom TLS settings are enabled.", - "is_advanced": true, - "kind": "scalar", - "name": "enabled", - "type": "bool" - }, - { - "default": false, - "description": "Whether to skip server side certificate verification.", - "is_advanced": true, - "kind": "scalar", - "name": "skip_cert_verify", - "type": "bool" - }, - { - "default": false, - "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", - "is_advanced": true, - "kind": "scalar", - "name": "enable_renegotiation", - "type": "bool", - "version": "3.45.0" - }, - { - "default": "", - "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" - ], - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "root_cas", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", - "examples": [ - "./root_cas.pem" - ], + "default": "0s", + "description": "Maximum amount of time a dial will wait for a connect to complete. Zero disables.", "is_advanced": true, "kind": "scalar", - "name": "root_cas_file", + "name": "connect_timeout", "type": "string" }, { "children": [ { - "default": "", - "description": "A plain text certificate to use.", - "is_advanced": true, - "kind": "scalar", - "name": "cert", - "type": "string" - }, - { - "default": "", - "description": "A plain text certificate key to use.", - "is_advanced": true, - "is_secret": true, - "kind": "scalar", - "name": "key", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" - }, - { - "default": "", - "description": "The path of a certificate to use.", + "default": "15s", + "description": "Duration the connection must be idle before sending the first keep-alive probe. Zero defaults to 15s. Negative values disable keep-alive probes.", "is_advanced": true, "kind": "scalar", - "name": "cert_file", + "name": "idle", "type": "string" }, { - "default": "", - "description": "The path of a certificate key to use.", + "default": "15s", + "description": "Duration between keep-alive probes. Zero defaults to 15s.", "is_advanced": true, "kind": "scalar", - "name": "key_file", + "name": "interval", "type": "string" }, { - "default": "", - "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", - "examples": [ - "foo", - "${KEY_PASSWORD}" - ], + "default": 9, + "description": "Maximum unanswered keep-alive probes before dropping the connection. Zero defaults to 9.", "is_advanced": true, - "is_secret": true, "kind": "scalar", - "name": "password", - "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", - "type": "string" + "name": "count", + "type": "int" } ], - "default": [], - "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", - "examples": [ - [ - { - "cert": "foo", - "key": "bar" - } - ], - [ - { - "cert_file": "./example.pem", - "key_file": "./example.key" - } - ] - ], + "description": "TCP keep-alive probe configuration.", "is_advanced": true, - "kind": "array", - "name": "client_certs", + "is_optional": true, + "kind": "scalar", + "name": "keep_alive", "type": "object" + }, + { + "default": "0s", + "description": "Maximum time to wait for acknowledgment of transmitted data before killing the connection. Linux-only (kernel 2.6.37+), ignored on other platforms. When enabled, keep_alive.idle must be greater than this value per RFC 5482. Zero disables.", + "is_advanced": true, + "kind": "scalar", + "name": "tcp_user_timeout", + "type": "string" } ], - "description": "Custom TLS settings can be used to override system defaults.\n\n**Troubleshooting**\n\nSome cloud hosted instances of Redis (such as Azure Cache) might need some hand holding in order to establish stable connections. Unfortunately, it is often the case that TLS issues will manifest as generic error messages such as \"i/o timeout\". If you're using TLS and are seeing connectivity problems consider setting `enable_renegotiation` to `true`, and ensuring that the server supports at least TLS version 1.2.", + "description": "TCP socket configuration.", "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "tls", + "name": "tcp", "type": "object" }, { - "default": 1000, - "description": "The maximum number of messages to allow for a given period of time.", - "kind": "scalar", - "linter": "root = if this <= 0 { [ \"count must be larger than zero\" ] }", - "name": "count", - "type": "int" - }, - { - "default": "1s", - "description": "The time window to limit requests by.", - "kind": "scalar", - "name": "interval", - "type": "string" - }, - { - "description": "The key to use for the rate limit.", - "kind": "scalar", - "name": "key", - "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "redis", - "plugin": true, - "status": "experimental", - "summary": "A rate limit implementation using Redis. It works by using a simple token bucket algorithm to limit the number of requests to a given count within a given time period. The rate limit is shared across all instances of Redpanda Connect that use the same Redis instance, which must all have a consistent count and interval.", - "type": "rate_limit", - "version": "4.12.0" - } - ], - "scanners": [ - { - "categories": null, - "config": { - "children": [ - { - "default": false, - "description": "Whether messages should be decoded into normal JSON (\"json that meets the expectations of regular internet json\") rather than https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^]. If `true` the schema returned from the subject should be decoded as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard json^] instead of as https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodec[avro json^]. There is a https://github.com/linkedin/goavro/blob/5ec5a5ee7ec82e16e6e2b438d610e1cab2588393/union.go#L224-L249[comment in goavro^], the https://github.com/linkedin/goavro[underlining library used for avro serialization^], that explains in more detail the difference between the standard json and avro json.", + "annotated_options": [ + [ + "least_backup", + "Chooses the least backed up partition (the partition with the fewest amount of buffered records). Partitions are selected per batch." + ], + [ + "manual", + "Manually select a partition for each message, requires the field `partition` to be specified." + ], + [ + "murmur2_hash", + "Kafka's default hash algorithm that uses a 32-bit murmur2 hash of the key to compute which partition the record will be on." + ], + [ + "round_robin", + "Round-robin's messages through all available partitions. This algorithm has lower throughput and causes higher CPU load on brokers, but can be useful if you want to ensure an even distribution of records to partitions." + ] + ], + "description": "Override the default murmur2 hashing partitioner.", "is_advanced": true, - "kind": "scalar", - "name": "raw_json", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Avro JSON format\n\nThis scanner yields documents formatted as https://avro.apache.org/docs/current/specification/_print/#json-encoding[Avro JSON^] when decoding with Avro schemas. In this format the value of a union is encoded in JSON as follows:\n\n- if its type is `null`, then it is encoded as a JSON `null`;\n- otherwise it is encoded as a JSON object with one name/value pair whose name is the type's name and whose value is the recursively encoded value. For Avro's named types (record, fixed or enum) the user-specified name is used, for other types the type name is used.\n\nFor example, the union schema `[\"null\",\"string\",\"Foo\"]`, where `Foo` is a record name, would encode:\n\n- `null` as `null`;\n- the string `\"a\"` as `{\"string\": \"a\"}`; and\n- a `Foo` instance as `{\"Foo\": {...}}`, where `{...}` indicates the JSON encoding of a `Foo` instance.\n\nHowever, it is possible to instead create documents in https://pkg.go.dev/github.com/linkedin/goavro/v2#NewCodecForStandardJSONFull[standard/raw JSON format^] by setting the field <> to `true`.\n\nThis scanner also emits the canonical Avro schema as `@avro_schema` metadata, along with the schema's fingerprint available via `@avro_schema_fingerprint`.\n", - "name": "avro", - "plugin": true, - "status": "stable", - "summary": "Consume a stream of Avro OCF datum.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The size of each chunk in bytes.", - "kind": "scalar", - "name": "size", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "chunker", - "plugin": true, - "status": "stable", - "summary": "Split an input stream into chunks of a given number of bytes.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "Use a provided custom delimiter instead of the default comma.", "is_optional": true, "kind": "scalar", - "name": "custom_delimiter", + "linter": "\nlet options = {\n \"least_backup\": true,\n \"manual\": true,\n \"murmur2_hash\": true,\n \"round_robin\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "partitioner", "type": "string" }, { "default": true, - "description": "Whether to reference the first row as a header row. If set to true the output structure for messages will be an object where field keys are determined by the header row. Otherwise, each message will consist of an array of values from the corresponding CSV row.", - "kind": "scalar", - "name": "parse_header_row", - "type": "bool" - }, - { - "default": false, - "description": "If set to `true`, a quote may appear in an unquoted field and a non-doubled quote may appear in a quoted field.", - "kind": "scalar", - "name": "lazy_quotes", - "type": "bool" - }, - { - "default": false, - "description": "If a row fails to parse due to any error emit an empty message marked with the error and then continue consuming subsequent rows when possible. This can sometimes be useful in situations where input data contains individual rows which are malformed. However, when a row encounters a parsing error it is impossible to guarantee that following rows are valid, as this indicates that the input data is unreliable and could potentially emit misaligned rows.", - "kind": "scalar", - "name": "continue_on_error", - "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis scanner adds the following metadata to each message:\n\n- `csv_row` The index of each row, beginning at 0.\n\n", - "name": "csv", - "plugin": true, - "status": "stable", - "summary": "Consume comma-separated values row by row, including support for custom delimiters.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "One of `gzip`, `pgzip`, `zlib`, `bzip2`, `flate`, `snappy`, `lz4`, `zstd`.", - "kind": "scalar", - "name": "algorithm", - "type": "string" - }, - { - "default": { - "to_the_end": {} - }, - "description": "The child scanner to feed the decompressed stream into.", - "kind": "scalar", - "name": "into", - "type": "scanner" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "decompress", - "plugin": true, - "status": "stable", - "summary": "Decompress the stream of bytes according to an algorithm, before feeding it into a child scanner.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "json_array", - "plugin": true, - "status": "stable", - "summary": "Consumes a stream of one or more JSON elements within a top level array.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "json_documents", - "plugin": true, - "status": "stable", - "summary": "Consumes a stream of one or more JSON documents.", - "type": "scanner", - "version": "4.27.0" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "Use a provided custom delimiter for detecting the end of a line rather than a single line break.", - "is_optional": true, - "kind": "scalar", - "name": "custom_delimiter", - "type": "string" - }, - { - "default": 65536, - "description": "Set the maximum buffer size for storing line data, this limits the maximum size that a line can be without causing an error.", - "kind": "scalar", - "name": "max_buffer_size", - "type": "int" - }, - { - "default": false, - "description": "Omit empty lines.", + "description": "Enable the idempotent write producer option. When enabled, the producer initializes a producer ID and uses it to guarantee exactly-once semantics per partition (no duplicates on retries). This requires the `IDEMPOTENT_WRITE` permission on the `CLUSTER` resource. If your cluster does not grant this permission or uses ACLs restrictively, disable this option. Note: Idempotent writes are strictly a win for data integrity but may be unavailable in restricted environments (e.g., some managed Kafka services, Redpanda with strict ACLs). Disabling this option is safe and only affects retry behavior—duplicates may occur on producer retries, but the pipeline will continue to function normally.", + "is_advanced": true, "kind": "scalar", - "name": "omit_empty", + "name": "idempotent_write", "type": "bool" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "lines", - "plugin": true, - "status": "stable", - "summary": "Split an input stream into a message per line of data.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "The pattern to match against.", - "examples": [ - "(?m)^\\d\\d:\\d\\d:\\d\\d" - ], - "kind": "scalar", - "name": "pattern", - "type": "string" - }, - { - "default": 65536, - "description": "Set the maximum buffer size for storing line data, this limits the maximum size that a message can be without causing an error.", - "kind": "scalar", - "name": "max_buffer_size", - "type": "int" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "re_match", - "plugin": true, - "status": "stable", - "summary": "Split an input stream into segments matching against a regular expression.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "default": { - "to_the_end": {} - }, - "description": "The child scanner to feed the resulting stream into.", - "kind": "scalar", - "name": "into", - "type": "scanner" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "skip_bom", - "plugin": true, - "status": "stable", - "summary": "Skip one or more byte order marks for each opened child scanner.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "children": [ - { - "description": "A regular expression to test against the name of each source of data fed into the scanner (filename or equivalent). If this pattern matches the child scanner is selected.", - "is_optional": true, - "kind": "scalar", - "name": "re_match_name", - "type": "string" }, { - "description": "The scanner to activate if this candidate passes.", - "kind": "scalar", - "name": "scanner", - "type": "scanner" - } - ], - "kind": "array", - "name": "", - "type": "object" - }, - "description": "This scanner outlines a list of potential child scanner candidates to be chosen, and for each source of data the first candidate to pass will be selected. A candidate without any conditions acts as a catch-all and will pass for every source, it is recommended to always have a catch-all scanner at the end of your list. If a given source of data does not pass a candidate an error is returned and the data is rejected.", - "examples": [ - { - "config": "\ninput:\n file:\n paths: [ ./data/* ]\n scanner:\n switch:\n - re_match_name: '\\.avro$'\n scanner: { avro: {} }\n\n - re_match_name: '\\.csv$'\n scanner: { csv: {} }\n\n - re_match_name: '\\.csv.gz$'\n scanner:\n decompress:\n algorithm: gzip\n into:\n csv: {}\n\n - re_match_name: '\\.tar$'\n scanner: { tar: {} }\n\n - re_match_name: '\\.tar.gz$'\n scanner:\n decompress:\n algorithm: gzip\n into:\n tar: {}\n\n - scanner: { to_the_end: {} }\n", - "summary": "In this example a file input chooses a scanner based on the extension of each file", - "title": "Switch based on file name" - } - ], - "name": "switch", - "plugin": true, - "status": "stable", - "summary": "Select a child scanner dynamically for source data based on factors such as the filename.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n== Metadata\n\nThis scanner adds the following metadata to each message:\n\n- `tar_name`\n\n", - "name": "tar", - "plugin": true, - "status": "stable", - "summary": "Consume a tar archive file by file.", - "type": "scanner" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "description": "\n[CAUTION]\n====\nSome sources of data may not have a logical end, therefore caution should be made to exclusively use this scanner when the end of an input stream is clearly defined (and well within memory).\n====\n", - "name": "to_the_end", - "plugin": true, - "status": "stable", - "summary": "Read the input stream all the way until the end and deliver it as a single message.", - "type": "scanner" - } - ], - "tracers": [ - { - "categories": null, - "config": { - "children": [ - { - "description": "The google project with Cloud Trace API enabled. If this is omitted then the Google Cloud SDK will attempt auto-detect it from the environment.", + "annotated_options": [ + [ + "all", + "Wait for all in-sync replicas to acknowledge (acks=-1). Required when idempotent_write is enabled." + ], + [ + "leader", + "Wait for the leader broker to acknowledge (acks=1). Messages are lost if the leader fails before replication." + ], + [ + "none", + "Do not wait for any acknowledgement (acks=0). Highest throughput but messages may be lost." + ] + ], + "default": "all", + "description": "The number of acknowledgements the leader broker must receive from ISR brokers before responding to the produce request. When `idempotent_write` is enabled this must be set to `all`.", + "is_advanced": true, "kind": "scalar", - "name": "project", + "linter": "\nlet options = {\n \"all\": true,\n \"leader\": true,\n \"none\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "acks", "type": "string" }, { - "default": 1, - "description": "Sets the ratio of traces to sample. Tuning the sampling ratio is recommended for high-volume production workloads.", - "examples": [ - 1 - ], + "description": "Optionally set an explicit compression type. The default preference is to use snappy when the broker supports it, and fall back to none if not.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "sampling_ratio", - "type": "float" + "linter": "\nlet options = {\n \"lz4\": true,\n \"snappy\": true,\n \"gzip\": true,\n \"none\": true,\n \"zstd\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "compression", + "options": [ + "lz4", + "snappy", + "gzip", + "none", + "zstd" + ], + "type": "string" }, { - "default": {}, - "description": "A map of tags to add to tracing spans.", + "default": true, + "description": "Enables topics to be auto created if they do not exist when fetching their metadata.", "is_advanced": true, - "kind": "map", - "name": "tags", - "type": "string" + "kind": "scalar", + "name": "allow_auto_topic_creation", + "type": "bool" }, { - "description": "The period of time between each flush of tracing spans.", - "is_optional": true, + "default": "10s", + "description": "The maximum period of time to wait for message sends before abandoning the request and retrying", + "is_advanced": true, "kind": "scalar", - "name": "flush_interval", + "name": "timeout", "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "gcp_cloudtrace", - "plugin": true, - "status": "experimental", - "summary": "Send tracing events to a https://cloud.google.com/trace[Google Cloud Trace^].", - "type": "tracer", - "version": "4.2.0" - }, - { - "categories": null, - "config": { - "children": [ + }, { - "default": "", - "description": "The address of a Jaeger agent to send tracing events to.", + "default": "1MiB", + "description": "The maximum size of a produced record batch in bytes. A `MESSAGE_TOO_LARGE` error is returned if a batch exceeds this limit. This field maps to the `max.message.bytes` Kafka property. Ensure the Redpanda broker's `kafka_batch_max_bytes` property is at least as large as this value, see https://docs.redpanda.com/current/reference/properties/cluster-properties/#kafka_batch_max_bytes.", "examples": [ - "jaeger-agent:6831" + "100MB", + "50mib" ], + "is_advanced": true, "kind": "scalar", - "name": "agent_address", + "name": "max_message_bytes", "type": "string" }, { - "default": "", - "description": "The URL of a Jaeger collector to send tracing events to. If set, this will override `agent_address`.", + "default": "100MiB", + "description": "The upper bound for the number of bytes written to a broker connection in a single write. This field corresponds to Kafka's `socket.request.max.bytes`.", "examples": [ - "https://jaeger-collector:14268/api/traces" + "128MB", + "50mib" ], + "is_advanced": true, "kind": "scalar", - "name": "collector_url", - "type": "string", - "version": "3.38.0" + "name": "broker_write_max_bytes", + "type": "string" }, { - "annotated_options": [ - [ - "const", - "Sample a percentage of traces. 1 or more means all traces are sampled, 0 means no traces are sampled and anything in between means a percentage of traces are sampled. Tuning the sampling rate is recommended for high-volume production workloads." - ] + "default": 10000, + "description": "The maximum number of records the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered and space frees up. Increase this value for high-throughput pipelines to avoid back-pressure stalls.", + "is_advanced": true, + "kind": "scalar", + "name": "max_buffered_records", + "type": "int" + }, + { + "default": "0", + "description": "The maximum number of bytes the client will buffer in memory before blocking. When this limit is reached, `Produce()` calls will block until buffered records are delivered. Set to `0` to disable the byte-level limit (only `max_buffered_records` applies). This limit is checked after `max_buffered_records`.", + "examples": [ + "256MB", + "50mib" ], - "default": "const", - "description": "The sampler type to use.", + "is_advanced": true, "kind": "scalar", - "linter": "\nlet options = {\n \"const\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", - "name": "sampler_type", + "name": "max_buffered_bytes", "type": "string" }, { "default": 1, - "description": "A parameter to use for sampling. This field is unused for some sampling types.", + "description": "The maximum number of produce requests in flight per broker connection. When `idempotent_write` is enabled, this is capped at 5 by the Kafka protocol (and at 1 for Kafka < v1.0.0). When `idempotent_write` is disabled, higher values improve throughput by pipelining requests but may cause out-of-order delivery.", "is_advanced": true, "kind": "scalar", - "name": "sampler_param", - "type": "float" + "name": "max_in_flight_requests", + "type": "int" }, { - "default": {}, - "description": "A map of tags to add to tracing spans.", + "default": 0, + "description": "The maximum number of times a record produce is retried on failure before the record is failed. When a record fails, all records buffered in the same partition are also failed to preserve gapless ordering. Set to `0` for unlimited retries (the default). With `idempotent_write` enabled, retries are only enforced when safe to do so without creating invalid sequence numbers.", "is_advanced": true, - "kind": "map", - "name": "tags", + "kind": "scalar", + "name": "record_retries", + "type": "int" + }, + { + "default": "0s", + "description": "The maximum time a record can sit in the producer buffer before it is failed, roughly equivalent to Kafka's `delivery.timeout.ms`. This is evaluated before writing a request or after a produce response. When a record times out, all records in the same partition are also failed. Set to `0s` for no timeout (the default). With `idempotent_write` enabled, timeouts are only enforced when safe to do so without creating invalid sequence numbers.", + "is_advanced": true, + "kind": "scalar", + "name": "record_delivery_timeout", "type": "string" }, { - "description": "The period of time between each flush of tracing spans.", - "is_optional": true, + "default": "otel-traces", + "description": "The name of the topic to emit spans to", "kind": "scalar", - "name": "flush_interval", + "name": "topic", "type": "string" - } - ], - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "jaeger", - "plugin": true, - "status": "stable", - "summary": "Send tracing events to a https://www.jaegertracing.io/[Jaeger^] agent or collector.", - "type": "tracer" - }, - { - "categories": null, - "config": { - "default": {}, - "kind": "scalar", - "name": "", - "type": "object" - }, - "name": "none", - "plugin": true, - "status": "stable", - "summary": "Do not send tracing events anywhere.", - "type": "tracer" - }, - { - "categories": null, - "config": { - "children": [ + }, { - "default": "benthos", - "description": "The name of the service in traces.", + "annotated_options": [ + [ + "json", + "Emit in JSON Format" + ], + [ + "protobuf", + "Emit in Protobuf Format" + ], + [ + "schema-registry-json", + "Emit in JSON Format with Schema Registry encoding" + ], + [ + "schema-registry-protobuf", + "Emit in Protobuf Format with Schema Registry encoding" + ] + ], + "default": "json", + "description": "The serialization format for individual spans in the topic.", "kind": "scalar", - "name": "service", + "linter": "\nlet options = {\n \"json\": true,\n \"protobuf\": true,\n \"schema-registry-json\": true,\n \"schema-registry-protobuf\": true,\n}\n\nroot = if !$options.exists(this.string().lowercase()) {\n {\"type\": 2, \"what\": \"value %v is not a valid option for this field\".format(this.string())}\n}\n", + "name": "format", "type": "string" }, { "children": [ { - "description": "The endpoint of a collector to send tracing events to.", - "examples": [ - "localhost:4318" - ], + "description": "The base URL of the schema registry service.", "is_optional": true, "kind": "scalar", - "name": "address", + "name": "url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", "type": "string" }, { - "default": "localhost:4318", - "description": "The URL of a collector to send tracing events to.", - "is_deprecated": true, + "children": [ + { + "default": false, + "description": "Whether to skip server side certificate verification.", + "is_advanced": true, + "kind": "scalar", + "name": "skip_cert_verify", + "type": "bool" + }, + { + "default": false, + "description": "Whether to allow the remote server to repeatedly request renegotiation. Enable this option if you're seeing the error message `local error: tls: no renegotiation`.", + "is_advanced": true, + "kind": "scalar", + "name": "enable_renegotiation", + "type": "bool", + "version": "3.45.0" + }, + { + "default": "", + "description": "An optional root certificate authority to use. This is a string, representing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "root_cas", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "An optional path of a root certificate authority file to use. This is a file, often with a .pem extension, containing a certificate chain from the parent trusted root certificate, to possible intermediate signing certificates, to the host certificate.", + "examples": [ + "./root_cas.pem" + ], + "is_advanced": true, + "kind": "scalar", + "name": "root_cas_file", + "type": "string" + }, + { + "children": [ + { + "default": "", + "description": "A plain text certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert", + "type": "string" + }, + { + "default": "", + "description": "A plain text certificate key to use.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "key", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate to use.", + "is_advanced": true, + "kind": "scalar", + "name": "cert_file", + "type": "string" + }, + { + "default": "", + "description": "The path of a certificate key to use.", + "is_advanced": true, + "kind": "scalar", + "name": "key_file", + "type": "string" + }, + { + "default": "", + "description": "A plain text password for when the private key is password encrypted in PKCS#1 or PKCS#8 format. The obsolete `pbeWithMD5AndDES-CBC` algorithm is not supported for the PKCS#8 format.\n\nBecause the obsolete pbeWithMD5AndDES-CBC algorithm does not authenticate the ciphertext, it is vulnerable to padding oracle attacks that can let an attacker recover the plaintext.\n", + "examples": [ + "foo", + "${KEY_PASSWORD}" + ], + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "default": [], + "description": "A list of client certificates to use. For each certificate either the fields `cert` and `key`, or `cert_file` and `key_file` should be specified, but not both.", + "examples": [ + [ + { + "cert": "foo", + "key": "bar" + } + ], + [ + { + "cert_file": "./example.pem", + "key_file": "./example.key" + } + ] + ], + "is_advanced": true, + "kind": "array", + "name": "client_certs", + "type": "object" + } + ], + "description": "Custom TLS settings can be used to override system defaults.", + "is_advanced": true, "kind": "scalar", - "name": "url", - "type": "string" + "name": "tls", + "type": "object" }, { - "default": false, - "description": "Connect to the collector over HTTPS", + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 2 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "client_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the client key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "client_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "The URL of the token provider.", + "is_advanced": true, + "kind": "scalar", + "name": "token_url", + "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", + "type": "string" + }, + { + "default": [], + "description": "A list of optional requested permissions.", + "is_advanced": true, + "kind": "array", + "name": "scopes", + "type": "string" + }, + { + "default": {}, + "description": "A list of optional endpoint parameters, values should be arrays of strings.", + "examples": [ + { + "audience": [ + "https://example.com" + ], + "resource": [ + "https://api.example.com" + ] + } + ], + "is_advanced": true, + "is_optional": true, + "kind": "map", + "linter": "\nroot = if this.type() == \"object\" {\n this.values().map_each(ele -> if ele.type() != \"array\" {\n \"field must be an object containing arrays of strings, got %s (%v)\".format(ele.format_json(no_indent: true), ele.type())\n } else {\n ele.map_each(str -> if str.type() != \"string\" {\n \"field values must be strings, got %s (%v)\".format(str.format_json(no_indent: true), str.type())\n } else { deleted() })\n }).\n flatten()\n}\n", + "name": "endpoint_params", + "type": "unknown" + } + ], + "description": "Allows you to specify open authentication via OAuth version 2 using the client credentials token flow.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "secure", - "type": "bool" - } - ], - "description": "A list of http collectors.", - "kind": "array", - "name": "http", - "type": "object" - }, - { - "children": [ + "name": "oauth2", + "type": "object" + }, { - "description": "The endpoint of a collector to send tracing events to.", - "examples": [ - "localhost:4317" + "children": [ + { + "default": false, + "description": "Whether to use OAuth version 1 in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A value used to identify the client to the service provider.", + "is_advanced": true, + "kind": "scalar", + "name": "consumer_key", + "type": "string" + }, + { + "default": "", + "description": "A secret used to establish ownership of the consumer key.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "consumer_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + }, + { + "default": "", + "description": "A value used to gain access to the protected resources on behalf of the user.", + "is_advanced": true, + "kind": "scalar", + "name": "access_token", + "type": "string" + }, + { + "default": "", + "description": "A secret provided in order to establish ownership of a given access token.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "access_token_secret", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } ], + "description": "Allows you to specify open authentication via OAuth version 1.", + "is_advanced": true, "is_optional": true, "kind": "scalar", - "name": "address", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "oauth", + "type": "object" }, { - "default": "localhost:4317", - "description": "The URL of a collector to send tracing events to.", - "is_deprecated": true, + "children": [ + { + "default": false, + "description": "Whether to use basic authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A username to authenticate as.", + "is_advanced": true, + "kind": "scalar", + "name": "username", + "type": "string" + }, + { + "default": "", + "description": "A password to authenticate with.", + "is_advanced": true, + "is_secret": true, + "kind": "scalar", + "name": "password", + "scrubber": "root = if this != null && this != \"\" && !this.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n} else if this == null { \"\" }", + "type": "string" + } + ], + "description": "Allows you to specify basic authentication.", + "is_advanced": true, + "is_optional": true, "kind": "scalar", - "name": "url", - "scrubber": "\nlet pass = this.parse_url().user.password.or(\"\")\nroot = if $pass != \"\" && !$pass.trim().re_match(\"\"\"^\\${[0-9A-Za-z_.]+(:((\\${[^}]+})|[^}])*)?}$\"\"\") {\n \"!!!SECRET_SCRUBBED!!!\"\n}\n", - "type": "string" + "name": "basic_auth", + "type": "object" }, { - "default": false, - "description": "Connect to the collector with client transport security", + "children": [ + { + "default": false, + "description": "Whether to use JWT authentication in requests.", + "is_advanced": true, + "kind": "scalar", + "name": "enabled", + "type": "bool" + }, + { + "default": "", + "description": "A file with the PEM encoded via PKCS1 or PKCS8 as private key.", + "is_advanced": true, + "kind": "scalar", + "name": "private_key_file", + "type": "string" + }, + { + "default": "", + "description": "A method used to sign the token such as RS256, RS384, RS512 or EdDSA.", + "is_advanced": true, + "kind": "scalar", + "name": "signing_method", + "type": "string" + }, + { + "default": {}, + "description": "A value used to identify the claims that issued the JWT.", + "is_advanced": true, + "kind": "map", + "name": "claims", + "type": "unknown" + }, + { + "default": {}, + "description": "Add optional key/value headers to the JWT.", + "is_advanced": true, + "kind": "map", + "name": "headers", + "type": "unknown" + } + ], + "description": "BETA: Allows you to specify JWT authentication.", + "is_advanced": true, "kind": "scalar", - "name": "secure", - "type": "bool" + "name": "jwt", + "type": "object" } ], - "description": "A list of grpc collectors.", - "kind": "array", - "name": "grpc", + "description": "Schema registry information to publish schemas for tracing data along with the data.", + "kind": "scalar", + "name": "schema_registry", "type": "object" }, + { + "default": "redpanda-connect", + "description": "The name of the service in traces.", + "kind": "scalar", + "name": "service", + "type": "string" + }, { "default": {}, "description": "A map of tags to add to all tracing spans.", @@ -57898,6 +66424,7 @@ { "description": "Sets the ratio of traces to sample.", "examples": [ + 0.05, 0.85, 0.5 ], @@ -57910,20 +66437,19 @@ "description": "Settings for trace sampling. Sampling is recommended for high-volume production workloads.", "kind": "scalar", "name": "sampling", - "type": "object", - "version": "4.25.0" + "type": "object" } ], "kind": "scalar", "name": "", "type": "object" }, - "name": "open_telemetry_collector", + "name": "redpanda", "plugin": true, - "status": "experimental", - "summary": "Send tracing events to an https://opentelemetry.io/docs/collector/[Open Telemetry collector^].", + "status": "stable", + "summary": "Send tracing events to a Redpanda Message Broker.", "type": "tracer" } ], - "version": "4.65.0" + "version": "4.100.0" } diff --git a/frontend/src/components/license/license-utils.tsx b/frontend/src/components/license/license-utils.tsx index d7defab795..3c05e15057 100644 --- a/frontend/src/components/license/license-utils.tsx +++ b/frontend/src/components/license/license-utils.tsx @@ -1,5 +1,6 @@ import { Link } from '@tanstack/react-router'; import { Button } from 'components/redpanda-ui/components/button'; +import { docsLinks } from 'utils/docs-links'; import { prettyMilliseconds } from 'utils/utils'; import { @@ -381,10 +382,9 @@ export const resolveEnterpriseCTALink = ( export const getEnterpriseCTALink = (type: EnterpriseLinkType): string => resolveEnterpriseCTALink(type, api.clusterOverview?.kafka?.clusterId, api.isRedpanda); -export const DISABLE_SSO_DOCS_LINK = 'https://docs.redpanda.com/current/console/config/configure-console/'; +export const DISABLE_SSO_DOCS_LINK = docsLinks.selfManaged.consoleConfig; -export const ENTERPRISE_FEATURES_DOCS_LINK = - 'https://docs.redpanda.com/current/get-started/licenses/#redpanda-enterprise-edition'; +export const ENTERPRISE_FEATURES_DOCS_LINK = docsLinks.selfManaged.enterpriseEdition; export const SERVERLESS_LINK = 'https://www.redpanda.com/product/serverless'; diff --git a/frontend/src/components/pages/admin/upload-license-page.tsx b/frontend/src/components/pages/admin/upload-license-page.tsx index 4f13cd0264..20dd1613a5 100644 --- a/frontend/src/components/pages/admin/upload-license-page.tsx +++ b/frontend/src/components/pages/admin/upload-license-page.tsx @@ -14,6 +14,7 @@ import { } from '@redpanda-data/ui'; import type { FC } from 'react'; import { useState } from 'react'; +import { docsLinks } from 'utils/docs-links'; import type { SetLicenseRequest, SetLicenseResponse } from '../../../protogen/redpanda/api/console/v1alpha1/license_pb'; import { appGlobal } from '../../../state/app-global'; @@ -167,11 +168,7 @@ const UploadLicensePageContent: FC = () => { our support team {' '} to request a license. To see a list of what is available with Redpanda Enterprise, check{' '} - + our documentation . diff --git a/frontend/src/components/pages/connect/create-connector.tsx b/frontend/src/components/pages/connect/create-connector.tsx index 370460be91..9c4632b6dd 100644 --- a/frontend/src/components/pages/connect/create-connector.tsx +++ b/frontend/src/components/pages/connect/create-connector.tsx @@ -32,6 +32,7 @@ import { useToast, } from '@redpanda-data/ui'; import { useEffect, useState } from 'react'; +import { docsLinks } from 'utils/docs-links'; import { ConnectorBoxCard, type ConnectorPlugin, getConnectorFriendlyName } from './connector-box-card'; import { ConfigPage } from './dynamic-ui/components'; @@ -143,10 +144,7 @@ const ConnectorType = (p: { Select a managed connector. Connectors simplify importing and exporting data between Redpanda and - popular data sources.{' '} - - Learn more - + popular data sources. Learn more diff --git a/frontend/src/components/pages/connect/helper.tsx b/frontend/src/components/pages/connect/helper.tsx index 7a332b15c5..1cc5baf02c 100644 --- a/frontend/src/components/pages/connect/helper.tsx +++ b/frontend/src/components/pages/connect/helper.tsx @@ -34,6 +34,7 @@ import { } from '@redpanda-data/ui'; import { AlertIcon, CheckCircleIcon, HourglassIcon, PauseCircleIcon, WarningIcon } from 'components/icons'; import { type CSSProperties, type JSX, useRef, useState } from 'react'; +import { docsLinks } from 'utils/docs-links'; import AmazonS3 from '../../../assets/connectors/amazon-s3.png'; import ApacheLogo from '../../../assets/connectors/apache.svg'; @@ -100,8 +101,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Apache Software Foundation', friendlyName: 'Kafka cluster topics', description: 'Imports messages from another Kafka cluster, using MirrorSourceConnector', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mmaker-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mmaker-source-connector'), } as const, { classNamePrefix: 'org.apache.kafka.connect.mirror.MirrorCheckpointConnector', @@ -109,8 +109,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Apache Software Foundation', friendlyName: 'Kafka cluster offsets', description: 'Imports consumer group offsets from another Kafka cluster, using MirrorCheckpointConnector', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mmaker-checkpoint-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mmaker-checkpoint-connector'), } as const, { classNamePrefix: 'org.apache.kafka.connect.mirror.MirrorHeartbeatConnector', @@ -118,8 +117,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Apache Software Foundation', friendlyName: 'Heartbeat', description: 'Generates heartbeat messages to local heartbeat topic', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mmaker-heartbeat-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mmaker-heartbeat-connector'), } as const, // Confluent Connectors { @@ -175,8 +173,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Debezium', friendlyName: 'MySQL (Debezium)', description: 'Imports a stream of changes from MySQL, Amazon RDS and Amazon Aurora', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mysql-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mysql-source-connector'), } as const, { classNamePrefix: 'io.debezium.connector.mongodb.', @@ -189,8 +186,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Debezium', friendlyName: 'PostgreSQL (Debezium)', description: 'Imports a stream of changes from PostgreSQL', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-postgresql-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-postgresql-connector'), } as const, { classNamePrefix: 'io.debezium.connector.sqlserver.', @@ -198,8 +194,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Debezium', friendlyName: 'SQL Server (Debezium)', description: 'Imports a stream of changes from Microsoft SQL Server', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-sqlserver-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-sqlserver-connector'), } as const, { classNamePrefix: 'io.debezium.connector.cassandra.', @@ -219,8 +214,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Redpanda', friendlyName: 'S3', description: 'Exports messages to files in S3 buckets', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-s3-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-s3-sink-connector'), } as const, { classNamePrefix: 'com.redpanda.kafka.connect.gcs.', @@ -228,8 +222,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Redpanda', friendlyName: 'Google Cloud Storage', description: 'Exports messages to files in Google Cloud Storage', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-gcs-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-gcs-connector'), } as const, { classNamePrefix: 'com.redpanda.kafka.connect.jdbc.JdbcSourceConnector', @@ -237,8 +230,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Redpanda', friendlyName: 'JDBC', description: 'Imports batches of rows from MySQL, PostgreSQL, SQLite and SQL Server', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-jdbc-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-jdbc-source-connector'), } as const, { classNamePrefix: 'com.redpanda.kafka.connect.jdbc.JdbcSinkConnector', @@ -246,8 +238,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Redpanda', friendlyName: 'JDBC', description: 'Exports messages to tables in MySQL, PostgreSQL, SQLite and SQL Server', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-jdbc-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-jdbc-sink-connector'), } as const, // Stream Reactor / Lenses @@ -264,8 +255,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'WePay', friendlyName: 'Google BigQuery', description: 'Exports messages to Google BigQuery tables', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-gcp-bigquery-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-gcp-bigquery-connector'), } as const, // Snowflake Connectors @@ -275,8 +265,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Snowflake', friendlyName: 'Snowflake', description: 'Exports messages to Snowflake tables', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-snowflake-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-snowflake-connector'), } as const, // MongoDB Connectors @@ -286,8 +275,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'MongoDB', friendlyName: 'MongoDB', description: 'Imports collections from MongoDB', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mongodb-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mongodb-source-connector'), } as const, { classNamePrefix: 'com.mongodb.kafka.connect.MongoSinkConnector', @@ -295,8 +283,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'MongoDB', friendlyName: 'MongoDB', description: 'Exports messages to MongoDB collections', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-mongodb-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-mongodb-sink-connector'), } as const, // Iceberg Connectors @@ -306,8 +293,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Tabular', friendlyName: 'Iceberg', description: 'Exports messages to Iceberg tables', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-iceberg-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectorGuide('create-iceberg-sink-connector'), } as const, // JMS Connectors @@ -317,8 +303,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'MacroNova', friendlyName: 'JMS', description: 'Exports messages to JMS queue', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-jms-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectors, } as const, { classNamePrefix: 'io.macronova.kafka.connect.jms.JmsSourceConnector', @@ -326,8 +311,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'MacroNova', friendlyName: 'JMS', description: 'Imports messages from JMS queue', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-jms-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectors, } as const, // IBM MQ Connectors @@ -337,8 +321,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'IBM Messaging', friendlyName: 'IBM MQ', description: 'Exports messages to IBM MQ queue', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-ibmmq-sink-connector/', + learnMoreLink: docsLinks.cloud.managedConnectors, } as const, { classNamePrefix: 'com.ibm.eventstreams.connect.mqsource.MQSourceConnector', @@ -346,8 +329,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'IBM Messaging', friendlyName: 'IBM MQ', description: 'Imports messages from IBM MQ queue', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-ibmmq-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectors, } as const, // Community Connector @@ -367,8 +349,7 @@ const connectorMetadata: ConnectorMetadata[] = [ author: 'Cástor Rodríguez', friendlyName: 'HTTP', description: 'Imports data from HTTP services as batches or increments', - learnMoreLink: - 'https://docs.redpanda.com/docs/deploy/deployment-option/cloud/managed-connectors/create-http-source-connector/', + learnMoreLink: docsLinks.cloud.managedConnectors, } as const, // Fallbacks with a very generous classname prefix (usually just the maintainers' logo) @@ -553,7 +534,7 @@ export function NotConfigured() { Setup the connection details to your Kafka Connect cluster in your Redpanda Console config, to view and control all your connectors and tasks. - + diff --git a/frontend/src/components/pages/connect/overview.tsx b/frontend/src/components/pages/connect/overview.tsx index 942feb0e93..d777b5e0a1 100644 --- a/frontend/src/components/pages/connect/overview.tsx +++ b/frontend/src/components/pages/connect/overview.tsx @@ -17,6 +17,7 @@ import { Link } from 'components/redpanda-ui/components/typography'; import { WaitingRedpanda } from 'components/redpanda-ui/components/waiting-redpanda'; import { Component, type FunctionComponent, useCallback, useMemo, useState } from 'react'; import { useKafkaConnectConnectorsQuery } from 'react-query/api/kafka-connect'; +import { docsLinks } from 'utils/docs-links'; import { ConnectorClass, @@ -163,7 +164,11 @@ class KafkaConnectOverview extends PageComponent<{ {this.props.isKafkaConnectEnabled ? 'Redpanda Connect is an alternative to Kafka Connect. Choose from a growing ecosystem of readily available connectors.' : 'Redpanda Connect is a data streaming service for building scalable, high-performance data pipelines that drive real-time analytics and actionable business insights. Integrate data across systems with hundreds of prebuilt connectors, change data capture (CDC) capabilities, and YAML-configurable pipelines.'}{' '} - + Learn more @@ -179,11 +184,7 @@ class KafkaConnectOverview extends PageComponent<{
Kafka Connect is our set of managed connectors. These provide a way to integrate your Redpanda data with different data systems.{' '} - + Learn more.
diff --git a/frontend/src/components/pages/mcp-servers/remote-mcp-component-type-description.tsx b/frontend/src/components/pages/mcp-servers/remote-mcp-component-type-description.tsx index 2ea89a331d..c15790b2e9 100644 --- a/frontend/src/components/pages/mcp-servers/remote-mcp-component-type-description.tsx +++ b/frontend/src/components/pages/mcp-servers/remote-mcp-component-type-description.tsx @@ -1,6 +1,7 @@ import { Link } from 'components/redpanda-ui/components/typography'; import { ExternalLink } from 'lucide-react'; import { MCPServer_Tool_ComponentType } from 'protogen/redpanda/api/dataplane/v1alpha3/mcp_pb'; +import { docsLinks } from 'utils/docs-links'; type RemoteMCPComponentTypeDescriptionProps = { componentType?: MCPServer_Tool_ComponentType; @@ -23,7 +24,7 @@ const getComponentTypeDescription = (componentType: MCPServer_Tool_ComponentType }; const getComponentTypeDocumentationUrl = (componentType: MCPServer_Tool_ComponentType) => { - const url = 'https://docs.redpanda.com/redpanda-connect/components/'; + const url = docsLinks.cloud.connectComponents; switch (componentType) { case MCPServer_Tool_ComponentType.PROCESSOR: return `${url}/processors/about/`; diff --git a/frontend/src/components/pages/overview/overview.tsx b/frontend/src/components/pages/overview/overview.tsx index 3f9fbfa3c1..82a5a4a6d7 100644 --- a/frontend/src/components/pages/overview/overview.tsx +++ b/frontend/src/components/pages/overview/overview.tsx @@ -10,6 +10,7 @@ */ import { isFeatureFlagEnabled } from 'config'; +import { docsLinks } from 'utils/docs-links'; import { appGlobal } from '../../../state/app-global'; import { api } from '../../../state/backend-api'; @@ -242,9 +243,9 @@ class Overview extends PageComponent { {Boolean(api.clusterOverview?.kafka?.distribution) && }
diff --git a/frontend/src/components/pages/quotas/quotas-list.tsx b/frontend/src/components/pages/quotas/quotas-list.tsx index b78bf6b226..70aa45cbb0 100644 --- a/frontend/src/components/pages/quotas/quotas-list.tsx +++ b/frontend/src/components/pages/quotas/quotas-list.tsx @@ -39,6 +39,7 @@ import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'compon import { Link } from 'components/redpanda-ui/components/typography'; import { ArrowDown, ArrowUp, ChevronsUpDown, InfoIcon, TriangleAlertIcon } from 'lucide-react'; import { useMemo } from 'react'; +import { docsLinks } from 'utils/docs-links'; import { clampPageIndex, @@ -258,7 +259,7 @@ const QuotasList = () => { - + diff --git a/frontend/src/components/pages/rp-connect/onboarding/add-topic-step.tsx b/frontend/src/components/pages/rp-connect/onboarding/add-topic-step.tsx index 80bbacaf83..911688f153 100644 --- a/frontend/src/components/pages/rp-connect/onboarding/add-topic-step.tsx +++ b/frontend/src/components/pages/rp-connect/onboarding/add-topic-step.tsx @@ -47,6 +47,13 @@ import { } from '../types/wizard'; import { isUsingDefaultRetentionSettings, parseTopicConfigFromExisting, TOPIC_FORM_DEFAULTS } from '../utils/topic'; +// Only describe what the mode can actually do — "create a new topic" with no input reads as broken. +const TOPIC_NAME_DESCRIPTIONS: Record<'new' | 'existing' | 'both', string> = { + new: 'Enter a name for the new topic.', + existing: 'Choose an existing topic to read or write data from.', + both: 'Choose an existing topic to read or write data from, or create a new topic.', +}; + type AddTopicStepProps = { defaultTopicName?: string; hideInternal?: boolean; @@ -291,9 +298,7 @@ export const AddTopicStep = forwardRef, AddTopicSt
Topic name - - Choose an existing topic to read or write data from, or create a new topic. - + {TOPIC_NAME_DESCRIPTIONS[selectionMode]}
{selectionMode === 'both' && ( + Learn more
diff --git a/frontend/src/components/pages/rp-connect/onboarding/onboarding-wizard.tsx b/frontend/src/components/pages/rp-connect/onboarding/onboarding-wizard.tsx index 995780abc3..10e9b3f1f1 100644 --- a/frontend/src/components/pages/rp-connect/onboarding/onboarding-wizard.tsx +++ b/frontend/src/components/pages/rp-connect/onboarding/onboarding-wizard.tsx @@ -11,7 +11,6 @@ import { CheckIcon, ChevronLeftIcon, ChevronRightIcon } from 'lucide-react'; import { AnimatePresence } from 'motion/react'; import { ComponentSpecSchema } from 'protogen/redpanda/api/dataplane/v1/pipeline_pb'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { useListComponentsQuery } from 'react-query/api/connect'; import { useResetRpcnWizardStore, useRpcnWizardStore } from 'state/rpcn-wizard-store'; import { useShallow } from 'zustand/react/shallow'; @@ -30,7 +29,7 @@ import { import type { ExtendedConnectComponentSpec } from '../types/schema'; import type { AddTopicFormData, BaseStepRef, ConnectTilesListFormData, UserStepRef } from '../types/wizard'; import { navigateToConnectClusters } from '../utils/navigation'; -import { parseSchema } from '../utils/schema'; +import { useEnrichedComponents } from '../utils/use-enriched-components'; import { handleStepResult, regenerateYamlForTopicUserComponents } from '../utils/wizard'; import { getConnectTemplate } from '../utils/yaml'; @@ -61,11 +60,7 @@ export const ConnectOnboardingWizard = ({ }: ConnectOnboardingWizardProps = {}) => { const navigate = useNavigate(); - const { data: componentListResponse, isLoading: isComponentListLoading } = useListComponentsQuery(); - const components = useMemo( - () => (componentListResponse?.components ? parseSchema(componentListResponse.components) : []), - [componentListResponse] - ); + const { components, componentList, isLoading: isComponentListLoading } = useEnrichedComponents(); const persistedInputConnectionName = useRpcnWizardStore(useShallow((state) => state.input?.connectionName)); const persistedOutputConnectionName = useRpcnWizardStore(useShallow((state) => state.output?.connectionName)); @@ -348,7 +343,7 @@ export const ConnectOnboardingWizard = ({ [WizardStep.ADD_INPUT]: () => ( ( pipelineResponse?.response?.pipeline, [pipelineResponse]); - const { data: componentListResponse } = useListComponentsQuery(); - const components = useMemo( - () => (componentListResponse?.components ? parseSchema(componentListResponse.components) : []), - [componentListResponse] - ); + const { components, componentList } = useEnrichedComponents(); const { data: schemaResponse } = useGetPipelineServiceConfigSchemaQuery(); const yamlEditorSchema = useMemo(() => parseYamlEditorSchema(schemaResponse?.configSchema), [schemaResponse]); @@ -1134,10 +1130,9 @@ function PipelinePageContent() { ); return ( - // Definite viewport-bounded height (7rem = app header + pt-8; the footer intentionally sits below - // the fold) so a tall lane scrolls within the framed panel instead of stretching the page. - // overflow-x-clip (not hidden) blocks stray horizontal overflow but keeps overflow-y. -
+ // Viewport-bounded height (7rem = app header + pt-8) so a tall lane scrolls within the framed panel. + // The -ml-3.5/pl-3.5 pair keeps the back button's overhang inside the overflow-x-clip region. +
{mode === 'view' && pipeline ? ( ({ line, column: 1, hint: text }) as LintHint; + +// Line numbers (1-based): 1 input:, 2 label, 3 redpanda:, 4 seed_brokers:, 5 list item, +// 6 topics:, 7 tls:, 8 enabled. +const YAML = `input: + label: "" + redpanda: + seed_brokers: + - localhost:9092 + topics: [] + tls: + enabled: true +`; + +const target: EditTarget = { kind: 'input' }; +const fieldKeys = new Set([ + 'label', + 'id', + 'seed_brokers', + 'topics', + 'consumer_group', + 'regexp_topics_include', + 'tls/enabled', +]); + +const run = (hints: LintHint[]) => + mapLintHintsToFields({ yaml: YAML, target, componentName: 'redpanda', hints, fieldKeys }); + +describe('mapLintHintsToFields', () => { + test('anchors a hint to the field whose YAML lines contain it', () => { + const { byField, unmapped } = run([hint(6, 'expected array value')]); + expect(byField.get('topics')).toEqual(['expected array value']); + expect(unmapped).toHaveLength(0); + }); + + test('multi-line values anchor to their field, and nested fields win over their parent', () => { + const { byField } = run([hint(5, 'bad broker address'), hint(8, 'expected bool value')]); + expect(byField.get('seed_brokers')).toEqual(['bad broker address']); + expect(byField.get('tls/enabled')).toEqual(['expected bool value']); + }); + + test('the label line anchors to the label field', () => { + const { byField } = run([hint(2, 'labels must match the pattern')]); + expect(byField.get('label')).toEqual(['labels must match the pattern']); + }); + + test('a component-line hint falls back to every field the message names', () => { + // "line 3" is the `redpanda:` line — no field range contains it, but the message names two fields. + const message = 'either topics or regexp_topics_include must be specified'; + const { byField, unmapped } = run([hint(3, message)]); + expect(byField.get('topics')).toEqual([message]); + expect(byField.get('regexp_topics_include')).toEqual([message]); + expect(unmapped).toHaveLength(0); + }); + + test('underscored field names match their spaced spelling in lint prose', () => { + const message = 'a consumer group is mandatory when not using explicit topic partitions'; + const { byField } = run([hint(3, message)]); + expect(byField.get('consumer_group')).toEqual([message]); + }); + + test('an explicit "field X is required" anchors even to short field names like id', () => { + // Too short for loose mention matching, but "field id …" is the lint convention — exact. + const message = 'field id is required'; + const { byField, unmapped } = run([hint(3, message)]); + expect(byField.get('id')).toEqual([message]); + expect(unmapped).toHaveLength(0); + }); + + test('an explicit field reference at the end of a sentence is not swallowed by the period', () => { + const message = 'this output requires the field topics.'; + const { byField } = run([hint(3, message)]); + expect(byField.get('topics')).toEqual([message]); + }); + + test('an explicit field reference beats the line anchor', () => { + // A missing-field error's line points at the component's mapping, which STARTS on some other + // field's line — the named field must win (observed with aws_s3's "field bucket is required" + // rendering under `path`). + const message = 'field seed_brokers is required'; + const { byField } = run([hint(6, message)]); // line 6 is `topics:` + expect(byField.get('seed_brokers')).toEqual([message]); + expect(byField.has('topics')).toBe(false); + }); + + test('an explicit field reference beats loose mentions of other fields', () => { + // "topics" appears in the prose, but the error is explicitly about regexp_topics_include. + const message = 'field regexp_topics_include is required when regexp topics are enabled'; + const { byField } = run([hint(3, message)]); + expect(byField.get('regexp_topics_include')).toEqual([message]); + expect(byField.has('topics')).toBe(false); + }); + + test('hints that anchor to nothing stay unmapped for the banner', () => { + const { byField, unmapped } = run([hint(3, 'component failed to initialise')]); + expect(byField.size).toBe(0); + expect(unmapped).toHaveLength(1); + // Line 3 is the component's own line — no field to name. + expect(unmapped[0].fieldLabel).toBeUndefined(); + }); + + test('an error on a null object anchors to its rendered group section', () => { + // e.g. `batching:` hollowed to null — no leaf input exists, but the batching group does. + const yamlWithNullGroup = 'input:\n redpanda:\n topics: []\n batching:\n'; + const { byField, unmapped } = mapLintHintsToFields({ + yaml: yamlWithNullGroup, + target, + componentName: 'redpanda', + hints: [hint(4, 'expected object value, got !!null')], + fieldKeys: new Set(['topics', 'batching']), + }); + expect(byField.get('batching')).toEqual(['expected object value, got !!null']); + expect(unmapped).toHaveLength(0); + }); + + test('a line inside an unrendered child anchors up to its group, noting the deeper path', () => { + const { byField } = mapLintHintsToFields({ + yaml: YAML, + target, + componentName: 'redpanda', + hints: [hint(8, 'expected bool value')], // tls/enabled's line + fieldKeys: new Set(['tls']), // only the group section is rendered + }); + expect(byField.get('tls')).toEqual(['expected bool value (enabled)']); + }); + + test('an unmapped hint on an unrendered field carries that field name for the banner', () => { + const { byField, unmapped } = mapLintHintsToFields({ + yaml: YAML, + target, + componentName: 'redpanda', + hints: [hint(8, 'expected bool value')], + // tls/enabled not rendered by this form. + fieldKeys: new Set(['topics']), + }); + expect(byField.size).toBe(0); + expect(unmapped).toHaveLength(1); + // The banner shows the field name instead of a line number the form view can't see. + expect(unmapped[0].fieldLabel).toBe('tls.enabled'); + }); +}); diff --git a/frontend/src/components/pages/rp-connect/pipeline/lint-field-mapping.ts b/frontend/src/components/pages/rp-connect/pipeline/lint-field-mapping.ts new file mode 100644 index 0000000000..2629b19b59 --- /dev/null +++ b/frontend/src/components/pages/rp-connect/pipeline/lint-field-mapping.ts @@ -0,0 +1,205 @@ +/** + * Copyright 2026 Redpanda Data, Inc. + * + * Use of this software is governed by the Business Source License + * included in the file https://github.com/redpanda-data/redpanda/blob/dev/licenses/bsl.md + * + * As of the Change Date specified in that file, in accordance with + * the Business Source License, use of this software will be governed + * by the Apache License, Version 2.0 + */ + +import type { LintHint } from '@buf/redpandadata_common.bufbuild_es/redpanda/api/common/v1/linthint_pb'; +import { isMap, isScalar, LineCounter, parseDocument } from 'yaml'; + +import { type EditTarget, editTargetPath } from '../utils/yaml'; + +/** Field/group key (`path.join('/')`) → the lint messages anchored to it. */ +export type FieldLintErrors = ReadonlyMap; + +type FieldSegment = { key: string; depth: number; startLine: number; endLine: number }; + +type RangedNode = { range?: [number, number, number] | null } | null | undefined; + +// Record a segment per mapping pair, recursing into nested mappings. Children are deeper, so the +// deepest segment containing a line wins during lookup. +function walkMapping(node: unknown, prefix: string[], out: FieldSegment[], lineCounter: LineCounter): void { + if (!isMap(node)) { + return; + } + for (const pair of node.items) { + const keyNode = pair.key; + if (!(isScalar(keyNode) && typeof keyNode.value === 'string' && keyNode.range)) { + continue; + } + const path = [...prefix, keyNode.value]; + const valueRange = (pair.value as RangedNode)?.range; + // Value-end (range[1]), not node-end (range[2]) — node-end swallows trailing blank lines, + // which would bleed a hint on the next sibling's line into this field. + const endOffset = valueRange ? valueRange[1] : keyNode.range[1]; + out.push({ + key: path.join('/'), + depth: path.length, + startLine: lineCounter.linePos(keyNode.range[0]).line, + endLine: lineCounter.linePos(Math.max(endOffset - 1, keyNode.range[0])).line, + }); + walkMapping(pair.value, path, out, lineCounter); + } +} + +// The key itself when rendered, else its closest rendered ancestor (a group section), else nothing. +function nearestRenderedAncestor(key: string, fieldKeys: ReadonlySet): string | undefined { + const path = key.split('/'); + while (path.length > 0) { + const candidate = path.join('/'); + if (fieldKeys.has(candidate)) { + return candidate; + } + path.pop(); + } + return; +} + +// The deepest field whose YAML range contains the line. +function fieldKeyForLine(segments: readonly FieldSegment[], line: number): string | undefined { + let best: FieldSegment | undefined; + for (const segment of segments) { + if (line >= segment.startLine && line <= segment.endLine && (!best || segment.depth > best.depth)) { + best = segment; + } + } + return best?.key; +} + +const ESCAPE_RE = /[.*+?^${}()|[\]\\]/g; +// Below this, a field name is too generic to trust as a loose mention ("id", "to") — but an +// explicit `field X …` reference (see FIELD_TOKEN_RE) is exact and has no length limit. +const MIN_MENTION_NAME_LENGTH = 3; + +// The lint convention for structural errors: "field id is required", "field `host` is required". +// Dots only BETWEEN segments ("batching.count"), so a sentence-ending period isn't swallowed. +const FIELD_TOKEN_RE = /\bfield\s+[`'"]?([A-Za-z0-9_]+(?:\.[A-Za-z0-9_]+)*)/gi; + +// Fields the hint names via the exact "field X" convention — precise, so any name length counts. +function fieldKeysNamedExplicitly(hintText: string, fieldKeys: ReadonlySet): string[] { + const tokens = new Set(); + for (const match of hintText.matchAll(FIELD_TOKEN_RE)) { + tokens.add(match[1].toLowerCase()); + } + if (tokens.size === 0) { + return []; + } + const matches: string[] = []; + for (const key of fieldKeys) { + const name = key.split('/').at(-1) ?? key; + // Match the leaf name or the dotted path ("field batching.count is required"). + if (tokens.has(name.toLowerCase()) || tokens.has(key.replaceAll('/', '.').toLowerCase())) { + matches.push(key); + } + } + return matches; +} + +function fieldKeysMentioned(hintText: string, fieldKeys: ReadonlySet): string[] { + const matches: string[] = []; + for (const key of fieldKeys) { + const name = key.split('/').at(-1) ?? key; + if (name.length < MIN_MENTION_NAME_LENGTH) { + continue; + } + // Lint prose writes field names verbatim or with spaces ("a consumer group is mandatory…"). + const variants = name.includes('_') ? [name, name.replaceAll('_', ' ')] : [name]; + if (variants.some((v) => new RegExp(`\\b${v.replace(ESCAPE_RE, '\\$&')}\\b`, 'i').test(hintText))) { + matches.push(key); + } + } + return matches; +} + +// Segments for the component entry at `target`: its `label` line plus every field of the inner +// config (paths relative to it, matching the form's leaf keys). +function collectFieldSegments(yaml: string, target: EditTarget, componentName: string): FieldSegment[] { + const segments: FieldSegment[] = []; + try { + const lineCounter = new LineCounter(); + const doc = parseDocument(yaml, { lineCounter }); + const wrapper = doc.getIn(editTargetPath(target), true); + if (!isMap(wrapper)) { + return segments; + } + for (const pair of wrapper.items) { + const keyNode = pair.key; + if (!(isScalar(keyNode) && typeof keyNode.value === 'string' && keyNode.range)) { + continue; + } + if (keyNode.value === 'label') { + const start = lineCounter.linePos(keyNode.range[0]).line; + segments.push({ key: 'label', depth: 1, startLine: start, endLine: start }); + } else if (keyNode.value === componentName) { + walkMapping(pair.value, [], segments, lineCounter); + } + } + } catch { + // Unparsable YAML: no line anchoring; mention matching below still applies. + } + return segments; +} + +/** An unanchored hint plus, when its line resolved to a (non-rendered) field, that field's dotted path. */ +export type UnmappedLintHint = { hint: LintHint; fieldLabel?: string }; + +type ResolvedHint = { anchors: { key: string; message: string }[]; fieldLabel?: string }; + +// Where one hint anchors, by signal strength: an explicit "field X …" reference (it beats the +// line, which for a MISSING field can only point at the component — whose mapping starts on some +// other field's line); then the line's field, walked up to its nearest rendered ancestor (a group +// section) with the deeper path noted; then loose prose mentions. No anchors = the banner keeps +// it, labelled with the line's field when known (a line number is meaningless in the form view). +function resolveHint(hint: LintHint, segments: readonly FieldSegment[], fieldKeys: ReadonlySet): ResolvedHint { + const named = fieldKeysNamedExplicitly(hint.hint, fieldKeys); + if (named.length > 0) { + return { anchors: named.map((key) => ({ key, message: hint.hint })) }; + } + const lineKey = hint.line > 0 ? fieldKeyForLine(segments, hint.line) : undefined; + const lineAnchor = lineKey ? nearestRenderedAncestor(lineKey, fieldKeys) : undefined; + if (lineKey && lineAnchor) { + const remainder = lineKey === lineAnchor ? '' : lineKey.slice(lineAnchor.length + 1).replaceAll('/', '.'); + return { anchors: [{ key: lineAnchor, message: remainder ? `${hint.hint} (${remainder})` : hint.hint }] }; + } + const mentioned = fieldKeysMentioned(hint.hint, fieldKeys); + if (mentioned.length > 0) { + return { anchors: mentioned.map((key) => ({ key, message: hint.hint })) }; + } + return { anchors: [], fieldLabel: lineKey?.replaceAll('/', '.') }; +} + +/** + * Anchors a node's lint hints to the inspector form fields they belong to, so an error renders + * under the field (or inside the group section) it's about instead of as "(line 12)" in a banner + * the user has to decode. Splits into per-field anchors (only keys the form renders) and the + * rest for the banner. See `resolveHint` for the anchoring tiers. + */ +export function mapLintHintsToFields(opts: { + yaml: string; + target: EditTarget; + componentName: string; + hints: readonly LintHint[]; + /** Keys the schema form actually renders — leaves and group sections (see `formFieldKeys`). */ + fieldKeys: ReadonlySet; +}): { byField: FieldLintErrors; unmapped: UnmappedLintHint[] } { + const segments = collectFieldSegments(opts.yaml, opts.target, opts.componentName); + const byField = new Map(); + const unmapped: UnmappedLintHint[] = []; + + for (const hint of opts.hints) { + const resolved = resolveHint(hint, segments, opts.fieldKeys); + if (resolved.anchors.length === 0) { + unmapped.push({ hint, fieldLabel: resolved.fieldLabel }); + continue; + } + for (const { key, message } of resolved.anchors) { + byField.set(key, [...(byField.get(key) ?? []), message]); + } + } + return { byField, unmapped }; +} diff --git a/frontend/src/components/pages/rp-connect/pipeline/list.tsx b/frontend/src/components/pages/rp-connect/pipeline/list.tsx index a19b45b2ab..b606876a18 100644 --- a/frontend/src/components/pages/rp-connect/pipeline/list.tsx +++ b/frontend/src/components/pages/rp-connect/pipeline/list.tsx @@ -66,6 +66,7 @@ import { } from 'react-query/api/pipeline'; import { toast } from 'sonner'; import { useResetRpcnWizardStore } from 'state/rpcn-wizard-store'; +import { docsLinks } from 'utils/docs-links'; import { formatToastErrorMessageGRPC } from 'utils/toast.utils'; import { TabKafkaConnect } from '../../connect/overview'; @@ -718,7 +719,7 @@ const RedpandaConnectContent = () => ( Redpanda Connect is a data streaming service for building scalable, high-performance data pipelines that drive real-time analytics and actionable business insights. Integrate data across systems with hundreds of prebuilt connectors, change data capture (CDC) capabilities, and YAML-configurable pipelines.{' '} - + Learn more
@@ -770,11 +771,7 @@ export const PipelineListPage = () => {
Kafka Connect is our set of managed connectors. These provide a way to integrate your Redpanda data with different data systems.{' '} - + Learn more
diff --git a/frontend/src/components/pages/rp-connect/pipeline/node-config-form.test.tsx b/frontend/src/components/pages/rp-connect/pipeline/node-config-form.test.tsx index f81463fdd0..7266f10a56 100644 --- a/frontend/src/components/pages/rp-connect/pipeline/node-config-form.test.tsx +++ b/frontend/src/components/pages/rp-connect/pipeline/node-config-form.test.tsx @@ -14,6 +14,12 @@ import { render, screen } from 'test-utils'; import { describe, expect, test, vi } from 'vitest'; import { NodeConfigForm } from './node-config-form'; + +// Cluster topics offered by topic-field pickers. +vi.mock('react-query/api/topic', () => ({ + useListTopicsQuery: () => ({ data: { topics: [{ name: 'orders' }, { name: 'events' }] }, isLoading: false }), +})); + import type { ConnectComponentSpec } from '../types/schema'; import { mockKafkaOutput } from '../utils/__fixtures__/component-schemas'; @@ -26,6 +32,8 @@ function renderForm(value: Record, onConfigChange = vi.fn()) { return onConfigChange; } +const CREATE_NEW_TOPIC_RE = /create new topic/i; + // The most recent config reported by the form (undefined if never called, null when clean). function lastReported(onConfigChange: ReturnType): unknown { return onConfigChange.mock.calls.at(-1)?.[0]; @@ -75,7 +83,7 @@ describe('NodeConfigForm — full schema', () => { test('reports a config only once something changes', async () => { const user = userEvent.setup(); - const onConfigChange = renderForm({ kafka: { topic: 'orig', addresses: ['a:9092'] } }); + const onConfigChange = renderForm({ kafka: { topic: 't', addresses: ['a:9092'], key: 'orig' } }); // Clean on mount → reports null (nothing to commit). expect(lastReported(onConfigChange)).toBeNull(); @@ -85,17 +93,16 @@ describe('NodeConfigForm — full schema', () => { test('reports a changed scalar and keeps the YAML minimal (no empty optionals)', async () => { const user = userEvent.setup(); - const onConfigChange = renderForm({ kafka: { topic: 'orig', addresses: ['a:9092'] } }); + const onConfigChange = renderForm({ kafka: { topic: 't', addresses: ['a:9092'], key: 'orig' } }); - const topic = screen.getByDisplayValue('orig'); - await user.clear(topic); - await user.type(topic, 'new-topic'); + const key = screen.getByDisplayValue('orig'); + await user.clear(key); + await user.type(key, 'new-key'); const next = lastReported(onConfigChange) as { kafka: Record }; - expect(next.kafka.topic).toBe('new-topic'); + expect(next.kafka.key).toBe('new-key'); expect(next.kafka.addresses).toEqual(['a:9092']); // Untouched optional fields are not written out. - expect(next.kafka).not.toHaveProperty('key'); expect(next.kafka).not.toHaveProperty('partitioner'); }); @@ -104,8 +111,9 @@ describe('NodeConfigForm — full schema', () => { // `metadata` is not in the schema; `count: 1000$` is a malformed int — both must survive. const onConfigChange = renderForm({ kafka: { - topic: 'orig', + topic: 't', addresses: ['a:9092'], + key: 'orig', metadata: { include_patterns: ['.*'] }, batching: { count: '1000$' }, }, @@ -156,6 +164,57 @@ describe('NodeConfigForm — full schema', () => { expect(screen.getAllByText(/reference a secret/i).length).toBeGreaterThan(0); }); + test('masks fields flagged secret by the schema even when the name heuristic misses them', () => { + // credentials_json doesn't match SECRET_NAME_RE — only the enrichment stamp catches it. + const secretSpec = { + ...spec, + config: { + ...spec.config, + children: [ + { name: 'credentials_json', type: 'string', kind: 'scalar', secret: true }, + { name: 'project', type: 'string', kind: 'scalar' }, + ], + }, + } as unknown as ConnectComponentSpec; + render( + + ); + + expect(screen.getByDisplayValue('top-secret')).toHaveAttribute('type', 'password'); + expect(screen.getByDisplayValue('p1')).toHaveAttribute('type', 'text'); + }); + + test('deprecated fields are not rendered as form fields; existing values fall to raw YAML', () => { + const deprecatedSpec = { + ...spec, + config: { + ...spec.config, + children: [ + { name: 'topic', type: 'string', kind: 'scalar' }, + { name: 'round_robin_partitions', type: 'string', kind: 'scalar', deprecated: true }, + ], + }, + } as unknown as ConnectComponentSpec; + render( + + ); + + // No form control for the deprecated field… + expect(screen.queryByLabelText('round_robin_partitions')).not.toBeInTheDocument(); + // …but its existing value is preserved in the raw-YAML fallback section. + expect(screen.getByText('Other settings (YAML)')).toBeInTheDocument(); + }); + test('keeps the saved label when a resource label field is cleared (references depend on it)', async () => { const user = userEvent.setup(); const onConfigChange = vi.fn(); @@ -288,3 +347,377 @@ describe('NodeConfigForm — list-valued components (switch/try/…)', () => { expect(screen.getByText(/edited on the canvas/i)).toBeInTheDocument(); }); }); + +describe('NodeConfigForm — all-optional components', () => { + // Mirrors an unstamped redpanda-style input where the schema marks every field optional. + const allOptionalSpec = { + name: 'redpanda', + type: 'input', + config: { + name: '', + type: 'object', + kind: 'scalar', + children: [ + { name: 'topics', type: 'string', kind: 'array', optional: true }, + { name: 'consumer_group', type: 'string', kind: 'scalar', optional: true }, + ], + }, + } as unknown as ConnectComponentSpec; + + test('renders fields plainly instead of burying everything under an "Optional" group', () => { + render(); + expect(screen.getByText('topics')).toBeInTheDocument(); + expect(screen.getByText('consumer_group')).toBeInTheDocument(); + // With no required fields to contrast against, the "Optional" label only misleads. + expect(screen.queryByText('Optional')).toBeNull(); + }); +}); + +describe('NodeConfigForm — field-anchored lint errors', () => { + test('renders the message under its field and hides it while the field is being edited', async () => { + const user = userEvent.setup(); + render( + + ); + + expect(screen.getByText('key must not be empty')).toBeInTheDocument(); + + // Typing in the field means the user is addressing it — the stale message gets out of the way. + await user.type(screen.getByDisplayValue('k'), 'ey-name'); + expect(screen.queryByText('key must not be empty')).not.toBeInTheDocument(); + }); + + test('renders a group-anchored error inside its (auto-opened) section', () => { + render( + + ); + // The batching group opens and shows the error at its top — no line numbers, no banner. + expect(screen.getByText('expected object value, got !!null')).toBeInTheDocument(); + }); + + test('a group stays open (state intact, no remount) after its error clears', () => { + const renderProps = (fieldErrors?: Map) => ( + + ); + const { rerender } = render(renderProps(new Map([['batching/count', ['expected number value']]]))); + // The error opened the batching group. + expect(screen.getByText('expected number value')).toBeInTheDocument(); + expect(screen.getByDisplayValue('5')).toBeInTheDocument(); + + // Lint clears (e.g. after a blur-commit): the group must NOT slam shut around the user. + rerender(renderProps(undefined)); + expect(screen.queryByText('expected number value')).not.toBeInTheDocument(); + expect(screen.getByDisplayValue('5')).toBeInTheDocument(); + }); + + test('renders an error anchored to the label field', () => { + render( + + ); + expect(screen.getByText('label collides with a previously defined label')).toBeInTheDocument(); + }); + + test('opens the Advanced group when it hides an errored field', () => { + render( + + ); + // client_id is an advanced field — visible without manually expanding the group. + expect(screen.getByText('invalid client id')).toBeInTheDocument(); + expect(screen.getByText('client_id')).toBeInTheDocument(); + }); +}); + +describe('NodeConfigForm — field-level commit', () => { + test('commits on field blur and marks the form clean on success', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + const onCommitField = vi.fn(() => true); + render( + + ); + + await user.type(screen.getByDisplayValue('orig'), '-2'); + expect(lastReported(onConfigChange)).not.toBeNull(); + + // Leaving the field (tab away) flushes the draft — no node deselect needed. + await user.tab(); + expect(onCommitField).toHaveBeenCalled(); + // The commit landed, so the form is clean again (reports null) but keeps the typed value. + expect(lastReported(onConfigChange)).toBeNull(); + expect(screen.getByDisplayValue('orig-2')).toBeInTheDocument(); + }); + + test('a blur commit does not silently revert an uncommitted malformed edit', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + const onCommitField = vi.fn(() => true); + render( + + ); + + // A malformed numeric ("won't be saved until fixed") plus a valid edit. + await user.click(screen.getByText('batching')); + const count = screen.getByDisplayValue('5'); + await user.clear(count); + await user.type(count, '10x'); + await user.type(screen.getByDisplayValue('orig'), '-2'); + + await user.tab(); + expect(onCommitField).toHaveBeenCalled(); + // The malformed edit stays visible and pending — not silently reverted to the saved value. + expect(screen.getByDisplayValue('10x')).toBeInTheDocument(); + expect(lastReported(onConfigChange)).not.toBeNull(); + }); + + test('keeps the draft pending when the commit fails', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + const onCommitField = vi.fn(() => false); + render( + + ); + + await user.type(screen.getByDisplayValue('orig'), '-2'); + await user.tab(); + expect(onCommitField).toHaveBeenCalled(); + // Write failed: the draft must survive so the node-leave flush can retry it. + expect(lastReported(onConfigChange)).not.toBeNull(); + }); + + test('shows an Apply affordance while dirty and commits through it', async () => { + const user = userEvent.setup(); + const onCommitField = vi.fn(() => true); + render( + + ); + + expect(screen.queryByRole('button', { name: 'Apply' })).not.toBeInTheDocument(); + await user.type(screen.getByDisplayValue('orig'), '-2'); + const apply = screen.getByRole('button', { name: 'Apply' }); + await user.click(apply); + expect(onCommitField).toHaveBeenCalled(); + // Clean again — the pending-edits footer goes away. + expect(screen.queryByRole('button', { name: 'Apply' })).not.toBeInTheDocument(); + }); + + test('re-syncs a clean form in place when the saved value changes externally', () => { + const { rerender } = render( + + ); + expect(screen.getByDisplayValue('orig')).toBeInTheDocument(); + + // e.g. the Topic dialog wrote into this component while its inspector is open. + rerender( + + ); + expect(screen.getByDisplayValue('from-dialog')).toBeInTheDocument(); + }); +}); + +describe('NodeConfigForm — topic fields', () => { + test('a topic scalar offers existing cluster topics; selecting one reports it', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + render( + + ); + + await user.click(screen.getByPlaceholderText('Select or enter a topic…')); + await user.click(await screen.findByText('orders')); + + const next = lastReported(onConfigChange) as { kafka: Record }; + expect(next.kafka.topic).toBe('orders'); + }); + + test('the create-topic affordance opens the Add-topic flow', async () => { + const user = userEvent.setup(); + const onCreateTopic = vi.fn(); + render( + + ); + + await user.click(screen.getByRole('button', { name: CREATE_NEW_TOPIC_RE })); + expect(onCreateTopic).toHaveBeenCalled(); + }); + + test('typing a custom topic reports it without needing Enter or a selection', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + render( + + ); + + const input = screen.getByPlaceholderText('Select or enter a topic…'); + await user.clear(input); + await user.type(input, 'custom:0'); + + // A blur-commit must see the typed text — the combobox alone only fires onChange on Enter/selection. + const next = lastReported(onConfigChange) as { kafka: Record }; + expect(next.kafka.topic).toBe('custom:0'); + }); + + test('topic fields on non-Redpanda connectors stay plain inputs', () => { + const onCreateTopic = vi.fn(); + const mqttSpec = { + name: 'mqtt', + type: 'output', + config: { + name: '', + type: 'object', + kind: 'scalar', + children: [{ name: 'topic', type: 'string', kind: 'scalar', optional: false }], + }, + } as unknown as ConnectComponentSpec; + render( + + ); + + // An mqtt topic is not a cluster topic: no picker, no create-topic affordance. + expect(screen.queryByPlaceholderText('Select or enter a topic…')).not.toBeInTheDocument(); + expect(screen.queryByRole('button', { name: CREATE_NEW_TOPIC_RE })).not.toBeInTheDocument(); + expect(screen.getByDisplayValue('sensors/temp')).toBeInTheDocument(); + }); + + test('a topics list keeps free text but can append an existing cluster topic', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + const topicsSpec = { + name: 'redpanda', + type: 'input', + config: { + name: '', + type: 'object', + kind: 'scalar', + children: [{ name: 'topics', type: 'string', kind: 'array', optional: true }], + }, + } as unknown as ConnectComponentSpec; + render( + + ); + + await user.click(screen.getByPlaceholderText('Add existing topic…')); + await user.click(await screen.findByText('events')); + + // The free-text line (explicit partition syntax) survives; the picked topic is appended. + const next = lastReported(onConfigChange) as { redpanda: { topics: string[] } }; + expect(next.redpanda.topics).toEqual(['my-topic:0', 'events']); + }); +}); + +describe('NodeConfigForm — enum (select) fields', () => { + const enumSpec = { + name: 'redpanda', + type: 'input', + config: { + name: '', + type: 'object', + kind: 'scalar', + children: [ + { + name: 'transaction_isolation_level', + type: 'string', + kind: 'scalar', + optional: false, + defaultValue: 'read_uncommitted', + annotatedOptions: [{ value: 'read_uncommitted' }, { value: 'read_committed' }], + }, + ], + }, + } as unknown as ConnectComponentSpec; + + test('a set enum value can be unset, removing the key from the config', async () => { + const user = userEvent.setup(); + const onConfigChange = vi.fn(); + render( + + ); + + await user.click(screen.getByRole('combobox', { name: 'transaction_isolation_level' })); + await user.click(await screen.findByText('Default (read_uncommitted)')); + + const next = lastReported(onConfigChange) as { redpanda: Record }; + expect(next.redpanda).not.toHaveProperty('transaction_isolation_level'); + }); +}); diff --git a/frontend/src/components/pages/rp-connect/pipeline/node-config-form.tsx b/frontend/src/components/pages/rp-connect/pipeline/node-config-form.tsx index 5421ed39d9..dca071db3b 100644 --- a/frontend/src/components/pages/rp-connect/pipeline/node-config-form.tsx +++ b/frontend/src/components/pages/rp-connect/pipeline/node-config-form.tsx @@ -9,7 +9,9 @@ * by the Apache License, Version 2.0 */ +import { Button } from 'components/redpanda-ui/components/button'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from 'components/redpanda-ui/components/collapsible'; +import { Combobox } from 'components/redpanda-ui/components/combobox'; import { CountDot } from 'components/redpanda-ui/components/count-dot'; import { Input } from 'components/redpanda-ui/components/input'; import { Label } from 'components/redpanda-ui/components/label'; @@ -24,13 +26,15 @@ import { Switch } from 'components/redpanda-ui/components/switch'; import { Textarea } from 'components/redpanda-ui/components/textarea'; import { cn } from 'components/redpanda-ui/lib/utils'; import { YamlEditor } from 'components/ui/yaml/yaml-editor'; -import { ChevronDown, ChevronRight, Plus } from 'lucide-react'; -import { createContext, useContext, useEffect, useId } from 'react'; +import { AlertCircle, ChevronDown, ChevronRight, Plus } from 'lucide-react'; +import { createContext, useContext, useEffect, useId, useRef, useState } from 'react'; import { type Control, Controller, type FieldPath, useForm, useWatch } from 'react-hook-form'; +import { useListTopicsQuery } from 'react-query/api/topic'; import { parse as parseYaml, stringify as yamlStringify } from 'yaml'; +import type { FieldLintErrors } from './lint-field-mapping'; import { ScrollShadow } from './scroll-shadow'; -import { getSecretSyntax } from '../types/constants'; +import { getSecretSyntax, REDPANDA_TOPIC_AND_USER_COMPONENTS } from '../types/constants'; import type { ConnectComponentSpec, RawFieldSpec } from '../types/schema'; import { checkRequired, @@ -41,6 +45,7 @@ import { isScalarArrayField, isScalarField, } from '../utils/schema'; +import { isTopicField } from '../utils/wizard'; import type { EditTarget, ResourceKind } from '../utils/yaml'; import { resourceKindForComponentName } from '../utils/yaml'; @@ -133,13 +138,20 @@ export const ChildItemsList = ({
); -// Resource-link context (existing labels + create-and-link), provided by the inspector so the form -// stays a pure config editor. +// Cluster-linking context (resource labels, create-resource, create-topic), provided by the +// inspector so the form stays a pure config editor. Context rather than props because the +// consumers are leaf controls several generic layers down (SchemaFields → SchemaField → +// Scalar/ArrayField → controls) — drilling would thread cluster concerns through every layer. type ResourceFieldContextValue = { labels: Record; onCreateResource?: (kind: ResourceKind) => void; // Kind of the edited component, so a plainly-typed `resource:` string field still reads as a link. componentResourceKind?: ResourceKind; + // True when the component's topic/topics fields name topics on THIS cluster (kafka/redpanda + // family) — gates the pickers, so an mqtt/pulsar topic field stays a plain input. + clusterTopicFields?: boolean; + /** Opens the Add-topic dialog; the created topic is written into the component's topic field. */ + onCreateTopic?: () => void; }; const ResourceFieldContext = createContext({ labels: { cache: [], rate_limit: [] } }); @@ -284,11 +296,14 @@ function coerceScalar(spec: RawFieldSpec, raw: string | boolean): string | numbe return text; } -function coerceArrayItems(spec: RawFieldSpec, text: string): unknown[] { - const lines = text +const arrayFieldLines = (text: string): string[] => + text .split('\n') .map((l) => l.trim()) .filter((l) => l !== ''); + +function coerceArrayItems(spec: RawFieldSpec, text: string): unknown[] { + const lines = arrayFieldLines(text); if (spec.type === 'int' || spec.type === 'float') { return lines.map(Number).filter((n) => !Number.isNaN(n)); } @@ -302,6 +317,9 @@ function collectLeaves(fields: RawFieldSpec[], base: string[] = []): { scalars: const scalars: Leaf[] = []; const arrays: Leaf[] = []; for (const f of fields) { + if (f.deprecated) { + continue; + } const path = [...base, f.name]; const leaf: Leaf = { spec: f, path, key: path.join('/') }; if (isScalarField(f)) { @@ -317,6 +335,76 @@ function collectLeaves(fields: RawFieldSpec[], base: string[] = []): { scalars: return { scalars, arrays }; } +// Paths of the nested object groups the form renders as collapsible sections (tls, batching, …). +function collectGroupKeys(fields: RawFieldSpec[], base: string[] = [], out: string[] = []): string[] { + for (const f of fields) { + if (f.deprecated || !isObjectGroupField(f)) { + continue; + } + const path = [...base, f.name]; + out.push(path.join('/')); + collectGroupKeys(f.children ?? [], path, out); + } + return out; +} + +/** + * Keys the schema form renders — leaf inputs, `label`, and object-group sections — i.e. the + * anchors lint errors can attach to. A group anchor renders at the top of its (auto-opened) + * section, which is as specific as an error on the group itself (`batching: null`) can get. + */ +export function formFieldKeys(spec: ConnectComponentSpec): ReadonlySet { + const fields = spec.config?.children ?? []; + const leaves = collectLeaves(fields); + return new Set([ + 'label', + ...leaves.scalars.map((l) => l.key), + ...leaves.arrays.map((l) => l.key), + ...collectGroupKeys(fields), + ]); +} + +// Lint messages anchored to field keys, provided by the inspector; read by the recursive field +// renderers (a prop would have to thread through every SchemaField level). +const FieldLintErrorsContext = createContext(new Map()); + +// Lint errors anchored under this field. Hidden while the field is dirty — the user is addressing +// it, and the message describes the SAVED value, not what they're typing. +const FieldLintErrorList = ({ fieldKey, dirty }: { fieldKey: string; dirty: boolean }) => { + const errors = useContext(FieldLintErrorsContext).get(fieldKey); + if (!errors || errors.length === 0 || dirty) { + return null; + } + return ( +
+ {errors.map((message, i) => ( + // Indexed key: identical messages can anchor to one field, and the list is rebuilt per render. +
+ + {message} +
+ ))} +
+ ); +}; + +// The lint errors anchored at `prefix` or anywhere under it, joined into one signature string. +// Groups force-open when the signature CHANGES: mere truthiness would leave errors that arrive +// after a manual collapse invisible, while reopening on every lint refresh would fight the user. +function lintErrorSignatureUnder(errors: FieldLintErrors, prefix: string): string { + const messages: string[] = []; + for (const [key, list] of errors) { + if (key === prefix || key.startsWith(`${prefix}/`)) { + messages.push(...list); + } + } + return messages.join('\n'); +} + +function useLintErrorSignatureUnder(prefix: string[]): string { + return lintErrorSignatureUnder(useContext(FieldLintErrorsContext), prefix.join('/')); +} + type FormValues = { label: string; raw: string; @@ -443,17 +531,20 @@ function buildComponentEntry({ const FieldLabel = ({ spec, htmlFor }: { spec: RawFieldSpec; htmlFor?: string }) => (
-