waterdrop 1.4.0

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.
@@ -0,0 +1,19 @@
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
+ # @note Does not validate message itself as it is not our concern
8
+ class MessageOptions < Dry::Validation::Contract
9
+ params do
10
+ required(:topic).filled(:str?, format?: TOPIC_REGEXP)
11
+ optional(:key).maybe(:str?, :filled?)
12
+ optional(:partition).filled(:int?, gteq?: 0)
13
+ optional(:partition_key).maybe(:str?, :filled?)
14
+ optional(:create_time).maybe(:time?)
15
+ optional(:headers).maybe(:hash?)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WaterDrop
4
+ # Namespace used to encapsulate all the internal errors of WaterDrop
5
+ module Errors
6
+ # Base class for all the WaterDrop internal errors
7
+ BaseError = Class.new(StandardError)
8
+
9
+ # Raised when configuration doesn't match with validation contract
10
+ InvalidConfiguration = Class.new(BaseError)
11
+
12
+ # Raised when we try to send message with invalid options
13
+ InvalidMessageOptions = Class.new(BaseError)
14
+
15
+ # Raised when want to hook up to an event that is not registered and supported
16
+ UnregisteredMonitorEvent = Class.new(BaseError)
17
+ end
18
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WaterDrop
4
+ # Namespace for all the things related with WaterDrop instrumentation process
5
+ module Instrumentation
6
+ # Monitor is used to hookup external monitoring services to monitor how WaterDrop works
7
+ # Since it is a pub-sub based on dry-monitor, you can use as many subscribers/loggers at the
8
+ # same time, which means that you might have for example file logging and NewRelic at the same
9
+ # time
10
+ # @note This class acts as a singleton because we are only permitted to have single monitor
11
+ # per running process (just as logger)
12
+ class Monitor < Dry::Monitor::Notifications
13
+ # List of events that we support in the system and to which a monitor client can hook up
14
+ # @note The non-error once support timestamp benchmarking
15
+ BASE_EVENTS = %w[
16
+ async_producer.call.error
17
+ async_producer.call.retry
18
+ sync_producer.call.error
19
+ sync_producer.call.retry
20
+ ].freeze
21
+
22
+ private_constant :BASE_EVENTS
23
+
24
+ # @return [WaterDrop::Instrumentation::Monitor] monitor instance for system instrumentation
25
+ def initialize
26
+ super(:waterdrop)
27
+ BASE_EVENTS.each(&method(:register_event))
28
+ end
29
+
30
+ # Allows us to subscribe to events with a code that will be yielded upon events
31
+ # @param event_name_or_listener [String, Object] name of the event we want to subscribe to
32
+ # or a listener if we decide to go with object listener
33
+ def subscribe(event_name_or_listener)
34
+ return super unless event_name_or_listener.is_a?(String)
35
+ return super if available_events.include?(event_name_or_listener)
36
+
37
+ raise Errors::UnregisteredMonitorEvent, event_name_or_listener
38
+ end
39
+
40
+ # @return [Array<String>] names of available events to which we can subscribe
41
+ def available_events
42
+ __bus__.events.keys
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module WaterDrop
4
+ module Instrumentation
5
+ # Default listener that hooks up to our instrumentation and uses its events for logging
6
+ # It can be removed/replaced or anything without any harm to the Waterdrop flow
7
+ # @note It is a module as we can use it then as a part of the Karafka framework listener
8
+ # as well as we can use it standalone
9
+ class StdoutListener
10
+ # Log levels that we use in this particular listener
11
+ USED_LOG_LEVELS = %i[
12
+ info
13
+ error
14
+ ].freeze
15
+
16
+ %i[
17
+ sync_producer
18
+ async_producer
19
+ ].each do |producer_type|
20
+ error_name = :"on_#{producer_type}_call_error"
21
+ retry_name = :"on_#{producer_type}_call_retry"
22
+
23
+ define_method error_name do |event|
24
+ options = event[:options]
25
+ error = event[:error]
26
+ error "Delivery failure to: #{options} because of #{error}"
27
+ end
28
+
29
+ define_method retry_name do |event|
30
+ attempts_count = event[:attempts_count]
31
+ options = event[:options]
32
+ error = event[:error]
33
+
34
+ info "Attempt #{attempts_count} of delivery to: #{options} because of #{error}"
35
+ end
36
+ end
37
+
38
+ USED_LOG_LEVELS.each do |log_level|
39
+ define_method log_level do |*args|
40
+ WaterDrop.logger.send(log_level, *args)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WaterDrop library
4
+ module WaterDrop
5
+ # Sync producer for messages
6
+ class SyncProducer < BaseProducer
7
+ # Performs message delivery using deliver_async method
8
+ # @param message [String] message that we want to send to Kafka
9
+ # @param options [Hash] options (including topic) for producer
10
+ # @raise [WaterDrop::Errors::InvalidMessageOptions] raised when message options are
11
+ # somehow invalid and we cannot perform delivery because of that
12
+ def self.call(message, options)
13
+ attempts_count ||= 0
14
+ attempts_count += 1
15
+
16
+ validate!(options)
17
+ return unless WaterDrop.config.deliver
18
+
19
+ DeliveryBoy.deliver(message, options)
20
+ rescue Kafka::Error => e
21
+ graceful_attempt?(attempts_count, message, options, e) ? retry : raise(e)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # WaterDrop library
4
+ module WaterDrop
5
+ # Current WaterDrop version
6
+ VERSION = '1.4.0'
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is used as a compatibility step
4
+ require 'water_drop'
File without changes
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'water_drop/version'
7
+
8
+ Gem::Specification.new do |spec|
9
+ spec.name = 'waterdrop'
10
+ spec.version = ::WaterDrop::VERSION
11
+ spec.platform = Gem::Platform::RUBY
12
+ spec.authors = ['Maciej Mensfeld']
13
+ spec.email = %w[maciej@mensfeld.pl]
14
+ spec.homepage = 'https://github.com/karafka/waterdrop'
15
+ spec.summary = 'Kafka messaging made easy!'
16
+ spec.description = spec.summary
17
+ spec.license = 'MIT'
18
+
19
+ spec.add_dependency 'delivery_boy', '>= 0.2', '< 2.x'
20
+ spec.add_dependency 'dry-configurable', '~> 0.8'
21
+ spec.add_dependency 'dry-monitor', '~> 0.3'
22
+ spec.add_dependency 'dry-validation', '~> 1.2'
23
+ spec.add_dependency 'ruby-kafka', '>= 0.7.8'
24
+ spec.add_dependency 'zeitwerk', '~> 2.1'
25
+
26
+ spec.required_ruby_version = '>= 2.5.0'
27
+
28
+ if $PROGRAM_NAME.end_with?('gem')
29
+ spec.signing_key = File.expand_path('~/.ssh/gem-private_key.pem')
30
+ end
31
+
32
+ spec.cert_chain = %w[certs/mensfeld.pem]
33
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
34
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
35
+ spec.require_paths = %w[lib]
36
+ end
metadata ADDED
@@ -0,0 +1,189 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: waterdrop
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.4.0
5
+ platform: ruby
6
+ authors:
7
+ - Maciej Mensfeld
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain:
11
+ - |
12
+ -----BEGIN CERTIFICATE-----
13
+ MIIEODCCAqCgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDDBhtYWNp
14
+ ZWovREM9bWVuc2ZlbGQvREM9cGwwHhcNMjAwODExMDkxNTM3WhcNMjEwODExMDkx
15
+ NTM3WjAjMSEwHwYDVQQDDBhtYWNpZWovREM9bWVuc2ZlbGQvREM9cGwwggGiMA0G
16
+ CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQDCpXsCgmINb6lHBXXBdyrgsBPSxC4/
17
+ 2H+weJ6L9CruTiv2+2/ZkQGtnLcDgrD14rdLIHK7t0o3EKYlDT5GhD/XUVhI15JE
18
+ N7IqnPUgexe1fbZArwQ51afxz2AmPQN2BkB2oeQHXxnSWUGMhvcEZpfbxCCJH26w
19
+ hS0Ccsma8yxA6hSlGVhFVDuCr7c2L1di6cK2CtIDpfDaWqnVNJEwBYHIxrCoWK5g
20
+ sIGekVt/admS9gRhIMaIBg+Mshth5/DEyWO2QjteTodItlxfTctrfmiAl8X8T5JP
21
+ VXeLp5SSOJ5JXE80nShMJp3RFnGw5fqjX/ffjtISYh78/By4xF3a25HdWH9+qO2Z
22
+ tx0wSGc9/4gqNM0APQnjN/4YXrGZ4IeSjtE+OrrX07l0TiyikzSLFOkZCAp8oBJi
23
+ Fhlosz8xQDJf7mhNxOaZziqASzp/hJTU/tuDKl5+ql2icnMv5iV/i6SlmvU29QNg
24
+ LCV71pUv0pWzN+OZbHZKWepGhEQ3cG9MwvkCAwEAAaN3MHUwCQYDVR0TBAIwADAL
25
+ BgNVHQ8EBAMCBLAwHQYDVR0OBBYEFImGed2AXS070ohfRidiCEhXEUN+MB0GA1Ud
26
+ EQQWMBSBEm1hY2llakBtZW5zZmVsZC5wbDAdBgNVHRIEFjAUgRJtYWNpZWpAbWVu
27
+ c2ZlbGQucGwwDQYJKoZIhvcNAQELBQADggGBAKiHpwoENVrMi94V1zD4o8/6G3AU
28
+ gWz4udkPYHTZLUy3dLznc/sNjdkJFWT3E6NKYq7c60EpJ0m0vAEg5+F5pmNOsvD3
29
+ 2pXLj9kisEeYhR516HwXAvtngboUcb75skqvBCU++4Pu7BRAPjO1/ihLSBexbwSS
30
+ fF+J5OWNuyHHCQp+kGPLtXJe2yUYyvSWDj3I2//Vk0VhNOIlaCS1+5/P3ZJThOtm
31
+ zJUBI7h3HgovwRpcnmk2mXTmU4Zx/bCzX8EA6VY0khEvnmiq7S6eBF0H9qH8KyQ6
32
+ EkVLpvmUDFcf/uNaBQdazEMB5jYtwoA8gQlANETNGPi51KlkukhKgaIEDMkBDJOx
33
+ 65N7DzmkcyY0/GwjIVIxmRhcrCt1YeCUElmfFx0iida1/YRm6sB2AXqScc1+ECRi
34
+ 2DND//YJUikn1zwbz1kT70XmHd97B4Eytpln7K+M1u2g1pHVEPW4owD/ammXNpUy
35
+ nt70FcDD4yxJQ+0YNiHd0N8IcVBM1TMIVctMNQ==
36
+ -----END CERTIFICATE-----
37
+ date: 2020-08-25 00:00:00.000000000 Z
38
+ dependencies:
39
+ - !ruby/object:Gem::Dependency
40
+ name: delivery_boy
41
+ requirement: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: '0.2'
46
+ - - "<"
47
+ - !ruby/object:Gem::Version
48
+ version: 2.x
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0.2'
56
+ - - "<"
57
+ - !ruby/object:Gem::Version
58
+ version: 2.x
59
+ - !ruby/object:Gem::Dependency
60
+ name: dry-configurable
61
+ requirement: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - "~>"
64
+ - !ruby/object:Gem::Version
65
+ version: '0.8'
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - "~>"
71
+ - !ruby/object:Gem::Version
72
+ version: '0.8'
73
+ - !ruby/object:Gem::Dependency
74
+ name: dry-monitor
75
+ requirement: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - "~>"
78
+ - !ruby/object:Gem::Version
79
+ version: '0.3'
80
+ type: :runtime
81
+ prerelease: false
82
+ version_requirements: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - "~>"
85
+ - !ruby/object:Gem::Version
86
+ version: '0.3'
87
+ - !ruby/object:Gem::Dependency
88
+ name: dry-validation
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: '1.2'
94
+ type: :runtime
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: '1.2'
101
+ - !ruby/object:Gem::Dependency
102
+ name: ruby-kafka
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: 0.7.8
108
+ type: :runtime
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 0.7.8
115
+ - !ruby/object:Gem::Dependency
116
+ name: zeitwerk
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '2.1'
122
+ type: :runtime
123
+ prerelease: false
124
+ version_requirements: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - "~>"
127
+ - !ruby/object:Gem::Version
128
+ version: '2.1'
129
+ description: Kafka messaging made easy!
130
+ email:
131
+ - maciej@mensfeld.pl
132
+ executables: []
133
+ extensions: []
134
+ extra_rdoc_files: []
135
+ files:
136
+ - ".coditsu/ci.yml"
137
+ - ".github/FUNDING.yml"
138
+ - ".gitignore"
139
+ - ".rspec"
140
+ - ".ruby-gemset"
141
+ - ".ruby-version"
142
+ - ".travis.yml"
143
+ - CHANGELOG.md
144
+ - Gemfile
145
+ - Gemfile.lock
146
+ - MIT-LICENCE
147
+ - README.md
148
+ - certs/mensfeld.pem
149
+ - config/errors.yml
150
+ - lib/water_drop.rb
151
+ - lib/water_drop/async_producer.rb
152
+ - lib/water_drop/base_producer.rb
153
+ - lib/water_drop/config.rb
154
+ - lib/water_drop/config_applier.rb
155
+ - lib/water_drop/contracts.rb
156
+ - lib/water_drop/contracts/config.rb
157
+ - lib/water_drop/contracts/message_options.rb
158
+ - lib/water_drop/errors.rb
159
+ - lib/water_drop/instrumentation/monitor.rb
160
+ - lib/water_drop/instrumentation/stdout_listener.rb
161
+ - lib/water_drop/sync_producer.rb
162
+ - lib/water_drop/version.rb
163
+ - lib/waterdrop.rb
164
+ - log/.gitkeep
165
+ - waterdrop.gemspec
166
+ homepage: https://github.com/karafka/waterdrop
167
+ licenses:
168
+ - MIT
169
+ metadata: {}
170
+ post_install_message:
171
+ rdoc_options: []
172
+ require_paths:
173
+ - lib
174
+ required_ruby_version: !ruby/object:Gem::Requirement
175
+ requirements:
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: 2.5.0
179
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - ">="
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ requirements: []
185
+ rubygems_version: 3.1.4
186
+ signing_key:
187
+ specification_version: 4
188
+ summary: Kafka messaging made easy!
189
+ test_files: []
Binary file