ruby_event_store 0.39.0 → 0.40.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/lib/ruby_event_store.rb +24 -6
  3. data/lib/ruby_event_store/broker.rb +43 -0
  4. data/lib/ruby_event_store/client.rb +3 -3
  5. data/lib/ruby_event_store/dispatcher.rb +18 -0
  6. data/lib/ruby_event_store/errors.rb +13 -12
  7. data/lib/ruby_event_store/event.rb +1 -0
  8. data/lib/ruby_event_store/mappers.rb +10 -0
  9. data/lib/ruby_event_store/mappers/default.rb +8 -24
  10. data/lib/ruby_event_store/mappers/encryption_key.rb +72 -0
  11. data/lib/ruby_event_store/mappers/encryption_mapper.rb +8 -239
  12. data/lib/ruby_event_store/mappers/forgotten_data.rb +28 -0
  13. data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +32 -0
  14. data/lib/ruby_event_store/mappers/null_mapper.rb +2 -18
  15. data/lib/ruby_event_store/mappers/pipeline.rb +29 -0
  16. data/lib/ruby_event_store/mappers/pipeline_mapper.rb +20 -0
  17. data/lib/ruby_event_store/mappers/protobuf.rb +9 -47
  18. data/lib/ruby_event_store/mappers/transformation/domain_event.rb +24 -0
  19. data/lib/ruby_event_store/mappers/transformation/encryption.rb +125 -0
  20. data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +22 -0
  21. data/lib/ruby_event_store/mappers/transformation/item.rb +55 -0
  22. data/lib/ruby_event_store/mappers/transformation/proto_event.rb +15 -0
  23. data/lib/ruby_event_store/mappers/transformation/protobuf_encoder.rb +28 -0
  24. data/lib/ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata.rb +29 -0
  25. data/lib/ruby_event_store/mappers/transformation/serialization.rb +32 -0
  26. data/lib/ruby_event_store/mappers/transformation/serialized_record.rb +25 -0
  27. data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +22 -0
  28. data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +22 -0
  29. data/lib/ruby_event_store/pub_sub.rb +21 -0
  30. data/lib/ruby_event_store/spec/broker_lint.rb +3 -3
  31. data/lib/ruby_event_store/spec/dispatcher_lint.rb +2 -2
  32. data/lib/ruby_event_store/subscriptions.rb +108 -0
  33. data/lib/ruby_event_store/version.rb +1 -1
  34. metadata +24 -6
  35. data/lib/ruby_event_store/pub_sub/broker.rb +0 -45
  36. data/lib/ruby_event_store/pub_sub/dispatcher.rb +0 -20
  37. data/lib/ruby_event_store/pub_sub/subscriptions.rb +0 -110
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93c4fb13b69f6c54171be328e0bb2f10ad9172afbad3b367762a831754523700
4
- data.tar.gz: e3ee2f58ed04b5df00c2e44685b9f6d1172fce73f8df639d35bae021930af3ae
3
+ metadata.gz: 257a578c0c9d6dac951fd1a21fc739227b3169cb14af5535b3c782501280461c
4
+ data.tar.gz: 55b40e9ab1dd90afb841ed1b92a8e950ea6aa3285ce0f187443f92341dbcfb31
5
5
  SHA512:
6
- metadata.gz: 3fd6b0a07e06ec3faf668a57e7564c8d151b567d0b24e07097042a29032ec9e598d430a9107c64d581dcb83f89a4ea52420798ee6039e7b08da5d417db337ef7
7
- data.tar.gz: e7426bddb8d654198b340c7aba9d882cb2296bf379dab83dd6182cb824ff3fd3f2839cdc17f2ddd8d3bd4a5f0846cf38c82fa29d9925bc7cc0dac4852fcf9677
6
+ metadata.gz: 90fa7cccf1b1832c441b2f63701e2a931afc778f819f9df6152463b90a9533adb087254adb879de64e6a94a4c9f35463da2cc14d481012ff5a0a3e1f6f8240c5
7
+ data.tar.gz: 70a286e9f88972228b392a4a56ef37035a0d571c9f75b19376313dc8147e518a270f70a2f1097983d6d547e1590c9c51f2005095d4d8229ad3919d3cffd4dac7
@@ -1,6 +1,7 @@
1
- require 'ruby_event_store/pub_sub/dispatcher'
2
- require 'ruby_event_store/pub_sub/subscriptions'
3
- require 'ruby_event_store/pub_sub/broker'
1
+ require 'ruby_event_store/dispatcher'
2
+ require 'ruby_event_store/subscriptions'
3
+ require 'ruby_event_store/broker'
4
+ require 'ruby_event_store/pub_sub'
4
5
  require 'ruby_event_store/in_memory_repository'
