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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/docs/appending_events.md +16 -22
- data/docs/catch_up_subscriptions.md +35 -70
- data/docs/configuration.md +6 -10
- data/docs/deleting_streams.md +14 -4
- data/docs/encrypting_events.md +2 -6
- data/docs/linking_events.md +20 -26
- data/docs/reading_events.md +33 -62
- data/lib/event_store_client/adapters/grpc/client.rb +13 -9
- data/lib/event_store_client/adapters/grpc/commands/command.rb +0 -2
- data/lib/event_store_client/adapters/grpc/commands/gossip/cluster_info.rb +1 -1
- data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +11 -5
- data/lib/event_store_client/adapters/grpc/commands/streams/append_multiple.rb +2 -6
- data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +7 -5
- data/lib/event_store_client/adapters/grpc/commands/streams/hard_delete.rb +7 -5
- data/lib/event_store_client/adapters/grpc/commands/streams/link_to_multiple.rb +5 -10
- data/lib/event_store_client/adapters/grpc/commands/streams/read_paginated.rb +5 -8
- data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +3 -5
- data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +6 -8
- data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +11 -13
- data/lib/event_store_client/adapters/grpc.rb +0 -1
- data/lib/event_store_client/deserialized_event.rb +21 -3
- data/lib/event_store_client/errors.rb +110 -0
- data/lib/event_store_client/mapper/default.rb +1 -1
- data/lib/event_store_client/mapper/encrypted.rb +2 -2
- data/lib/event_store_client/rspec/has_option_matcher.rb +88 -0
- data/lib/event_store_client/serializer/event_deserializer.rb +4 -2
- data/lib/event_store_client/serializer/event_serializer.rb +41 -17
- data/lib/event_store_client/version.rb +1 -1
- data/lib/event_store_client.rb +3 -1
- 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.
|
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 =
|
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
|
-
|
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
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
46
|
-
|
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
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
data/lib/event_store_client.rb
CHANGED
@@ -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 [
|
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:
|
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:
|
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.
|
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.
|
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:
|
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:
|
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:
|
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.
|
256
|
+
rubygems_version: 3.4.10
|
255
257
|
signing_key:
|
256
258
|
specification_version: 4
|
257
259
|
summary: Ruby integration for https://eventstore.org
|