ruby_event_store 0.39.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
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