5
6
  require 'ruby_event_store/projection'
6
7
  require 'ruby_event_store/errors'
@@ -15,11 +16,28 @@ require 'ruby_event_store/stream'
15
16
  require 'ruby_event_store/expected_version'
16
17
  require 'ruby_event_store/serialized_record'
17
18
  require 'ruby_event_store/transform_keys'
19
+ require 'ruby_event_store/mappers'
20
+ require 'ruby_event_store/mappers/encryption_key'
21
+ require 'ruby_event_store/mappers/in_memory_encryption_key_repository'
22
+ require 'ruby_event_store/mappers/transformation/domain_event'
23
+ require 'ruby_event_store/mappers/transformation/encryption'
24
+ require 'ruby_event_store/mappers/transformation/event_class_remapper'
25
+ require 'ruby_event_store/mappers/transformation/item'
26
+ require 'ruby_event_store/mappers/transformation/proto_event'
27
+ require 'ruby_event_store/mappers/transformation/protobuf_encoder'
28
+ require 'ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata'
29
+ require 'ruby_event_store/mappers/transformation/serialization'
30
+ require 'ruby_event_store/mappers/transformation/serialized_record'
31
+ require 'ruby_event_store/mappers/transformation/stringify_metadata_keys'
32
+ require 'ruby_event_store/mappers/transformation/symbolize_metadata_keys'
33
+ require 'ruby_event_store/mappers/pipeline'
34
+ require 'ruby_event_store/mappers/pipeline_mapper'
18
35
  require 'ruby_event_store/mappers/default'
19
- require 'ruby_event_store/mappers/protobuf'
20
- require 'ruby_event_store/mappers/null_mapper'
21
- require 'ruby_event_store/mappers/instrumented_mapper'
36
+ require 'ruby_event_store/mappers/forgotten_data'
22
37
  require 'ruby_event_store/mappers/encryption_mapper'
38
+ require 'ruby_event_store/mappers/instrumented_mapper'
39
+ require 'ruby_event_store/mappers/null_mapper'
40
+ require 'ruby_event_store/mappers/protobuf'
23
41
  require 'ruby_event_store/batch_enumerator'
24
42
  require 'ruby_event_store/correlated_commands'
25
43
  require 'ruby_event_store/link_by_metadata'
@@ -0,0 +1,43 @@
1
+ module RubyEventStore
2
+ class Broker
3
+ def initialize(subscriptions:, dispatcher:)
4
+ @subscriptions = subscriptions
5
+ @dispatcher = dispatcher
6
+ end
7
+
8
+ def call(event, serialized_event)
9
+ subscribers = subscriptions.all_for(event.type)
10
+ subscribers.each do |subscriber|
11
+ dispatcher.call(subscriber, event, serialized_event)
12
+ end
13
+ end
14
+
15
+ def add_subscription(subscriber, event_types)
16
+ verify_subscription(subscriber)
17
+ subscriptions.add_subscription(subscriber, event_types)
18
+ end
19
+
20
+ def add_global_subscription(subscriber)
21
+ verify_subscription(subscriber)
22
+ subscriptions.add_global_subscription(subscriber)
23
+ end
24
+
25
+ def add_thread_subscription(subscriber, event_types)
26
+ verify_subscription(subscriber)
27
+ subscriptions.add_thread_subscription(subscriber, event_types)
28
+ end
29
+
30
+ def add_thread_global_subscription(subscriber)
31
+ verify_subscription(subscriber)
32
+ subscriptions.add_thread_global_subscription(subscriber)
33
+ end
34
+
35
+ private
36
+ attr_reader :subscriptions, :dispatcher
37
+
38
+ def verify_subscription(subscriber)
39
+ raise SubscriberNotExist, "subscriber must be first argument or block" unless subscriber
40
+ raise InvalidHandler.new("Handler #{subscriber} is invalid for dispatcher #{dispatcher}") unless dispatcher.verify(subscriber)
41
+ end
42
+ end
43
+ end
@@ -5,12 +5,12 @@ module RubyEventStore
5
5
 
