event_store_client 3.0.0 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9e8e4782cfd1b70376d4531ae7452d721e7af3078f23a2c6d345e654fed5d1d
4
- data.tar.gz: b7de94d902e0ff87747b5f58f3740c580473514638901ed666fc07d98fc47b7f
3
+ metadata.gz: 3d4a4dc809c49fb6d9ab52ea68c6bfcb58217cb463d64e7591d5de7e0e5c80af
4
+ data.tar.gz: 29c38f7e04dd6b4b73a61966353ec51246037dfb6ffd25d38a8ebfc06c9faa8a
5
5
  SHA512:
6
- metadata.gz: 03015ede0f74da75119247433579079ee2d71a9ab9f347d23a2f52915c5bc3441574f098a9509843325c5dd568c95c6c77681a7aaa81c364b90397e95181f929
7
- data.tar.gz: 10f9a45843bc18f0e2e35294b9dee0c7e0c1f9a33e1e4c41881f407c82ee5df4714aaef6f6d4879b271f7ef6ea032e7e765d0f97ff23b99046a68ff2b230bffb
6
+ metadata.gz: 4c15bb242df2456c57fbddd4dbe553113e803d26062081e1261ba134b64db2e445ae0f6a56a218555f3007e858c030212285749765aa941152fc6e395325acf6
7
+ data.tar.gz: 04654757ca1d4ed722255ac16d53cfa6415a7dc098ded62fbc91479fef681ed66f80ecd61e408f7a2f8bdafbf724cc4e1b9766c39f015cd79a799b327f15a3b3
data/README.md CHANGED
@@ -9,7 +9,7 @@ An easy-to use GRPC API client for connecting ruby applications with [EventStore
9
9
 
10
10
  `event_store_client` gem requires:
11
11
 
12
- - ruby 2.7 or newer.
12
+ - ruby 3.0 or newer.
13
13
  - EventstoreDB version `>= "20.*"`.
14
14
 
15
15
  ## Installation
@@ -1,6 +1,4 @@
1
- # @title Appending events
2
-
3
- # Append your first event
1
+ # Appending Events
4
2
 
5
3
  ## Append your first event
6
4
 
@@ -15,10 +13,10 @@ event = SomethingHappened.new(
15
13
  )
16
14
 
17
15
  begin
18
- EventStoreClient.client.append_to_stream('some-stream', event)
16
+ EventStoreClient.client.append_to_stream('some-stream', event)
19
17
  # => EventStore::Client::Streams::AppendResp
20
18
  rescue EventStoreClient::WrongExpectedVersionError => e
21
- puts e.message
19
+ puts e.message
22
20
  end
23
21
  ```
24
22
 
@@ -38,7 +36,7 @@ event2 = SomethingHappened.new(
38
36
  )
39
37
 
40
38
  begin
41
- EventStoreClient.client.append_to_stream('some-stream', [event1, event2])
39
+ EventStoreClient.client.append_to_stream('some-stream', [event1, event2])
42
40
  # => Array<EventStore::Client::Streams::AppendResp>
43
41
  rescue EventStoreClient::WrongExpectedVersionError => e
44
42
  puts e.message
@@ -1,6 +1,4 @@
1
- # @title Catch-up subscriptions
2
-
3
- # Catch-up subscriptions
1
+ # Catch-up Subscriptions
4
2
 
5
3
  Subscriptions allow you to subscribe to a stream and receive notifications about new events added to the stream.
6
4
 
@@ -111,7 +109,7 @@ An application, which hosts the subscription, can go offline for a period of tim
111
109
  checkpoint = :start
112
110
  handler = proc do |event|
113
111
  handle_event(event)
114
- checkpoint = event.stream_revision
112
+ checkpoint = event.stream_revision
115
113
  end
116
114
 
117
115
  EventStoreClient.client.subscribe_to_stream('some-stream', handler: handler, options: { from_revision: checkpoint })
@@ -1,19 +1,18 @@
1
- # @title Configuration
2
-
3
1
  # Configuration
4
2
 
5
3
  Configuration options:
6
4
 
7
- | name | value | default value | description |
8
- |----------------------|--------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
9
- | eventstore_url | String | `'esdb://localhost:2113'` | Connection string. See description of possible values below. |
10
- | per_page | Integer | `20` | Number of events to return in one response. |
11
- | mapper | `EventStoreClient::Mapper::Default.new` or `EventStoreClient::Mapper::Encrypted.new` | `EventStoreClient::Mapper::Default.new` | An object that is responsible for serialization / deserialization and encryption / decryption of events. |
12
- | default_event_class | `DeserializedEvent` or any class, inherited from it | `DeserializedEvent` | This class will be used during the deserialization process when deserializer fails to resolve an event's class from response. |
13
- | logger | `Logger` | `nil` | A logger that would log messages from `event_store_client` and `grpc` gems. |
14
- | skip_deserialization | Boolean | `false` | Whether to skip event deserialization using the given `mapper` setting. If you set it to `true` decryption will be skipped as well. It is useful when you want to defer deserialization and handle it later by yourself. |
15
- | skip_decryption | Boolean | `false` | Whether to skip decrypting encrypted event payloads. |
16
- | channel_args | Hash | `{ 'grpc.min_reconnect_backoff_ms' => 100, 'grpc.max_reconnect_backoff_ms' => 100, 'grpc.initial_reconnect_backoff_ms' => 100 }` | GRPC-specific connection options. This hash will be passed into the `:channel_args` argument of a Stub class of your request. More GRPC options can be found [here](https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h). |
5
+ | name | value | default value | description |
6
+ |----------------------|--------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
7
+ | eventstore_url | String | `'esdb://localhost:2113'` | Connection string. See description of possible values below. |
8
+ | per_page | Integer | `20` | Number of events to return in one response. |
9
+ | mapper | `EventStoreClient::Mapper::Default.new` or `EventStoreClient::Mapper::Encrypted.new` | `EventStoreClient::Mapper::Default.new` | An object that is responsible for serialization / deserialization and encryption / decryption of events. |
10
+ | default_event_class | `DeserializedEvent` or any class, inherited from it | `EventStoreClient::DeserializedEvent` | This class will be used during the deserialization process when deserializer fails to resolve an event's class from response. |
11
+ | event_class_resolver | `Proc` | `nil` | A Proc that accepts a string and returns event class. |
12
+ | logger | `Logger` | `nil` | A logger that would log messages from `event_store_client` and `grpc` gems. |
13
+ | skip_deserialization | Boolean | `false` | Whether to skip event deserialization using the given `mapper` setting. If you set it to `true` decryption will be skipped as well. It is useful when you want to defer deserialization and handle it later by yourself. |
14
+ | skip_decryption | Boolean | `false` | Whether to skip decrypting encrypted event payloads. |
15
+ | channel_args | Hash | `{ 'grpc.min_reconnect_backoff_ms' => 100, 'grpc.max_reconnect_backoff_ms' => 100, 'grpc.initial_reconnect_backoff_ms' => 100 }` | GRPC-specific connection options. This hash will be passed into the `:channel_args` argument of a Stub class of your request. More GRPC options can be found [here](https://github.com/grpc/grpc/blob/v1.55.1/include/grpc/impl/grpc_types.h#L141). |
17
16
 
18
17
  ## A note about channel_args option
19
18
 
@@ -28,9 +27,9 @@ end
28
27
  the resulting `channel_args` value will be
29
28
 
30
29
  ```ruby
31
- {
32
- "grpc.min_reconnect_backoff_ms" => 200,
33
- "grpc.max_reconnect_backoff_ms" => 100,
30
+ {
31
+ "grpc.min_reconnect_backoff_ms" => 200,
32
+ "grpc.max_reconnect_backoff_ms" => 100,
34
33
  "grpc.initial_reconnect_backoff_ms" => 100
35
34
  }
36
35
  ```
@@ -39,7 +38,7 @@ This behaviour is intentional. So, if you want to override them all - you should
39
38
 
40
39
  ```ruby
41
40
  EventStoreClient.configure do |config|
42
- config.channel_args = {
41
+ config.channel_args = {
43
42
  'grpc.min_reconnect_backoff_ms' => 500,
44
43
  'grpc.max_reconnect_backoff_ms' => 500,
45
44
  'grpc.initial_reconnect_backoff_ms' => 500
@@ -139,7 +138,7 @@ EventStoreClient.configure(name: :es_db_1) do |config|
139
138
  end
140
139
  EventStoreClient.configure(name: :es_db_2) do |config|
141
140
  # adjust your second config here
142
- config.eventstore_url = 'esdb://localhost:2114'
141
+ config.eventstore_url = 'esdb://localhost:2114'
143
142
  end
144
143
  ```
145
144
 
@@ -162,7 +161,7 @@ Setup your default config:
162
161
  ```ruby
163
162
  EventStoreClient.configure do |config|
164
163
  # config goes here
165
- config.eventstore_url = 'esdb://localhost:2113'
164
+ config.eventstore_url = 'esdb://localhost:2113'
166
165
  end
167
166
  ```
168
167
 
@@ -1,6 +1,4 @@
1
- # @title Deleting streams
2
-
3
- # Deleting streams
1
+ # Deleting Streams
4
2
 
5
3
  ## Soft deleting streams
6
4
 
@@ -1,6 +1,4 @@
1
- # @title Encrypting events
2
-
3
- # Encrypting events
1
+ # Encrypting Events
4
2
 
5
3
  To encrypt/decrypt events payload, you can use an encrypted mapper.
6
4
 
@@ -1,6 +1,4 @@
1
- # @title Linking events
2
-
3
- # Linking events
1
+ # Linking Events
4
2
 
5
3
  ## Linking single event
6
4
 
@@ -20,7 +18,7 @@ EventStoreClient.client.append_to_stream(stream_name_1, event)
20
18
  # Get persisted event
21
19
  event = EventStoreClient.client.read(stream_name_1).first
22
20
  # Link event from first stream into second stream
23
- EventStoreClient.client.link_to(stream_name_2, event)
21
+ EventStoreClient.client.link_to(stream_name_2, event)
24
22
  # => EventStore::Client::Streams::AppendResp
25
23
  ```
26
24
 
@@ -55,7 +53,7 @@ end
55
53
  # Get persisted events
56
54
  events = EventStoreClient.client.read(stream_name_1)
57
55
  # Link events from first stream into second stream one by one
58
- EventStoreClient.client.link_to(stream_name_2, events)
56
+ EventStoreClient.client.link_to(stream_name_2, events)
59
57
  # => Array<EventStore::Client::Streams::AppendResp>
60
58
  ```
61
59
 
@@ -86,7 +84,7 @@ events = EventStoreClient.client.read(stream_name_1)
86
84
  begin
87
85
  EventStoreClient.client.link_to(stream_name_2, events, options: { expected_revision: :no_stream })
88
86
  rescue EventStoreClient::WrongExpectedVersionError => e
89
- puts e.message
87
+ puts e.message
90
88
  end
91
89
  ```
92
90
 
@@ -1,6 +1,4 @@
1
- # @title Reading events
2
-
3
- # Reading events
1
+ # Reading Events
4
2
 
5
3
  ## Reading from a stream
6
4
 
@@ -63,7 +61,7 @@ In case a stream with given name does not exist - `EventStoreClient::StreamNotFo
63
61
 
64
62
  ```ruby
65
63
  begin
66
- EventStoreClient.client.read('non-existing-stream')
64
+ EventStoreClient.client.read('non-existing-stream')
67
65
  rescue EventStoreClient::StreamNotFoundError => e
68
66
  puts e.message # => Stream "non-existing-stream" does not exist.
69
67
  puts e.stream_name # => "non-existing-stream"
@@ -23,6 +23,7 @@ module EventStoreClient
23
23
  # https://github.com/grpc/grpc/blob/master/include/grpc/impl/codegen/grpc_types.h
24
24
  option(:channel_args) # Hash
25
25
  option(:name) { :default }
26
+ option(:event_class_resolver) # Proc that excepts a string and returns a class
26
27
 
27
28
  def eventstore_url=(value)
28
29
  @eventstore_url =
@@ -48,28 +48,43 @@ module EventStoreClient
48
48
 
49
49
  # @return [String]
50
50
  def user_friendly_message
51
- if wrong_expected_version.expected_stream_exists
52
- return "Expected stream to exist, but it doesn't."
53
- end
54
- if wrong_expected_version.expected_no_stream
55
- return "Expected stream to be absent, but it actually exists."
56
- end
57
- if wrong_expected_version.current_no_stream
58
- return <<~TEXT.strip
59
- Stream revision #{wrong_expected_version.expected_revision.inspect} is expected, but \
60
- stream does not exist.
61
- TEXT
62
- end
51
+ return expected_stream_exists if wrong_expected_version.expected_stream_exists
52
+ return expected_no_stream if wrong_expected_version.expected_no_stream
53
+ return current_no_stream if wrong_expected_version.current_no_stream
63
54
  unless wrong_expected_version.expected_revision == wrong_expected_version.current_revision
64
- return <<~TEXT.strip
65
- Stream revision #{wrong_expected_version.expected_revision.inspect} is expected, but \
66
- actual stream revision is #{wrong_expected_version.current_revision.inspect}.
67
- TEXT
55
+ return unmatched_stream_revision
68
56
  end
57
+
69
58
  # Unhandled case. Could happen if something else would be added to proto and I don't add it
70
59
  # here.
71
60
  self.class.to_s
72
61
  end
62
+
63
+ # @return [String]
64
+ def expected_stream_exists
65
+ "Expected stream to exist, but it doesn't."
66
+ end
67
+
68
+ # @return [String]
69
+ def expected_no_stream
70
+ "Expected stream to be absent, but it actually exists."
71
+ end
72
+
73
+ # @return [String]
74
+ def current_no_stream
75
+ <<~TEXT.strip
76
+ Stream revision #{wrong_expected_version.expected_revision.inspect} is expected, but \
77
+ stream does not exist.
78
+ TEXT
79
+ end
80
+
81
+ # @return [String]
82
+ def unmatched_stream_revision
83
+ <<~TEXT.strip
84
+ Stream revision #{wrong_expected_version.expected_revision.inspect} is expected, but \
85
+ actual stream revision is #{wrong_expected_version.current_revision.inspect}.
86
+ TEXT
87
+ end
73
88
  end
74
89
 
75
90
  class StreamDeletionError < Error
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreClient
4
+ class EventClassResolver
5
+ attr_reader :config
6
+ private :config
7
+
8
+ # @param config [EventStoreClient::Config]
9
+ def initialize(config)
10
+ @config = config
11
+ end
12
+
13
+ # @param event_type [String, nil]
14
+ # @return [Class<EventStoreClient::DeserializedEvent>]
15
+ def resolve(event_type)
16
+ return config.event_class_resolver.(event_type) || config.default_event_class if config.event_class_resolver
17
+
18
+ Object.const_get(event_type)
19
+ rescue NameError, TypeError
20
+ config.logger&.debug(<<~TEXT.strip)
21
+ Unable to resolve class by `#{event_type}' event type. \
22
+ Picking default `#{config.default_event_class}' event class to instantiate the event.
23
+ TEXT
24
+ config.default_event_class
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This matcher is defined to test options which are defined by using
4
+ # EventStoreClient::Extensions::OptionsExtension option. Example:
5
+ # Let's say you have next class
6
+ # class SomeClass
7
+ # include EventStoreClient::Extensions::OptionsExtension
8
+ #
9
+ # option(:some_opt) { '1' }
10
+ # end
11
+ #
12
+ # To test that its instance has the proper option with the proper default value you can use this
13
+ # matcher:
14
+ # RSpec.describe SomeClass do
15
+ # subject { described_class.new }
16
+ #
17
+ # # Check that :some_opt is present
18
+ # it { is_expected.to have_option(:some_opt) }
19
+ # # Check that :some_opt is present and has the correct default value
20
+ # it { is_expected.to have_option(:some_opt).with_default_value('1') }
21
+ # end
22
+ #
23
+ # If you have more complex implementation of default value of your option - you should handle it
24
+ # customly. For example:
25
+ # class SomeClass
26
+ # include EventStoreClient::Extensions::OptionsExtension
27
+ #
28
+ # option(:some_opt) { calc_value }
29
+ # end
30
+ # You could test it like so:
31
+ # RSpec.described SomeClass do
32
+ # let(:instance) { described_class.new }
33
+ #
34
+ # describe ':some_opt default value' do
35
+ # subject { instance.some_opt }
36
+ #
37
+ # let(:value) { 'some val' }
38
+ #
39
+ # before do
40
+ # allow(instance).to receive(:calc_value).and_return(value)
41
+ # end
42
+ #
43
+ # it { is_expected.to eq(value) }
44
+ # end
45
+ # end
46
+ RSpec::Matchers.define :has_option do |option_name|
47
+ match do |obj|
48
+ option_presence = obj.class.respond_to?(:options) && obj.class.options.include?(option_name)
49
+ if @default_value
50
+ option_presence && obj.class.allocate.public_send(option_name) == @default_value
51
+ else
52
+ option_presence
53
+ end
54
+ end
55
+
56
+ failure_message do |obj|
57
+ option_presence = obj.class.respond_to?(:options) && obj.class.options.include?(option_name)
58
+ if option_presence && @default_value
59
+ msg = "Expected #{obj.class} to have `#{option_name.inspect}' option with #{@default_value.inspect}"
60
+ msg += ' default value, but default value is'
61
+ msg += " #{obj.class.allocate.public_send(option_name).inspect}"
62
+ else
63
+ msg = "Expected #{obj} to have `#{option_name.inspect}' option."
64
+ end
65
+
66
+ msg
67
+ end
68
+
69
+ description do
70
+ expected_list = RSpec::Matchers::EnglishPhrasing.list(expected)
71
+ sentences =
72
+ @chained_method_clauses.map do |(method_name, method_args)|
73
+ next '' if method_name == :required_kwargs
74
+
75
+ english_name = RSpec::Matchers::EnglishPhrasing.split_words(method_name)
76
+ arg_list = RSpec::Matchers::EnglishPhrasing.list(method_args)
77
+ " #{english_name}#{arg_list}"
78
+ end.join
79
+
80
+ "have#{expected_list} option#{sentences}"
81
+ end
82
+
83
+ chain :with_default_value do |val|
84
+ @default_value = val
85
+ end
86
+ end
87
+
88
+ RSpec::Matchers.alias_matcher :have_option, :has_option
@@ -32,7 +32,8 @@ module EventStoreClient
32
32
  custom_metadata = serializer.deserialize(normalize_serialized(raw_event.custom_metadata))
33
33
  metadata = raw_event.metadata.to_h
34
34
 
35
- event_class(metadata['type']).new(
35
+ event_class = EventStoreClient::EventClassResolver.new(config).resolve(metadata['type'])
36
+ event_class.new(
36
37
  skip_validation: true,
37
38
  id: raw_event.id.string,
38
39
  title: "#{raw_event.stream_revision}@#{raw_event.stream_identifier.stream_name}",
@@ -49,18 +50,6 @@ module EventStoreClient
49
50
 
50
51
  private
51
52
 
52
- # @param event_type [String]
53
- # @return [Class<EventStoreClient::DeserializedEvent>]
54
- def event_class(event_type)
55
- Object.const_get(event_type)
56
- rescue NameError, TypeError
57
- config.logger&.debug(<<~TEXT.strip)
58
- Unable to resolve class by `#{event_type}' event type. \
59
- Picking default `#{config.default_event_class}' event class to instantiate the event.
60
- TEXT
61
- config.default_event_class
62
- end
63
-
64
53
  # @param raw_data [String]
65
54
  # @return [String]
66
55
  def normalize_serialized(raw_data)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '3.0.0'
4
+ VERSION = '3.2.0'
5
5
  end
@@ -17,6 +17,7 @@ require 'event_store_client/connection/url'
17
17
  require 'event_store_client/connection/url_parser'
18
18
  require 'event_store_client/deserialized_event'
19
19
  require 'event_store_client/serialized_event'
20
+ require 'event_store_client/event_class_resolver'
20
21
  require 'event_store_client/config'
21
22
 
22
23
  require 'event_store_client/mapper'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_store_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Wilgosz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-01-06 00:00:00.000000000 Z
11
+ date: 2023-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: grpc
@@ -44,14 +44,14 @@ dependencies:
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.11'
47
+ version: '3.12'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.11'
54
+ version: '3.12'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: simplecov
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -222,10 +222,12 @@ files:
222
222
  - lib/event_store_client/deserialized_event.rb
223
223
  - lib/event_store_client/encryption_metadata.rb
224
224
  - lib/event_store_client/errors.rb
225
+ - lib/event_store_client/event_class_resolver.rb
225
226
  - lib/event_store_client/extensions/options_extension.rb
226
227
  - lib/event_store_client/mapper.rb
227
228
  - lib/event_store_client/mapper/default.rb
228
229
  - lib/event_store_client/mapper/encrypted.rb
230
+ - lib/event_store_client/rspec/has_option_matcher.rb
229
231
  - lib/event_store_client/serialized_event.rb
230
232
  - lib/event_store_client/serializer/event_deserializer.rb
231
233
  - lib/event_store_client/serializer/event_serializer.rb
@@ -245,14 +247,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
245
247
  requirements:
246
248
  - - ">="
247
249
  - !ruby/object:Gem::Version
248
- version: '0'
250
+ version: 3.0.0
249
251
  required_rubygems_version: !ruby/object:Gem::Requirement
250
252
  requirements:
251
253
  - - ">="
252
254
  - !ruby/object:Gem::Version
253
255
  version: '0'
254
256
  requirements: []
255
- rubygems_version: 3.3.7
257
+ rubygems_version: 3.4.10
256
258
  signing_key:
257
259
  specification_version: 4
258
260
  summary: Ruby integration for https://eventstore.org