event_store_client 1.4.9 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -145
  3. data/docs/appending_events.md +155 -0
  4. data/docs/catch_up_subscriptions.md +253 -0
  5. data/docs/configuration.md +83 -0
  6. data/docs/deleting_streams.md +25 -0
  7. data/docs/encrypting_events.md +84 -0
  8. data/docs/linking_events.md +149 -0
  9. data/docs/reading_events.md +200 -0
  10. data/lib/event_store_client/adapters/grpc/client.rb +244 -105
  11. data/lib/event_store_client/adapters/grpc/cluster/gossip_discover.rb +131 -0
  12. data/lib/event_store_client/adapters/grpc/cluster/insecure_connection.rb +21 -0
  13. data/lib/event_store_client/adapters/grpc/cluster/member.rb +18 -0
  14. data/lib/event_store_client/adapters/grpc/cluster/queryless_discover.rb +25 -0
  15. data/lib/event_store_client/adapters/grpc/cluster/secure_connection.rb +71 -0
  16. data/lib/event_store_client/adapters/grpc/command_registrar.rb +7 -7
  17. data/lib/event_store_client/adapters/grpc/commands/command.rb +63 -25
  18. data/lib/event_store_client/adapters/grpc/commands/gossip/cluster_info.rb +24 -0
  19. data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +43 -68
  20. data/lib/event_store_client/adapters/grpc/commands/streams/append_multiple.rb +44 -0
  21. data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +21 -17
  22. data/lib/event_store_client/adapters/grpc/commands/streams/hard_delete.rb +39 -0
  23. data/lib/event_store_client/adapters/grpc/commands/streams/link_to.rb +7 -52
  24. data/lib/event_store_client/adapters/grpc/commands/streams/link_to_multiple.rb +44 -0
  25. data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +20 -85
  26. data/lib/event_store_client/adapters/grpc/commands/streams/read_paginated.rb +174 -0
  27. data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +31 -106
  28. data/lib/event_store_client/adapters/grpc/connection.rb +56 -36
  29. data/lib/event_store_client/adapters/grpc/discover.rb +75 -0
  30. data/lib/event_store_client/adapters/grpc/generated/cluster_pb.rb +106 -18
  31. data/lib/event_store_client/adapters/grpc/generated/cluster_services_pb.rb +12 -12
  32. data/lib/event_store_client/adapters/grpc/generated/code_pb.rb +34 -0
  33. data/lib/event_store_client/adapters/grpc/generated/gossip_pb.rb +3 -2
  34. data/lib/event_store_client/adapters/grpc/generated/gossip_services_pb.rb +3 -3
  35. data/lib/event_store_client/adapters/grpc/generated/monitoring_pb.rb +25 -0
  36. data/lib/event_store_client/adapters/grpc/generated/monitoring_services_pb.rb +26 -0
  37. data/lib/event_store_client/adapters/grpc/generated/operations_pb.rb +2 -1
  38. data/lib/event_store_client/adapters/grpc/generated/operations_services_pb.rb +8 -7
  39. data/lib/event_store_client/adapters/grpc/generated/persistent_pb.rb +199 -38
  40. data/lib/event_store_client/adapters/grpc/generated/persistent_services_pb.rb +7 -3
  41. data/lib/event_store_client/adapters/grpc/generated/projections_pb.rb +9 -26
  42. data/lib/event_store_client/adapters/grpc/generated/projections_services_pb.rb +4 -3
  43. data/lib/event_store_client/adapters/grpc/generated/serverfeatures_pb.rb +29 -0
  44. data/lib/event_store_client/adapters/grpc/generated/serverfeatures_services_pb.rb +26 -0
  45. data/lib/event_store_client/adapters/grpc/generated/shared_pb.rb +54 -12
  46. data/lib/event_store_client/adapters/grpc/generated/status_pb.rb +23 -0
  47. data/lib/event_store_client/adapters/grpc/generated/streams_pb.rb +104 -64
  48. data/lib/event_store_client/adapters/grpc/generated/streams_services_pb.rb +3 -2
  49. data/lib/event_store_client/adapters/grpc/generated/users_services_pb.rb +2 -2
  50. data/lib/event_store_client/adapters/grpc/options/streams/read_options.rb +78 -0
  51. data/lib/event_store_client/adapters/grpc/options/streams/write_options.rb +39 -0
  52. data/lib/event_store_client/adapters/grpc/shared/event_deserializer.rb +52 -0
  53. data/lib/event_store_client/adapters/grpc/shared/options/filter_options.rb +76 -0
  54. data/lib/event_store_client/adapters/grpc/shared/options/stream_options.rb +91 -0
  55. data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +28 -0
  56. data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +33 -0
  57. data/lib/event_store_client/adapters/grpc.rb +28 -12
  58. data/lib/event_store_client/configuration.rb +39 -54
  59. data/lib/event_store_client/connection/url.rb +57 -0
  60. data/lib/event_store_client/connection/url_parser.rb +144 -0
  61. data/lib/event_store_client/data_decryptor.rb +2 -9
  62. data/lib/event_store_client/deserialized_event.rb +35 -10
  63. data/lib/event_store_client/encryption_metadata.rb +0 -1
  64. data/lib/event_store_client/event.rb +4 -2
  65. data/lib/event_store_client/extensions/options_extension.rb +87 -0
  66. data/lib/event_store_client/mapper/default.rb +12 -9
  67. data/lib/event_store_client/mapper/encrypted.rb +18 -17
  68. data/lib/event_store_client/types.rb +1 -1
  69. data/lib/event_store_client/utils.rb +30 -0
  70. data/lib/event_store_client/version.rb +1 -1
  71. data/lib/event_store_client.rb +8 -7
  72. metadata +75 -84
  73. data/lib/event_store_client/adapters/grpc/Protos/cluster.proto +0 -149
  74. data/lib/event_store_client/adapters/grpc/Protos/gossip.proto +0 -44
  75. data/lib/event_store_client/adapters/grpc/Protos/operations.proto +0 -45
  76. data/lib/event_store_client/adapters/grpc/Protos/persistent.proto +0 -180
  77. data/lib/event_store_client/adapters/grpc/Protos/projections.proto +0 -174
  78. data/lib/event_store_client/adapters/grpc/Protos/shared.proto +0 -22
  79. data/lib/event_store_client/adapters/grpc/Protos/streams.proto +0 -242
  80. data/lib/event_store_client/adapters/grpc/Protos/users.proto +0 -119
  81. data/lib/event_store_client/adapters/grpc/README.md +0 -16
  82. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/create.rb +0 -46
  83. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/delete.rb +0 -34
  84. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/read.rb +0 -77
  85. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/settings_schema.rb +0 -38
  86. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/update.rb +0 -48
  87. data/lib/event_store_client/adapters/grpc/commands/projections/create.rb +0 -48
  88. data/lib/event_store_client/adapters/grpc/commands/projections/delete.rb +0 -34
  89. data/lib/event_store_client/adapters/grpc/commands/projections/update.rb +0 -44
  90. data/lib/event_store_client/adapters/grpc/commands/streams/read_all.rb +0 -43
  91. data/lib/event_store_client/adapters/grpc/commands/streams/tombstone.rb +0 -35
  92. data/lib/event_store_client/adapters/http/README.md +0 -16
  93. data/lib/event_store_client/adapters/http/client.rb +0 -161
  94. data/lib/event_store_client/adapters/http/commands/command.rb +0 -27
  95. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/ack.rb +0 -15
  96. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/create.rb +0 -35
  97. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/read.rb +0 -60
  98. data/lib/event_store_client/adapters/http/commands/projections/create.rb +0 -33
  99. data/lib/event_store_client/adapters/http/commands/projections/update.rb +0 -31
  100. data/lib/event_store_client/adapters/http/commands/streams/append.rb +0 -49
  101. data/lib/event_store_client/adapters/http/commands/streams/delete.rb +0 -16
  102. data/lib/event_store_client/adapters/http/commands/streams/link_to.rb +0 -49
  103. data/lib/event_store_client/adapters/http/commands/streams/read.rb +0 -52
  104. data/lib/event_store_client/adapters/http/commands/streams/tombstone.rb +0 -17
  105. data/lib/event_store_client/adapters/http/connection.rb +0 -46
  106. data/lib/event_store_client/adapters/http/request_method.rb +0 -28
  107. data/lib/event_store_client/adapters/http.rb +0 -17
  108. data/lib/event_store_client/adapters/in_memory.rb +0 -144
  109. data/lib/event_store_client/broker.rb +0 -40
  110. data/lib/event_store_client/catch_up_subscription.rb +0 -42
  111. data/lib/event_store_client/catch_up_subscriptions.rb +0 -92
  112. data/lib/event_store_client/client.rb +0 -73
  113. data/lib/event_store_client/error_handler.rb +0 -10
  114. data/lib/event_store_client/subscription.rb +0 -23
  115. data/lib/event_store_client/subscriptions.rb +0 -38
  116. data/lib/event_store_client/value_objects/read_direction.rb +0 -43
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grpc'
4
- require 'event_store_client/adapters/grpc/connection'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  class CommandRegistrar
@@ -19,13 +16,16 @@ module EventStoreClient
19
16
  end