6
6
  def initialize(repository:,
7
7
  mapper: Mappers::Default.new,
8
- subscriptions: PubSub::Subscriptions.new,
9
- dispatcher: PubSub::Dispatcher.new,
8
+ subscriptions: Subscriptions.new,
9
+ dispatcher: Dispatcher.new,
10
10
  clock: ->{ Time.now.utc })
11
11
  @repository = repository
12
12
  @mapper = mapper
13
- @broker = PubSub::Broker.new(subscriptions: subscriptions, dispatcher: dispatcher)
13
+ @broker = Broker.new(subscriptions: subscriptions, dispatcher: dispatcher)
14
14
  @clock = clock
15
15
  @metadata = Concurrent::ThreadLocalVar.new
16
16
  end
@@ -0,0 +1,18 @@
1
+ module RubyEventStore
2
+ class Dispatcher
3
+ def call(subscriber, event, _)
4
+ subscriber = subscriber.new if Class === subscriber
5
+ subscriber.call(event)
6
+ end
7
+
8
+ def verify(subscriber)
9
+ begin
10
+ subscriber_instance = Class === subscriber ? subscriber.new : subscriber
11
+ rescue ArgumentError
12
+ false
13
+ else
14
+ subscriber_instance.respond_to?(:call)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,17 +1,18 @@
1
1
  module RubyEventStore
2
- WrongExpectedEventVersion = Class.new(StandardError)
3
- InvalidExpectedVersion = Class.new(StandardError)
4
- IncorrectStreamData = Class.new(StandardError)
5
- SubscriberNotExist = Class.new(StandardError)
6
- InvalidPageStart = Class.new(ArgumentError)
7
- InvalidPageStop = Class.new(ArgumentError)
8
- InvalidPageSize = Class.new(ArgumentError)
9
- EventDuplicatedInStream = Class.new(StandardError)
10
- ReservedInternalName = Class.new(StandardError)
11
- InvalidHandler = Class.new(StandardError)
12
- ProtobufEncodingFailed = Class.new(StandardError)
2
+ Error = Class.new(StandardError)
3
+ WrongExpectedEventVersion = Class.new(Error)
4
+ InvalidExpectedVersion = Class.new(Error)
5
+ IncorrectStreamData = Class.new(Error)
6
+ SubscriberNotExist = Class.new(Error)
7
+ InvalidPageStart = Class.new(Error)
8
+ InvalidPageStop = Class.new(Error)
9
+ InvalidPageSize = Class.new(Error)
10
+ EventDuplicatedInStream = Class.new(Error)
11
+ ReservedInternalName = Class.new(Error)
12
+ InvalidHandler = Class.new(Error)
13
+ ProtobufEncodingFailed = Class.new(Error)
13
14
 
14
- class EventNotFound < StandardError
15
+ class EventNotFound < Error
15
16
  attr_reader :event_id
16
17
 
17
18
  def initialize(event_id)
@@ -133,6 +133,7 @@ module RubyEventStore
133
133
  def correlate_with(other_message)
134
134
  self.correlation_id = other_message.correlation_id || other_message.message_id
135
135
  self.causation_id = other_message.message_id
136
+ self
136
137
  end
137
138
 
138
139
  alias_method :eql?, :==
