diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 543c33a..11d9e4a 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -18,13 +18,12 @@ jobs: fail-fast: false matrix: otp: - - 25.3.2-2 + - 26.2.5-2 elixir: - - 1.14.5 + - 1.15.7 os: - ubuntu22.04 - - container: "ghcr.io/emqx/emqx-builder/5.2-3:${{ matrix.elixir }}-${{ matrix.otp }}-${{ matrix.os }}" + container: "ghcr.io/emqx/emqx-builder/5.3-8:${{ matrix.elixir }}-${{ matrix.otp }}-${{ matrix.os }}" steps: - name: Checkout diff --git a/.tool-versions b/.tool-versions index 4bfd461..688b1e2 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -erlang 25.3.2 -elixir 1.14.5-otp-25 +erlang 26.2.5-2 +elixir 1.15.7-otp-26 diff --git a/README.md b/README.md index 807c7cd..5772cb6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Example EMQX Elixir Plugin Template -An example Mix project that can be used to build an EMQX 5.0.0 plugin. +An example Mix project that can be used to build an EMQX 5.7.0 plugin. ## Quickstart @@ -33,3 +33,18 @@ An example Mix project that can be used to build an EMQX 5.0.0 plugin. topic: "topic" } ``` + +## Custom configuration schema + +EMQX 5.7.0 introduced the Avro configuration schema feature which allows plugins to define their own config schema to be used and managed. In order to use this feature: + +- Be sure to use EMQX 5.7.0 or newer; +- Rename and edit the following files: + - `priv/config.hocon.example` -> `priv/config.hocon` + - `priv/config_i18n.json.example` -> `priv/config_i18n.json` + - `priv/config_schema.avsc.example` -> `priv/config_schema.avsc` + - Refer to `priv/config_schema.avsc.enterprise.example` if using EMQX Enterprise + Edition. +- Set `:with_config_schema?` to `true` under the `emqx_plugin_opts` release config in + `mix.exs`. +- Add `:emqx_plugins` as a dependency by uncommenting the line containing `emqx_dep.(:emqx_plugins)` in `mix.exs`'s dependencies. diff --git a/lib/elixir_plugin_template.ex b/lib/elixir_plugin_template.ex index 7267017..6601011 100644 --- a/lib/elixir_plugin_template.ex +++ b/lib/elixir_plugin_template.ex @@ -1,6 +1,8 @@ defmodule ElixirPluginTemplate do use GenServer + require Logger + @moduledoc """ A dummy example server """ @@ -20,6 +22,15 @@ defmodule ElixirPluginTemplate do @impl GenServer def init(_) do + ## If your plugin defines a custom configuration schema, you may access it with + ## `:emqx_plugins.get_config`. + ## Ex: + # this_app = Application.get_application(__MODULE__) + # with {:ok, vsn} <- :application.get_key(this_app, :vsn), + # app_vsn = IO.iodata_to_binary([to_string(this_app), "-", vsn]), + # {:ok, %{"hostname" => hostname, "port" => port}} <- :emqx_plugins.get_config(app_vsn) do + # Logger.warning("using server #{hostname}:#{port}") + # end {:ok, %{pings: 0}} end @@ -30,10 +41,12 @@ defmodule ElixirPluginTemplate do end @impl GenServer - def handle_cast({:log, msg}, state) do - msg - |> :emqx_message.to_map() - |> IO.inspect(label: :emqx_msg) + def handle_cast({:log, _msg}, state) do + ## Do stuff here. + ## Ex: + # msg + # |> :emqx_message.to_map() + # |> tap(&Logger.debug/1) {:noreply, state} end diff --git a/mix.exs b/mix.exs index fe9e1a4..3d57d14 100644 --- a/mix.exs +++ b/mix.exs @@ -47,13 +47,20 @@ defmodule ElixirPluginTemplate.MixProject do repo: "https://github.com/emqx/emqx-elixir-plugin-template", functionality: ["Demo"], compatibility: %{ - emqx: "~> 5.0" + emqx: "~> 5.7.0" }, description: "This is a demo plugin" }, emqx_plugin_opts: [ include_src?: true, - exclude_elixir?: false + ## Set this to `false` if your EMQX installation does not already ship with + ## Elixir. + exclude_elixir?: true, + ## Set`:with_config_schema?` to `true` if your application uses its own Avro + ## configuration. + ## See `priv/config.hocon.example`, `config_i18n.json.example`, and + ## `priv/config_schema.avsc{.enterprise,}.example` files for examples. + with_config_schema?: false ] ] ] @@ -174,6 +181,7 @@ defmodule ElixirPluginTemplate.MixProject do end defp make_plugin_release_json(release, lib_dirs) do + with_config_schema? = get_in(release.options, [:emqx_plugin_opts, :with_config_schema?]) extra_info = %{ name: release.name, rel_vsn: release.version, @@ -182,7 +190,8 @@ defmodule ElixirPluginTemplate.MixProject do git_commit_or_build_date: get_date(), metadata_vsn: @emqx_metadata_vsn, built_on_otp_release: System.otp_release(), - built_on_elixir_release: System.version() + built_on_elixir_release: System.version(), + with_config_schema: with_config_schema? } release.options[:emqx_plugin_info] @@ -225,33 +234,33 @@ defmodule ElixirPluginTemplate.MixProject do # Run "mix help deps" to learn about dependencies. defp deps do + # emqx v5.7.0 + emqx_ref = "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c" + emqx_dep = fn app -> + {app, + git: "https://github.com/emqx/emqx", + ref: emqx_ref, + sparse: "apps/#{app}", + override: true, + runtime: false} + end [ # This is just for building the tarball. Remove `runtime: # false` if your application depends on this lib. {:jason, "~> 1.3", runtime: false}, # If your plugin depends on emqx, you may include it here. Be # sure to use `runtime: false`. - {:emqx, - git: "https://github.com/emqx/emqx", - ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", - sparse: "apps/emqx", - runtime: false}, - {:emqx_utils, - git: "https://github.com/emqx/emqx", - ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", - sparse: "apps/emqx_utils", - runtime: false, - override: true}, - {:emqx_durable_storage, - git: "https://github.com/emqx/emqx", - ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", - sparse: "apps/emqx_durable_storage", - runtime: false, - override: true, - app: false}, + emqx_dep.(:emqx), + emqx_dep.(:emqx_ctl), + emqx_dep.(:emqx_utils), + emqx_dep.(:emqx_durable_storage), + ## Uncomment the `:emqx_plugins` line if your application uses custom configuration + ## schema: + # emqx_dep.(:emqx_plugins), # temporarily needed due to clashing dependencies of # dependencies of emqx. {:cowlib, "2.8.0", override: true, runtime: false}, + {:jiffy, github: "emqx/jiffy", tag: "1.0.6", override: true, runtime: false}, # These are dependencies for your plugin {:hallux, "~> 1.2"} ] diff --git a/mix.lock b/mix.lock index 6eaa361..e02a415 100644 --- a/mix.lock +++ b/mix.lock @@ -1,33 +1,38 @@ %{ + "aten": {:hex, :aten, "0.6.0", "7a57b275a6daf515ac3683fb9853e280b4d0dcdd74292fd66ac4a01c8694f8c7", [:rebar3], [], "hexpm", "5f39a164206ae3f211ef5880b1f7819415686436e3229d30b6a058564fbaa168"}, "bcrypt": {:git, "https://github.com/emqx/erlang-bcrypt.git", "dc2ba66acf2332c111362d01137746eefecc5e90", [tag: "0.6.0"]}, "cowboy": {:git, "https://github.com/emqx/cowboy", "f4a05678e3e66633d4e3c25bc390c02b8996918e", [tag: "2.9.2"]}, "cowlib": {:hex, :cowlib, "2.8.0", "fd0ff1787db84ac415b8211573e9a30a3ebe71b5cbff7f720089972b2319c8a4", [:rebar3], [], "hexpm", "79f954a7021b302186a950a32869dbc185523d99d3e44ce430cd1f3289f41ed4"}, "eetcd": {:git, "https://github.com/zhongwencool/eetcd", "69d50aca98247953ee8a3ff58423a693f8318d90", [tag: "v0.3.4"]}, - "ekka": {:git, "https://github.com/emqx/ekka", "2912b6938ae4562f0ac3a96c7075de464524dcd6", [tag: "0.15.14"]}, - "emqx": {:git, "https://github.com/emqx/emqx", "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", [ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", sparse: "apps/emqx"]}, - "emqx_durable_storage": {:git, "https://github.com/emqx/emqx", "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", [ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", sparse: "apps/emqx_durable_storage"]}, + "ekka": {:git, "https://github.com/emqx/ekka", "27840afe2f0d9e04e63a3a73cc18be449443908b", [tag: "0.19.3"]}, + "emqx": {:git, "https://github.com/emqx/emqx", "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", [ref: "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", sparse: "apps/emqx"]}, + "emqx_ctl": {:git, "https://github.com/emqx/emqx", "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", [ref: "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", sparse: "apps/emqx_ctl"]}, + "emqx_durable_storage": {:git, "https://github.com/emqx/emqx", "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", [ref: "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", sparse: "apps/emqx_durable_storage"]}, "emqx_http_lib": {:git, "https://github.com/emqx/emqx_http_lib.git", "6adc836747d346635066801e38c60de4f7f6fe20", [tag: "0.5.3"]}, - "emqx_utils": {:git, "https://github.com/emqx/emqx", "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", [ref: "7b3ce3cbc740af4707f5bef5aee499f1e9842ac9", sparse: "apps/emqx_utils"]}, - "esockd": {:git, "https://github.com/emqx/esockd", "04e20992b3be10d453adc3da503c625b5f452531", [tag: "5.9.7"]}, - "gen_rpc": {:git, "https://github.com/emqx/gen_rpc", "f1029663e632ab03c1ab7d66b12bce4cbea7e6e6", [tag: "3.1.0"]}, + "emqx_utils": {:git, "https://github.com/emqx/emqx", "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", [ref: "d885ac3dd8626fb6ad3d246db279c922e4eb5a0c", sparse: "apps/emqx_utils"]}, + "esockd": {:git, "https://github.com/emqx/esockd", "313713eff1ecaf4d5c9e6d575ebd1b5f4e14e9c5", [tag: "5.11.2"]}, + "gen_batch_server": {:hex, :gen_batch_server, "0.8.8", "7840a1fa63ee1effc83e8a91d22664847a2ba1192d30eafffd914acb51578068", [:rebar3], [], "hexpm", "c3e6a1a2a0fb62aee631a98cfa0fd8903e9562422cbf72043953e2fb1d203017"}, + "gen_rpc": {:git, "https://github.com/emqx/gen_rpc", "3251f41065786945580be7f0eadeb4246d3b256a", [tag: "3.3.1"]}, "getopt": {:hex, :getopt, "1.0.1", "c73a9fa687b217f2ff79f68a3b637711bb1936e712b521d8ce466b29cbf7808a", [:rebar3], [], "hexpm", "53e1ab83b9ceb65c9672d3e7a35b8092e9bdc9b3ee80721471a161c10c59959c"}, "gproc": {:git, "https://github.com/emqx/gproc", "21a5995812498969bb5e47b520b47ea7c514f16b", [tag: "0.9.0.1"]}, "gun": {:hex, :gun, "1.3.3", "cf8b51beb36c22b9c8df1921e3f2bc4d2b1f68b49ad4fbc64e91875aa14e16b4", [:rebar3], [{:cowlib, "~> 2.7.0", [hex: :cowlib, repo: "hexpm", optional: false]}], "hexpm", "3106ce167f9c9723f849e4fb54ea4a4d814e3996ae243a1c828b256e749041e0"}, "hallux": {:hex, :hallux, "1.2.0", "0d26c4f4db8a988683c5a2fadd0c02807e72e26c09d2a57b2ee670fff8397c75", [:mix], [], "hexpm", "6d19ea15b2603bfbf584b6b016abb97c9512b2fad6f55d4f2933061bdce9e86b"}, - "hocon": {:git, "https://github.com/emqx/hocon.git", "de7428d83cb048556ba49ca30867c7fd0948d06b", [tag: "0.39.16"]}, + "hocon": {:git, "https://github.com/emqx/hocon.git", "a796199f672b2ff8f0286b9af70513d00fc4ccb9", [tag: "0.42.2"]}, "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, - "jiffy": {:git, "https://github.com/emqx/jiffy", "baa1f4e750ae3c5c9e54f9c2e52280b7fc24a8d9", [tag: "1.0.5"]}, + "jiffy": {:git, "https://github.com/emqx/jiffy.git", "8727c771d56978cf121316b2d88761467b151420", [tag: "1.0.6"]}, "lc": {:git, "https://github.com/emqx/lc.git", "ef76ab213f78f5efcc49a30ae3b41822c6db1ba0", [tag: "0.3.2"]}, - "mnesia_rocksdb": {:git, "https://github.com/emqx/mnesia_rocksdb", "99d9193778d0fa545b722369319ab0c76a544ceb", [tag: "0.1.14"]}, - "mria": {:git, "https://github.com/emqx/mria", "efda2264d5f634c6ea387341151d9f2a3a5bd417", [tag: "0.6.3"]}, + "mnesia_rocksdb": {:git, "https://github.com/emqx/mnesia_rocksdb", "6ae456e536b8cb8f3ffa33bbe57dc90484eb1479", [tag: "0.1.15"]}, + "mria": {:git, "https://github.com/emqx/mria", "21c5954ff4db050be651d37e753f882d3b707813", [tag: "0.8.6"]}, "optvar": {:git, "https://github.com/emqx/optvar", "2331bbff4ddce7d9dc64698936a9d158174f3315", [tag: "1.0.5"]}, "pbkdf2": {:git, "https://github.com/emqx/erlang-pbkdf2.git", "45d9981209ea07a83a58cf85aaf8236457da4342", [tag: "2.0.4"]}, - "quicer": {:git, "https://github.com/emqx/quic.git", "ef73617d0f10f0f30f3aa77eb4a2f6ae071a2e29", [tag: "0.0.9"]}, + "quicer": {:git, "https://github.com/emqx/quic.git", "69de6f1c81629e3dbee32a9eea4aed3b5455b7b6", [tag: "0.0.313"]}, + "ra": {:hex, :ra, "2.7.3", "e2d98860f13e671388bbb8cae740f61c0df7d7589c1d59e556cdac491eff9c77", [:rebar3], [{:aten, "0.6.0", [hex: :aten, repo: "hexpm", optional: false]}, {:gen_batch_server, "0.8.8", [hex: :gen_batch_server, repo: "hexpm", optional: false]}, {:seshat, "0.6.0", [hex: :seshat, repo: "hexpm", optional: false]}], "hexpm", "6f2b912a779f4efa4deea762b65192ed6e87111c7d98cbbe8a29576964739147"}, "ranch": {:git, "https://github.com/emqx/ranch", "d3d6420b190b4ed084b3a5db82c35a3eeae75212", [tag: "1.8.1-emqx"]}, "recon": {:git, "https://github.com/ferd/recon", "f7b6c08e6e9e2219db58bfb012c58c178822e01e", [tag: "2.5.1"]}, "replayq": {:git, "https://github.com/emqx/replayq", "1d389ac552eb6952ce515298ef60284df659e617", [tag: "0.3.6"]}, - "rocksdb": {:git, "https://github.com/emqx/erlang-rocksdb.git", "5c08f88b045b2b09a474c2f1ee894fb2740bcdae", [tag: "1.8.0-emqx-1"]}, + "rocksdb": {:git, "https://github.com/emqx/erlang-rocksdb.git", "27226d982f5f74965190672bbca5e348a9d9ea94", [tag: "1.8.0-emqx-2"]}, + "seshat": {:hex, :seshat, "0.6.0", "3172eb1d7a2a4f66108cd6933a4e465aff80f84aa90ed83f047b92f636123ccd", [:rebar3], [], "hexpm", "7cef700f92831dd7cae6a6dd223ccc55ac88ecce0631ee9ab0f2b5fb70e79b90"}, "sext": {:hex, :sext, "1.8.0", "90a95b889f5c781b70bbcf44278b763148e313c376b60d87ce664cb1c1dd29b5", [:rebar3], [], "hexpm", "bc6016cb8690baf677eacacfe6e7cadfec8dc7e286cbbed762f6cd55b0678e73"}, - "snabbkaffe": {:git, "https://github.com/kafka4beam/snabbkaffe.git", "732ac2c9f2daff1d5c2fe4bd225037d5820095a0", [tag: "1.0.8"]}, + "snabbkaffe": {:git, "https://github.com/kafka4beam/snabbkaffe.git", "b59298334ed349556f63405d1353184c63c66534", [tag: "1.0.10"]}, "typerefl": {:git, "https://github.com/ieQu1/typerefl.git", "310b82ff02f96207c519b9556491433b6ea02d01", [tag: "0.9.1"]}, } diff --git a/priv/config.hocon.example b/priv/config.hocon.example new file mode 100644 index 0000000..aac3d33 --- /dev/null +++ b/priv/config.hocon.example @@ -0,0 +1,20 @@ +## This is a demo config in HOCON format +## The same format used by EMQX since 5.0 + +hostname = "localhost" +port = 3306 + +connectionOptions = [ + { + optionName = "autoReconnect" + optionType = "string" + optionValue = "true" + } +] + +auth { + username = "admin" + password { + string = "Public123" + } +} diff --git a/priv/config_i18n.json.example b/priv/config_i18n.json.example new file mode 100644 index 0000000..b474db7 --- /dev/null +++ b/priv/config_i18n.json.example @@ -0,0 +1,74 @@ +{ + "$hostname_label": { + "zh": "主机名", + "en": "Hostname" + }, + "$hostname_desc": { + "zh": "主机名是一个标识符,用于识别网络上的设备。主机名通常是一个域名,例如:www.example.com。", + "en": "The hostname is an identifier used to identify devices on a network. The hostname is usually a domain name, such as www.example.com." + }, + "$hostname_validate": { + "zh": "主机名必须是一个有效的域名。", + "en": "The hostname must be a valid domain name." + }, + "$port_label": { + "zh": "端口", + "en": "Port" + }, + "$port_desc": { + "zh": "端口是一个数字,用于标识网络上的服务。常见的端口有:80(HTTP)、443(HTTPS)、21(FTP)。", + "en": "The port is a number used to identify services on a network. Common ports include: 80 (HTTP), 443 (HTTPS), 21 (FTP)." + }, + "$port_range_validate": { + "zh": "端口必须在 1 到 65535 之间。", + "en": "The port must be between 1 and 65535." + }, + "$connection_options_label": { + "en": "Connection Options", + "zh": "连接选项" + }, + "$connection_options_desc": { + "en": "A list of additional options for the database connection.", + "zh": "数据库连接的附加选项列表。" + }, + "$username_label": { + "en": "Username", + "zh": "用户名" + }, + "$username_desc": { + "zh": "连接数据库的用户名", + "en": "The username used to connect to the database." + }, + "$password_label": { + "en": "Password", + "zh": "密码" + }, + "$password_desc": { + "en": "The password used to connect to the database.", + "zh": "连接数据库的密码。" + }, + "$password_length_validate": { + "en": "The password must be at least 8 characters long.", + "zh": "密码长度必须至少为 8 个字符。" + }, + "$password_validate": { + "en": "The password must contain at least one uppercase letter, one lowercase letter, one number, and one special character.", + "zh": "密码必须包含至少一个大写字母、一个小写字母、一个数字和一个特殊字符。" + }, + "$option_name_label": { + "en": "Option Name", + "zh": "选项名称" + }, + "$option_name_desc": { + "en": "The name of the connection option.", + "zh": "连接选项的名称。" + }, + "$option_value_label": { + "en": "Option Value", + "zh": "选项值" + }, + "$option_value_desc": { + "en": "The value of the connection option.", + "zh": "连接选项的值。" + } +} diff --git a/priv/config_schema.avsc.enterprise.example b/priv/config_schema.avsc.enterprise.example new file mode 100644 index 0000000..851bea2 --- /dev/null +++ b/priv/config_schema.avsc.enterprise.example @@ -0,0 +1,141 @@ +{ + "type": "record", + "name": "ExtendedConfig", + "fields": [ + { + "name": "hostname", + "type": "string", + "default": "localhost", + "$ui": { + "component": "input", + "flex": 12, + "required": true, + "label": "$hostname_label", + "description": "$hostname_desc", + "rules": [ + { + "type": "pattern", + "pattern": "^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])(\\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]))*$", + "message": "$hostname_validate" + } + ] + } + }, + { + "name": "port", + "type": "int", + "default": 3306, + "$ui": { + "component": "input-number", + "flex": 12, + "required": true, + "label": "$port_label", + "description": "$port_desc", + "rules": [ + { + "type": "range", + "min": 1, + "max": 65535, + "message": "$port_range_validate" + } + ] + } + }, + { + "name": "connectionOptions", + "type": { + "type": "array", + "items": { + "type": "record", + "name": "ConnectionOption", + "fields": [ + { + "name": "optionName", + "type": "string" + }, + { + "name": "optionValue", + "type": "string" + }, + { + "name": "optionType", + "type": "string" + } + ] + } + }, + "default": [ + { + "optionName": "autoReconnect", + "optionValue": "true", + "optionType": "boolean" + } + ], + "$ui": { + "component": "maps-editor", + "flex": 24, + "items": { + "optionName": { + "label": "$option_name_label", + "description": "$option_name_desc", + "type": "string" + }, + "optionValue": { + "label": "$option_value_label", + "description": "$option_value_desc", + "type": "string" + } + }, + "label": "$connection_options_label", + "description": "$connection_options_desc" + } + }, + { + "name": "auth", + "type": { + "type": "record", + "name": "authConfigs", + "fields": [ + { + "name": "username", + "type": "string", + "$ui": { + "component": "input", + "flex": 12, + "required": true, + "label": "$username_label", + "description": "$username_desc" + } + }, + { + "name": "password", + "type": [ + "null", + "string" + ], + "default": null, + "$ui": { + "component": "input-password", + "flex": 12, + "label": "$password_label", + "description": "$password_desc", + "rules": [ + { + "type": "length", + "minLength": 8, + "maxLength": 128, + "message": "$password_length_validate" + }, + { + "type": "pattern", + "pattern": "^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)[a-zA-Z\\d]*$", + "message": "$password_validate" + } + ] + } + } + ] + } + } + ] +} diff --git a/priv/config_schema.avsc.example b/priv/config_schema.avsc.example new file mode 100644 index 0000000..a349c06 --- /dev/null +++ b/priv/config_schema.avsc.example @@ -0,0 +1,68 @@ +{ + "type": "record", + "name": "ExtendedConfig", + "fields": [ + { + "name": "hostname", + "type": "string", + "default": "localhost" + }, + { + "name": "port", + "type": "int", + "default": 3306 + }, + { + "name": "connectionOptions", + "type": { + "type": "array", + "items": { + "type": "record", + "name": "ConnectionOption", + "fields": [ + { + "name": "optionName", + "type": "string" + }, + { + "name": "optionValue", + "type": "string" + }, + { + "name": "optionType", + "type": "string" + } + ] + } + }, + "default": [ + { + "optionName": "autoReconnect", + "optionValue": "true", + "optionType": "boolean" + } + ] + }, + { + "name": "auth", + "type": { + "type": "record", + "name": "authConfigs", + "fields": [ + { + "name": "username", + "type": "string" + }, + { + "name": "password", + "type": [ + "null", + "string" + ], + "default": null + } + ] + } + } + ] +}