20
17
 
21
18
  def self.request(command_klass)
22
- @commands[command_klass][:request]
19
+ @commands.dig(command_klass, :request)
23
20
  end
24
21
 
22
+ # @param command_klass [Class]
23
+ # Examples:
24
+ # - EventStoreClient::GRPC::Commands::Streams::Append
25
+ # - EventStoreClient::GRPC::Commands::Streams::Read
26
+ # @return [Object] GRPC service class
25
27
  def self.service(command_klass)
26
- EventStoreClient::GRPC::Connection.new.call(
27
- @commands[command_klass][:service]
28
- )
28
+ @commands.dig(command_klass, :service)
29
29
  end
30
30
  end
31
31
  end
@@ -1,43 +1,81 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry-monads'
4
- require 'event_store_client/adapters/grpc/command_registrar'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
9
6
  class Command
10
- include Configuration
11
-
12
- class GRPCUnavailableRetryFailed < StandardError; end
7
+ class << self
8
+ def use_request(request_klass)
9
+ CommandRegistrar.register_request(self, request: request_klass)
10
+ end
13
11
 
14
- def self.inherited(klass)
15
- super
16
- klass.class_eval do
17
- include Dry::Monads[:try, :result]
12
+ def use_service(service_klass)
13
+ CommandRegistrar.register_service(self, service: service_klass)
14
+ end
15
+ end
18
16
 
