waterdrop 1.4.0 → 2.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.diffend.yml +3 -0
- data/.github/workflows/ci.yml +53 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +30 -14
- data/LICENSE +165 -0
- data/README.md +192 -53
- data/config/errors.yml +3 -16
- data/docker-compose.yml +17 -0
- data/lib/water_drop.rb +4 -24
- data/lib/water_drop/config.rb +41 -142
- data/lib/water_drop/contracts.rb +0 -2
- data/lib/water_drop/contracts/config.rb +8 -121
- data/lib/water_drop/contracts/message.rb +41 -0
- data/lib/water_drop/errors.rb +30 -5
- data/lib/water_drop/instrumentation.rb +7 -0
- data/lib/water_drop/instrumentation/monitor.rb +16 -23
- data/lib/water_drop/instrumentation/stdout_listener.rb +113 -32
- data/lib/water_drop/producer.rb +142 -0
- data/lib/water_drop/producer/async.rb +51 -0
- data/lib/water_drop/producer/buffer.rb +113 -0
- data/lib/water_drop/producer/builder.rb +63 -0
- data/lib/water_drop/producer/dummy_client.rb +32 -0
- data/lib/water_drop/producer/statistics_decorator.rb +71 -0
- data/lib/water_drop/producer/status.rb +52 -0
- data/lib/water_drop/producer/sync.rb +65 -0
- data/lib/water_drop/version.rb +1 -1
- data/waterdrop.gemspec +4 -4
- metadata +27 -26
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -35
- data/MIT-LICENCE +0 -18
- data/lib/water_drop/async_producer.rb +0 -26
- data/lib/water_drop/base_producer.rb +0 -57
- data/lib/water_drop/config_applier.rb +0 -52
- data/lib/water_drop/contracts/message_options.rb +0 -19
- data/lib/water_drop/sync_producer.rb +0 -24
data/config/errors.yml
CHANGED
@@ -1,19 +1,6 @@
|
|
1
1
|
en:
|
2
2
|
dry_validation:
|
3
3
|
errors:
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
Example: kafka://127.0.0.1:9092 or kafka+ssl://127.0.0.1:9092
|
8
|
-
ssl_client_cert_with_ssl_client_cert_key: >
|
9
|
-
Both ssl_client_cert and ssl_client_cert_key need to be provided.
|
10
|
-
ssl_client_cert_key_with_ssl_client_cert: >
|
11
|
-
Both ssl_client_cert_key and ssl_client_cert need to be provided.
|
12
|
-
ssl_client_cert_chain_with_ssl_client_cert: >
|
13
|
-
Both ssl_client_cert_chain and ssl_client_cert need to be provided.
|
14
|
-
ssl_client_cert_chain_with_ssl_client_cert_key: >
|
15
|
-
Both ssl_client_cert_chain and ssl_client_cert_key need to be provided.
|
16
|
-
ssl_client_cert_key_password_with_ssl_client_cert_key: >
|
17
|
-
Both ssl_client_cert_key_password and ssl_client_cert_key need to be provided.
|
18
|
-
sasl_oauth_token_provider_respond_to_token: >
|
19
|
-
sasl_oauth_token_provider needs to respond to a #token method.
|
4
|
+
invalid_key_type: all keys need to be of type String
|
5
|
+
invalid_value_type: all values need to be of type String
|
6
|
+
max_payload_size: is more than `max_payload_size` config value
|
data/docker-compose.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
version: '2'
|
2
|
+
services:
|
3
|
+
zookeeper:
|
4
|
+
image: wurstmeister/zookeeper
|
5
|
+
ports:
|
6
|
+
- "2181:2181"
|
7
|
+
kafka:
|
8
|
+
image: wurstmeister/kafka:1.0.1
|
9
|
+
ports:
|
10
|
+
- "9092:9092"
|
11
|
+
environment:
|
12
|
+
KAFKA_ADVERTISED_HOST_NAME: localhost
|
13
|
+
KAFKA_ADVERTISED_PORT: 9092
|
14
|
+
KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181
|
15
|
+
KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
|
16
|
+
volumes:
|
17
|
+
- /var/run/docker.sock:/var/run/docker.sock
|
data/lib/water_drop.rb
CHANGED
@@ -3,39 +3,19 @@
|
|
3
3
|
# External components
|
4
4
|
# delegate should be removed because we don't need it, we just add it because of ruby-kafka
|
5
5
|
%w[
|
6
|
-
|
7
|
-
json
|
8
|
-
delivery_boy
|
9
|
-
singleton
|
6
|
+
concurrent/array
|
10
7
|
dry-configurable
|
11
8
|
dry/monitor/notifications
|
12
9
|
dry-validation
|
10
|
+
rdkafka
|
11
|
+
json
|
13
12
|
zeitwerk
|
13
|
+
securerandom
|
14
14
|
].each { |lib| require lib }
|
15
15
|
|
16
16
|
# WaterDrop library
|
17
17
|
module WaterDrop
|
18
18
|
class << self
|
19
|
-
attr_accessor :logger
|
20
|
-
|
21
|
-
# Sets up the whole configuration
|
22
|
-
# @param [Block] block configuration block
|
23
|
-
def setup(&block)
|
24
|
-
Config.setup(&block)
|
25
|
-
DeliveryBoy.logger = self.logger = config.logger
|
26
|
-
ConfigApplier.call(DeliveryBoy.config, Config.config.to_h)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [WaterDrop::Config] config instance
|
30
|
-
def config
|
31
|
-
Config.config
|
32
|
-
end
|
33
|
-
|
34
|
-
# @return [::WaterDrop::Monitor] monitor that we want to use
|
35
|
-
def monitor
|
36
|
-
config.monitor
|
37
|
-
end
|
38
|
-
|
39
19
|
# @return [String] root path of this gem
|
40
20
|
def gem_root
|
41
21
|
Pathname.new(File.expand_path('..', __dir__))
|
data/lib/water_drop/config.rb
CHANGED
@@ -5,158 +5,57 @@
|
|
5
5
|
module WaterDrop
|
6
6
|
# Configuration object for setting up all options required by WaterDrop
|
7
7
|
class Config
|
8
|
-
|
9
|
-
|
10
|
-
# Config schema definition
|
11
|
-
# @note We use a single instance not to create new one upon each usage
|
12
|
-
SCHEMA = Contracts::Config.new.freeze
|
13
|
-
|
14
|
-
private_constant :SCHEMA
|
8
|
+
include Dry::Configurable
|
15
9
|
|
16
10
|
# WaterDrop options
|
17
|
-
#
|
18
|
-
|
19
|
-
#
|
20
|
-
setting
|
11
|
+
#
|
12
|
+
# option [String] id of the producer. This can be helpful when building producer specific
|
13
|
+
# instrumentation or loggers. It is not the kafka producer id
|
14
|
+
setting(:id, false) { |id| id || SecureRandom.uuid }
|
15
|
+
# option [Instance] logger that we want to use
|
16
|
+
# @note Due to how rdkafka works, this setting is global for all the producers
|
17
|
+
setting(:logger, false) { |logger| logger || Logger.new($stdout, level: Logger::WARN) }
|
21
18
|
# option [Instance] monitor that we want to use. See instrumentation part of the README for
|
22
19
|
# more details
|
23
|
-
setting
|
20
|
+
setting(:monitor, false) { |monitor| monitor || WaterDrop::Instrumentation::Monitor.new }
|
21
|
+
# option [Integer] max payload size allowed for delivery to Kafka
|
22
|
+
setting :max_payload_size, 1_000_012
|
23
|
+
# option [Integer] Wait that long for the delivery report or raise an error if this takes
|
24
|
+
# longer than the timeout.
|
25
|
+
setting :max_wait_timeout, 5
|
26
|
+
# option [Numeric] how long should we wait between re-checks on the availability of the
|
27
|
+
# delivery report. In a really robust systems, this describes the min-delivery time
|
28
|
+
# for a single sync message when produced in isolation
|
29
|
+
setting :wait_timeout, 0.005 # 5 milliseconds
|
24
30
|
# option [Boolean] should we send messages. Setting this to false can be really useful when
|
25
|
-
# testing and or developing because when set to false, won't actually ping Kafka
|
31
|
+
# testing and or developing because when set to false, won't actually ping Kafka but will
|
32
|
+
# run all the validations, etc
|
26
33
|
setting :deliver, true
|
27
|
-
#
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
# Network timeouts
|
39
|
-
# option connect_timeout [Integer] Sets the number of seconds to wait while connecting to
|
40
|
-
# a broker for the first time. When ruby-kafka initializes, it needs to connect to at
|
41
|
-
# least one host.
|
42
|
-
setting :connect_timeout, 10
|
43
|
-
# option socket_timeout [Integer] Sets the number of seconds to wait when reading from or
|
44
|
-
# writing to a socket connection to a broker. After this timeout expires the connection
|
45
|
-
# will be killed. Note that some Kafka operations are by definition long-running, such as
|
46
|
-
# waiting for new messages to arrive in a partition, so don't set this value too low
|
47
|
-
setting :socket_timeout, 30
|
48
|
-
|
49
|
-
# Buffering for async producer
|
50
|
-
# @option [Integer] The maximum number of bytes allowed in the buffer before new messages
|
51
|
-
# are rejected.
|
52
|
-
setting :max_buffer_bytesize, 10_000_000
|
53
|
-
# @option [Integer] The maximum number of messages allowed in the buffer before new messages
|
54
|
-
# are rejected.
|
55
|
-
setting :max_buffer_size, 1000
|
56
|
-
# @option [Integer] The maximum number of messages allowed in the queue before new messages
|
57
|
-
# are rejected. The queue is used to ferry messages from the foreground threads of your
|
58
|
-
# application to the background thread that buffers and delivers messages.
|
59
|
-
setting :max_queue_size, 1000
|
60
|
-
|
61
|
-
# option [Integer] A timeout executed by a broker when the client is sending messages to it.
|
62
|
-
# It defines the number of seconds the broker should wait for replicas to acknowledge the
|
63
|
-
# write before responding to the client with an error. As such, it relates to the
|
64
|
-
# required_acks setting. It should be set lower than socket_timeout.
|
65
|
-
setting :ack_timeout, 5
|
66
|
-
# option [Integer] The number of seconds between background message
|
67
|
-
# deliveries. Default is 10 seconds. Disable timer-based background deliveries by
|
68
|
-
# setting this to 0.
|
69
|
-
setting :delivery_interval, 10
|
70
|
-
# option [Integer] The number of buffered messages that will trigger a background message
|
71
|
-
# delivery. Default is 100 messages. Disable buffer size based background deliveries by
|
72
|
-
# setting this to 0.
|
73
|
-
setting :delivery_threshold, 100
|
74
|
-
# option [Boolean]
|
75
|
-
setting :idempotent, false
|
76
|
-
# option [Boolean]
|
77
|
-
setting :transactional, false
|
78
|
-
# option [Integer]
|
79
|
-
setting :transactional_timeout, 60
|
80
|
-
|
81
|
-
# option [Integer] The number of retries when attempting to deliver messages.
|
82
|
-
setting :max_retries, 2
|
83
|
-
# option [Integer]
|
84
|
-
setting :required_acks, -1
|
85
|
-
# option [Integer]
|
86
|
-
setting :retry_backoff, 1
|
87
|
-
|
88
|
-
# option [Integer] The minimum number of messages that must be buffered before compression is
|
89
|
-
# attempted. By default only one message is required. Only relevant if compression_codec
|
90
|
-
# is set.
|
91
|
-
setting :compression_threshold, 1
|
92
|
-
# option [Symbol] The codec used to compress messages. Must be either snappy or gzip.
|
93
|
-
setting :compression_codec, nil
|
94
|
-
|
95
|
-
# SSL authentication related settings
|
96
|
-
# option ca_cert [String, nil] SSL CA certificate
|
97
|
-
setting :ssl_ca_cert, nil
|
98
|
-
# option ssl_ca_cert_file_path [String, nil] SSL CA certificate file path
|
99
|
-
setting :ssl_ca_cert_file_path, nil
|
100
|
-
# option ssl_ca_certs_from_system [Boolean] Use the CA certs from your system's default
|
101
|
-
# certificate store
|
102
|
-
setting :ssl_ca_certs_from_system, false
|
103
|
-
# option ssl_verify_hostname [Boolean] Verify the hostname for client certs
|
104
|
-
setting :ssl_verify_hostname, true
|
105
|
-
# option ssl_client_cert [String, nil] SSL client certificate
|
106
|
-
setting :ssl_client_cert, nil
|
107
|
-
# option ssl_client_cert_key [String, nil] SSL client certificate password
|
108
|
-
setting :ssl_client_cert_key, nil
|
109
|
-
# option sasl_gssapi_principal [String, nil] sasl principal
|
110
|
-
setting :sasl_gssapi_principal, nil
|
111
|
-
# option sasl_gssapi_keytab [String, nil] sasl keytab
|
112
|
-
setting :sasl_gssapi_keytab, nil
|
113
|
-
# option sasl_plain_authzid [String] The authorization identity to use
|
114
|
-
setting :sasl_plain_authzid, ''
|
115
|
-
# option sasl_plain_username [String, nil] The username used to authenticate
|
116
|
-
setting :sasl_plain_username, nil
|
117
|
-
# option sasl_plain_password [String, nil] The password used to authenticate
|
118
|
-
setting :sasl_plain_password, nil
|
119
|
-
# option sasl_scram_username [String, nil] The username used to authenticate
|
120
|
-
setting :sasl_scram_username, nil
|
121
|
-
# option sasl_scram_password [String, nil] The password used to authenticate
|
122
|
-
setting :sasl_scram_password, nil
|
123
|
-
# option sasl_scram_mechanism [String, nil] Scram mechanism, either 'sha256' or 'sha512'
|
124
|
-
setting :sasl_scram_mechanism, nil
|
125
|
-
# option sasl_over_ssl [Boolean] whether to enforce SSL with SASL
|
126
|
-
setting :sasl_over_ssl, true
|
127
|
-
# option ssl_client_cert_chain [String, nil] client cert chain or nil if not used
|
128
|
-
setting :ssl_client_cert_chain, nil
|
129
|
-
# option ssl_client_cert_key_password [String, nil] the password required to read
|
130
|
-
# the ssl_client_cert_key
|
131
|
-
setting :ssl_client_cert_key_password, nil
|
132
|
-
# @param sasl_oauth_token_provider [Object, nil] OAuthBearer Token Provider instance that
|
133
|
-
# implements method token.
|
134
|
-
setting :sasl_oauth_token_provider, nil
|
135
|
-
end
|
136
|
-
|
137
|
-
class << self
|
138
|
-
# Configuration method
|
139
|
-
# @yield Runs a block of code providing a config singleton instance to it
|
140
|
-
# @yieldparam [WaterDrop::Config] WaterDrop config instance
|
141
|
-
def setup
|
142
|
-
configure do |config|
|
143
|
-
yield(config)
|
144
|
-
validate!(config.to_h)
|
145
|
-
end
|
34
|
+
# rdkafka options
|
35
|
+
# @see https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md
|
36
|
+
setting :kafka, {}
|
37
|
+
|
38
|
+
# Configuration method
|
39
|
+
# @yield Runs a block of code providing a config singleton instance to it
|
40
|
+
# @yieldparam [WaterDrop::Config] WaterDrop config instance
|
41
|
+
def setup
|
42
|
+
configure do |config|
|
43
|
+
yield(config)
|
44
|
+
validate!(config.to_h)
|
146
45
|
end
|
46
|
+
end
|
147
47
|
|
148
|
-
|
48
|
+
private
|
149
49
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
50
|
+
# Validates the configuration and if anything is wrong, will raise an exception
|
51
|
+
# @param config_hash [Hash] config hash with setup details
|
52
|
+
# @raise [WaterDrop::Errors::ConfigurationInvalidError] raised when something is wrong with
|
53
|
+
# the configuration
|
54
|
+
def validate!(config_hash)
|
55
|
+
result = Contracts::Config.new.call(config_hash)
|
56
|
+
return true if result.success?
|
157
57
|
|
158
|
-
|
159
|
-
end
|
58
|
+
raise Errors::ConfigurationInvalidError, result.errors.to_h
|
160
59
|
end
|
161
60
|
end
|
162
61
|
end
|
data/lib/water_drop/contracts.rb
CHANGED
@@ -4,134 +4,21 @@ module WaterDrop
|
|
4
4
|
module Contracts
|
5
5
|
# Contract with validation rules for WaterDrop configuration details
|
6
6
|
class Config < Dry::Validation::Contract
|
7
|
-
#
|
8
|
-
|
7
|
+
# Ensure valid format of each seed broker so that rdkafka doesn't fail silently
|
8
|
+
SEED_BROKER_FORMAT_REGEXP = %r{\A([^\:\/,]+:[0-9]+)(,[^\:\/,]+:[0-9]+)*\z}.freeze
|
9
9
|
|
10
|
-
|
11
|
-
SASL_SCRAM_MECHANISMS = %w[sha256 sha512].freeze
|
12
|
-
|
13
|
-
# Supported compression codecs
|
14
|
-
COMPRESSION_CODECS = %i[snappy gzip lz4 zstd].freeze
|
15
|
-
|
16
|
-
config.messages.load_paths << File.join(WaterDrop.gem_root, 'config', 'errors.yml')
|
17
|
-
|
18
|
-
class << self
|
19
|
-
private
|
20
|
-
|
21
|
-
# Builder for kafka scoped data custom rules
|
22
|
-
# @param keys [Symbol, Hash] the keys names
|
23
|
-
# @param block [Proc] block we want to run with validations within the kafka scope
|
24
|
-
def kafka_scope_rule(*keys, &block)
|
25
|
-
rule(*[:kafka].product(keys)) do
|
26
|
-
instance_exec(values[:kafka], &block)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# Uri validator to check if uri is in a Kafka acceptable format
|
34
|
-
# @param uri [String] uri we want to validate
|
35
|
-
# @return [Boolean] true if it is a valid uri, otherwise false
|
36
|
-
def broker_schema?(uri)
|
37
|
-
uri = URI.parse(uri)
|
38
|
-
URI_SCHEMES.include?(uri.scheme) && uri.port
|
39
|
-
rescue URI::InvalidURIError
|
40
|
-
false
|
41
|
-
end
|
10
|
+
private_constant :SEED_BROKER_FORMAT_REGEXP
|
42
11
|
|
43
12
|
params do
|
44
|
-
required(:
|
13
|
+
required(:id).filled(:str?)
|
45
14
|
required(:logger).filled
|
46
15
|
required(:deliver).filled(:bool?)
|
47
|
-
required(:
|
16
|
+
required(:max_payload_size).filled(:int?, gteq?: 1)
|
17
|
+
required(:max_wait_timeout).filled(:number?, gteq?: 0)
|
18
|
+
required(:wait_timeout).filled(:number?, gt?: 0)
|
48
19
|
|
49
20
|
required(:kafka).schema do
|
50
|
-
required(:
|
51
|
-
required(:connect_timeout).filled(:int?, gt?: 0)
|
52
|
-
required(:socket_timeout).filled(:int?, gt?: 0)
|
53
|
-
required(:compression_threshold).filled(:int?, gteq?: 1)
|
54
|
-
optional(:compression_codec).maybe(included_in?: COMPRESSION_CODECS)
|
55
|
-
|
56
|
-
required(:max_buffer_bytesize).filled(:int?, gt?: 0)
|
57
|
-
required(:max_buffer_size).filled(:int?, gt?: 0)
|
58
|
-
required(:max_queue_size).filled(:int?, gt?: 0)
|
59
|
-
|
60
|
-
required(:ack_timeout).filled(:int?, gt?: 0)
|
61
|
-
required(:delivery_interval).filled(:int?, gteq?: 0)
|
62
|
-
required(:delivery_threshold).filled(:int?, gteq?: 0)
|
63
|
-
|
64
|
-
required(:max_retries).filled(:int?, gteq?: 0)
|
65
|
-
required(:retry_backoff).filled(:int?, gteq?: 0)
|
66
|
-
required(:required_acks).filled(included_in?: [1, 0, -1, :all])
|
67
|
-
|
68
|
-
%i[
|
69
|
-
ssl_ca_cert
|
70
|
-
ssl_ca_cert_file_path
|
71
|
-
ssl_client_cert
|
72
|
-
ssl_client_cert_key
|
73
|
-
ssl_client_cert_chain
|
74
|
-
ssl_client_cert_key_password
|
75
|
-
sasl_gssapi_principal
|
76
|
-
sasl_gssapi_keytab
|
77
|
-
sasl_plain_authzid
|
78
|
-
sasl_plain_username
|
79
|
-
sasl_plain_password
|
80
|
-
sasl_scram_username
|
81
|
-
sasl_scram_password
|
82
|
-
].each do |encryption_attribute|
|
83
|
-
optional(encryption_attribute).maybe(:str?)
|
84
|
-
end
|
85
|
-
|
86
|
-
optional(:ssl_verify_hostname).maybe(:bool?)
|
87
|
-
optional(:ssl_ca_certs_from_system).maybe(:bool?)
|
88
|
-
optional(:sasl_over_ssl).maybe(:bool?)
|
89
|
-
optional(:sasl_oauth_token_provider).value(:any)
|
90
|
-
|
91
|
-
# It's not with other encryptions as it has some more rules
|
92
|
-
optional(:sasl_scram_mechanism)
|
93
|
-
.maybe(:str?, included_in?: SASL_SCRAM_MECHANISMS)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
kafka_scope_rule(:seed_brokers) do |kafka|
|
98
|
-
unless kafka[:seed_brokers].all?(&method(:broker_schema?))
|
99
|
-
key(%i[kafka seed_brokers]).failure(:broker_schema)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
kafka_scope_rule(:ssl_client_cert, :ssl_client_cert_key) do |kafka|
|
104
|
-
if kafka[:ssl_client_cert] &&
|
105
|
-
kafka[:ssl_client_cert_key].nil?
|
106
|
-
key(%i[kafka ssl_client_cert_key]).failure(:ssl_client_cert_with_ssl_client_cert_key)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
kafka_scope_rule(:ssl_client_cert_key, :ssl_client_cert) do |kafka|
|
111
|
-
if kafka[:ssl_client_cert_key] &&
|
112
|
-
kafka[:ssl_client_cert].nil?
|
113
|
-
key.failure(:ssl_client_cert_key_with_ssl_client_cert)
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
kafka_scope_rule(:ssl_client_cert_chain, :ssl_client_cert) do |kafka|
|
118
|
-
if kafka[:ssl_client_cert_chain] &&
|
119
|
-
kafka[:ssl_client_cert].nil?
|
120
|
-
key.failure(:ssl_client_cert_chain_with_ssl_client_cert)
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
kafka_scope_rule(:ssl_client_cert_key_password, :ssl_client_cert_key) do |kafka|
|
125
|
-
if kafka[:ssl_client_cert_key_password] &&
|
126
|
-
kafka[:ssl_client_cert_key].nil?
|
127
|
-
key.failure(:ssl_client_cert_key_password_with_ssl_client_cert_key)
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
kafka_scope_rule(:sasl_oauth_token_provider) do |kafka|
|
132
|
-
if kafka[:sasl_oauth_token_provider] &&
|
133
|
-
!kafka[:sasl_oauth_token_provider].respond_to?(:token)
|
134
|
-
key.failure(:sasl_oauth_token_provider_respond_to_token)
|
21
|
+
required(:'bootstrap.servers').filled(:str?, format?: SEED_BROKER_FORMAT_REGEXP)
|
135
22
|
end
|
136
23
|
end
|
137
24
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Contracts
|
5
|
+
# Contract with validation rules for validating that all the message options that
|
6
|
+
# we provide to producer ale valid and usable
|
7
|
+
class Message < Dry::Validation::Contract
|
8
|
+
# Regex to check that topic has a valid format
|
9
|
+
TOPIC_REGEXP = /\A(\w|\-|\.)+\z/.freeze
|
10
|
+
|
11
|
+
# Checks, that the given value is a string
|
12
|
+
STRING_ASSERTION = ->(value) { value.is_a?(String) }.to_proc
|
13
|
+
|
14
|
+
private_constant :TOPIC_REGEXP, :STRING_ASSERTION
|
15
|
+
|
16
|
+
config.messages.load_paths << File.join(WaterDrop.gem_root, 'config', 'errors.yml')
|
17
|
+
|
18
|
+
option :max_payload_size
|
19
|
+
|
20
|
+
params do
|
21
|
+
required(:topic).filled(:str?, format?: TOPIC_REGEXP)
|
22
|
+
required(:payload).filled(:str?)
|
23
|
+
optional(:key).maybe(:str?, :filled?)
|
24
|
+
optional(:partition).filled(:int?, gteq?: -1)
|
25
|
+
optional(:timestamp).maybe { time? | int? }
|
26
|
+
optional(:headers).maybe(:hash?)
|
27
|
+
end
|
28
|
+
|
29
|
+
rule(:headers) do
|
30
|
+
next unless value.is_a?(Hash)
|
31
|
+
|
32
|
+
key.failure(:invalid_key_type) unless value.keys.all?(&STRING_ASSERTION)
|
33
|
+
key.failure(:invalid_value_type) unless value.values.all?(&STRING_ASSERTION)
|
34
|
+
end
|
35
|
+
|
36
|
+
rule(:payload) do
|
37
|
+
key.failure(:max_payload_size) if value.bytesize > max_payload_size
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|