ruby_event_store 2.1.0 → 2.4.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/lib/ruby_event_store/batch_enumerator.rb +3 -3
- data/lib/ruby_event_store/broker.rb +5 -4
- data/lib/ruby_event_store/client.rb +81 -48
- data/lib/ruby_event_store/composed_dispatcher.rb +1 -3
- data/lib/ruby_event_store/correlated_commands.rb +4 -15
- data/lib/ruby_event_store/errors.rb +11 -10
- data/lib/ruby_event_store/event.rb +9 -14
- data/lib/ruby_event_store/expected_version.rb +3 -7
- data/lib/ruby_event_store/in_memory_repository.rb +100 -37
- data/lib/ruby_event_store/instrumented_dispatcher.rb +11 -2
- data/lib/ruby_event_store/instrumented_repository.rb +13 -8
- data/lib/ruby_event_store/link_by_metadata.rb +4 -21
- data/lib/ruby_event_store/mappers/default.rb +6 -4
- data/lib/ruby_event_store/mappers/encryption_key.rb +7 -16
- data/lib/ruby_event_store/mappers/encryption_mapper.rb +6 -6
- data/lib/ruby_event_store/mappers/forgotten_data.rb +1 -1
- data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +1 -1
- data/lib/ruby_event_store/mappers/null_mapper.rb +0 -1
- data/lib/ruby_event_store/mappers/pipeline.rb +3 -10
- data/lib/ruby_event_store/mappers/pipeline_mapper.rb +1 -0
- data/lib/ruby_event_store/mappers/transformation/domain_event.rb +22 -12
- data/lib/ruby_event_store/mappers/transformation/encryption.rb +21 -25
- data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +6 -5
- data/lib/ruby_event_store/mappers/transformation/upcast.rb +2 -6
- data/lib/ruby_event_store/metadata.rb +46 -17
- data/lib/ruby_event_store/projection.rb +12 -20
- data/lib/ruby_event_store/record.rb +14 -26
- data/lib/ruby_event_store/serialized_record.rb +14 -26
- data/lib/ruby_event_store/serializers/yaml.rb +17 -0
- data/lib/ruby_event_store/spec/broker_lint.rb +38 -28
- data/lib/ruby_event_store/spec/event_lint.rb +10 -10
- data/lib/ruby_event_store/spec/event_repository_lint.rb +746 -730
- data/lib/ruby_event_store/spec/mapper_lint.rb +2 -2
- data/lib/ruby_event_store/spec/subscriptions_lint.rb +58 -68
- data/lib/ruby_event_store/specification.rb +20 -16
- data/lib/ruby_event_store/specification_reader.rb +2 -3
- data/lib/ruby_event_store/specification_result.rb +52 -46
- data/lib/ruby_event_store/stream.rb +3 -7
- data/lib/ruby_event_store/subscriptions.rb +17 -17
- data/lib/ruby_event_store/transform_keys.rb +1 -1
- data/lib/ruby_event_store/version.rb +1 -1
- data/lib/ruby_event_store.rb +44 -43
- metadata +6 -4
@@ -1,26 +1,49 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "ostruct"
|
4
4
|
module RubyEventStore
|
5
5
|
class InMemoryRepository
|
6
|
+
class UnsupportedVersionAnyUsage < StandardError
|
7
|
+
def initialize
|
8
|
+
super <<~EOS
|
9
|
+
Mixing expected version :any and specific position (or :auto) is unsupported.
|
10
|
+
|
11
|
+
Read more about expected versions here:
|
12
|
+
https://railseventstore.org/docs/v2/expected_version/
|
13
|
+
EOS
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class EventInStream
|
18
|
+
def initialize(event_id, position)
|
19
|
+
@event_id = event_id
|
20
|
+
@position = position
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :event_id, :position
|
24
|
+
end
|
6
25
|
|
7
|
-
def initialize(serializer: NULL)
|
26
|
+
def initialize(serializer: NULL, ensure_supported_any_usage: false)
|
8
27
|
@serializer = serializer
|
9
28
|
@streams = Hash.new { |h, k| h[k] = Array.new }
|
10
|
-
@mutex
|
29
|
+
@mutex = Mutex.new
|
11
30
|
@storage = Hash.new
|
31
|
+
@ensure_supported_any_usage = ensure_supported_any_usage
|
12
32
|
end
|
13
33
|
|
14
34
|
def append_to_stream(records, stream, expected_version)
|
15
35
|
serialized_records = records.map { |record| record.serialize(serializer) }
|
16
36
|
|
17
37
|
with_synchronize(expected_version, stream) do |resolved_version|
|
18
|
-
|
38
|
+
ensure_supported_any_usage(resolved_version, stream)
|
39
|
+
unless resolved_version.nil? || last_stream_version(stream).equal?(resolved_version)
|
40
|
+
raise WrongExpectedEventVersion
|
41
|
+
end
|
19
42
|
|
20
|
-
serialized_records.
|
43
|
+
serialized_records.each_with_index do |serialized_record, index|
|
21
44
|
raise EventDuplicatedInStream if has_event?(serialized_record.event_id)
|
22
45
|
storage[serialized_record.event_id] = serialized_record
|
23
|
-
|
46
|
+
add_to_stream(stream, serialized_record, resolved_version, index)
|
24
47
|
end
|
25
48
|
end
|
26
49
|
self
|
@@ -30,11 +53,14 @@ module RubyEventStore
|
|
30
53
|
serialized_records = event_ids.map { |id| read_event(id) }
|
31
54
|
|
32
55
|
with_synchronize(expected_version, stream) do |resolved_version|
|
33
|
-
|
56
|
+
ensure_supported_any_usage(resolved_version, stream)
|
57
|
+
unless resolved_version.nil? || last_stream_version(stream).equal?(resolved_version)
|
58
|
+
raise WrongExpectedEventVersion
|
59
|
+
end
|
34
60
|
|
35
|
-
serialized_records.
|
61
|
+
serialized_records.each_with_index do |serialized_record, index|
|
36
62
|
raise EventDuplicatedInStream if has_event_in_stream?(serialized_record.event_id, stream.name)
|
37
|
-
|
63
|
+
add_to_stream(stream, serialized_record, resolved_version, index)
|
38
64
|
end
|
39
65
|
end
|
40
66
|
self
|
@@ -69,9 +95,7 @@ module RubyEventStore
|
|
69
95
|
serialized_records.last&.deserialize(serializer)
|
70
96
|
else
|
71
97
|
Enumerator.new do |y|
|
72
|
-
serialized_records.each
|
73
|
-
y << serialized_record.deserialize(serializer)
|
74
|
-
end
|
98
|
+
serialized_records.each { |serialized_record| y << serialized_record.deserialize(serializer) }
|
75
99
|
end
|
76
100
|
end
|
77
101
|
end
|
@@ -84,38 +108,61 @@ module RubyEventStore
|
|
84
108
|
records.each do |record|
|
85
109
|
read_event(record.event_id)
|
86
110
|
serialized_record =
|
87
|
-
Record
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
111
|
+
Record
|
112
|
+
.new(
|
113
|
+
event_id: record.event_id,
|
114
|
+
event_type: record.event_type,
|
115
|
+
data: record.data,
|
116
|
+
metadata: record.metadata,
|
117
|
+
timestamp: Time.iso8601(storage.fetch(record.event_id).timestamp),
|
118
|
+
valid_at: record.valid_at
|
119
|
+
)
|
120
|
+
.serialize(serializer)
|
95
121
|
storage[record.event_id] = serialized_record
|
96
122
|
end
|
97
123
|
end
|
98
124
|
|
99
125
|
def streams_of(event_id)
|
100
|
-
streams
|
101
|
-
|
102
|
-
|
126
|
+
streams.select { |name,| has_event_in_stream?(event_id, name) }.map { |name,| Stream.new(name) }
|
127
|
+
end
|
128
|
+
|
129
|
+
def position_in_stream(event_id, stream)
|
130
|
+
event_in_stream = streams[stream.name].find { |event_in_stream| event_in_stream.event_id.eql?(event_id) }
|
131
|
+
raise EventNotFoundInStream if event_in_stream.nil?
|
132
|
+
event_in_stream.position
|
133
|
+
end
|
134
|
+
|
135
|
+
def global_position(event_id)
|
136
|
+
storage.keys.index(event_id) or raise EventNotFound.new(event_id)
|
137
|
+
end
|
138
|
+
|
139
|
+
def event_in_stream?(event_id, stream)
|
140
|
+
!streams[stream.name].find { |event_in_stream| event_in_stream.event_id.eql?(event_id) }.nil?
|
103
141
|
end
|
104
142
|
|
105
143
|
private
|
144
|
+
|
106
145
|
def read_scope(spec)
|
107
146
|
serialized_records = serialized_records_of_stream(spec.stream)
|
108
147
|
serialized_records = ordered(serialized_records, spec)
|
109
|
-
serialized_records = serialized_records.select{|e| spec.with_ids.any?{|x| x.eql?(e.event_id)}} if spec
|
110
|
-
|
148
|
+
serialized_records = serialized_records.select { |e| spec.with_ids.any? { |x| x.eql?(e.event_id) } } if spec
|
149
|
+
.with_ids?
|
150
|
+
serialized_records = serialized_records.select { |e| spec.with_types.any? { |x| x.eql?(e.event_type) } } if spec
|
151
|
+
.with_types?
|
111
152
|
serialized_records = serialized_records.reverse if spec.backward?
|
112
153
|
serialized_records = serialized_records.drop(index_of(serialized_records, spec.start) + 1) if spec.start
|
113
154
|
serialized_records = serialized_records.take(index_of(serialized_records, spec.stop)) if spec.stop
|
114
155
|
serialized_records = serialized_records.take(spec.limit) if spec.limit?
|
115
|
-
serialized_records = serialized_records.select { |sr| Time.iso8601(sr.timestamp) < spec.older_than } if spec
|
116
|
-
|
117
|
-
serialized_records =
|
118
|
-
|
156
|
+
serialized_records = serialized_records.select { |sr| Time.iso8601(sr.timestamp) < spec.older_than } if spec
|
157
|
+
.older_than
|
158
|
+
serialized_records =
|
159
|
+
serialized_records.select { |sr| Time.iso8601(sr.timestamp) <= spec.older_than_or_equal } if spec
|
160
|
+
.older_than_or_equal
|
161
|
+
serialized_records = serialized_records.select { |sr| Time.iso8601(sr.timestamp) > spec.newer_than } if spec
|
162
|
+
.newer_than
|
163
|
+
serialized_records =
|
164
|
+
serialized_records.select { |sr| Time.iso8601(sr.timestamp) >= spec.newer_than_or_equal } if spec
|
165
|
+
.newer_than_or_equal
|
119
166
|
serialized_records
|
120
167
|
end
|
121
168
|
|
@@ -124,7 +171,7 @@ module RubyEventStore
|
|
124
171
|
end
|
125
172
|
|
126
173
|
def event_ids_of_stream(stream)
|
127
|
-
streams.fetch(stream.name, Array.new)
|
174
|
+
streams.fetch(stream.name, Array.new).map(&:event_id)
|
128
175
|
end
|
129
176
|
|
130
177
|
def serialized_records_of_stream(stream)
|
@@ -143,7 +190,7 @@ module RubyEventStore
|
|
143
190
|
end
|
144
191
|
|
145
192
|
def last_stream_version(stream)
|
146
|
-
|
193
|
+
streams.fetch(stream.name, Array.new).size - 1
|
147
194
|
end
|
148
195
|
|
149
196
|
def with_synchronize(expected_version, stream, &block)
|
@@ -157,18 +204,34 @@ module RubyEventStore
|
|
157
204
|
# conditions more likely. And we only use mutex.synchronize for writing
|
158
205
|
# not for the whole read+write algorithm.
|
159
206
|
Thread.pass
|
160
|
-
mutex.synchronize
|
161
|
-
resolved_version = last_stream_version(stream) if expected_version.any?
|
162
|
-
block.call(resolved_version)
|
163
|
-
end
|
207
|
+
mutex.synchronize { block.call(resolved_version) }
|
164
208
|
end
|
165
209
|
|
166
210
|
def has_event_in_stream?(event_id, stream_name)
|
167
|
-
streams.fetch(stream_name, Array.new).any? { |
|
211
|
+
streams.fetch(stream_name, Array.new).any? { |event_in_stream| event_in_stream.event_id.eql?(event_id) }
|
168
212
|
end
|
169
213
|
|
170
214
|
def index_of(source, event_id)
|
171
|
-
source.index {|item| item.event_id.eql?(event_id)}
|
215
|
+
source.index { |item| item.event_id.eql?(event_id) }
|
216
|
+
end
|
217
|
+
|
218
|
+
def compute_position(resolved_version, index)
|
219
|
+
resolved_version + index + 1 unless resolved_version.nil?
|
220
|
+
end
|
221
|
+
|
222
|
+
def add_to_stream(stream, serialized_record, resolved_version, index)
|
223
|
+
streams[stream.name] << EventInStream.new(serialized_record.event_id, compute_position(resolved_version, index))
|
224
|
+
end
|
225
|
+
|
226
|
+
def ensure_supported_any_usage(resolved_version, stream)
|
227
|
+
if @ensure_supported_any_usage
|
228
|
+
stream_positions = streams.fetch(stream.name, Array.new).map(&:position)
|
229
|
+
if resolved_version.nil?
|
230
|
+
raise UnsupportedVersionAnyUsage if !stream_positions.compact.empty?
|
231
|
+
else
|
232
|
+
raise UnsupportedVersionAnyUsage if stream_positions.include?(nil)
|
233
|
+
end
|
234
|
+
end
|
172
235
|
end
|
173
236
|
|
174
237
|
attr_reader :streams, :mutex, :storage, :serializer
|
@@ -13,11 +13,20 @@ module RubyEventStore
|
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
|
16
|
+
def method_missing(method_name, *arguments, **keyword_arguments, &block)
|
17
|
+
if respond_to?(method_name)
|
18
|
+
dispatcher.public_send(method_name, *arguments, **keyword_arguments, &block)
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def respond_to_missing?(method_name, _include_private)
|
25
|
+
dispatcher.respond_to?(method_name)
|
18
26
|
end
|
19
27
|
|
20
28
|
private
|
29
|
+
|
21
30
|
attr_reader :instrumentation, :dispatcher
|
22
31
|
end
|
23
32
|
end
|
@@ -25,14 +25,6 @@ module RubyEventStore
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def has_event?(event_id)
|
29
|
-
repository.has_event?(event_id)
|
30
|
-
end
|
31
|
-
|
32
|
-
def last_stream_event(stream)
|
33
|
-
repository.last_stream_event(stream)
|
34
|
-
end
|
35
|
-
|
36
28
|
def read(specification)
|
37
29
|
instrumentation.instrument("read.repository.rails_event_store", specification: specification) do
|
38
30
|
repository.read(specification)
|
@@ -57,7 +49,20 @@ module RubyEventStore
|
|
57
49
|
end
|
58
50
|
end
|
59
51
|
|
52
|
+
def method_missing(method_name, *arguments, **keyword_arguments, &block)
|
53
|
+
if respond_to?(method_name)
|
54
|
+
repository.public_send(method_name, *arguments, **keyword_arguments, &block)
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def respond_to_missing?(method_name, _include_private)
|
61
|
+
repository.respond_to?(method_name)
|
62
|
+
end
|
63
|
+
|
60
64
|
private
|
65
|
+
|
61
66
|
attr_reader :repository, :instrumentation
|
62
67
|
end
|
63
68
|
end
|
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
module RubyEventStore
|
4
4
|
class LinkByMetadata
|
5
|
-
|
6
5
|
def initialize(event_store:, key:, prefix: nil)
|
7
6
|
@event_store = event_store
|
8
7
|
@key = key
|
@@ -12,31 +11,19 @@ module RubyEventStore
|
|
12
11
|
def call(event)
|
13
12
|
return unless event.metadata.has_key?(@key)
|
14
13
|
|
15
|
-
@event_store.link(
|
16
|
-
[event.event_id],
|
17
|
-
stream_name: "#{@prefix}#{event.metadata.fetch(@key)}"
|
18
|
-
)
|
14
|
+
@event_store.link([event.event_id], stream_name: "#{@prefix}#{event.metadata.fetch(@key)}")
|
19
15
|
end
|
20
|
-
|
21
16
|
end
|
22
17
|
|
23
18
|
class LinkByCorrelationId < LinkByMetadata
|
24
19
|
def initialize(event_store:, prefix: nil)
|
25
|
-
super(
|
26
|
-
event_store: event_store,
|
27
|
-
prefix: prefix,
|
28
|
-
key: :correlation_id,
|
29
|
-
)
|
20
|
+
super(event_store: event_store, prefix: prefix, key: :correlation_id)
|
30
21
|
end
|
31
22
|
end
|
32
23
|
|
33
24
|
class LinkByCausationId < LinkByMetadata
|
34
25
|
def initialize(event_store:, prefix: nil)
|
35
|
-
super(
|
36
|
-
event_store: event_store,
|
37
|
-
prefix: prefix,
|
38
|
-
key: :causation_id,
|
39
|
-
)
|
26
|
+
super(event_store: event_store, prefix: prefix, key: :causation_id)
|
40
27
|
end
|
41
28
|
end
|
42
29
|
|
@@ -47,11 +34,7 @@ module RubyEventStore
|
|
47
34
|
end
|
48
35
|
|
49
36
|
def call(event)
|
50
|
-
@event_store.link(
|
51
|
-
[event.event_id],
|
52
|
-
stream_name: "#{@prefix}#{event.event_type}"
|
53
|
-
)
|
37
|
+
@event_store.link([event.event_id], stream_name: "#{@prefix}#{event.event_type}")
|
54
38
|
end
|
55
39
|
end
|
56
|
-
|
57
40
|
end
|
@@ -4,10 +4,12 @@ module RubyEventStore
|
|
4
4
|
module Mappers
|
5
5
|
class Default < PipelineMapper
|
6
6
|
def initialize(events_class_remapping: {})
|
7
|
-
super(
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
super(
|
8
|
+
Pipeline.new(
|
9
|
+
Transformation::EventClassRemapper.new(events_class_remapping),
|
10
|
+
Transformation::SymbolizeMetadataKeys.new
|
11
|
+
)
|
12
|
+
)
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
@@ -5,31 +5,22 @@ module RubyEventStore
|
|
5
5
|
class EncryptionKey
|
6
6
|
def initialize(cipher:, key:)
|
7
7
|
@cipher = cipher
|
8
|
-
@key
|
8
|
+
@key = key
|
9
9
|
end
|
10
10
|
|
11
11
|
def encrypt(message, iv)
|
12
|
-
crypto
|
13
|
-
crypto.iv
|
12
|
+
crypto = prepare_encrypt(cipher)
|
13
|
+
crypto.iv = iv
|
14
14
|
crypto.key = key
|
15
15
|
|
16
|
-
|
17
|
-
encrypt_authenticated(crypto, message)
|
18
|
-
else
|
19
|
-
crypto.update(message) + crypto.final
|
20
|
-
end
|
16
|
+
crypto.authenticated? ? encrypt_authenticated(crypto, message) : crypto.update(message) + crypto.final
|
21
17
|
end
|
22
18
|
|
23
19
|
def decrypt(message, iv)
|
24
|
-
crypto
|
25
|
-
crypto.iv
|
20
|
+
crypto = prepare_decrypt(cipher)
|
21
|
+
crypto.iv = iv
|
26
22
|
crypto.key = key
|
27
|
-
ciphertext =
|
28
|
-
if crypto.authenticated?
|
29
|
-
ciphertext_from_authenticated(crypto, message)
|
30
|
-
else
|
31
|
-
message
|
32
|
-
end
|
23
|
+
ciphertext = crypto.authenticated? ? ciphertext_from_authenticated(crypto, message) : message
|
33
24
|
(crypto.update(ciphertext) + crypto.final).force_encoding("UTF-8")
|
34
25
|
end
|
35
26
|
|
@@ -1,14 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'yaml'
|
4
|
-
|
5
3
|
module RubyEventStore
|
6
4
|
module Mappers
|
7
5
|
class EncryptionMapper < PipelineMapper
|
8
|
-
def initialize(key_repository, serializer: YAML, forgotten_data: ForgottenData.new)
|
9
|
-
super(
|
10
|
-
|
11
|
-
|
6
|
+
def initialize(key_repository, serializer: Serializers::YAML, forgotten_data: ForgottenData.new)
|
7
|
+
super(
|
8
|
+
Pipeline.new(
|
9
|
+
Transformation::Encryption.new(key_repository, serializer: serializer, forgotten_data: forgotten_data)
|
10
|
+
)
|
11
|
+
)
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -4,22 +4,15 @@ module RubyEventStore
|
|
4
4
|
module Mappers
|
5
5
|
class Pipeline
|
6
6
|
def initialize(*transformations, to_domain_event: Transformation::DomainEvent.new)
|
7
|
-
@transformations = [
|
8
|
-
to_domain_event,
|
9
|
-
transformations,
|
10
|
-
].flatten.freeze
|
7
|
+
@transformations = [to_domain_event, transformations].flatten.freeze
|
11
8
|
end
|
12
9
|
|
13
10
|
def dump(domain_event)
|
14
|
-
transformations.reduce(domain_event)
|
15
|
-
transform.dump(item)
|
16
|
-
end
|
11
|
+
transformations.reduce(domain_event) { |item, transform| transform.dump(item) }
|
17
12
|
end
|
18
13
|
|
19
14
|
def load(record)
|
20
|
-
transformations.reverse.reduce(record)
|
21
|
-
transform.load(item)
|
22
|
-
end
|
15
|
+
transformations.reverse.reduce(record) { |item, transform| transform.load(item) }
|
23
16
|
end
|
24
17
|
|
25
18
|
attr_reader :transformations
|
@@ -5,27 +5,37 @@ module RubyEventStore
|
|
5
5
|
module Transformation
|
6
6
|
class DomainEvent
|
7
7
|
def dump(domain_event)
|
8
|
-
metadata = domain_event.metadata.to_h
|
8
|
+
metadata = domain_event.metadata.dup.to_h
|
9
9
|
timestamp = metadata.delete(:timestamp)
|
10
10
|
valid_at = metadata.delete(:valid_at)
|
11
11
|
Record.new(
|
12
|
-
event_id:
|
13
|
-
metadata:
|
14
|
-
data:
|
12
|
+
event_id: domain_event.event_id,
|
13
|
+
metadata: metadata,
|
14
|
+
data: domain_event.data,
|
15
15
|
event_type: domain_event.event_type,
|
16
|
-
timestamp:
|
17
|
-
valid_at:
|
16
|
+
timestamp: timestamp,
|
17
|
+
valid_at: valid_at
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
21
|
def load(record)
|
22
|
-
Object
|
22
|
+
Object
|
23
|
+
.const_get(record.event_type)
|
24
|
+
.new(
|
25
|
+
event_id: record.event_id,
|
26
|
+
data: record.data,
|
27
|
+
metadata: record.metadata.merge(timestamp: record.timestamp, valid_at: record.valid_at)
|
28
|
+
)
|
29
|
+
rescue NameError
|
30
|
+
Event.new(
|
23
31
|
event_id: record.event_id,
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
32
|
+
data: record.data,
|
33
|
+
metadata:
|
34
|
+
record.metadata.merge(
|
35
|
+
timestamp: record.timestamp,
|
36
|
+
valid_at: record.valid_at,
|
37
|
+
event_type: record.event_type
|
38
|
+
)
|
29
39
|
)
|
30
40
|
end
|
31
41
|
end
|
@@ -6,38 +6,38 @@ module RubyEventStore
|
|
6
6
|
class Encryption
|
7
7
|
class Leaf
|
8
8
|
def self.===(hash)
|
9
|
-
hash.keys.sort.eql? %i
|
9
|
+
hash.keys.sort.eql? %i[cipher identifier iv]
|
10
10
|
end
|
11
11
|
end
|
12
12
|
private_constant :Leaf
|
13
13
|
|
14
14
|
class MissingEncryptionKey < StandardError
|
15
15
|
def initialize(key_identifier)
|
16
|
-
super
|
16
|
+
super "Could not find encryption key for '#{key_identifier}'"
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
def initialize(key_repository, serializer: YAML, forgotten_data: ForgottenData.new)
|
20
|
+
def initialize(key_repository, serializer: Serializers::YAML, forgotten_data: ForgottenData.new)
|
21
21
|
@key_repository = key_repository
|
22
22
|
@serializer = serializer
|
23
23
|
@forgotten_data = forgotten_data
|
24
24
|
end
|
25
25
|
|
26
26
|
def dump(record)
|
27
|
-
data
|
28
|
-
metadata
|
27
|
+
data = record.data
|
28
|
+
metadata = record.metadata.dup
|
29
29
|
event_class = Object.const_get(record.event_type)
|
30
30
|
|
31
|
-
crypto_description
|
31
|
+
crypto_description = encryption_metadata(data, encryption_schema(event_class))
|
32
32
|
metadata[:encryption] = crypto_description unless crypto_description.empty?
|
33
33
|
|
34
34
|
Record.new(
|
35
|
-
event_id:
|
35
|
+
event_id: record.event_id,
|
36
36
|
event_type: record.event_type,
|
37
|
-
data:
|
38
|
-
metadata:
|
39
|
-
timestamp:
|
40
|
-
valid_at:
|
37
|
+
data: encrypt_data(deep_dup(data), crypto_description),
|
38
|
+
metadata: metadata,
|
39
|
+
timestamp: record.timestamp,
|
40
|
+
valid_at: record.valid_at
|
41
41
|
)
|
42
42
|
end
|
43
43
|
|
@@ -46,16 +46,17 @@ module RubyEventStore
|
|
46
46
|
crypto_description = Hash(metadata.delete(:encryption))
|
47
47
|
|
48
48
|
Record.new(
|
49
|
-
event_id:
|
49
|
+
event_id: record.event_id,
|
50
50
|
event_type: record.event_type,
|
51
|
-
data:
|
52
|
-
metadata:
|
53
|
-
timestamp:
|
54
|
-
valid_at:
|
51
|
+
data: decrypt_data(record.data, crypto_description),
|
52
|
+
metadata: metadata,
|
53
|
+
timestamp: record.timestamp,
|
54
|
+
valid_at: record.valid_at
|
55
55
|
)
|
56
56
|
end
|
57
57
|
|
58
58
|
private
|
59
|
+
|
59
60
|
attr_reader :key_repository, :serializer, :forgotten_data
|
60
61
|
|
61
62
|
def encryption_schema(event_class)
|
@@ -64,9 +65,7 @@ module RubyEventStore
|
|
64
65
|
|
65
66
|
def deep_dup(hash)
|
66
67
|
duplicate = hash.dup
|
67
|
-
duplicate.each
|
68
|
-
duplicate[k] = v.instance_of?(Hash) ? deep_dup(v) : v
|
69
|
-
end
|
68
|
+
duplicate.each { |k, v| duplicate[k] = v.instance_of?(Hash) ? deep_dup(v) : v }
|
70
69
|
duplicate
|
71
70
|
end
|
72
71
|
|
@@ -79,11 +78,7 @@ module RubyEventStore
|
|
79
78
|
key_identifier = value.call(data)
|
80
79
|
encryption_key = key_repository.key_of(key_identifier)
|
81
80
|
raise MissingEncryptionKey.new(key_identifier) unless encryption_key
|
82
|
-
acc[key] = {
|
83
|
-
cipher: encryption_key.cipher,
|
84
|
-
iv: encryption_key.random_iv,
|
85
|
-
identifier: key_identifier,
|
86
|
-
}
|
81
|
+
acc[key] = { cipher: encryption_key.cipher, iv: encryption_key.random_iv, identifier: key_identifier }
|
87
82
|
end
|
88
83
|
acc
|
89
84
|
end
|
@@ -122,7 +117,8 @@ module RubyEventStore
|
|
122
117
|
cryptogram = data.fetch(attribute)
|
123
118
|
return unless cryptogram
|
124
119
|
|
125
|
-
encryption_key = key_repository.key_of(meta.fetch(:identifier), cipher: meta.fetch(:cipher)) or
|
120
|
+
encryption_key = key_repository.key_of(meta.fetch(:identifier), cipher: meta.fetch(:cipher)) or
|
121
|
+
return forgotten_data
|
126
122
|
serializer.load(encryption_key.decrypt(cryptogram, meta.fetch(:iv)))
|
127
123
|
when Hash
|
128
124
|
decrypt_data(data.fetch(attribute), meta)
|
@@ -14,16 +14,17 @@ module RubyEventStore
|
|
14
14
|
|
15
15
|
def load(record)
|
16
16
|
Record.new(
|
17
|
-
event_id:
|
17
|
+
event_id: record.event_id,
|
18
18
|
event_type: class_map[record.event_type] || record.event_type,
|
19
|
-
data:
|
20
|
-
metadata:
|
21
|
-
timestamp:
|
22
|
-
valid_at:
|
19
|
+
data: record.data,
|
20
|
+
metadata: record.metadata,
|
21
|
+
timestamp: record.timestamp,
|
22
|
+
valid_at: record.valid_at
|
23
23
|
)
|
24
24
|
end
|
25
25
|
|
26
26
|
private
|
27
|
+
|
27
28
|
attr_reader :class_map
|
28
29
|
end
|
29
30
|
end
|