eventsimple 1.8.1 → 2.0.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/CHANGELOG.md +12 -0
- data/Gemfile.lock +138 -130
- data/README.md +37 -526
- data/Rakefile +0 -3
- data/app/views/eventsimple/entities/show.html.erb +2 -2
- data/eventsimple.gemspec +2 -2
- data/lib/eventsimple/data_type.rb +64 -11
- data/lib/eventsimple/event.rb +16 -7
- data/lib/eventsimple/message.rb +43 -0
- data/lib/eventsimple/metadata.rb +2 -2
- data/lib/eventsimple/types/encrypted_type.rb +42 -0
- data/lib/eventsimple/types.rb +51 -0
- data/lib/eventsimple/version.rb +1 -1
- data/lib/eventsimple.rb +1 -3
- metadata +11 -10
- data/lib/dry_types.rb +0 -5
|
@@ -17,22 +17,22 @@ module Eventsimple
|
|
|
17
17
|
case value
|
|
18
18
|
when String
|
|
19
19
|
decoded = ActiveSupport::JSON.decode(value)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
decoded
|
|
20
|
+
prepare_data(decoded)
|
|
23
21
|
when Hash
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
value
|
|
27
|
-
when event_klass::Message
|
|
22
|
+
prepare_data(value)
|
|
23
|
+
when message_class
|
|
28
24
|
value
|
|
29
25
|
end
|
|
30
26
|
end
|
|
31
27
|
|
|
32
28
|
def serialize(value)
|
|
33
29
|
case value
|
|
34
|
-
when Hash
|
|
35
|
-
|
|
30
|
+
when Hash
|
|
31
|
+
encrypted_data = encrypt_message_data(value)
|
|
32
|
+
ActiveSupport::JSON.encode(encrypted_data)
|
|
33
|
+
when event_klass::Message
|
|
34
|
+
encrypted_data = encrypt_message_data(value.attributes)
|
|
35
|
+
ActiveSupport::JSON.encode(encrypted_data)
|
|
36
36
|
else
|
|
37
37
|
super
|
|
38
38
|
end
|
|
@@ -40,9 +40,62 @@ module Eventsimple
|
|
|
40
40
|
|
|
41
41
|
def deserialize(value)
|
|
42
42
|
decoded = ActiveSupport::JSON.decode(value)
|
|
43
|
-
|
|
43
|
+
decrypted_data = decrypt_message_data(decoded)
|
|
44
|
+
message_class ? message_class.new(decrypted_data) : decrypted_data
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def changed_in_place?(raw_old_value, new_value)
|
|
48
|
+
old_value = deserialize(raw_old_value)
|
|
49
|
+
old_value != new_value
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def mutable?
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def message_class
|
|
59
|
+
event_klass.const_defined?(:Message) ? event_klass::Message : nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def prepare_data(data_hash)
|
|
63
|
+
decrypted_data = decrypt_message_data(data_hash)
|
|
64
|
+
message_class ? message_class.new(decrypted_data) : decrypted_data
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def encrypt_message_data(data_hash)
|
|
68
|
+
transform_message_data(data_hash, :encrypt)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def decrypt_message_data(data_hash)
|
|
72
|
+
transform_message_data(data_hash, :decrypt)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def transform_message_data(data_hash, operation)
|
|
76
|
+
return data_hash unless message_class
|
|
77
|
+
|
|
78
|
+
schema = message_class.schema
|
|
79
|
+
result = data_hash.transform_keys(&:to_sym).dup
|
|
80
|
+
|
|
81
|
+
schema.keys.each do |key| # rubocop:disable Style/HashEachMethods
|
|
82
|
+
attr_name = key.name
|
|
83
|
+
type = find_type_with_operation(key.type, operation)
|
|
84
|
+
value = result[attr_name]
|
|
85
|
+
|
|
86
|
+
next if value.nil? || type.nil?
|
|
87
|
+
|
|
88
|
+
result[attr_name] = type.public_send(operation, value.to_s)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
result
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def find_type_with_operation(type, operation)
|
|
95
|
+
return type if type.respond_to?(operation)
|
|
44
96
|
|
|
45
|
-
|
|
97
|
+
# For Sum types (optional), check if the right side has the operation
|
|
98
|
+
type.respond_to?(:right) && type.right.respond_to?(operation) ? type.right : nil
|
|
46
99
|
end
|
|
47
100
|
end
|
|
48
101
|
end
|
data/lib/eventsimple/event.rb
CHANGED
|
@@ -51,10 +51,13 @@ module Eventsimple
|
|
|
51
51
|
|
|
52
52
|
default_scope { order(:created_at) }
|
|
53
53
|
|
|
54
|
+
after_initialize :readonly!, if: :persisted?
|
|
55
|
+
|
|
54
56
|
before_validation :extend_validation
|
|
55
|
-
after_validation :perform_transition_checks
|
|
57
|
+
after_validation :perform_transition_checks, on: :create
|
|
56
58
|
before_create :apply_and_persist
|
|
57
59
|
after_create :dispatch
|
|
60
|
+
after_create :readonly!
|
|
58
61
|
|
|
59
62
|
include InstanceMethods
|
|
60
63
|
extend ClassMethods
|
|
@@ -73,7 +76,6 @@ module Eventsimple
|
|
|
73
76
|
false
|
|
74
77
|
end
|
|
75
78
|
|
|
76
|
-
# Apply the event to the aggregate passed in. The default behaviour is a no-op
|
|
77
79
|
def apply(aggregate); end
|
|
78
80
|
|
|
79
81
|
def can_apply?(_aggregate)
|
|
@@ -101,12 +103,10 @@ module Eventsimple
|
|
|
101
103
|
self.aggregate = aggregate.extend(validate_form) if validate_form
|
|
102
104
|
end
|
|
103
105
|
|
|
104
|
-
# Apply the transformation to the aggregate and save it.
|
|
105
106
|
def apply_and_persist
|
|
106
107
|
apply_timestamps(aggregate)
|
|
107
108
|
apply(aggregate)
|
|
108
109
|
|
|
109
|
-
# Persist!
|
|
110
110
|
aggregate.enable_writes!
|
|
111
111
|
aggregate.save!
|
|
112
112
|
aggregate.readonly!
|
|
@@ -125,6 +125,16 @@ module Eventsimple
|
|
|
125
125
|
def aggregate=(aggregate)
|
|
126
126
|
public_send(:"#{_aggregate_klass.model_name.element}=", aggregate)
|
|
127
127
|
end
|
|
128
|
+
|
|
129
|
+
def enable_writes!(&block)
|
|
130
|
+
was_readonly = @readonly
|
|
131
|
+
@readonly = false
|
|
132
|
+
|
|
133
|
+
return unless block
|
|
134
|
+
|
|
135
|
+
yield self
|
|
136
|
+
@readonly = was_readonly
|
|
137
|
+
end
|
|
128
138
|
end
|
|
129
139
|
|
|
130
140
|
module ClassMethods
|
|
@@ -169,8 +179,7 @@ module Eventsimple
|
|
|
169
179
|
|
|
170
180
|
# We want to automatically retry writes on concurrency failures. However events with sync
|
|
171
181
|
# reactors may have multiple nested events that are written within the same transaction.
|
|
172
|
-
# We can only catch and retry writes when
|
|
173
|
-
# transaction.
|
|
182
|
+
# We can only catch and retry writes when we are in the outermost transaction.
|
|
174
183
|
def create(*args, &block)
|
|
175
184
|
with_locks do
|
|
176
185
|
with_retries(args) { super }
|
|
@@ -185,7 +194,7 @@ module Eventsimple
|
|
|
185
194
|
|
|
186
195
|
def with_locks(&)
|
|
187
196
|
if _outbox_enabled
|
|
188
|
-
base_class.with_advisory_lock(base_class.name, { transaction:
|
|
197
|
+
base_class.with_advisory_lock(base_class.name, { transaction: existing_transaction_in_progress? }, &)
|
|
189
198
|
else
|
|
190
199
|
yield
|
|
191
200
|
end
|
data/lib/eventsimple/message.rb
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'dry-struct'
|
|
4
|
+
|
|
3
5
|
module Eventsimple
|
|
4
6
|
class Message < Dry::Struct
|
|
5
7
|
transform_keys(&:to_sym)
|
|
@@ -19,5 +21,46 @@ module Eventsimple
|
|
|
19
21
|
def inspect
|
|
20
22
|
as_json
|
|
21
23
|
end
|
|
24
|
+
|
|
25
|
+
def self.attribute(name, type = Dry::Types::Any, **options)
|
|
26
|
+
validate_type(type)
|
|
27
|
+
super
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.attribute?(name, type = Dry::Types::Any, **options)
|
|
31
|
+
validate_type(type)
|
|
32
|
+
super
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class << self
|
|
36
|
+
def validate_type(type)
|
|
37
|
+
return if valid_eventsimple_type?(type)
|
|
38
|
+
|
|
39
|
+
source_location = begin
|
|
40
|
+
source = const_source_location(name)&.first if respond_to?(:const_source_location) && name
|
|
41
|
+
source || caller_locations.find { |loc| !loc.path.include?('eventsimple') }&.path
|
|
42
|
+
rescue StandardError
|
|
43
|
+
'unknown'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
message = [
|
|
47
|
+
"Only Eventsimple::Types are allowed in Message attributes. \n",
|
|
48
|
+
"File: #{source_location} \n",
|
|
49
|
+
"Received: #{type.inspect} \n",
|
|
50
|
+
"Use Eventsimple::Types::String, Eventsimple::Types::Integer, etc. \n",
|
|
51
|
+
].join(' ')
|
|
52
|
+
|
|
53
|
+
deprecator = ActiveSupport::Deprecation.new
|
|
54
|
+
deprecator.behavior = Rails.application.config.active_support.deprecation
|
|
55
|
+
deprecator.warn(message)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def valid_eventsimple_type?(type)
|
|
59
|
+
return true if type.respond_to?(:meta) && type.meta[:eventsimple] == true
|
|
60
|
+
return true if type.is_a?(Class) && type < Eventsimple::Message
|
|
61
|
+
|
|
62
|
+
false
|
|
63
|
+
end
|
|
64
|
+
end
|
|
22
65
|
end
|
|
23
66
|
end
|
data/lib/eventsimple/metadata.rb
CHANGED
|
@@ -5,7 +5,7 @@ module Eventsimple
|
|
|
5
5
|
class Metadata < Eventsimple::Message
|
|
6
6
|
schema schema.strict
|
|
7
7
|
|
|
8
|
-
attribute? :actor_id,
|
|
9
|
-
attribute? :reason,
|
|
8
|
+
attribute? :actor_id, Eventsimple::Types::String.optional
|
|
9
|
+
attribute? :reason, Eventsimple::Types::String.optional
|
|
10
10
|
end
|
|
11
11
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# require 'active_support/core_ext/object/blank'
|
|
4
|
+
|
|
5
|
+
module Eventsimple
|
|
6
|
+
module Types
|
|
7
|
+
# Encrypted decorator type that wraps any type and adds encryption methods
|
|
8
|
+
class EncryptedType
|
|
9
|
+
include Dry::Types::Type
|
|
10
|
+
include Dry::Types::Decorator
|
|
11
|
+
include Dry::Types::Builder
|
|
12
|
+
|
|
13
|
+
def initialize(type, **options)
|
|
14
|
+
super
|
|
15
|
+
@type = type
|
|
16
|
+
freeze
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def meta(data = nil)
|
|
20
|
+
return { eventsimple: true } if data.nil?
|
|
21
|
+
|
|
22
|
+
self.class.new(@type.meta(data))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def encrypt(value)
|
|
26
|
+
return value if value.blank?
|
|
27
|
+
|
|
28
|
+
return value if ActiveRecord::Encryption::Encryptor.new.encrypted?(value)
|
|
29
|
+
|
|
30
|
+
ActiveRecord::Encryption::Encryptor.new.encrypt(value)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def decrypt(value)
|
|
34
|
+
return value if value.blank?
|
|
35
|
+
|
|
36
|
+
ActiveRecord::Encryption::Encryptor.new.decrypt(value)
|
|
37
|
+
rescue StandardError
|
|
38
|
+
value
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'eventsimple/types/encrypted_type'
|
|
4
|
+
|
|
5
|
+
module Eventsimple
|
|
6
|
+
module DryTypes
|
|
7
|
+
include Dry.Types(:strict, :json)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module Types
|
|
11
|
+
EVENTSIMPLE_META = { eventsimple: true }.freeze
|
|
12
|
+
|
|
13
|
+
# Extension that preserves eventsimple meta through all type transformations
|
|
14
|
+
module MetaPreservingBuilder
|
|
15
|
+
CHAINABLE_METHODS = %i[optional default enum constrained of].freeze
|
|
16
|
+
|
|
17
|
+
CHAINABLE_METHODS.each do |method_name|
|
|
18
|
+
define_method(method_name) do |*args, &block|
|
|
19
|
+
result = super(*args, &block)
|
|
20
|
+
# Re-apply eventsimple meta if the original type had it
|
|
21
|
+
if respond_to?(:meta) && meta[:eventsimple]
|
|
22
|
+
result.meta(EVENTSIMPLE_META)
|
|
23
|
+
else
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def encrypted
|
|
30
|
+
is_string_type = respond_to?(:primitive) && primitive == ::String
|
|
31
|
+
raise ArgumentError, "encrypted is only supported for String types" unless is_string_type
|
|
32
|
+
|
|
33
|
+
EncryptedType.new(self)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Include the meta-preserving builder in Dry::Types
|
|
38
|
+
Dry::Types::Builder.prepend(MetaPreservingBuilder)
|
|
39
|
+
|
|
40
|
+
Bool = DryTypes::Strict::Bool.meta(EVENTSIMPLE_META)
|
|
41
|
+
Array = DryTypes::Strict::Array.meta(EVENTSIMPLE_META)
|
|
42
|
+
Hash = DryTypes::Strict::Hash.meta(EVENTSIMPLE_META)
|
|
43
|
+
Integer = DryTypes::Strict::Integer.meta(EVENTSIMPLE_META)
|
|
44
|
+
String = DryTypes::Strict::String.meta(EVENTSIMPLE_META)
|
|
45
|
+
|
|
46
|
+
Decimal = DryTypes::JSON::Decimal.meta(EVENTSIMPLE_META)
|
|
47
|
+
Date = DryTypes::JSON::Date.meta(EVENTSIMPLE_META)
|
|
48
|
+
DateTime = DryTypes::JSON::DateTime.meta(EVENTSIMPLE_META)
|
|
49
|
+
Time = DryTypes::JSON::Time.meta(EVENTSIMPLE_META)
|
|
50
|
+
end
|
|
51
|
+
end
|
data/lib/eventsimple/version.rb
CHANGED
data/lib/eventsimple.rb
CHANGED
|
@@ -7,13 +7,11 @@ require 'active_model'
|
|
|
7
7
|
require 'active_job'
|
|
8
8
|
require 'active_support'
|
|
9
9
|
require 'dry-types'
|
|
10
|
-
require 'dry-struct'
|
|
11
10
|
require 'retriable'
|
|
12
11
|
|
|
13
|
-
require 'dry_types'
|
|
14
|
-
|
|
15
12
|
require 'eventsimple/active_job/arguments'
|
|
16
13
|
require 'eventsimple/configuration'
|
|
14
|
+
require 'eventsimple/types'
|
|
17
15
|
require 'eventsimple/message'
|
|
18
16
|
require 'eventsimple/data_type'
|
|
19
17
|
require 'eventsimple/metadata_type'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: eventsimple
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 2.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Zulfiqar Ali
|
|
@@ -27,30 +27,30 @@ dependencies:
|
|
|
27
27
|
name: dry-struct
|
|
28
28
|
requirement: !ruby/object:Gem::Requirement
|
|
29
29
|
requirements:
|
|
30
|
-
- - "
|
|
30
|
+
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
32
|
+
version: 1.8.0
|
|
33
33
|
type: :runtime
|
|
34
34
|
prerelease: false
|
|
35
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
36
|
requirements:
|
|
37
|
-
- - "
|
|
37
|
+
- - ">="
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
39
|
+
version: 1.8.0
|
|
40
40
|
- !ruby/object:Gem::Dependency
|
|
41
41
|
name: dry-types
|
|
42
42
|
requirement: !ruby/object:Gem::Requirement
|
|
43
43
|
requirements:
|
|
44
|
-
- - "
|
|
44
|
+
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
46
|
+
version: 1.7.0
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
|
-
- - "
|
|
51
|
+
- - ">="
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
53
|
+
version: 1.7.0
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: pg
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -340,7 +340,6 @@ files:
|
|
|
340
340
|
- catalog-info.yaml
|
|
341
341
|
- config/routes.rb
|
|
342
342
|
- eventsimple.gemspec
|
|
343
|
-
- lib/dry_types.rb
|
|
344
343
|
- lib/eventsimple.rb
|
|
345
344
|
- lib/eventsimple/active_job/arguments.rb
|
|
346
345
|
- lib/eventsimple/configuration.rb
|
|
@@ -364,6 +363,8 @@ files:
|
|
|
364
363
|
- lib/eventsimple/reactor.rb
|
|
365
364
|
- lib/eventsimple/reactor_worker.rb
|
|
366
365
|
- lib/eventsimple/support/spec_helpers.rb
|
|
366
|
+
- lib/eventsimple/types.rb
|
|
367
|
+
- lib/eventsimple/types/encrypted_type.rb
|
|
367
368
|
- lib/eventsimple/version.rb
|
|
368
369
|
- log/development.log
|
|
369
370
|
- sonar-project.properties
|