event_store_client 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 700f420a0fa9e3fb0352784f3bc0431cac18216130ba397897dfb77c98a4f7c6
4
- data.tar.gz: e408f3a666dd7f1e95634591923bf8df51ce30c6b521cd9042072f551e5ff645
3
+ metadata.gz: 308f1684d09b0b00ca5639074c5f1b79d4e689d5d42d44e4a33412db43a2b3c4
4
+ data.tar.gz: 67c2d8dd1bbf6070f9c6938c88577a5aa57bda35399586e75af30476c50488e3
5
5
  SHA512:
6
- metadata.gz: 2232248c19c04924effd9bab97271f5a0bf3e7b4cf4e56e5453fc32a27c56d587777239b48d0aafc026c265b1086691aa5e8c04e7863209d9bb1e07c046946e6
7
- data.tar.gz: 2db191ee47dae6f22b5858ab85bde4d3129af6220f534d9b11e502150058c7211285afa2562aae70ae08877e21c8d2205c8d1400ec8365e10567e7c5295b7afc
6
+ metadata.gz: 87eea741c382403a0ccd8904a689dfae713180c8a03198c4bbedf82dfc22d4a3e463cfd518d0f9fca678fffb54b521fa100d928b515a048ec13daffb1d9077dd
7
+ data.tar.gz: b380b64c37b959821178d7cdfcb887f6ed2f6fdb7b72f5a36b39ba81ac14660c0453c9f4fd03b422e284a36f1edd346ae9b4375c81531ac6e3284aee072dfd74
@@ -11,7 +11,7 @@ module EventStoreClient
11
11
  #
12
12
  def append_to_stream(stream_name, events, options: {})
13
13
  Commands::Streams::Append.new.call(
14
- stream_name, events, options: {}
14
+ stream_name, events, options: options
15
15
  )
16
16
  end
17
17
 
@@ -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.rb'
5
- require 'event_store_client/adapters/grpc/generated/streams_services_pb.rb'
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
- # TODO: Add support to verify the expected version
19
- def call(stream, events, options: {}) # rubocop:disable Lint/UnusedMethodArgument,Metrics/LineLength
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
- serialized_events.each do |event|
23
- event_metadata = JSON.parse(event.metadata)
24
- custom_metadata = {
25
- "type": event.type,
26
- "created_at": Time.now,
27
- 'content-type': event_metadata['content-type']
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
- Success(events)
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
@@ -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,36 +4,53 @@ require 'dry/schema'
4
4
 
5
5
  module EventStoreClient
6
6
  class DeserializedEvent
7
- attr_reader :id
8
- attr_reader :type
9
- attr_reader :title
10
- attr_reader :data
11
- attr_reader :metadata
12
-
13
- def schema
14
- Dry::Schema.Params do
15
- end
16
- end
7
+ InvalidDataError = Class.new(StandardError)
8
+ private_constant :InvalidDataError
9
+
10
+ attr_reader :id, :type, :title, :data, :metadata
17
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
+ #
18
20
  def initialize(args = {})
19
- validation = schema.call(args[:data] || {}) unless args[:skip_validation]
21
+ validate(args[:data]) unless args[:skip_validation]
22
+
20
23
  @data = args.fetch(:data) { {} }
21
- @metadata = args.fetch(:metadata) { {} }.merge(
22
- 'type' => self.class.name,
23
- 'content-type' => content_type
24
- )
25
- if !args[:skip_validation] && validation.errors.any?
26
- @metadata.merge!('validation-errors' => validation.errors.to_h)
27
- end
24
+ @metadata =
25
+ args.fetch(:metadata) { {} }
26
+ .merge(
27
+ 'type' => self.class.name,
28
+ 'content-type' => payload_content_type
29
+ )
30
+
28
31
  @type = args[:type] || self.class.name
29
32
  @title = args[:title]
30
33
  @id = args[:id]
31
34
  end
32
35
 
33
- def content_type
36
+ # event schema
37
+ def schema; end
38
+
39
+ # content type of the event data
40
+ def payload_content_type
34
41
  return 'application/json' if EventStoreClient.config.adapter == :grpc
35
42
 
36
43
  'application/vnd.eventstore.events+json'
37
44
  end
45
+
46
+ private
47
+
48
+ def validate(data)
49
+ validation = schema.call(data || {})
50
+
51
+ retun unless validation.errors.any?
52
+
53
+ raise(InvalidDataError.new(message: "#{schema.class.name} #{validation.errors.to_h}"))
54
+ end
38
55
  end
39
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '1.3.0'
4
+ VERSION = '1.4.0'
5
5
  end
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.3.0
4
+ version: 1.4.0
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-08-09 00:00:00.000000000 Z
11
+ date: 2021-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-configurable