@@ -0,0 +1,10 @@
1
+ module RubyEventStore
2
+ module Mappers
3
+ def self.const_missing(const_name)
4
+ super unless const_name.equal?(:MissingEncryptionKey)
5
+ warn "`RubyEventStore::Mappers::MissingEncryptionKey` has been deprecated. Use `RubyEventStore::Mappers::Transformation::Encryption::MissingEncryptionKey` instead."
6
+
7
+ Transformation::Encryption::MissingEncryptionKey
8
+ end
9
+ end
10
+ end
@@ -2,32 +2,16 @@ require 'yaml'
2
2
 
3
3
  module RubyEventStore
4
4
  module Mappers
5
- class Default
5
+ class Default < PipelineMapper
6
6
  def initialize(serializer: YAML, events_class_remapping: {})
7
- @serializer = serializer
8
- @events_class_remapping = events_class_remapping
7
+ super(Pipeline.new(
8
+ transformations: [
9
+ Transformation::EventClassRemapper.new(events_class_remapping),
10
+ Transformation::SymbolizeMetadataKeys.new,
11
+ Transformation::Serialization.new(serializer: serializer),
12
+ ]
13
+ ))
9
14
  end
10
-
11
- def event_to_serialized_record(domain_event)
12
- SerializedRecord.new(
13
- event_id: domain_event.event_id,
14
- metadata: serializer.dump(domain_event.metadata.to_h),
15
- data: serializer.dump(domain_event.data),
16
- event_type: domain_event.class.name
17
- )
18
- end
19
-
20
- def serialized_record_to_event(record)
21
- event_type = events_class_remapping.fetch(record.event_type) { record.event_type }
22
- Object.const_get(event_type).new(
23
- event_id: record.event_id,
24
- metadata: TransformKeys.symbolize(serializer.load(record.metadata)),
25
- data: serializer.load(record.data)
26
- )
27
- end
28
-
29
- private
30
- attr_reader :serializer, :events_class_remapping
31
15
  end
32
16
  end
33
17
  end
@@ -0,0 +1,72 @@
1
+ module RubyEventStore
2
+ module Mappers
3
+ class EncryptionKey
4
+ def initialize(cipher:, key:)
5
+ @cipher = cipher
6
+ @key = key
7
+ end
8
+
9
+ def encrypt(message, iv)
10
+ crypto = prepare_encrypt(cipher)
11
+ crypto.iv = iv
12
+ crypto.key = key
13
+
14
+ if crypto.authenticated?
15
+ encrypt_authenticated(crypto, message)
16
+ else
17
+ crypto.update(message) + crypto.final
18
+ end
19
+ end
20
+
21
+ def decrypt(message, iv)
22
+ crypto = prepare_decrypt(cipher)
23
+ crypto.iv = iv
24
+ crypto.key = key
25
+ ciphertext =
26
+ if crypto.authenticated?
27
+ ciphertext_from_authenticated(crypto, message)
28
+ else
29
+ message
30
+ end
31
+ (crypto.update(ciphertext) + crypto.final).force_encoding("UTF-8")
32
+ end
33
+
34
+ def random_iv
35
+ crypto = prepare_encrypt(cipher)
36
+ crypto.random_iv
37
+ end
38
+
39
+ attr_reader :cipher, :key
40
+
41
+ private
42
+
43
+ def ciphertext_from_authenticated(crypto, message)
44
+ prepare_auth_data(crypto)
45
+ crypto.auth_tag = message[-16...message.length]
46
+ message[0...-16]
47
+ end
48
+
49
+ def encrypt_authenticated(crypto, message)
50
+ prepare_auth_data(crypto)
51
+ crypto.update(message) + crypto.final + crypto.auth_tag
52
+ end
53
+
54
+ def prepare_auth_data(crypto)
55
+ crypto.auth_data = ""
56
+ crypto
57
+ end
58
+
59
+ def prepare_encrypt(cipher)
60
+ crypto = OpenSSL::Cipher.new(cipher)
61
+ crypto.encrypt
62
+ crypto
63
+ end
64
+
65
+ def prepare_decrypt(cipher)
66
+ crypto = OpenSSL::Cipher.new(cipher)
67
+ crypto.decrypt
68
+ crypto
69
+ end
70
+ end
71
+ end
72
+ end
@@ -1,245 +1,14 @@
1
1
  module RubyEventStore
