event_store_client 2.3.0 → 3.1.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/docs/appending_events.md +16 -22
  4. data/docs/catch_up_subscriptions.md +35 -70
  5. data/docs/configuration.md +6 -10
  6. data/docs/deleting_streams.md +14 -4
  7. data/docs/encrypting_events.md +2 -6
  8. data/docs/linking_events.md +20 -26
  9. data/docs/reading_events.md +33 -62
  10. data/lib/event_store_client/adapters/grpc/client.rb +13 -9
  11. data/lib/event_store_client/adapters/grpc/commands/command.rb +0 -2
  12. data/lib/event_store_client/adapters/grpc/commands/gossip/cluster_info.rb +1 -1
  13. data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +11 -5
  14. data/lib/event_store_client/adapters/grpc/commands/streams/append_multiple.rb +2 -6
  15. data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +7 -5
  16. data/lib/event_store_client/adapters/grpc/commands/streams/hard_delete.rb +7 -5
  17. data/lib/event_store_client/adapters/grpc/commands/streams/link_to_multiple.rb +5 -10
  18. data/lib/event_store_client/adapters/grpc/commands/streams/read_paginated.rb +5 -8
  19. data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +3 -5
  20. data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +6 -8
  21. data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +11 -13
  22. data/lib/event_store_client/adapters/grpc.rb +0 -1
  23. data/lib/event_store_client/deserialized_event.rb +21 -3
  24. data/lib/event_store_client/errors.rb +110 -0
  25. data/lib/event_store_client/mapper/default.rb +1 -1
  26. data/lib/event_store_client/mapper/encrypted.rb +2 -2
  27. data/lib/event_store_client/rspec/has_option_matcher.rb +88 -0
  28. data/lib/event_store_client/serializer/event_deserializer.rb +4 -2
  29. data/lib/event_store_client/serializer/event_serializer.rb +41 -17
  30. data/lib/event_store_client/version.rb +1 -1
  31. data/lib/event_store_client.rb +3 -1
  32. metadata +24 -22
@@ -18,7 +18,7 @@ module EventStoreClient
18
18
  # @param event [EventStoreClient::DeserializedEvent]
19
19
  # @return [EventStoreClient::SerializedEvent]
20
20
  def serialize(event)
21
- Serializer::EventSerializer.call(event, serializer: serializer)
21
+ Serializer::EventSerializer.call(event, serializer: serializer, config: config)
22
22
  end
23
23
 
24
24
  # @param event_or_raw_event [EventStoreClient::DeserializedEvent, EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
@@ -32,7 +32,7 @@ module EventStoreClient
32
32
  # Links don't need to be encrypted
33
33
  return Default.new(serializer: serializer, config: config).serialize(event) if event.link?
34
34
 
35
- serialized = Serializer::EventSerializer.call(event, serializer: serializer)
35
+ serialized = Serializer::EventSerializer.call(event, serializer: serializer, config: config)
36
36
  encryption_schema =
37
37
  if event.class.respond_to?(:encryption_schema)
38
38
  event.class.encryption_schema
@@ -70,7 +70,7 @@ module EventStoreClient
70
70
  decrypted_data =
71
71
  EventStoreClient::DataDecryptor.new(
72
72
  data: event.data,
73
- schema: event.metadata['encryption'],
73
+ schema: event.custom_metadata['encryption'],
74
74
  repository: key_repository
75
75
  ).call
76
76
  event.class.new(**event.to_h.merge(data: decrypted_data, skip_validation: true))
@@ -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
@@ -7,8 +7,8 @@ module EventStoreClient
7
7
  class EventDeserializer
8
8
  class << self
9
9
  # @param raw_event [EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
10
- # @param serializer [#serialize, #deserialize]
11
10
  # @param config [EventStoreClient::Config]
11
+ # @param serializer [#serialize, #deserialize]
12
12
  # @return [EventStoreClient::DeserializedEvent]
13
13
  def call(raw_event, config:, serializer: Serializer::Json)
14
14
  new(config: config, serializer: serializer).call(raw_event)
@@ -19,6 +19,7 @@ module EventStoreClient
19
19
  private :serializer, :config
20
20
 
21
21
  # @param serializer [#serialize, #deserialize]
22
+ # @param config [EventStoreClient::Config]
22
23
  def initialize(serializer:, config:)
23
24
  @serializer = serializer
24
25
  @config = config
@@ -29,7 +30,7 @@ module EventStoreClient
29
30
  def call(raw_event)
30
31
  data = serializer.deserialize(normalize_serialized(raw_event.data))