19
- def self.use_request(request_klass)
20
- CommandRegistrar.register_request(self, request: request_klass)
21
- end
17
+ include Configuration
18
+ include Dry::Monads[:try, :result]
22
19
 
23
- def self.use_service(service_klass)
24
- CommandRegistrar.register_service(self, service: service_klass)
25
- end
20
+ attr_reader :connection
21
+ private :connection
26
22
 
27
- def request
28
- CommandRegistrar.request(self.class)
29
- end
23
+ # @param conn_options [Hash]
24
+ # @option conn_options [String] :host
25
+ # @option conn_options [Integer] :port
26
+ # @option conn_options [String] :username
27
+ # @option conn_options [String] :password
28
+ def initialize(**conn_options)
29
+ @connection = EventStoreClient::GRPC::Connection.new(**conn_options)
30
+ end
30
31
 
31
- def service
32
- CommandRegistrar.service(self.class)
33
- end
34
- end
32
+ # Override it in your implementation of command.
33
+ def call
34
+ raise NotImplementedError
35
35
  end
36
36
 
37
+ # @return [Hash]
37
38
  def metadata
39
+ return {} unless connection.class.secure?
40
+
38
41
  credentials =
39
- Base64.encode64("#{config.eventstore_user}:#{config.eventstore_password}")
40
- { 'authorization' => "Basic #{credentials.delete("\n")}" }
42
+ Base64.encode64("#{connection.username}:#{connection.password}").delete("\n")
43
+ { 'authorization' => "Basic #{credentials}" }
44
+ end
45
+
46
+ # @return GRPC params class to be used in the request.
47
+ # E.g.EventStore::Client::Streams::ReadReq
48
+ def request
49
+ CommandRegistrar.request(self.class)
50
+ end
51
+
52
+ # @return GRPC request stub class. E.g. EventStore::Client::Streams::Streams::Stub
53
+ def service
54
+ connection.call(CommandRegistrar.service(self.class))
55
+ end
56
+
57
+ # @return [Hash] connection options' hash
58
+ def connection_options
59
+ @connection.options_hash
60
+ end
61
+
62
+ private
63
+
64
+ def retry_request(skip_retry: false)
65
+ return yield if skip_retry
66
+
67
+ retries = 0
68
+ begin
69
+ yield
70
+ rescue ::GRPC::Unavailable => e
71
+ sleep config.eventstore_url.grpc_retry_interval / 1000.0
72
+ retries += 1
73
+ if retries <= config.eventstore_url.grpc_retry_attempts
74
+ config.logger&.debug("Request failed. Reason: #{e.class}. Retying.")
75
+ retry
76
+ end
77
+ raise
78
+ end
41
79
  end
