event_store_client 1.2.3 → 1.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/event_store_client/adapters/grpc/client.rb +1 -1
- data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +86 -37
- data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +18 -1
- data/lib/event_store_client/configuration.rb +15 -15
- data/lib/event_store_client/deserialized_event.rb +39 -18
- data/lib/event_store_client/mapper/default.rb +1 -0
- data/lib/event_store_client/mapper/encrypted.rb +2 -1
- data/lib/event_store_client/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f3c838968ea465d5b8c8facea48379e2cd8deab75622df62e124e68e52a123e
|
4
|
+
data.tar.gz: 7ab8a39443e9eaa15a4dccb95c32309fbfc1081de9bc854faef9b433a58f54a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80ccafc122496126f946ff52437a0fe1fbe856628e508f97c96cf8f802350b94399c557ce276042a4dd4ef0ee5f669952f131bddaeac08f9da73707382bb15bc
|
7
|
+
data.tar.gz: cd3c8af6fb3dfe71cc710ed2bfdc5cce89a954afc9bedbe2381438564a9584e777741bb43bd9c69e2fb3d2822cf1449048e1baba80617f695e93cdcaad10ebe3
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'grpc'
|
4
|
-
require 'event_store_client/adapters/grpc/generated/streams_pb
|
5
|
-
require 'event_store_client/adapters/grpc/generated/streams_services_pb
|
4
|
+
require 'event_store_client/adapters/grpc/generated/streams_pb'
|
5
|
+
require 'event_store_client/adapters/grpc/generated/streams_services_pb'
|
6
6
|
|
7
7
|
require 'event_store_client/adapters/grpc/commands/command'
|
8
8
|
|
@@ -14,45 +14,94 @@ module EventStoreClient
|
|
14
14
|
use_request EventStore::Client::Streams::AppendReq
|
15
15
|
use_service EventStore::Client::Streams::Streams::Stub
|
16
16
|
|
17
|
+
ALLOWED_EVENT_METADATA = %w[type content-type created_at].freeze
|
18
|
+
|
17
19
|
# @api private
|
18
|
-
|
19
|
-
|
20
|
+
def call(stream, events, options: {})
|
21
|
+
return unless events.any?
|
22
|
+
|
20
23
|
serialized_events = events.map { |event| config.mapper.serialize(event) }
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
custom_metadata['encryption'] = event_metadata['encryption'] unless event_metadata['encryption'].nil?
|
30
|
-
custom_metadata['transaction'] = event_metadata['transaction'] unless event_metadata['transaction'].nil?
|
31
|
-
event_metadata = event_metadata.select { |k| ['type', 'content-type', 'created_at'].include?(k) }
|
32
|
-
|
33
|
-
payload = [
|
34
|
-
request.new(
|
35
|
-
options: {
|
36
|
-
stream_identifier: {
|
37
|
-
streamName: stream
|
38
|
-
},
|
39
|
-
any: {}
|
40
|
-
}
|
41
|
-
),
|
42
|
-
request.new(
|
43
|
-
proposed_message: {
|
44
|
-
id: {
|
45
|
-
string: SecureRandom.uuid
|
46
|
-
},
|
47
|
-
data: event.data.b,
|
48
|
-
custom_metadata: JSON.generate(custom_metadata),
|
49
|
-
metadata: event_metadata
|
50
|
-
}
|
51
|
-
)
|
52
|
-
]
|
53
|
-
service.append(payload, metadata: metadata)
|
25
|
+
expected_version = options[:expected_version]
|
26
|
+
|
27
|
+
res = nil
|
28
|
+
serialized_events.each_with_index do |event, i|
|
29
|
+
expected_version += i if expected_version
|
30
|
+
res = append(stream, event, expected_version)
|
31
|
+
break if res.failure?
|
54
32
|
end
|
55
|
-
|
33
|
+
|
34
|
+
res.success? ? Success(events) : Failure(res.failure)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def append(stream, event, expected_version)
|
40
|
+
event_metadata = JSON.parse(event.metadata)
|
41
|
+
|
42
|
+
payload = append_request_payload(
|
43
|
+
options(stream, expected_version),
|
44
|
+
message(
|
45
|
+
data: event.data.b,
|
46
|
+
event_metadata: event_metadata.select { |k| ALLOWED_EVENT_METADATA.include?(k) },
|
47
|
+
custom_metadata: custom_metadata(event.type, event_metadata)
|
48
|
+
)
|
49
|
+
)
|
50
|
+
|
51
|
+
resp = service.append(payload, metadata: metadata)
|
52
|
+
|
53
|
+
validate_response(resp)
|
54
|
+
end
|
55
|
+
|
56
|
+
def custom_metadata(event_type, event_metadata)
|
57
|
+
{
|
58
|
+
type: event_type,
|
59
|
+
created_at: Time.current,
|
60
|
+
encryption: event_metadata['encryption'],
|
61
|
+
'content-type': event_metadata['content-type'],
|
62
|
+
transaction: event_metadata['transaction']
|
63
|
+
}.compact
|
64
|
+
end
|
65
|
+
|
66
|
+
def append_request_payload(options, message)
|
67
|
+
[
|
68
|
+
request.new(
|
69
|
+
options: options
|
70
|
+
),
|
71
|
+
request.new(
|
72
|
+
proposed_message: message
|
73
|
+
)
|
74
|
+
]
|
75
|
+
end
|
76
|
+
|
77
|
+
def options(stream, expected_version)
|
78
|
+
{
|
79
|
+
stream_identifier: {
|
80
|
+
streamName: stream
|
81
|
+
},
|
82
|
+
revision: expected_version,
|
83
|
+
any: (expected_version ? nil : {})
|
84
|
+
}.compact
|
85
|
+
end
|
86
|
+
|
87
|
+
def message(data:, event_metadata:, custom_metadata:)
|
88
|
+
{
|
89
|
+
id: {
|
90
|
+
string: SecureRandom.uuid
|
91
|
+
},
|
92
|
+
data: data,
|
93
|
+
custom_metadata: JSON.generate(custom_metadata),
|
94
|
+
metadata: event_metadata
|
95
|
+
}
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_response(resp)
|
99
|
+
return Success() if resp.success
|
100
|
+
|
101
|
+
Failure(
|
102
|
+
"current version: #{resp.wrong_expected_version.current_revision} | "\
|
103
|
+
"expected: #{resp.wrong_expected_version.expected_revision}"
|
104
|
+
)
|
56
105
|
end
|
57
106
|
end
|
58
107
|
end
|
@@ -44,7 +44,12 @@ module EventStoreClient
|
|
44
44
|
end
|
45
45
|
|
46
46
|
skip_decryption = options[:skip_decryption] || false
|
47
|
-
events =
|
47
|
+
events =
|
48
|
+
if options[:skip_deserialization]
|
49
|
+
read_stream_raw(opts)
|
50
|
+
else
|
51
|
+
read_stream(opts, skip_decryption)
|
52
|
+
end
|
48
53
|
Success(events)
|
49
54
|
rescue StreamNotFound
|
50
55
|
Failure(:not_found)
|
@@ -64,6 +69,18 @@ module EventStoreClient
|
|
64
69
|
raise GRPCUnavailableRetryFailed
|
65
70
|
end
|
66
71
|
|
72
|
+
def read_stream_raw(options)
|
73
|
+
retries ||= 0
|
74
|
+
service.read(request.new(options: options), metadata: metadata).map do |res|
|
75
|
+
raise StreamNotFound if res.stream_not_found
|
76
|
+
res.event.event
|
77
|
+
end
|
78
|
+
rescue ::GRPC::Unavailable
|
79
|
+
sleep config.grpc_unavailable_retry_sleep
|
80
|
+
retry if (retries += 1) <= config.grpc_unavailable_retry_count
|
81
|
+
raise GRPCUnavailableRetryFailed
|
82
|
+
end
|
83
|
+
|
67
84
|
def deserialize_event(entry, skip_decryption: false)
|
68
85
|
data = (entry.data.nil? || entry.data.empty?) ? '{}' : entry.data
|
69
86
|
|
@@ -8,35 +8,35 @@ module EventStoreClient
|
|
8
8
|
|
9
9
|
# Supported adapters: %i[api in_memory grpc]
|
10
10
|
#
|
11
|
-
setting :adapter, :grpc
|
12
|
-
setting :verify_ssl, true
|
11
|
+
setting :adapter, default: :grpc
|
12
|
+
setting :verify_ssl, default: true
|
13
13
|
|
14
|
-
setting :error_handler, ErrorHandler.new
|
15
|
-
setting :eventstore_url, 'http://localhost:2113' do |value|
|
14
|
+
setting :error_handler, default: ErrorHandler.new
|
15
|
+
setting :eventstore_url, default: 'http://localhost:2113' do |value|
|
16
16
|
value.is_a?(URI) ? value : URI(value)
|
17
17
|
end
|
18
18
|
|
19
|
-
setting :eventstore_user, 'admin'
|
20
|
-
setting :eventstore_password, 'changeit'
|
19
|
+
setting :eventstore_user, default: 'admin'
|
20
|
+
setting :eventstore_password, default: 'changeit'
|
21
21
|
|
22
|
-
setting :db_port, 2113
|
22
|
+
setting :db_port, default: 2113
|
23
23
|
|
24
|
-
setting :per_page, 20
|
25
|
-
setting :pid_path, 'tmp/poll.pid'
|
24
|
+
setting :per_page, default: 20
|
25
|
+
setting :pid_path, default: 'tmp/poll.pid'
|
26
26
|
|
27
|
-
setting :service_name, 'default'
|
27
|
+
setting :service_name, default: 'default'
|
28
28
|
|
29
|
-
setting :mapper, Mapper::Default.new
|
29
|
+
setting :mapper, default: Mapper::Default.new
|
30
30
|
|
31
31
|
setting :subscriptions_repo
|
32
32
|
|
33
33
|
setting :logger
|
34
34
|
|
35
|
-
setting :socket_error_retry_sleep, 0.5
|
36
|
-
setting :socket_error_retry_count, 3
|
35
|
+
setting :socket_error_retry_sleep, default: 0.5
|
36
|
+
setting :socket_error_retry_count, default: 3
|
37
37
|
|
38
|
-
setting :grpc_unavailable_retry_sleep, 0.5
|
39
|
-
setting :grpc_unavailable_retry_count, 3
|
38
|
+
setting :grpc_unavailable_retry_sleep, default: 0.5
|
39
|
+
setting :grpc_unavailable_retry_count, default: 3
|
40
40
|
|
41
41
|
def self.configure
|
42
42
|
yield(config) if block_given?
|
@@ -4,34 +4,55 @@ require 'dry/schema'
|
|
4
4
|
|
5
5
|
module EventStoreClient
|
6
6
|
class DeserializedEvent
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
attr_reader :data
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
InvalidDataError = Class.new(StandardError)
|
8
|
+
private_constant :InvalidDataError
|
9
|
+
|
10
|
+
attr_reader :id, :type, :title, :data, :metadata
|
11
|
+
|
12
|
+
# @args [Hash] opts
|
13
|
+
# @option opts [Boolean] :skip_validation
|
14
|
+
# @option opts [Hash] :data
|
15
|
+
# @option opts [Hash] :metadata
|
16
|
+
# @option opts [String] :type
|
17
|
+
# @option opts [String] :title
|
18
|
+
# @option opts [UUID] :id
|
19
|
+
#
|
20
|
+
def initialize(args = {})
|
21
|
+
validate(args[:data]) unless args[:skip_validation]
|
17
22
|
|
18
|
-
def initialize(**args)
|
19
|
-
validation = schema.call(args[:data] || {})
|
20
23
|
@data = args.fetch(:data) { {} }
|
21
|
-
@metadata =
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
@metadata =
|
25
|
+
args.fetch(:metadata) { {} }
|
26
|
+
.merge(
|
27
|
+
'type' => self.class.name,
|
28
|
+
'content-type' => payload_content_type
|
29
|
+
)
|
30
|
+
|
26
31
|
@type = args[:type] || self.class.name
|
27
32
|
@title = args[:title]
|
28
33
|
@id = args[:id]
|
29
34
|
end
|
30
35
|
|
31
|
-
|
36
|
+
# event schema
|
37
|
+
def schema; end
|
38
|
+
|
39
|
+
# content type of the event data
|
40
|
+
def payload_content_type
|
32
41
|
return 'application/json' if EventStoreClient.config.adapter == :grpc
|
33
42
|
|
34
43
|
'application/vnd.eventstore.events+json'
|
35
44
|
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def validate(data)
|
49
|
+
return unless schema
|
50
|
+
|
51
|
+
validation = schema.call(data || {})
|
52
|
+
|
53
|
+
return unless validation.errors.any?
|
54
|
+
|
55
|
+
raise(InvalidDataError.new(message: "#{schema.class.name} #{validation.errors.to_h}"))
|
56
|
+
end
|
36
57
|
end
|
37
58
|
end
|
@@ -51,7 +51,7 @@ module EventStoreClient
|
|
51
51
|
|
52
52
|
def deserialize(event, skip_decryption: false)
|
53
53
|
metadata = serializer.deserialize(event.metadata)
|
54
|
-
encryption_schema =
|
54
|
+
encryption_schema = metadata['encryption']
|
55
55
|
|
56
56
|
decrypted_data =
|
57
57
|
if skip_decryption
|
@@ -72,6 +72,7 @@ module EventStoreClient
|
|
72
72
|
end
|
73
73
|
|
74
74
|
event_class.new(
|
75
|
+
skip_validation: true,
|
75
76
|
id: event.id,
|
76
77
|
type: event.type,
|
77
78
|
title: event.title,
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: event_store_client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2
|
4
|
+
version: 1.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sebastian Wilgosz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-12-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dry-configurable
|