ruby_event_store 0.30.0 → 1.3.1
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/Gemfile +9 -1
- data/Makefile +15 -62
- data/lib/ruby_event_store/batch_enumerator.rb +15 -5
- data/lib/ruby_event_store/broker.rb +45 -0
- data/lib/ruby_event_store/client.rb +220 -130
- data/lib/ruby_event_store/composed_dispatcher.rb +24 -0
- data/lib/ruby_event_store/constants.rb +2 -0
- data/lib/ruby_event_store/correlated_commands.rb +42 -0
- data/lib/ruby_event_store/dispatcher.rb +20 -0
- data/lib/ruby_event_store/errors.rb +16 -16
- data/lib/ruby_event_store/event.rb +70 -14
- data/lib/ruby_event_store/expected_version.rb +2 -0
- data/lib/ruby_event_store/immediate_async_dispatcher.rb +17 -0
- data/lib/ruby_event_store/in_memory_repository.rb +45 -17
- data/lib/ruby_event_store/instrumented_dispatcher.rb +23 -0
- data/lib/ruby_event_store/instrumented_repository.rb +63 -0
- data/lib/ruby_event_store/link_by_metadata.rb +57 -0
- data/lib/ruby_event_store/mappers/default.rb +10 -26
- data/lib/ruby_event_store/mappers/encryption_key.rb +74 -0
- data/lib/ruby_event_store/mappers/encryption_mapper.rb +16 -0
- data/lib/ruby_event_store/mappers/forgotten_data.rb +30 -0
- data/lib/ruby_event_store/mappers/in_memory_encryption_key_repository.rb +34 -0
- data/lib/ruby_event_store/mappers/instrumented_mapper.rb +28 -0
- data/lib/ruby_event_store/mappers/json_mapper.rb +16 -0
- data/lib/ruby_event_store/mappers/null_mapper.rb +5 -8
- data/lib/ruby_event_store/mappers/pipeline.rb +31 -0
- data/lib/ruby_event_store/mappers/pipeline_mapper.rb +22 -0
- data/lib/ruby_event_store/mappers/protobuf.rb +13 -67
- data/lib/ruby_event_store/mappers/transformation/domain_event.rb +26 -0
- data/lib/ruby_event_store/mappers/transformation/encryption.rb +128 -0
- data/lib/ruby_event_store/mappers/transformation/event_class_remapper.rb +24 -0
- data/lib/ruby_event_store/mappers/transformation/item.rb +56 -0
- data/lib/ruby_event_store/mappers/transformation/proto_event.rb +17 -0
- data/lib/ruby_event_store/mappers/transformation/protobuf_encoder.rb +30 -0
- data/lib/ruby_event_store/mappers/transformation/protobuf_nested_struct_metadata.rb +30 -0
- data/lib/ruby_event_store/mappers/transformation/serialization.rb +34 -0
- data/lib/ruby_event_store/mappers/transformation/serialized_record.rb +27 -0
- data/lib/ruby_event_store/mappers/transformation/stringify_metadata_keys.rb +24 -0
- data/lib/ruby_event_store/mappers/transformation/symbolize_metadata_keys.rb +24 -0
- data/lib/ruby_event_store/metadata.rb +4 -2
- data/lib/ruby_event_store/projection.rb +34 -12
- data/lib/ruby_event_store/serialized_record.rb +3 -1
- data/lib/ruby_event_store/spec/broker_lint.rb +92 -0
- data/lib/ruby_event_store/spec/dispatcher_lint.rb +4 -36
- data/lib/ruby_event_store/spec/event_lint.rb +71 -0
- data/lib/ruby_event_store/spec/event_repository_lint.rb +1092 -962
- data/lib/ruby_event_store/spec/mapper_lint.rb +17 -0
- data/lib/ruby_event_store/spec/scheduler_lint.rb +9 -0
- data/lib/ruby_event_store/spec/subscriptions_lint.rb +111 -0
- data/lib/ruby_event_store/specification.rb +201 -56
- data/lib/ruby_event_store/specification_reader.rb +43 -0
- data/lib/ruby_event_store/specification_result.rb +212 -0
- data/lib/ruby_event_store/stream.rb +2 -0
- data/lib/ruby_event_store/subscriptions.rb +110 -0
- data/lib/ruby_event_store/transform_keys.rb +31 -0
- data/lib/ruby_event_store/version.rb +3 -1
- data/lib/ruby_event_store.rb +34 -4
- data/ruby_event_store.gemspec +1 -10
- metadata +47 -126
- data/exe/res-deprecated-read-api-migrator +0 -19
- data/lib/ruby_event_store/deprecated_read_api_rewriter.rb +0 -67
- data/lib/ruby_event_store/deprecated_read_api_runner.rb +0 -64
- data/lib/ruby_event_store/deprecations.rb +0 -7
- data/lib/ruby_event_store/pub_sub/broker.rb +0 -73
- data/lib/ruby_event_store/pub_sub/dispatcher.rb +0 -25
- data/lib/ruby_event_store/spec/event_broker_lint.rb +0 -211
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class CorrelatedCommands
|
5
|
+
|
6
|
+
def initialize(event_store, command_bus)
|
7
|
+
@event_store = event_store
|
8
|
+
@command_bus = command_bus
|
9
|
+
end
|
10
|
+
|
11
|
+
class MiniEvent < Struct.new(:correlation_id, :message_id)
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(command)
|
15
|
+
correlation_id = event_store.metadata[:correlation_id]
|
16
|
+
causation_id = event_store.metadata[:causation_id]
|
17
|
+
|
18
|
+
if correlation_id && causation_id
|
19
|
+
command.correlate_with(MiniEvent.new(
|
20
|
+
correlation_id,
|
21
|
+
causation_id,
|
22
|
+
)) if command.respond_to?(:correlate_with)
|
23
|
+
event_store.with_metadata(
|
24
|
+
causation_id: command.message_id,
|
25
|
+
) do
|
26
|
+
command_bus.call(command)
|
27
|
+
end
|
28
|
+
else
|
29
|
+
event_store.with_metadata(
|
30
|
+
correlation_id: command.message_id,
|
31
|
+
causation_id: command.message_id,
|
32
|
+
) do
|
33
|
+
command_bus.call(command)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_reader :event_store, :command_bus
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class Dispatcher
|
5
|
+
def call(subscriber, event, _)
|
6
|
+
subscriber = subscriber.new if Class === subscriber
|
7
|
+
subscriber.call(event)
|
8
|
+
end
|
9
|
+
|
10
|
+
def verify(subscriber)
|
11
|
+
begin
|
12
|
+
subscriber_instance = Class === subscriber ? subscriber.new : subscriber
|
13
|
+
rescue ArgumentError
|
14
|
+
false
|
15
|
+
else
|
16
|
+
subscriber_instance.respond_to?(:call)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,25 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RubyEventStore
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
Error = Class.new(StandardError)
|
5
|
+
WrongExpectedEventVersion = Class.new(Error)
|
6
|
+
InvalidExpectedVersion = Class.new(Error)
|
7
|
+
IncorrectStreamData = Class.new(Error)
|
8
|
+
SubscriberNotExist = Class.new(Error)
|
9
|
+
InvalidPageStart = Class.new(Error)
|
10
|
+
InvalidPageStop = Class.new(Error)
|
11
|
+
InvalidPageSize = Class.new(Error)
|
12
|
+
EventDuplicatedInStream = Class.new(Error)
|
13
|
+
ReservedInternalName = Class.new(Error)
|
14
|
+
InvalidHandler = Class.new(Error)
|
15
|
+
ProtobufEncodingFailed = Class.new(Error)
|
11
16
|
|
12
|
-
class EventNotFound <
|
17
|
+
class EventNotFound < Error
|
13
18
|
attr_reader :event_id
|
19
|
+
|
14
20
|
def initialize(event_id)
|
15
21
|
super("Event not found: #{event_id}")
|
16
22
|
@event_id = event_id
|
17
23
|
end
|
18
24
|
end
|
19
|
-
|
20
|
-
class InvalidHandler < StandardError
|
21
|
-
def initialize(object)
|
22
|
-
super("#call method not found in #{object.inspect} subscriber. Are you sure it is a valid subscriber?")
|
23
|
-
end
|
24
|
-
end
|
25
25
|
end
|
@@ -1,45 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'securerandom'
|
2
4
|
|
3
5
|
module RubyEventStore
|
6
|
+
|
7
|
+
# Data structure representing an event
|
4
8
|
class Event
|
5
|
-
|
9
|
+
# Instantiates a new event
|
10
|
+
#
|
11
|
+
# @param event_id [String] event id
|
12
|
+
# @param data [Hash] event data which belong to your application domain
|
13
|
+
# @param metadata [Hash] event metadata which are technical and not
|
14
|
+
# part of your domain such as remote_ip, request_id, correlation_id,
|
15
|
+
# causation_id etc.
|
16
|
+
# @return [Event]
|
17
|
+
def initialize(event_id: SecureRandom.uuid, metadata: nil, data: {})
|
6
18
|
@event_id = event_id.to_s
|
7
19
|
@metadata = Metadata.new(metadata.to_h)
|
8
|
-
@data = data
|
20
|
+
@data = data
|
9
21
|
end
|
22
|
+
|
10
23
|
attr_reader :event_id, :metadata, :data
|
11
24
|
|
25
|
+
# Event id
|
26
|
+
# @return [String]
|
12
27
|
def message_id
|
13
28
|
event_id
|
14
29
|
end
|
15
30
|
|
16
|
-
|
31
|
+
# Type of event. Used when matching with subscribed handlers.
|
32
|
+
# @return [String]
|
33
|
+
def event_type
|
17
34
|
self.class.name
|
18
35
|
end
|
19
36
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
metadata: metadata.to_h,
|
24
|
-
data: data,
|
25
|
-
type: type,
|
26
|
-
}
|
27
|
-
end
|
28
|
-
|
37
|
+
# Timestamp from metadata
|
38
|
+
#
|
39
|
+
# @return [Time, nil]
|
29
40
|
def timestamp
|
30
41
|
metadata[:timestamp]
|
31
42
|
end
|
32
43
|
|
44
|
+
# Two events are equal if:
|
45
|
+
# * they are of the same class
|
46
|
+
# * have identical event id
|
47
|
+
# * have identical data (verified with eql? method)
|
48
|
+
#
|
49
|
+
# @param other_event [Event, Object] object to compare
|
50
|
+
#
|
51
|
+
# Event equality ignores metadata!
|
52
|
+
# @return [TrueClass, FalseClass]
|
33
53
|
def ==(other_event)
|
34
54
|
other_event.instance_of?(self.class) &&
|
35
55
|
other_event.event_id.eql?(event_id) &&
|
36
56
|
other_event.data.eql?(data)
|
37
57
|
end
|
38
58
|
|
59
|
+
# @private
|
39
60
|
BIG_VALUE = 0b111111100100000010010010110011101011000101010101001100100110000
|
40
61
|
|
41
|
-
#
|
62
|
+
# Generates a Fixnum hash value for this object. This function
|
63
|
+
# have the property that a.eql?(b) implies a.hash == b.hash.
|
64
|
+
#
|
65
|
+
# The hash value is used along with eql? by the Hash class to
|
66
|
+
# determine if two objects reference the same hash key.
|
67
|
+
#
|
68
|
+
# This hash is based on
|
69
|
+
# * class
|
70
|
+
# * event_id
|
71
|
+
# * data
|
42
72
|
def hash
|
73
|
+
# We don't use metadata because == does not use metadata
|
43
74
|
[
|
44
75
|
self.class,
|
45
76
|
event_id,
|
@@ -47,27 +78,52 @@ module RubyEventStore
|
|
47
78
|
].hash ^ BIG_VALUE
|
48
79
|
end
|
49
80
|
|
81
|
+
# Reads correlation_id from metadata.
|
82
|
+
# {http://railseventstore.org/docs/correlation_causation/ Find out more}
|
83
|
+
#
|
84
|
+
# @return [String, nil]
|
50
85
|
def correlation_id
|
51
86
|
metadata[:correlation_id]
|
52
87
|
end
|
53
88
|
|
89
|
+
# Sets correlation_id in metadata.
|
90
|
+
# {http://railseventstore.org/docs/correlation_causation/ Find out more}
|
91
|
+
#
|
92
|
+
# @param val [String]
|
93
|
+
# @return [String]
|
54
94
|
def correlation_id=(val)
|
55
95
|
metadata[:correlation_id] = val
|
56
96
|
end
|
57
97
|
|
98
|
+
# Reads causation_id from metadata.
|
99
|
+
# {http://railseventstore.org/docs/correlation_causation/ Find out more}
|
100
|
+
#
|
101
|
+
# @return [String, nil]
|
58
102
|
def causation_id
|
59
103
|
metadata[:causation_id]
|
60
104
|
end
|
61
105
|
|
106
|
+
# Sets causation_id= in metadata.
|
107
|
+
# {http://railseventstore.org/docs/correlation_causation/ Find out more}
|
108
|
+
#
|
109
|
+
# @param val [String]
|
110
|
+
# @return [String]
|
62
111
|
def causation_id=(val)
|
63
112
|
metadata[:causation_id]= val
|
64
113
|
end
|
65
114
|
|
115
|
+
# Sets correlation_id and causation_id in metadata based
|
116
|
+
# on correlation_id and message_id of the provided message.
|
117
|
+
# {http://railseventstore.org/docs/correlation_causation/ Find out more}
|
118
|
+
#
|
119
|
+
# @param other_message [Event, Proto, command] message to correlate with. Most likely an event or a command. Must respond to correlation_id and message_id.
|
120
|
+
# @return [String] set causation_id
|
66
121
|
def correlate_with(other_message)
|
67
122
|
self.correlation_id = other_message.correlation_id || other_message.message_id
|
68
123
|
self.causation_id = other_message.message_id
|
124
|
+
self
|
69
125
|
end
|
70
126
|
|
71
127
|
alias_method :eql?, :==
|
72
128
|
end
|
73
|
-
end
|
129
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class ImmediateAsyncDispatcher
|
5
|
+
def initialize(scheduler:)
|
6
|
+
@scheduler = scheduler
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(subscriber, _, serialized_event)
|
10
|
+
@scheduler.call(subscriber, serialized_event)
|
11
|
+
end
|
12
|
+
|
13
|
+
def verify(subscriber)
|
14
|
+
@scheduler.verify(subscriber)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
|
-
|
2
|
-
require 'thread'
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
require 'ostruct'
|
4
4
|
module RubyEventStore
|
5
5
|
class InMemoryRepository
|
6
6
|
|
@@ -15,7 +15,7 @@ module RubyEventStore
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def link_to_stream(event_ids, stream, expected_version)
|
18
|
-
events =
|
18
|
+
events = Array(event_ids).map {|eid| read_event(eid)}
|
19
19
|
add_to_stream(events, expected_version, stream, nil)
|
20
20
|
end
|
21
21
|
|
@@ -31,36 +31,64 @@ module RubyEventStore
|
|
31
31
|
stream_of(stream.name).last
|
32
32
|
end
|
33
33
|
|
34
|
-
def read_event(event_id)
|
35
|
-
global.find {|e| event_id.eql?(e.event_id)} or raise EventNotFound.new(event_id)
|
36
|
-
end
|
37
|
-
|
38
34
|
def read(spec)
|
39
|
-
events =
|
40
|
-
events = events.reverse if spec.backward?
|
41
|
-
events = events.drop(index_of(events, spec.start) + 1) unless spec.head?
|
42
|
-
events = events[0...spec.count] if spec.limit?
|
35
|
+
events = read_scope(spec)
|
43
36
|
if spec.batched?
|
44
37
|
batch_reader = ->(offset, limit) { events.drop(offset).take(limit) }
|
45
38
|
BatchEnumerator.new(spec.batch_size, events.size, batch_reader).each
|
39
|
+
elsif spec.first?
|
40
|
+
events.first
|
41
|
+
elsif spec.last?
|
42
|
+
events.last
|
46
43
|
else
|
47
44
|
events.each
|
48
45
|
end
|
49
46
|
end
|
50
47
|
|
48
|
+
def count(spec)
|
49
|
+
read_scope(spec).count
|
50
|
+
end
|
51
|
+
|
52
|
+
def update_messages(messages)
|
53
|
+
messages.each do |new_msg|
|
54
|
+
location = global.index{|m| new_msg.event_id.eql?(m.event_id)}
|
55
|
+
raise EventNotFound.new(new_msg.event_id) unless location
|
56
|
+
global[location] = new_msg
|
57
|
+
streams.values.each do |str|
|
58
|
+
location = str.index{|m| new_msg.event_id.eql?(m.event_id)}
|
59
|
+
str[location] = new_msg if location
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def streams_of(event_id)
|
65
|
+
streams.select do |_, stream_events|
|
66
|
+
stream_events.any? { |event| event.event_id.eql?(event_id) }
|
67
|
+
end.map { |name, | Stream.new(name) }
|
68
|
+
end
|
69
|
+
|
51
70
|
private
|
71
|
+
def read_scope(spec)
|
72
|
+
events = spec.stream.global? ? global : stream_of(spec.stream.name)
|
73
|
+
events = events.select{|e| spec.with_ids.any?{|x| x.eql?(e.event_id)}} if spec.with_ids?
|
74
|
+
events = events.select{|e| spec.with_types.any?{|x| x.eql?(e.event_type)}} if spec.with_types?
|
75
|
+
events = events.reverse if spec.backward?
|
76
|
+
events = events.drop(index_of(events, spec.start) + 1) if spec.start
|
77
|
+
events = events.take(index_of(events, spec.stop)) if spec.stop
|
78
|
+
events = events[0...spec.limit] if spec.limit?
|
79
|
+
events
|
80
|
+
end
|
52
81
|
|
53
|
-
def
|
54
|
-
|
82
|
+
def read_event(event_id)
|
83
|
+
global.find {|e| event_id.eql?(e.event_id)} or raise EventNotFound.new(event_id)
|
55
84
|
end
|
56
85
|
|
57
|
-
def
|
58
|
-
|
86
|
+
def stream_of(name)
|
87
|
+
streams.fetch(name, Array.new)
|
59
88
|
end
|
60
89
|
|
61
90
|
def add_to_stream(events, expected_version, stream, include_global)
|
62
|
-
events
|
63
|
-
append_with_synchronize(events, expected_version, stream, include_global)
|
91
|
+
append_with_synchronize(Array(events), expected_version, stream, include_global)
|
64
92
|
end
|
65
93
|
|
66
94
|
def last_stream_version(stream)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class InstrumentedDispatcher
|
5
|
+
def initialize(dispatcher, instrumentation)
|
6
|
+
@dispatcher = dispatcher
|
7
|
+
@instrumentation = instrumentation
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(subscriber, event, serialized_event)
|
11
|
+
instrumentation.instrument("call.dispatcher.rails_event_store", event: event, subscriber: subscriber) do
|
12
|
+
dispatcher.call(subscriber, event, serialized_event)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def verify(subscriber)
|
17
|
+
dispatcher.verify(subscriber)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
attr_reader :instrumentation, :dispatcher
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class InstrumentedRepository
|
5
|
+
def initialize(repository, instrumentation)
|
6
|
+
@repository = repository
|
7
|
+
@instrumentation = instrumentation
|
8
|
+
end
|
9
|
+
|
10
|
+
def append_to_stream(events, stream, expected_version)
|
11
|
+
instrumentation.instrument("append_to_stream.repository.rails_event_store", events: events, stream: stream) do
|
12
|
+
repository.append_to_stream(events, stream, expected_version)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def link_to_stream(event_ids, stream, expected_version)
|
17
|
+
instrumentation.instrument("link_to_stream.repository.rails_event_store", event_ids: event_ids, stream: stream) do
|
18
|
+
repository.link_to_stream(event_ids, stream, expected_version)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_stream(stream)
|
23
|
+
instrumentation.instrument("delete_stream.repository.rails_event_store", stream: stream) do
|
24
|
+
repository.delete_stream(stream)
|
25
|
+
end
|
26
|
+
end
|
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
|
+
def read(specification)
|
37
|
+
instrumentation.instrument("read.repository.rails_event_store", specification: specification) do
|
38
|
+
repository.read(specification)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def count(specification)
|
43
|
+
instrumentation.instrument("count.repository.rails_event_store", specification: specification) do
|
44
|
+
repository.count(specification)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def update_messages(messages)
|
49
|
+
instrumentation.instrument("update_messages.repository.rails_event_store", messages: messages) do
|
50
|
+
repository.update_messages(messages)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def streams_of(event_id)
|
55
|
+
instrumentation.instrument("streams_of.repository.rails_event_store", event_id: event_id) do
|
56
|
+
repository.streams_of(event_id)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
attr_reader :repository, :instrumentation
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
class LinkByMetadata
|
5
|
+
|
6
|
+
def initialize(event_store:, key:, prefix: nil)
|
7
|
+
@event_store = event_store
|
8
|
+
@key = key
|
9
|
+
@prefix = prefix || ["$by", key, nil].join("_")
|
10
|
+
end
|
11
|
+
|
12
|
+
def call(event)
|
13
|
+
return unless event.metadata.has_key?(@key)
|
14
|
+
|
15
|
+
@event_store.link(
|
16
|
+
[event.event_id],
|
17
|
+
stream_name: "#{@prefix}#{event.metadata.fetch(@key)}"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
class LinkByCorrelationId < LinkByMetadata
|
24
|
+
def initialize(event_store:, prefix: nil)
|
25
|
+
super(
|
26
|
+
event_store: event_store,
|
27
|
+
prefix: prefix,
|
28
|
+
key: :correlation_id,
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class LinkByCausationId < LinkByMetadata
|
34
|
+
def initialize(event_store:, prefix: nil)
|
35
|
+
super(
|
36
|
+
event_store: event_store,
|
37
|
+
prefix: prefix,
|
38
|
+
key: :causation_id,
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class LinkByEventType
|
44
|
+
def initialize(event_store:, prefix: nil)
|
45
|
+
@event_store = event_store
|
46
|
+
@prefix = prefix || "$by_type_"
|
47
|
+
end
|
48
|
+
|
49
|
+
def call(event)
|
50
|
+
@event_store.link(
|
51
|
+
[event.event_id],
|
52
|
+
stream_name: "#{@prefix}#{event.event_type}"
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -1,35 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'yaml'
|
2
4
|
|
3
5
|
module RubyEventStore
|
4
6
|
module Mappers
|
5
|
-
class Default
|
7
|
+
class Default < PipelineMapper
|
6
8
|
def initialize(serializer: YAML, events_class_remapping: {})
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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: serializer.load(record.metadata),
|
25
|
-
data: serializer.load(record.data)
|
26
|
-
)
|
9
|
+
super(Pipeline.new(
|
10
|
+
transformations: [
|
11
|
+
Transformation::EventClassRemapper.new(events_class_remapping),
|
12
|
+
Transformation::SymbolizeMetadataKeys.new,
|
13
|
+
Transformation::Serialization.new(serializer: serializer),
|
14
|
+
]
|
15
|
+
))
|
27
16
|
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
attr_reader :serializer, :events_class_remapping
|
32
|
-
|
33
17
|
end
|
34
18
|
end
|
35
19
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Mappers
|
5
|
+
class EncryptionKey
|
6
|
+
def initialize(cipher:, key:)
|
7
|
+
@cipher = cipher
|
8
|
+
@key = key
|
9
|
+
end
|
10
|
+
|
11
|
+
def encrypt(message, iv)
|
12
|
+
crypto = prepare_encrypt(cipher)
|
13
|
+
crypto.iv = iv
|
14
|
+
crypto.key = key
|
15
|
+
|
16
|
+
if crypto.authenticated?
|
17
|
+
encrypt_authenticated(crypto, message)
|
18
|
+
else
|
19
|
+
crypto.update(message) + crypto.final
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def decrypt(message, iv)
|
24
|
+
crypto = prepare_decrypt(cipher)
|
25
|
+
crypto.iv = iv
|
26
|
+
crypto.key = key
|
27
|
+
ciphertext =
|
28
|
+
if crypto.authenticated?
|
29
|
+
ciphertext_from_authenticated(crypto, message)
|
30
|
+
else
|
31
|
+
message
|
32
|
+
end
|
33
|
+
(crypto.update(ciphertext) + crypto.final).force_encoding("UTF-8")
|
34
|
+
end
|
35
|
+
|
36
|
+
def random_iv
|
37
|
+
crypto = prepare_encrypt(cipher)
|
38
|
+
crypto.random_iv
|
39
|
+
end
|
40
|
+
|
41
|
+
attr_reader :cipher, :key
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def ciphertext_from_authenticated(crypto, message)
|
46
|
+
prepare_auth_data(crypto)
|
47
|
+
crypto.auth_tag = message[-16...message.length]
|
48
|
+
message[0...-16]
|
49
|
+
end
|
50
|
+
|
51
|
+
def encrypt_authenticated(crypto, message)
|
52
|
+
prepare_auth_data(crypto)
|
53
|
+
crypto.update(message) + crypto.final + crypto.auth_tag
|
54
|
+
end
|
55
|
+
|
56
|
+
def prepare_auth_data(crypto)
|
57
|
+
crypto.auth_data = ""
|
58
|
+
crypto
|
59
|
+
end
|
60
|
+
|
61
|
+
def prepare_encrypt(cipher)
|
62
|
+
crypto = OpenSSL::Cipher.new(cipher)
|
63
|
+
crypto.encrypt
|
64
|
+
crypto
|
65
|
+
end
|
66
|
+
|
67
|
+
def prepare_decrypt(cipher)
|
68
|
+
crypto = OpenSSL::Cipher.new(cipher)
|
69
|
+
crypto.decrypt
|
70
|
+
crypto
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Mappers
|
5
|
+
class EncryptionMapper < PipelineMapper
|
6
|
+
def initialize(key_repository, serializer: YAML, forgotten_data: ForgottenData.new)
|
7
|
+
super(Pipeline.new(
|
8
|
+
transformations: [
|
9
|
+
Transformation::Encryption.new(key_repository, serializer: serializer, forgotten_data: forgotten_data),
|
10
|
+
Transformation::Serialization.new(serializer: serializer),
|
11
|
+
]
|
12
|
+
))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubyEventStore
|
4
|
+
module Mappers
|
5
|
+
class ForgottenData
|
6
|
+
FORGOTTEN_DATA = 'FORGOTTEN_DATA'.freeze
|
7
|
+
|
8
|
+
def initialize(string = FORGOTTEN_DATA)
|
9
|
+
@string = string
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
@string
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
@string == other
|
18
|
+
end
|
19
|
+
alias_method :eql?, :==
|
20
|
+
|
21
|
+
def method_missing(*)
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_missing?(*)
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|