waterdrop 2.3.2 → 2.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile.lock +1 -28
- data/config/errors.yml +28 -5
- data/lib/waterdrop/contractable/contract.rb +183 -0
- data/lib/waterdrop/contractable/result.rb +57 -0
- data/lib/waterdrop/contractable/rule.rb +8 -0
- data/lib/waterdrop/contractable.rb +13 -0
- data/lib/waterdrop/contracts/config.rb +25 -15
- data/lib/waterdrop/contracts/message.rb +40 -20
- data/lib/waterdrop/instrumentation/vendors/datadog/listener.rb +11 -5
- data/lib/waterdrop/version.rb +1 -1
- data/lib/waterdrop.rb +3 -1
- data/waterdrop.gemspec +0 -1
- data.tar.gz.sig +0 -0
- metadata +6 -17
- metadata.gz.sig +0 -0
- data/lib/waterdrop/contracts/base.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1482f0d2955c43c2ab479372c9cbb4a8c039723121c26b2ff75d0e26700d71e
|
4
|
+
data.tar.gz: 1bb3e675c0c632d4940b2b661bdb75a74c44358a7e53fa9dc7051fdad62218b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8921844cc44625916974c63478562b66a7f30a833b6f95e71e8c3448109d673bd3520a8a48bdf0fe7296e5e00b988c15c3dd92cab2e11ee37914f667966a14de
|
7
|
+
data.tar.gz: 6c6038c4f9a718d107fd885e66bf29cc3dde09c6f6e0be2ba41651f3494672ddd925e71574f53ec8ec70d4e766024018f73a43a5fadf99402117ac3a0751b0c0
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,9 @@
|
|
1
1
|
# WaterDrop changelog
|
2
2
|
|
3
|
+
## 2.3.3 (2022-07-18)
|
4
|
+
- Replace `dry-validation` with home-brew validation layer and drop direct dependency on `dry-validation`.
|
5
|
+
- Remove indirect dependency on dry-configurable from DataDog listener (no changes required).
|
6
|
+
|
3
7
|
## 2.3.2 (2022-07-17)
|
4
8
|
- Replace `dry-configurable` with home-brew config and drop direct dependency on `dry-configurable`.
|
5
9
|
|
data/Gemfile.lock
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
waterdrop (2.3.
|
4
|
+
waterdrop (2.3.3)
|
5
5
|
concurrent-ruby (>= 1.1)
|
6
6
|
dry-monitor (~> 0.5)
|
7
|
-
dry-validation (~> 1.7)
|
8
7
|
rdkafka (>= 0.10)
|
9
8
|
zeitwerk (~> 2.3)
|
10
9
|
|
@@ -23,42 +22,16 @@ GEM
|
|
23
22
|
dry-configurable (0.15.0)
|
24
23
|
concurrent-ruby (~> 1.0)
|
25
24
|
dry-core (~> 0.6)
|
26
|
-
dry-container (0.10.0)
|
27
|
-
concurrent-ruby (~> 1.0)
|
28
25
|
dry-core (0.8.0)
|
29
26
|
concurrent-ruby (~> 1.0)
|
30
27
|
dry-events (0.3.0)
|
31
28
|
concurrent-ruby (~> 1.0)
|
32
29
|
dry-core (~> 0.5, >= 0.5)
|
33
|
-
dry-inflector (0.3.0)
|
34
|
-
dry-initializer (3.1.1)
|
35
|
-
dry-logic (1.2.0)
|
36
|
-
concurrent-ruby (~> 1.0)
|
37
|
-
dry-core (~> 0.5, >= 0.5)
|
38
30
|
dry-monitor (0.6.1)
|
39
31
|
dry-configurable (~> 0.13, >= 0.13.0)
|
40
32
|
dry-core (~> 0.5, >= 0.5)
|
41
33
|
dry-events (~> 0.2)
|
42
34
|
zeitwerk (~> 2.5)
|
43
|
-
dry-schema (1.9.3)
|
44
|
-
concurrent-ruby (~> 1.0)
|
45
|
-
dry-configurable (~> 0.13, >= 0.13.0)
|
46
|
-
dry-core (~> 0.5, >= 0.5)
|
47
|
-
dry-initializer (~> 3.0)
|
48
|
-
dry-logic (~> 1.0)
|
49
|
-
dry-types (~> 1.5)
|
50
|
-
dry-types (1.5.1)
|
51
|
-
concurrent-ruby (~> 1.0)
|
52
|
-
dry-container (~> 0.3)
|
53
|
-
dry-core (~> 0.5, >= 0.5)
|
54
|
-
dry-inflector (~> 0.1, >= 0.1.2)
|
55
|
-
dry-logic (~> 1.0, >= 1.0.2)
|
56
|
-
dry-validation (1.8.1)
|
57
|
-
concurrent-ruby (~> 1.0)
|
58
|
-
dry-container (~> 0.7, >= 0.7.1)
|
59
|
-
dry-core (~> 0.5, >= 0.5)
|
60
|
-
dry-initializer (~> 3.0)
|
61
|
-
dry-schema (~> 1.8, >= 1.8.0)
|
62
35
|
factory_bot (6.2.1)
|
63
36
|
activesupport (>= 5.0.0)
|
64
37
|
ffi (1.15.5)
|
data/config/errors.yml
CHANGED
@@ -1,7 +1,30 @@
|
|
1
1
|
en:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
validations:
|
3
|
+
config:
|
4
|
+
missing: must be present
|
5
|
+
logger_format: must be present
|
6
|
+
deliver_format: must be boolean
|
7
|
+
id_format: must be a non-empty string
|
8
|
+
max_payload_size_format: must be an integer that is equal or bigger than 1
|
9
|
+
wait_timeout_format: must be a numeric that is bigger than 0
|
10
|
+
max_wait_timeout_format: must be an integer that is equal or bigger than 0
|
11
|
+
kafka_format: must be a hash with symbol based keys
|
7
12
|
kafka_key_must_be_a_symbol: All keys under the kafka settings scope need to be symbols
|
13
|
+
|
14
|
+
message:
|
15
|
+
missing: must be present
|
16
|
+
partition_format: must be an integer greater or equal to -1
|
17
|
+
topic_format: 'does not match the topic allowed format'
|
18
|
+
partition_key_format: must be a non-empty string
|
19
|
+
timestamp_format: must be either time or integer
|
20
|
+
payload_format: must be string
|
21
|
+
headers_format: must be a hash
|
22
|
+
key_format: must be a non-empty string
|
23
|
+
payload_max_size: is more than `max_payload_size` config value
|
24
|
+
headers_invalid_key_type: all headers keys need to be of type String
|
25
|
+
headers_invalid_value_type: all headers values need to be of type String
|
26
|
+
|
27
|
+
test:
|
28
|
+
missing: must be present
|
29
|
+
nested.id_format: 'is invalid'
|
30
|
+
nested.id2_format: 'is invalid'
|
@@ -0,0 +1,183 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Contractable
|
5
|
+
# Base contract for all the contracts that check data format
|
6
|
+
#
|
7
|
+
# @note This contract does NOT support rules inheritance as it was never needed in Karafka
|
8
|
+
class Contract
|
9
|
+
extend Configurable
|
10
|
+
|
11
|
+
# Yaml based error messages data
|
12
|
+
setting(:error_messages)
|
13
|
+
|
14
|
+
# Class level API definitions
|
15
|
+
class << self
|
16
|
+
# @return [Array<Rule>] all the validation rules defined for a given contract
|
17
|
+
attr_reader :rules
|
18
|
+
|
19
|
+
# Allows for definition of a scope/namespace for nested validations
|
20
|
+
#
|
21
|
+
# @param path [Symbol] path in the hash for nesting
|
22
|
+
# @param block [Proc] nested rule code or more nestings inside
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# nested(:key) do
|
26
|
+
# required(:inside) { |inside| inside.is_a?(String) }
|
27
|
+
# end
|
28
|
+
def nested(path, &block)
|
29
|
+
init_accu
|
30
|
+
@nested << path
|
31
|
+
instance_eval(&block)
|
32
|
+
@nested.pop
|
33
|
+
end
|
34
|
+
|
35
|
+
# Defines a rule for a required field (required means, that will automatically create an
|
36
|
+
# error if missing)
|
37
|
+
#
|
38
|
+
# @param keys [Array<Symbol>] single or full path
|
39
|
+
# @param block [Proc] validation rule
|
40
|
+
def required(*keys, &block)
|
41
|
+
init_accu
|
42
|
+
@rules << Rule.new(@nested + keys, :required, block).freeze
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param keys [Array<Symbol>] single or full path
|
46
|
+
# @param block [Proc] validation rule
|
47
|
+
def optional(*keys, &block)
|
48
|
+
init_accu
|
49
|
+
@rules << Rule.new(@nested + keys, :optional, block).freeze
|
50
|
+
end
|
51
|
+
|
52
|
+
# @param block [Proc] validation rule
|
53
|
+
#
|
54
|
+
# @note Virtual rules have different result expectations. Please see contracts or specs for
|
55
|
+
# details.
|
56
|
+
def virtual(&block)
|
57
|
+
init_accu
|
58
|
+
@rules << Rule.new([], :virtual, block).freeze
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Initializes nestings and rules building accumulator
|
64
|
+
def init_accu
|
65
|
+
@nested ||= []
|
66
|
+
@rules ||= []
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Runs the validation
|
71
|
+
#
|
72
|
+
# @param data [Hash] hash with data we want to validate
|
73
|
+
# @return [Result] validaton result
|
74
|
+
def call(data)
|
75
|
+
errors = []
|
76
|
+
|
77
|
+
self.class.rules.map do |rule|
|
78
|
+
case rule.type
|
79
|
+
when :required
|
80
|
+
validate_required(data, rule, errors)
|
81
|
+
when :optional
|
82
|
+
validate_optional(data, rule, errors)
|
83
|
+
when :virtual
|
84
|
+
validate_virtual(data, rule, errors)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Result.new(errors, self)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @param data [Hash] data for validation
|
92
|
+
# @param error_class [Class] error class that should be used when validation fails
|
93
|
+
# @return [Boolean] true
|
94
|
+
# @raise [StandardError] any error provided in the error_class that inherits from the
|
95
|
+
# standard error
|
96
|
+
def validate!(data, error_class)
|
97
|
+
result = call(data)
|
98
|
+
|
99
|
+
return true if result.success?
|
100
|
+
|
101
|
+
raise error_class, result.errors
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
# Runs validation for rules on fields that are required and adds errors (if any) to the
|
107
|
+
# errors array
|
108
|
+
#
|
109
|
+
# @param data [Hash] input hash
|
110
|
+
# @param rule [Rule] validation rule
|
111
|
+
# @param errors [Array] array with errors from previous rules (if any)
|
112
|
+
def validate_required(data, rule, errors)
|
113
|
+
for_checking = dig(data, rule.path)
|
114
|
+
|
115
|
+
if for_checking.first == :match
|
116
|
+
result = rule.validator.call(for_checking.last, data, errors, self)
|
117
|
+
|
118
|
+
return if result == true
|
119
|
+
|
120
|
+
errors << [rule.path, result || :format]
|
121
|
+
else
|
122
|
+
errors << [rule.path, :missing]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# Runs validation for rules on fields that are optional and adds errors (if any) to the
|
127
|
+
# errors array
|
128
|
+
#
|
129
|
+
# @param data [Hash] input hash
|
130
|
+
# @param rule [Rule] validation rule
|
131
|
+
# @param errors [Array] array with errors from previous rules (if any)
|
132
|
+
def validate_optional(data, rule, errors)
|
133
|
+
for_checking = dig(data, rule.path)
|
134
|
+
|
135
|
+
return unless for_checking.first == :match
|
136
|
+
|
137
|
+
result = rule.validator.call(for_checking.last, data, errors, self)
|
138
|
+
|
139
|
+
return if result == true
|
140
|
+
|
141
|
+
errors << [rule.path, result || :format]
|
142
|
+
end
|
143
|
+
|
144
|
+
# Runs validation for rules on virtual fields (aggregates, etc) and adds errors (if any) to
|
145
|
+
# the errors array
|
146
|
+
#
|
147
|
+
# @param data [Hash] input hash
|
148
|
+
# @param rule [Rule] validation rule
|
149
|
+
# @param errors [Array] array with errors from previous rules (if any)
|
150
|
+
def validate_virtual(data, rule, errors)
|
151
|
+
result = rule.validator.call(data, errors, self)
|
152
|
+
|
153
|
+
return if result == true
|
154
|
+
|
155
|
+
errors.push(*result)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Tries to dig for a given key in a hash and returns it with indication whether or not it was
|
159
|
+
# possible to find it (dig returns nil and we don't know if it wasn't the digged key value)
|
160
|
+
#
|
161
|
+
# @param data [Hash]
|
162
|
+
# @param keys [Array<Symbol>]
|
163
|
+
# @return [Array<Symbol, Object>] array where the first element is `:match` or `:miss` and
|
164
|
+
# the digged value or nil if not found
|
165
|
+
def dig(data, keys)
|
166
|
+
current = data
|
167
|
+
result = :match
|
168
|
+
|
169
|
+
keys.each do |nesting|
|
170
|
+
unless current.key?(nesting)
|
171
|
+
result = :miss
|
172
|
+
|
173
|
+
break
|
174
|
+
end
|
175
|
+
|
176
|
+
current = current[nesting]
|
177
|
+
end
|
178
|
+
|
179
|
+
[result, current]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
module Contractable
|
5
|
+
# Representation of a validaton result with resolved error messages
|
6
|
+
class Result
|
7
|
+
attr_reader :errors
|
8
|
+
|
9
|
+
# Builds a result object and remaps (if needed) error keys to proper error messages
|
10
|
+
#
|
11
|
+
# @param errors [Array<Array>] array with sub-arrays with paths and error keys
|
12
|
+
# @param contract [Object] contract that generated the error
|
13
|
+
def initialize(errors, contract)
|
14
|
+
# Short track to skip object allocation for the happy path
|
15
|
+
if errors.empty?
|
16
|
+
@errors = errors
|
17
|
+
return
|
18
|
+
end
|
19
|
+
|
20
|
+
hashed = {}
|
21
|
+
|
22
|
+
errors.each do |error|
|
23
|
+
scope = error.first.map(&:to_s).join('.').to_sym
|
24
|
+
|
25
|
+
# This will allow for usage of custom messages instead of yaml keys if needed
|
26
|
+
hashed[scope] = if error.last.is_a?(String)
|
27
|
+
error.last
|
28
|
+
else
|
29
|
+
build_message(contract, scope, error.last)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
@errors = hashed
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Boolean] true if no errors
|
37
|
+
def success?
|
38
|
+
errors.empty?
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Builds message based on the error messages
|
44
|
+
# @param contract [Object] contract for which we build the result
|
45
|
+
# @param scope [Symbol] path to the key that has an error
|
46
|
+
# @param error_key [Symbol] error key for yaml errors lookup
|
47
|
+
# @return [String] error message
|
48
|
+
def build_message(contract, scope, error_key)
|
49
|
+
messages = contract.class.config.error_messages
|
50
|
+
|
51
|
+
messages.fetch(error_key.to_s) do
|
52
|
+
messages.fetch("#{scope}_#{error_key}")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module WaterDrop
|
4
|
+
# Contract layer for WaterDrop and Karafka
|
5
|
+
# It aims to be "dry-validation" like but smaller and easier to handle + without dependencies
|
6
|
+
#
|
7
|
+
# It allows for nested validations, etc
|
8
|
+
#
|
9
|
+
# @note It is thread-safe to run but validations definitions should happen before threads are
|
10
|
+
# used.
|
11
|
+
module Contractable
|
12
|
+
end
|
13
|
+
end
|
@@ -3,27 +3,37 @@
|
|
3
3
|
module WaterDrop
|
4
4
|
module Contracts
|
5
5
|
# Contract with validation rules for WaterDrop configuration details
|
6
|
-
class Config <
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
required(:wait_timeout).filled(:number?, gt?: 0)
|
14
|
-
required(:kafka).filled(:hash?)
|
6
|
+
class Config < Contractable::Contract
|
7
|
+
configure do |config|
|
8
|
+
config.error_messages = YAML.safe_load(
|
9
|
+
File.read(
|
10
|
+
File.join(WaterDrop.gem_root, 'config', 'errors.yml')
|
11
|
+
)
|
12
|
+
).fetch('en').fetch('validations').fetch('config')
|
15
13
|
end
|
16
14
|
|
15
|
+
required(:id) { |id| id.is_a?(String) && !id.empty? }
|
16
|
+
required(:logger) { |logger| !logger.nil? }
|
17
|
+
required(:deliver) { |deliver| [true, false].include?(deliver) }
|
18
|
+
required(:max_payload_size) { |ps| ps.is_a?(Integer) && ps >= 1 }
|
19
|
+
required(:max_wait_timeout) { |mwt| mwt.is_a?(Numeric) && mwt >= 0 }
|
20
|
+
required(:wait_timeout) { |wt| wt.is_a?(Numeric) && wt.positive? }
|
21
|
+
required(:kafka) { |kafka| kafka.is_a?(Hash) && !kafka.empty? }
|
22
|
+
|
17
23
|
# rdkafka allows both symbols and strings as keys for config but then casts them to strings
|
18
24
|
# This can be confusing, so we expect all keys to be symbolized
|
19
|
-
|
20
|
-
next unless
|
25
|
+
virtual do |config, errors|
|
26
|
+
next true unless errors.empty?
|
27
|
+
|
28
|
+
errors = []
|
21
29
|
|
22
|
-
|
23
|
-
|
30
|
+
config
|
31
|
+
.fetch(:kafka)
|
32
|
+
.keys
|
33
|
+
.reject { |key| key.is_a?(Symbol) }
|
34
|
+
.each { |key| errors << [[:kafka, key], :kafka_key_must_be_a_symbol] }
|
24
35
|
|
25
|
-
|
26
|
-
end
|
36
|
+
errors
|
27
37
|
end
|
28
38
|
end
|
29
39
|
end
|
@@ -4,36 +4,56 @@ module WaterDrop
|
|
4
4
|
module Contracts
|
5
5
|
# Contract with validation rules for validating that all the message options that
|
6
6
|
# we provide to producer ale valid and usable
|
7
|
-
class Message <
|
7
|
+
class Message < Contractable::Contract
|
8
|
+
configure do |config|
|
9
|
+
config.error_messages = YAML.safe_load(
|
10
|
+
File.read(
|
11
|
+
File.join(WaterDrop.gem_root, 'config', 'errors.yml')
|
12
|
+
)
|
13
|
+
).fetch('en').fetch('validations').fetch('message')
|
14
|
+
end
|
15
|
+
|
8
16
|
# Regex to check that topic has a valid format
|
9
17
|
TOPIC_REGEXP = /\A(\w|-|\.)+\z/
|
10
18
|
|
11
|
-
|
12
|
-
STRING_ASSERTION = ->(value) { value.is_a?(String) }.to_proc
|
19
|
+
private_constant :TOPIC_REGEXP
|
13
20
|
|
14
|
-
|
21
|
+
attr_reader :max_payload_size
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
required(:payload).filled(:str?)
|
21
|
-
optional(:key).maybe(:str?, :filled?)
|
22
|
-
optional(:partition).filled(:int?, gteq?: -1)
|
23
|
-
optional(:partition_key).maybe(:str?, :filled?)
|
24
|
-
optional(:timestamp).maybe { time? | int? }
|
25
|
-
optional(:headers).maybe(:hash?)
|
23
|
+
# @param max_payload_size [Integer] max payload size
|
24
|
+
def initialize(max_payload_size:)
|
25
|
+
super()
|
26
|
+
@max_payload_size = max_payload_size
|
26
27
|
end
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
required(:topic) { |topic| topic.is_a?(String) && TOPIC_REGEXP.match?(topic) }
|
30
|
+
required(:payload) { |payload| payload.is_a?(String) }
|
31
|
+
optional(:key) { |key| key.nil? || (key.is_a?(String) && !key.empty?) }
|
32
|
+
optional(:partition) { |partition| partition.is_a?(Integer) && partition >= -1 }
|
33
|
+
optional(:partition_key) { |p_key| p_key.nil? || (p_key.is_a?(String) && !p_key.empty?) }
|
34
|
+
optional(:timestamp) { |ts| ts.nil? || (ts.is_a?(Time) || ts.is_a?(Integer)) }
|
35
|
+
optional(:headers) { |headers| headers.nil? || headers.is_a?(Hash) }
|
36
|
+
|
37
|
+
virtual do |config, errors|
|
38
|
+
next true unless errors.empty?
|
39
|
+
next true unless config.key?(:headers)
|
40
|
+
next true if config[:headers].nil?
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
errors = []
|
43
|
+
|
44
|
+
config.fetch(:headers).each do |key, value|
|
45
|
+
errors << [%i[headers], :invalid_key_type] unless key.is_a?(String)
|
46
|
+
errors << [%i[headers], :invalid_value_type] unless value.is_a?(String)
|
47
|
+
end
|
48
|
+
|
49
|
+
errors
|
33
50
|
end
|
34
51
|
|
35
|
-
|
36
|
-
|
52
|
+
virtual do |config, errors, validator|
|
53
|
+
next true unless errors.empty?
|
54
|
+
next true if config[:payload].bytesize <= validator.max_payload_size
|
55
|
+
|
56
|
+
[[%i[payload], :max_size]]
|
37
57
|
end
|
38
58
|
end
|
39
59
|
end
|
@@ -11,26 +11,29 @@ module WaterDrop
|
|
11
11
|
#
|
12
12
|
# @note You need to setup the `dogstatsd-ruby` client and assign it
|
13
13
|
class Listener
|
14
|
-
include
|
14
|
+
include WaterDrop::Configurable
|
15
|
+
extend Forwardable
|
16
|
+
|
17
|
+
def_delegators :config, :client, :rd_kafka_metrics, :namespace, :default_tags
|
15
18
|
|
16
19
|
# Value object for storing a single rdkafka metric publishing details
|
17
20
|
RdKafkaMetric = Struct.new(:type, :scope, :name, :key_location)
|
18
21
|
|
19
22
|
# Namespace under which the DD metrics should be published
|
20
|
-
setting :namespace, default: 'waterdrop'
|
23
|
+
setting :namespace, default: 'waterdrop'
|
21
24
|
|
22
25
|
# Datadog client that we should use to publish the metrics
|
23
|
-
setting :client
|
26
|
+
setting :client
|
24
27
|
|
25
28
|
# Default tags we want to publish (for example hostname)
|
26
29
|
# Format as followed (example for hostname): `["host:#{Socket.gethostname}"]`
|
27
|
-
setting :default_tags, default: []
|
30
|
+
setting :default_tags, default: []
|
28
31
|
|
29
32
|
# All the rdkafka metrics we want to publish
|
30
33
|
#
|
31
34
|
# By default we publish quite a lot so this can be tuned
|
32
35
|
# Note, that the once with `_d` come from WaterDrop, not rdkafka or Kafka
|
33
|
-
setting :rd_kafka_metrics,
|
36
|
+
setting :rd_kafka_metrics, default: [
|
34
37
|
# Client metrics
|
35
38
|
RdKafkaMetric.new(:count, :root, 'calls', 'tx_d'),
|
36
39
|
RdKafkaMetric.new(:histogram, :root, 'queue.size', 'msg_cnt_d'),
|
@@ -47,8 +50,11 @@ module WaterDrop
|
|
47
50
|
RdKafkaMetric.new(:gauge, :brokers, 'network.latency.p99', %w[rtt p99])
|
48
51
|
].freeze
|
49
52
|
|
53
|
+
configure
|
54
|
+
|
50
55
|
# @param block [Proc] configuration block
|
51
56
|
def initialize(&block)
|
57
|
+
configure
|
52
58
|
setup(&block) if block
|
53
59
|
end
|
54
60
|
|
data/lib/waterdrop/version.rb
CHANGED
data/lib/waterdrop.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
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
|
+
forwardable
|
6
7
|
concurrent/array
|
8
|
+
concurrent/hash
|
9
|
+
yaml
|
7
10
|
dry/monitor/notifications
|
8
|
-
dry-validation
|
9
11
|
rdkafka
|
10
12
|
json
|
11
13
|
zeitwerk
|
data/waterdrop.gemspec
CHANGED
@@ -18,7 +18,6 @@ Gem::Specification.new do |spec|
|
|
18
18
|
|
19
19
|
spec.add_dependency 'concurrent-ruby', '>= 1.1'
|
20
20
|
spec.add_dependency 'dry-monitor', '~> 0.5'
|
21
|
-
spec.add_dependency 'dry-validation', '~> 1.7'
|
22
21
|
spec.add_dependency 'rdkafka', '>= 0.10'
|
23
22
|
spec.add_dependency 'zeitwerk', '~> 2.3'
|
24
23
|
|
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: waterdrop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.3.
|
4
|
+
version: 2.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maciej Mensfeld
|
@@ -34,7 +34,7 @@ cert_chain:
|
|
34
34
|
R2P11bWoCtr70BsccVrN8jEhzwXngMyI2gVt750Y+dbTu1KgRqZKp/ECe7ZzPzXj
|
35
35
|
pIy9vHxTANKYVyI4qj8OrFdEM5BQNu8oQpL0iQ==
|
36
36
|
-----END CERTIFICATE-----
|
37
|
-
date: 2022-07-
|
37
|
+
date: 2022-07-18 00:00:00.000000000 Z
|
38
38
|
dependencies:
|
39
39
|
- !ruby/object:Gem::Dependency
|
40
40
|
name: concurrent-ruby
|
@@ -64,20 +64,6 @@ dependencies:
|
|
64
64
|
- - "~>"
|
65
65
|
- !ruby/object:Gem::Version
|
66
66
|
version: '0.5'
|
67
|
-
- !ruby/object:Gem::Dependency
|
68
|
-
name: dry-validation
|
69
|
-
requirement: !ruby/object:Gem::Requirement
|
70
|
-
requirements:
|
71
|
-
- - "~>"
|
72
|
-
- !ruby/object:Gem::Version
|
73
|
-
version: '1.7'
|
74
|
-
type: :runtime
|
75
|
-
prerelease: false
|
76
|
-
version_requirements: !ruby/object:Gem::Requirement
|
77
|
-
requirements:
|
78
|
-
- - "~>"
|
79
|
-
- !ruby/object:Gem::Version
|
80
|
-
version: '1.7'
|
81
67
|
- !ruby/object:Gem::Dependency
|
82
68
|
name: rdkafka
|
83
69
|
requirement: !ruby/object:Gem::Requirement
|
@@ -133,8 +119,11 @@ files:
|
|
133
119
|
- lib/waterdrop/configurable.rb
|
134
120
|
- lib/waterdrop/configurable/leaf.rb
|
135
121
|
- lib/waterdrop/configurable/node.rb
|
122
|
+
- lib/waterdrop/contractable.rb
|
123
|
+
- lib/waterdrop/contractable/contract.rb
|
124
|
+
- lib/waterdrop/contractable/result.rb
|
125
|
+
- lib/waterdrop/contractable/rule.rb
|
136
126
|
- lib/waterdrop/contracts.rb
|
137
|
-
- lib/waterdrop/contracts/base.rb
|
138
127
|
- lib/waterdrop/contracts/config.rb
|
139
128
|
- lib/waterdrop/contracts/message.rb
|
140
129
|
- lib/waterdrop/errors.rb
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WaterDrop
|
4
|
-
module Contracts
|
5
|
-
# Base for all the contracts in WaterDrop
|
6
|
-
class Base < Dry::Validation::Contract
|
7
|
-
config.messages.load_paths << File.join(WaterDrop.gem_root, 'config', 'errors.yml')
|
8
|
-
|
9
|
-
# @param data [Hash] data for validation
|
10
|
-
# @param error_class [Class] error class that should be used when validation fails
|
11
|
-
# @return [Boolean] true
|
12
|
-
# @raise [StandardError] any error provided in the error_class that inherits from the
|
13
|
-
# standard error
|
14
|
-
def validate!(data, error_class)
|
15
|
-
result = call(data)
|
16
|
-
|
17
|
-
return true if result.success?
|
18
|
-
|
19
|
-
raise error_class, result.errors.to_h
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|