31
32
  custom_metadata = serializer.deserialize(normalize_serialized(raw_event.custom_metadata))
32
- metadata = custom_metadata.merge(raw_event.metadata.to_h)
33
+ metadata = raw_event.metadata.to_h
33
34
 
34
35
  event_class(metadata['type']).new(
35
36
  skip_validation: true,
@@ -38,6 +39,7 @@ module EventStoreClient
38
39
  type: metadata['type'],
39
40
  data: data,
40
41
  metadata: metadata,
42
+ custom_metadata: custom_metadata,
41
43
  stream_revision: raw_event.stream_revision,
42
44
  commit_position: raw_event.commit_position,
43
45
  prepare_position: raw_event.prepare_position,
@@ -3,35 +3,39 @@
3
3
  module EventStoreClient
4
4
  module Serializer
5
5
  class EventSerializer
6
- ALLOWED_EVENT_METADATA = %w[type content-type created_at].freeze
6
+ # So far there are only these keys can be persisted in the metadata. You can pass **whatever**
7
+ # you want into a metadata hash, but all keys, except these - will be rejected. Define
8
+ # whitelisted keys and cut unwanted keys explicitly(later in this class).
9
+ ALLOWED_EVENT_METADATA = %w[type content-type].freeze
7
10
 
8
11
  class << self
9
12
  # @param event [EventStoreClient::DeserializedEvent]
13
+ # @param config [EventStoreClient::Config]
10
14
  # @param serializer [#serialize, #deserialize]
11
15
  # @return [EventStoreClient::SerializedEvent]
12
- def call(event, serializer: Serializer::Json)
13
- new(serializer: serializer).call(event)
16
+ def call(event, config:, serializer: Serializer::Json)
17
+ new(serializer: serializer, config: config).call(event)
14
18
  end
15
19
  end
16
20
 
17
- attr_reader :serializer
18
- private :serializer
21
+ attr_reader :serializer, :config
22
+ private :serializer, :config
19
23
 
20
24
  # @param serializer [#serialize, #deserialize]
21
- def initialize(serializer:)
25
+ # @param config [EventStoreClient::Config]
26
+ def initialize(serializer:, config:)
22
27
  @serializer = serializer
28
+ @config = config
23
29
  end
24
30
 
25
31
  # @param event [EventStoreClient::DeserializedEvent]
26
32
  # @return [EventStoreClient::SerializedEvent]
27
33
  def call(event)
28
- event_metadata = metadata(event)
29
- event_custom_metadata = custom_metadata(event, event_metadata)
30
34
  SerializedEvent.new(
31
35
  id: event.id || SecureRandom.uuid,
32
36
  data: data(event),
33
- custom_metadata: event_custom_metadata,
34
- metadata: event_metadata.slice(*ALLOWED_EVENT_METADATA),
37
+ metadata: metadata(event),
38
+ custom_metadata: custom_metadata(event),
35
39
  serializer: serializer
36
40
  )
37
41
  end
@@ -42,17 +46,24 @@ module EventStoreClient
42
46
  # @return [Hash]
43
47
  def metadata(event)
44
48
  metadata = serializer.deserialize(serializer.serialize(event.metadata))
45
- metadata['created_at'] ||= Time.now.utc.to_s
46
- metadata
49
+ # 'created' is returned in the metadata hash of the event when reading from a stream. It,
50
+ # however, can not be overridden - it is always defined automatically by EventStore db when
51
+ # appending new event. Thus, just ignore it - no need even to mention it in the
52
+ # #log_metadata_difference method's message.
53
+ metadata = metadata.slice(*(metadata.keys - ['created']))
54
+ filtered_metadata = metadata.slice(*ALLOWED_EVENT_METADATA)
55
+ log_metadata_difference(metadata) unless filtered_metadata == metadata
56
+ filtered_metadata
47
57
  end
48
58
 
59
+ # Compute custom metadata for the event. **Exactly these** values you can see in ES admin's
60
+ # web UI under "Metadata" section of the event.
49
61
  # @param event [EventStoreClient::DeserializedEvent]
50
- # @param metadata [Hash]
51
62
  # @return [Hash]
52
- def custom_metadata(event, metadata)
53
- metadata.
54
- slice('created_at', 'encryption', 'content-type', 'transaction').
55
- merge('type' => event.type.to_s)
63
+ def custom_metadata(event)
64
+ custom_metadata = serializer.deserialize(serializer.serialize(event.custom_metadata))
65
+ custom_metadata['created_at'] ||= Time.now.utc.to_s
66
+ custom_metadata
56
67
  end
57
68
 
58
69
  # @param event [EventStoreClient::DeserializedEvent]
@@ -64,6 +75,19 @@ module EventStoreClient
64
75
 
65
76
  serializer.deserialize(serializer.serialize(event.data))
66
77
  end
78
+
79
+ # @param metadata [Hash]
80
+ # @return [void]
81
+ def log_metadata_difference(metadata)
82
+ rest_hash = metadata.slice(*(metadata.keys - ALLOWED_EVENT_METADATA))
83
+ debug_message = <<~TEXT
84
+ Next keys were filtered from metadata during serialization: \
85
+ #{(metadata.keys - ALLOWED_EVENT_METADATA).map(&:inspect).join(', ')}. If you would like \
86
+ to provide your custom values in the metadata - please provide them via custom_metadata. \
87
+ Example: EventStoreClient::DeserializedEvent.new(custom_metadata: #{rest_hash.inspect})
88
+ TEXT
89
+ config.logger&.debug(debug_message)
90
+ end
67
91
  end
68
92
  end
69
93
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '2.3.0'
4
+ VERSION = '3.1.0'
5
5
  end
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'json'
4
4
  require 'set'
5
+ require 'securerandom'
5
6
 
7
+ require 'event_store_client/errors'
6
8
  require 'event_store_client/serializer/json'
7
9
  require 'event_store_client/serializer/event_serializer'
8
10
  require 'event_store_client/serializer/event_deserializer'
@@ -29,7 +31,7 @@ module EventStoreClient
29
31
  end
30
32
 
31
33
  # @param name [Symbol, String]
32
- # @return [EventStore::Config]
34
+ # @return [EventStoreClient::Config]
33
35
  def config(name = :default)
34
36
  @config[name] ||= Config.new(name: name)
35
37
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: event_store_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 3.1.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: 2022-12-23 00:00:00.000000000 Z
11
+ date: 2023-05-17 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: dry-monads
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - "~>"
18
- - !ruby/object:Gem::Version
19
- version: '1'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - "~>"
25
- - !ruby/object:Gem::Version
26
- version: '1'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: grpc
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +44,14 @@ dependencies:
58
44
  requirements:
59
45
  - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '3.11'
47
+ version: '3.12'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '3.11'
54
+ version: '3.12'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: simplecov
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +128,28 @@ dependencies:
142
128
  requirements:
143
129
  - - "~>"
144
130
  - !ruby/object:Gem::Version
145
- version: '1'
131
+ version: 1.13.0
146
132
  type: :development
147
133
  prerelease: false
148
134
  version_requirements: !ruby/object:Gem::Requirement
149
135
  requirements:
150
136
  - - "~>"
151
137
  - !ruby/object:Gem::Version
152
- version: '1'
138
+ version: 1.13.0
139
+ - !ruby/object:Gem::Dependency
140
+ name: dry-monads
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.6'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.6'
153
153
  description: Easy to use client for event-sources applications written in ruby
154
154
  email:
155
155
  - sebastian@driggl.com
@@ -221,10 +221,12 @@ files:
221
221
  - lib/event_store_client/data_encryptor.rb
222
222
  - lib/event_store_client/deserialized_event.rb
223
223
  - lib/event_store_client/encryption_metadata.rb
224
+ - lib/event_store_client/errors.rb
224
225
  - lib/event_store_client/extensions/options_extension.rb
225
226
  - lib/event_store_client/mapper.rb
226
227
  - lib/event_store_client/mapper/default.rb
227
228
  - lib/event_store_client/mapper/encrypted.rb
229
+ - lib/event_store_client/rspec/has_option_matcher.rb
228
230
  - lib/event_store_client/serialized_event.rb
229
231
  - lib/event_store_client/serializer/event_deserializer.rb
230
232
  - lib/event_store_client/serializer/event_serializer.rb
@@ -244,14 +246,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
244
246
  requirements:
245
247
  - - ">="
246
248
  - !ruby/object:Gem::Version
247
- version: '0'
249
+ version: 3.0.0
248
250
  required_rubygems_version: !ruby/object:Gem::Requirement
249
251
  requirements:
250
252
  - - ">="
251
253
  - !ruby/object:Gem::Version
252
254
  version: '0'
253
255
  requirements: []
254
- rubygems_version: 3.3.7
256
+ rubygems_version: 3.4.10
255
257
  signing_key:
256
258
  specification_version: 4
257
259
  summary: Ruby integration for https://eventstore.org