42
80
  end
43
81
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'event_store_client/adapters/grpc/generated/shared_pb'
4
+ require 'event_store_client/adapters/grpc/generated/gossip_pb'
5
+ require 'event_store_client/adapters/grpc/generated/gossip_services_pb'
6
+
7
+ module EventStoreClient
8
+ module GRPC
9
+ module Commands
10
+ module Gossip
11
+ class ClusterInfo < Command
12
+ use_request EventStore::Client::Empty
13
+ use_service EventStore::Client::Gossip::Gossip::Stub
14
+
15
+ # @api private
16
+ # @see {EventStoreClient::GRPC::Client#cluster_info}
17
+ def call
18
+ Success(retry_request { service.read(request.new, metadata: metadata) })
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grpc'
4
3
  require 'event_store_client/adapters/grpc/generated/streams_pb'
5
4
  require 'event_store_client/adapters/grpc/generated/streams_services_pb'
6
5
 
7
- require 'event_store_client/adapters/grpc/commands/command'
8
-
9
6
  module EventStoreClient
10
7
  module GRPC
11
8
  module Commands
@@ -17,91 +14,69 @@ module EventStoreClient
17
14
  ALLOWED_EVENT_METADATA = %w[type content-type created_at].freeze
18
15
 
19
16
  # @api private
20
- def call(stream, events, options: {})
21
- return unless events.any?
22
-
23
- serialized_events = events.map { |event| config.mapper.serialize(event) }
24
-
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?
17
+ # @see {EventStoreClient::GRPC::Client#append_to_stream}
18
+ def call(stream_name, event, options:, &blk)
19
+ payload =
20
+ [
21
+ request.new(options: options(stream_name, options)),
22
+ request.new(proposed_message: proposed_message(event))
23
+ ]
24
+ yield(*payload) if blk
25
+ response = retry_request(skip_retry: config.eventstore_url.throw_on_append_failure) do
26
+ service.append(payload, metadata: metadata)
32
27
  end
33
-
34
- res.success? ? Success(events) : Failure(res.failure)
28
+ validate_response(response)
29
+ rescue ::GRPC::Unavailable => e
30
+ Failure(e)
35
31
  end
36
32
 
37
33
  private
38
34
 
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)
35
+ # @param event [EventStoreClient::DeserializedEvent]
36
+ # @return [EventStore::Client::Streams::AppendReq::ProposedMessage]
37
+ def proposed_message(event)
38
+ serialized_event = config.mapper.serialize(event)
39
+ event_metadata = JSON.parse(serialized_event.metadata)
40
+ custom_metadata = custom_metadata(serialized_event.type, event_metadata)
41
+ opts =
42
+ {
43
+ id: {
44
+ string: serialized_event.id
45
+ },
46
+ data: serialized_event.data.b,
47
+ custom_metadata: custom_metadata.to_json,
48
+ metadata: event_metadata.slice(*ALLOWED_EVENT_METADATA)
49
+ }
50
+ EventStore::Client::Streams::AppendReq::ProposedMessage.new(opts)
54
51
  end
55
52
 
53
+ # @param event_type [String]
54
+ # @param event_metadata [Hash]
55
+ # @return [Hash]
56
56
  def custom_metadata(event_type, event_metadata)
57
57
  {
58
58
  type: event_type,
59
- created_at: Time.current,
59
+ created_at: Time.now.utc,
60
60
  encryption: event_metadata['encryption'],
61
61
  'content-type': event_metadata['content-type'],
62
62
  transaction: event_metadata['transaction']
63
63
  }.compact
64
64
  end
65
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
- }
66
+ # @param stream_name [String]
67
+ # @param options [Hash]
68
+ # @return [EventStore::Client::Streams::AppendReq::Options]
69
+ def options(stream_name, options)
70
+ opts = Options::Streams::WriteOptions.new(stream_name, options).request_options
71
+ EventStore::Client::Streams::AppendReq::Options.new(opts)
96
72
  end
97
73
 
74
+ # @param resp [EventStore::Client::Streams::AppendResp]
75
+ # @return [Dry::Monads::Success, Dry::Monads::Failure]
98
76
  def validate_response(resp)