2
2
  module Mappers
3
- class ForgottenData
4
- FORGOTTEN_DATA = 'FORGOTTEN_DATA'.freeze
5
-
6
- def initialize(string = FORGOTTEN_DATA)
7
- @string = string
8
- end
9
-
10
- def to_s
11
- @string
12
- end
13
-
14
- def ==(other)
15
- @string == other
16
- end
17
-
18
- def method_missing(*)
19
- self
20
- end
21
-
22
- def respond_to_missing?(*)
23
- true
24
- end
25
- end
26
-
27
- class InMemoryEncryptionKeyRepository
28
- DEFAULT_CIPHER = 'aes-256-gcm'.freeze
29
-
30
- def initialize
31
- @keys = {}
32
- end
33
-
34
- def key_of(identifier, cipher: DEFAULT_CIPHER)
35
- @keys[[identifier, cipher]]
36
- end
37
-
38
- def create(identifier, cipher: DEFAULT_CIPHER)
39
- crypto = prepare_encrypt(cipher)
40
- @keys[[identifier, cipher]] = EncryptionKey.new(cipher: cipher, key: crypto.random_key)
41
- end
42
-
43
- def forget(identifier)
44
- @keys = @keys.reject { |(id, _)| id.eql?(identifier) }
45
- end
46
-
47
- private
48
-
49
- def prepare_encrypt(cipher)
50
- crypto = OpenSSL::Cipher.new(cipher)
51
- crypto.encrypt
52
- crypto
53
- end
54
- end
55
-
56
- class EncryptionKey
57
- def initialize(cipher:, key:)
58
- @cipher = cipher
59
- @key = key
60
- end
61
-
62
- def encrypt(message, iv)
63
- crypto = prepare_encrypt(cipher)
64
- crypto.iv = iv
65
- crypto.key = key
66
-
67
- if crypto.authenticated?
68
- encrypt_authenticated(crypto, message)
69
- else
70
- crypto.update(message) + crypto.final
71
- end
72
- end
73
-
74
- def decrypt(message, iv)
75
- crypto = prepare_decrypt(cipher)
76
- crypto.iv = iv
77
- crypto.key = key
78
- ciphertext =
79
- if crypto.authenticated?
80
- ciphertext_from_authenticated(crypto, message)
81
- else
82
- message
83
- end
84
- (crypto.update(ciphertext) + crypto.final).force_encoding("UTF-8")
85
- end
86
-
87
- def random_iv
88
- crypto = prepare_encrypt(cipher)
89
- crypto.random_iv
90
- end
91
-
92
- attr_reader :cipher, :key
93
-
94
- private
95
-
96
- def ciphertext_from_authenticated(crypto, message)
97
- prepare_auth_data(crypto)
98
- crypto.auth_tag = message[-16...message.length]
99
- message[0...-16]
100
- end
101
-
102
- def encrypt_authenticated(crypto, message)
103
- prepare_auth_data(crypto)
104
- crypto.update(message) + crypto.final + crypto.auth_tag
105
- end
106
-
107
- def prepare_auth_data(crypto)
108
- crypto.auth_data = ""
109
- crypto
110
- end
111
-
112
- def prepare_encrypt(cipher)
113
- crypto = OpenSSL::Cipher.new(cipher)
114
- crypto.encrypt
115
- crypto
116
- end
117
-
118
- def prepare_decrypt(cipher)
119
- crypto = OpenSSL::Cipher.new(cipher)
120
- crypto.decrypt
121
- crypto
122
- end
123
- end
124
-
125
- class MissingEncryptionKey < StandardError
126
- def initialize(key_identifier)
127
- super %Q|Could not find encryption key for '#{key_identifier}'|
128
- end
129
- end
130
-
131
- class EncryptionMapper
132
- class Leaf
133
- def self.===(hash)
134
- hash.keys.sort.eql? %i(cipher identifier iv)
135
- end
136
- end
137
- private_constant :Leaf
138
-
3
+ class EncryptionMapper < PipelineMapper
139
4
  def initialize(key_repository, serializer: YAML, forgotten_data: ForgottenData.new)
