event_store_client 1.4.9 → 2.0.0

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.
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 +74 -83
  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