99
- return Success() if resp.success
77
+ return Success(resp) if resp.success
100
78
 
101
- Failure(
102
- "current version: #{resp.wrong_expected_version.current_revision} | "\
103
- "expected: #{resp.wrong_expected_version.expected_revision}"
104
- )
79
+ Failure(resp.wrong_expected_version)
105
80
  end
106
81
  end
107
82
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Naming/PredicateName
4
+
5
+ module EventStoreClient
6
+ module GRPC
7
+ module Commands
8
+ module Streams
9
+ class AppendMultiple < Command
10
+ # @api private
11
+ # @see {EventStoreClient::GRPC::Client#append_to_stream}
12
+ def call(stream_name, events, options:, &blk)
13
+ result = []
14
+ events.each.with_index do |event, index|
15
+ response = Commands::Streams::Append.new(**connection_options).call(
16
+ stream_name, event, options: options
17
+ ) do |req_opts, proposed_msg_opts|
18
+ req_opts.options.revision += index if has_revision_option?(req_opts.options)
19
+
20
+ yield(req_opts, proposed_msg_opts) if blk
21
+ end
22
+ result.push(response)
23
+ break if response.failure?
24
+ end
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ # Even if #revision is not set explicitly - its value defaults to 0. Thus, you can't
31
+ # detect whether #revision is set just by calling #revision method. Instead - check if
32
+ # option does not set #no_stream, #any or #stream_exists options - they are self-exclusive
33
+ # options and only one of them can be active at a time
34
+ # @param options [EventStore::Client::Streams::AppendReq::Options]
35
+ # @return [Boolean]
36
+ def has_revision_option?(options)
37
+ [options.no_stream, options.any, options.stream_exists].all?(&:nil?)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable Naming/PredicateName
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'grpc'
4
- require 'event_store_client/adapters/grpc/generated/projections_pb.rb'
5
- require 'event_store_client/adapters/grpc/generated/projections_services_pb.rb'
6
-
7
- require 'event_store_client/adapters/grpc/commands/command'
3
+ require 'event_store_client/adapters/grpc/generated/streams_pb'
4
+ require 'event_store_client/adapters/grpc/generated/streams_services_pb'
8
5
 
9
6
  module EventStoreClient
10
7
  module GRPC
@@ -14,19 +11,26 @@ module EventStoreClient
14
11
  use_request EventStore::Client::Streams::DeleteReq
15
12
  use_service EventStore::Client::Streams::Streams::Stub
16
13
 
17
- def call(name, options: {}) # rubocop:disable Lint/UnusedMethodArgument
18
- opts =
19
- {
20
- stream_identifier: {
21
- streamName: name
22
- },
23
- any: {}
24
- }
25
-
26
- service.delete(request.new(options: opts), metadata: metadata)
27
- Success()
14
+ # @api private
15
+ # @see {EventStoreClient::GRPC::Client#delete_stream}
16
+ def call(stream_name, options:, &blk)
17
+ options = normalize_options(stream_name, options)
18
+ yield options if blk
19
+ Success(
20
+ retry_request { service.delete(request.new(options: options), metadata: metadata) }
21
+ )
28
22
  rescue ::GRPC::FailedPrecondition
29
- Failure(:not_found)
23
+ Failure(:stream_not_found)
24
+ end
25
+
26
+ private
27
+
28
+ # @param stream_name [String]
29
+ # @param options [Hash]
30
+ # @return [EventStore::Client::Streams::TombstoneReq::Options]
31
+ def normalize_options(stream_name, options)
32
+ opts = Options::Streams::WriteOptions.new(stream_name, options).request_options
33
+ EventStore::Client::Streams::DeleteReq::Options.new(opts)
30
34
  end
31
35
  end