140
- @key_repository = key_repository
141
- @serializer = serializer
142
- @forgotten_data = forgotten_data
143
- end
144
-
145
- def event_to_serialized_record(domain_event)
146
- metadata = domain_event.metadata.to_h
147
- crypto_description = encryption_metadata(domain_event.data, encryption_schema(domain_event))
148
- metadata[:encryption] = crypto_description unless crypto_description.empty?
149
-
150
- SerializedRecord.new(
151
- event_id: domain_event.event_id,
152
- metadata: serializer.dump(metadata),
153
- data: serializer.dump(encrypt_data(deep_dup(domain_event.data), crypto_description)),
154
- event_type: domain_event.class.to_s
155
- )
156
- end
157
-
158
- def serialized_record_to_event(record)
159
- metadata = serializer.load(record.metadata)
160
- crypto_description = Hash(metadata.delete(:encryption))
161
-
162
- Object.const_get(record.event_type).new(
163
- event_id: record.event_id,
164
- data: decrypt_data(serializer.load(record.data), crypto_description),
165
- metadata: metadata,
166
- )
167
- end
168
-
169
- private
170
- attr_reader :key_repository, :serializer, :forgotten_data
171
-
172
- def encryption_schema(event)
173
- event.class.respond_to?(:encryption_schema) ? event.class.encryption_schema : {}
174
- end
175
-
176
- def deep_dup(hash)
177
- duplicate = hash.dup
178
- duplicate.each do |k, v|
179
- duplicate[k] = v.instance_of?(Hash) ? deep_dup(v) : v
180
- end
181
- duplicate
182
- end
183
-
184
- def encryption_metadata(data, schema)
185
- schema.inject({}) do |acc, (key, value)|
186
- case value
187
- when Hash
188
- acc[key] = encryption_metadata(data, value)
189
- when Proc
190
- key_identifier = value.call(data)
191
- encryption_key = key_repository.key_of(key_identifier) or raise MissingEncryptionKey.new(key_identifier)
192
- acc[key] = {
193
- cipher: encryption_key.cipher,
194
- iv: encryption_key.random_iv,
195
- identifier: key_identifier,
196
- }
197
- end
198
- acc
199
- end
200
- end
201
-
202
- def encrypt_data(data, meta)
203
- meta.reduce(data) do |acc, (key, value)|
204
- acc[key] = encrypt_attribute(acc, key, value)
205
- acc
206
- end
207
- end
208
-
209
- def decrypt_data(data, meta)
210
- meta.reduce(data) do |acc, (key, value)|
211
- acc[key] = decrypt_attribute(data, key, value)
212
- acc
213
- end
214
- end
215
-
216
- def encrypt_attribute(data, attribute, meta)
217
- case meta
218
- when Leaf
219
- value = data.fetch(attribute)
220
- return unless value
221
-
222
- encryption_key = key_repository.key_of(meta.fetch(:identifier))
223
- encryption_key.encrypt(serializer.dump(value), meta.fetch(:iv))
224
- when Hash
225
- encrypt_data(data.fetch(attribute), meta)
226
- end
227
- end
228
-
229
- def decrypt_attribute(data, attribute, meta)
230
- case meta
231
- when Leaf
232
- cryptogram = data.fetch(attribute)
233
- return unless cryptogram
234
-
235
- encryption_key = key_repository.key_of(meta.fetch(:identifier), cipher: meta.fetch(:cipher)) or return forgotten_data
236
- serializer.load(encryption_key.decrypt(cryptogram, meta.fetch(:iv)))
237
- when Hash
238
- decrypt_data(data.fetch(attribute), meta)
239
- end
240
- rescue OpenSSL::Cipher::CipherError
241
- forgotten_data
5
+ super(Pipeline.new(
6
+ transformations: [
7
+ Transformation::Encryption.new(key_repository, serializer: serializer, forgotten_data: forgotten_data),
8
+ Transformation::Serialization.new(serializer: serializer),
9
+ ]
10
+ ))
242
11
  end
243
12
  end
244
13
  end
245
- end
14
+ end