32
36
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'event_store_client/adapters/grpc/generated/streams_pb'
4
+ require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
+
6
+ module EventStoreClient
7
+ module GRPC
8
+ module Commands
9
+ module Streams
10
+ class HardDelete < Command
11
+ use_request EventStore::Client::Streams::TombstoneReq
12
+ use_service EventStore::Client::Streams::Streams::Stub
13
+
14
+ # @api private
15
+ # @see {EventStoreClient::GRPC::Client#hard_delete_stream}
16
+ def call(stream_name, options:, &blk)
17
+ options = normalize_options(stream_name, options)
18
+ yield options if blk
19
+ Success(
20
+ retry_request { service.delete(request.new(options: options), metadata: metadata) }
21
+ )
22
+ rescue ::GRPC::FailedPrecondition
23
+ Failure(:stream_not_found)
24
+ end
25
+
26
+ private
27
+
28
+ # @param stream_name [String]
29
+ # @param options [Hash]
30
+ # @return [EventStore::Client::Streams::TombstoneReq::Options]
31
+ def normalize_options(stream_name, options)
32
+ opts = Options::Streams::WriteOptions.new(stream_name, options).request_options
33
+ EventStore::Client::Streams::TombstoneReq::Options.new(opts)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -1,62 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'securerandom'
4
- require 'grpc'
5
- require 'event_store_client/adapters/grpc/generated/streams_pb.rb'
6
- require 'event_store_client/adapters/grpc/generated/streams_services_pb.rb'
7
-
8
- require 'event_store_client/adapters/grpc/commands/command'
9
-
10
3
  module EventStoreClient
11
4
  module GRPC
12
5
  module Commands
13
6
  module Streams
14
7
  class LinkTo < Command
15
- include Configuration
16
-
17
- use_request EventStore::Client::Streams::AppendReq
18
- use_service EventStore::Client::Streams::Streams::Stub
19
-
20
- def call(stream_name, events, options: {})
21
- events.each_with_index do |event, index|
22
- custom_metadata = JSON.generate(
23
- "type": '$>',
24
- "created_at": Time.now,
25
- "encryption": event.metadata['encryption'] || ''
26
- )
27
-
28
- event_metadata = event.metadata.tap do |h|
29
- h['type'] = '$>'
30
- h['content-type'] = 'application/json'
31
- h.delete('encryption')
32
- end
33
-
34
- event_id = event.id
35
- event_id = SecureRandom.uuid if event.id.nil? || event.id.empty?
36
-
37
- payload = [
38
- request.new(
39
- options: {
40
- stream_identifier: {
41
- streamName: stream_name
42
- },
43
- any: {}
44
- }
45
- ),
46
- request.new(
47
- proposed_message: {
48
- id: {
49
- string: event_id
50
- },
51
- data: event.title,
52
- custom_metadata: custom_metadata,
53
- metadata: event_metadata
54
- }
55
- )
56
- ]
57
- service.append(payload, metadata: metadata)
58
- end
59
- Success()
8
+ # @see {EventStoreClient::GRPC::Client#hard_delete_stream}
9
+ def call(stream_name, event, options:, &blk)
10
+ append_cmd = Append.new(**connection_options)
11
+ link_event = EventStoreClient::DeserializedEvent.new(
12
+ id: event.id, type: '$>', data: event.title
13
+ )
14
+ append_cmd.call(stream_name, link_event, options: options, &blk)
60
15
  end
61
16
  end
62
17
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Naming/PredicateName
4
+
5
+ module EventStoreClient
6
+ module GRPC
7
+ module Commands
8
+ module Streams
9
+ class LinkToMultiple < Command
10
+ # @api private
11
+ # @see {EventStoreClient::GRPC::Client#link_to}
12
+ def call(stream_name, events, options:, &blk)
13
+ result = []
14
+ link_cmd = Commands::Streams::LinkTo.new(**connection_options)
15
+ events.each.with_index do |event, index|
16
+ response =
17
+ link_cmd.call(stream_name, event, options: options) do |req_opts, proposed_msg_opts|
18
+ req_opts.options.revision += index if has_revision_option?(req_opts.options)
19
+
20
+ yield(req_opts, proposed_msg_opts) if blk
21
+ end
22
+ result.push(response)
23
+ break if response.failure?
24
+ end
25
+ result
26
+ end
27
+
28
+ private
29
+
30
+ # Even if #revision is not set explicitly - its value defaults to 0. Thus, you can't
31
+ # detect whether #revision is set just by calling #revision method. Instead - check if
32
+ # option does not set #no_stream, #any or #stream_exists options - they are self-exclusive
33
+ # options and only one of them can be active at a time
34
+ # @param options [EventStore::Client::Streams::AppendReq::Options]
35
+ # @return [Boolean]
36
+ def has_revision_option?(options)
37
+ [options.no_stream, options.any, options.stream_exists].all?(&:nil?)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ # rubocop:enable Naming/PredicateName