event_store_client 1.1.0 → 1.1.5

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: 301a92ed47fa04e52d45989042f82571e30eeba810c73b7c0f73af55a95304f0
4
- data.tar.gz: 0a58c9b4dc19bb56a2f2082b06552de05f39e0983f5c7379a1e058cbe5c65332
3
+ metadata.gz: 2624a6c8e08a686f5e307eccfb00ac9321eb5e2e32aed9093c0c67e8cb01b9e5
4
+ data.tar.gz: 0b4ff333a547924f54aa182e0bb1f821a7382db93c5d9304038c43a514ee6d3b
5
5
  SHA512:
6
- metadata.gz: f87b1b7516fdae3a51a23970485ad6d492a50b91550208eee10d3d01530cbda57641941c0dc7a9c2696f25c2f442b87bfc02dc70149fdea499dbb11e02bc8d73
7
- data.tar.gz: 903f6f6b64725911be55f7d4730f988f1a4596b4f4c8de05c226a77fb6ac477f90063854c0ae2b0981abba5cc78928feebc3aff52c38f6b5863f12408f8bcc84
6
+ metadata.gz: dfda05e2c763e85977c63a3b0e27034050b37c7ded273750b1aadec42e55fc459475c4a8d50167fe66952bb39c7145242ed017b5548abbece412beaa4e98d37a
7
+ data.tar.gz: ca27251198df7d78a9b60482ee8dfef7f8b6cfdbda08d3a36f0af1723ee487b85239d0d046a6d777e5c6e5161725faaa4b4fac6aa3b650460eb622a428dc0960
@@ -110,7 +110,10 @@ module EventStoreClient
110
110
  # @return Dry::Monads::Result::Success or Dry::Monads::Result::Failure
111
111
  #
112
112
  def join_streams(name, streams)
113
- Commands::Projections::Create.new.call(name, streams)
113
+ res = Commands::Projections::Create.new.call(name, streams)
114
+ return if res.success?
115
+
116
+ Commands::Projections::Update.new.call(name, streams)
114
117
  end
115
118
 
116
119
  # @api private
@@ -12,7 +12,7 @@ module EventStoreClient
12
12
  def self.inherited(klass)
13
13
  super
14
14
  klass.class_eval do
15
- include Dry::Monads[:result]
15
+ include Dry::Monads[:try, :result]
16
16
 
17
17
  def self.use_request(request_klass)
18
18
  CommandRegistrar.register_request(self, request: request_klass)
@@ -38,16 +38,17 @@ module EventStoreClient
38
38
 
39
39
  requests = [request.new(options: opts)] # please notice that it's an array. Should be?
40
40
 
41
+ skip_decryption = options[:skip_decryption] || false
41
42
  service.read(requests, metadata: metadata).each do |res|
42
43
  next if res.subscription_confirmation
43
- yield deserialize_event(res.event.event) if block_given?
44
+ yield deserialize_event(res.event.event, skip_decryption: skip_decryption) if block_given?
44
45
  end
45
46
  Success()
46
47
  end
47
48
 
48
49
  private
49
50
 
50
- def deserialize_event(entry)
51
+ def deserialize_event(entry, skip_decryption: false)
51
52
  id = entry.id.string
52
53
  id = SecureRandom.uuid if id.nil? || id.empty?
53
54
 
@@ -65,7 +66,8 @@ module EventStoreClient
65
66
  type: entry.metadata['type'],
66
67
  data: data,
67
68
  metadata: metadata
68
- )
69
+ ),
70
+ skip_decryption: skip_decryption
69
71
  )
70
72
  end
71
73
  end
@@ -33,8 +33,11 @@ module EventStoreClient
33
33
  }
34
34
  }
35
35
 
36
- service.create(request.new(options: options), metadata: metadata)
37
- Success()
36
+ res = Try do
37
+ service.create(request.new(options: options), metadata: metadata)
38
+ end
39
+
40
+ res.error? ? res.to_result : Success()
38
41
  rescue ::GRPC::Unknown => e
39
42
  Failure(:conflict) if e.message.include?('Conflict')
40
43
  end
@@ -30,7 +30,9 @@ module EventStoreClient
30
30
  name: name,
31
31
  emit_enabled: true
32
32
  }
33
+
33
34
  service.update(request.new(options: options), metadata: metadata)
35
+
34
36
  Success()
35
37
  rescue ::GRPC::AlreadyExists
36
38
  Failure(:conflict)
@@ -43,10 +43,10 @@ module EventStoreClient
43
43
  opts[:stream][:revision] = options[:start]
44
44
  end
45
45
 
46
+ skip_decryption = options[:skip_decryption] || false
46
47
  events = service.read(request.new(options: opts), metadata: metadata).map do |res|
47
48
  raise StreamNotFound if res.stream_not_found
48
-
49
- deserialize_event(res.event.event)
49
+ deserialize_event(res.event.event, skip_decryption: skip_decryption)
50
50
  end
51
51
  Success(events)
52
52
  rescue StreamNotFound
@@ -55,7 +55,7 @@ module EventStoreClient
55
55
 
56
56
  private
57
57
 
58
- def deserialize_event(entry)
58
+ def deserialize_event(entry, skip_decryption: false)
59
59
  data = (entry.data.nil? || entry.data.empty?) ? '{}' : entry.data
60
60
 
61
61
  metadata =
@@ -71,7 +71,7 @@ module EventStoreClient
71
71
  metadata: metadata
72
72
  )
73
73
 
74
- config.mapper.deserialize(event)
74
+ config.mapper.deserialize(event, skip_decryption: skip_decryption)
75
75
  end
76
76
  end
77
77
  end
@@ -6,6 +6,7 @@ require 'event_store_client/adapters/http/commands/persistent_subscriptions/crea
6
6
  require 'event_store_client/adapters/http/commands/persistent_subscriptions/read'
7
7
 
8
8
  require 'event_store_client/adapters/http/commands/projections/create'
9
+ require 'event_store_client/adapters/http/commands/projections/update'
9
10
 
10
11
  require 'event_store_client/adapters/http/commands/streams/append'
11
12
  require 'event_store_client/adapters/http/commands/streams/delete'
@@ -137,7 +137,10 @@ module EventStoreClient
137
137
  # @return Dry::Monads::Result::Success or Dry::Monads::Result::Failure
138
138
  #
139
139
  def join_streams(name, streams, options: {})
140
- Commands::Projections::Create.new(connection).call(name, streams, options: options)
140
+ res = Commands::Projections::Create.new(connection).call(name, streams, options: options)
141
+ return res if res.success?
142
+
143
+ Commands::Projections::Update.new(connection).call(name, streams, options: options)
141
144
  end
142
145
 
143
146
  # @api private
@@ -28,16 +28,19 @@ module EventStoreClient
28
28
 
29
29
  ack_info = body['links'].find { |link| link['relation'] == 'ackAll' }
30
30
  return { events: [] } unless ack_info
31
+
32
+ skip_decryption = options[:skip_decryption] || false
31
33
  body['entries'].map do |entry|
32
- yield deserialize_event(entry)
34
+ yield deserialize_event(entry, skip_decryption: skip_decryption)
33
35
  end
36
+
34
37
  Ack.new(connection).call(ack_info['uri'])
35
38
  Success()
36
39
  end
37
40
 
38
41
  private
39
42
 
40
- def deserialize_event(entry)
43
+ def deserialize_event(entry, skip_decryption: false)
41
44
  event = EventStoreClient::Event.new(
42
45
  id: entry['eventId'],
43
46
  title: entry['title'],
@@ -46,7 +49,7 @@ module EventStoreClient
46
49
  metadata: entry['isMetaData'] ? entry['metaData'] : '{}'
47
50
  )
48
51
 
49
- config.mapper.deserialize(event)
52
+ config.mapper.deserialize(event, skip_decryption: skip_decryption)
50
53
  rescue EventStoreClient::DeserializedEvent::InvalidDataError
51
54
  event
52
55
  end
@@ -16,12 +16,15 @@ module EventStoreClient
16
16
  })
17
17
  STRING
18
18
 
19
- connection.call(
19
+
20
+ res = connection.call(
20
21
  :post,
21
22
  "/projections/continuous?name=#{name}&type=js&enabled=yes&emit=true&trackemittedstreams=true", # rubocop:disable Metrics/LineLength
22
23
  body: data,
23
24
  headers: {}
24
25
  )
26
+
27
+ (200...300).cover?(res.status) ? Success() : Failure(res)
25
28
  end
26
29
  end
27
30
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreClient
4
+ module HTTP
5
+ module Commands
6
+ module Projections
7
+ class Update < Command
8
+ def call(name, streams, options: {})
9
+ data =
10
+ <<~STRING
11
+ fromStreams(#{streams})
12
+ .when({
13
+ $any: function(s,e) {
14
+ linkTo("#{name}", e)
15
+ }
16
+ })
17
+ STRING
18
+ res = connection.call(
19
+ :put,
20
+ "/projection/#{name}/query?type=js&enabled=yes&emit=true&trackemittedstreams=true", # rubocop:disable Metrics/LineLength
21
+ body: data,
22
+ headers: {}
23
+ )
24
+
25
+ (200...300).cover?(res.status) ? Success() : Failure(res)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -23,8 +23,9 @@ module EventStoreClient
23
23
 
24
24
  return Failure(:stream_not_found) unless response.success? || response.status == 404
25
25
  return Failure(:connection_failed) if response.body.nil? || response.body.empty?
26
+ skip_decryption = options[:skip_decryption] || false
26
27
  entries = JSON.parse(response.body)['entries'].map do |entry|
27
- deserialize_event(entry)
28
+ deserialize_event(entry, skip_decryption: skip_decryption)
28
29
  end.reverse
29
30
  Success(entries)
30
31
  rescue Faraday::ConnectionFailed
@@ -33,7 +34,7 @@ module EventStoreClient
33
34
 
34
35
  private
35
36
 
36
- def deserialize_event(entry)
37
+ def deserialize_event(entry, skip_decryption: false)
37
38
  event = EventStoreClient::Event.new(
38
39
  id: entry['eventId'],
39
40
  title: entry['title'],
@@ -42,7 +43,7 @@ module EventStoreClient
42
43
  metadata: entry['isMetaData'] ? entry['metaData'] : '{}'
43
44
  )
44
45
 
45
- config.mapper.deserialize(event)
46
+ config.mapper.deserialize(event, skip_decryption: skip_decryption)
46
47
  end
47
48
  end
48
49
  end
@@ -46,8 +46,9 @@ module EventStoreClient
46
46
  res = Response.new(response.to_json, 200)
47
47
 
48
48
  return [] if res.body.nil? || res.body.empty?
49
+ skip_decryption = options[:skip_decryption] || false
49
50
  events = JSON.parse(res.body)['entries'].map do |entry|
50
- deserialize_event(entry)
51
+ deserialize_event(entry, skip_decryption: skip_decryption)
51
52
  end.reverse
52
53
  Dry::Monads::Success(events)
53
54
  end
@@ -127,7 +128,7 @@ module EventStoreClient
127
128
  end
128
129
  end
129
130
 
130
- def deserialize_event(entry)
131
+ def deserialize_event(entry, skip_decryption: false)
131
132
  event = EventStoreClient::Event.new(
132
133
  id: entry['eventId'],
133
134
  title: entry['title'],
@@ -136,7 +137,7 @@ module EventStoreClient
136
137
  metadata: entry['isMetaData'] ? entry['metaData'] : '{}'
137
138
  )
138
139
 
139
- mapper.deserialize(event)
140
+ mapper.deserialize(event, skip_decryption: skip_decryption)
140
141
  end
141
142
  end
142
143
  end
@@ -10,6 +10,14 @@ module EventStoreClient
10
10
  # main app process (useful for debugging)
11
11
  #
12
12
  def call(subscriptions, wait: false)
13
+ Signal.trap('TERM') do
14
+ Thread.new { logger&.info('Broker: TERM Signal has been received') }
15
+ threads.each do |thread|
16
+ thread.thread_variable_set(:terminate, true)
17
+ end
18
+ Thread.new { logger&.info('Broker: Terminate variable for subscription threads set') }
19
+ end
20
+
13
21
  subscriptions.each do |subscription|
14
22
  threads << Thread.new do
15
23
  subscriptions.listen(subscription)
@@ -20,12 +28,13 @@ module EventStoreClient
20
28
 
21
29
  private
22
30
 
23
- attr_reader :connection
31
+ attr_reader :connection, :logger
24
32
  attr_accessor :threads
25
33
 
26
34
  def initialize(connection:)
27
35
  @connection = connection
28
36
  @threads = []
37
+ @logger = EventStoreClient.config.logger
29
38
  end
30
39
  end
31
40
  end
@@ -34,6 +34,13 @@ module EventStoreClient
34
34
  next unless event
35
35
 
36
36
  subscription.subscriber.call(event)
37
+
38
+ if Thread.current.thread_variable_get(:terminate)
39
+ msg =
40
+ "CatchUpSubscriptions: Terminating subscription listener for #{subscription.subscriber}"
41
+ logger&.info(msg)
42
+ break
43
+ end
37
44
  rescue StandardError
38
45
  subscription.position = old_position
39
46
  subscription_store.update_position(subscription)
@@ -45,14 +52,19 @@ module EventStoreClient
45
52
  subscription_store.clean_unused(subscriptions.map(&:name))
46
53
  end
47
54
 
55
+ def reset
56
+ subscription_store.reset(subscriptions)
57
+ end
58
+
48
59
  private
49
60
 
50
- attr_reader :connection, :subscriptions, :subscription_store
61
+ attr_reader :connection, :subscriptions, :subscription_store, :logger
51
62
 
52
63
  def initialize(connection:, subscription_store:)
53
64
  @connection = connection
54
65
  @subscription_store = subscription_store
55
66
  @subscriptions = []
67
+ @logger = EventStoreClient.config.logger
56
68
  end
57
69
 
58
70
  def confirmation?(event_data)
@@ -33,6 +33,12 @@ module EventStoreClient
33
33
  @subscriptions.create_or_load(subscriber, filter: filter)
34
34
  end
35
35
 
36
+ def reset_subscriptions
37
+ return unless @subscriptions.respond_to?(:reset)
38
+
39
+ @subscriptions.reset
40
+ end
41
+
36
42
  def listen(wait: false)
37
43
  broker.call(@subscriptions, wait: wait)
38
44
  end
@@ -30,6 +30,8 @@ module EventStoreClient
30
30
 
31
31
  setting :subscriptions_repo
32
32
 
33
+ setting :logger
34
+
33
35
  def self.configure
34
36
  yield(config) if block_given?
35
37
  end
@@ -9,28 +9,28 @@ module EventStoreClient
9
9
  KeyNotFoundError = Class.new(StandardError)
10
10
 
11
11
  def call
12
- return decrypted_data if encryption_metadata.empty?
12
+ return encrypted_data if encryption_metadata.empty?
13
13
 
14
14
  decrypt_attributes(
15
15
  key: find_key(encryption_metadata['key']),
16
- data: decrypted_data,
16
+ data: encrypted_data,
17
17
  attributes: encryption_metadata['attributes']
18
18
  )
19
19
  end
20
20
 
21
- attr_reader :decrypted_data, :encryption_metadata
22
-
23
21
  private
24
22
 
25
- attr_reader :key_repository
23
+ attr_reader :key_repository, :encryption_metadata, :encrypted_data
26
24
 
27
25
  def initialize(data:, schema:, repository:)
28
- @decrypted_data = deep_dup(data).transform_keys!(&:to_s)
26
+ @encrypted_data = deep_dup(data).transform_keys!(&:to_s)
29
27
  @key_repository = repository
30
28
  @encryption_metadata = schema&.transform_keys(&:to_s) || {}
31
29
  end
32
30
 
33
31
  def decrypt_attributes(key:, data:, attributes: {}) # rubocop:disable Lint/UnusedMethodArgument
32
+ return data unless key
33
+
34
34
  res = key_repository.decrypt(key: key, message: data['es_encrypted'])
35
35
  return data if res.failure?
36
36
 
@@ -42,6 +42,7 @@ module EventStoreClient
42
42
  end
43
43
 
44
44
  def deep_dup(hash)
45
+ return hash unless hash.instance_of?(Hash)
45
46
  dupl = hash.dup
46
47
  dupl.each { |k, v| dupl[k] = v.instance_of?(Hash) ? deep_dup(v) : v }
47
48
  dupl
@@ -53,8 +54,8 @@ module EventStoreClient
53
54
  key_repository.find(identifier).value!
54
55
  rescue StandardError => e
55
56
  config.error_handler&.call(e)
57
+ nil
56
58
  end
57
- raise KeyNotFoundError unless key
58
59
 
59
60
  key
60
61
  end
@@ -12,7 +12,7 @@ module EventStoreClient
12
12
  )
13
13
  end
14
14
 
15
- def deserialize(event)
15
+ def deserialize(event, **)
16
16
  metadata = serializer.deserialize(event.metadata)
17
17
  data = serializer.deserialize(event.data)
18
18
 
@@ -49,14 +49,20 @@ module EventStoreClient
49
49
  # which key should be used as an identifier.
50
50
  # *Returns*: Specific event class with all data decrypted
51
51
 
52
- def deserialize(event)
52
+ def deserialize(event, skip_decryption: false)
53
53
  metadata = serializer.deserialize(event.metadata)
54
54
  encryption_schema = serializer.deserialize(event.metadata)['encryption']
55
- decrypted_data = EventStoreClient::DataDecryptor.new(
56
- data: serializer.deserialize(event.data),
57
- schema: encryption_schema,
58
- repository: key_repository
59
- ).call
55
+
56
+ decrypted_data =
57
+ if skip_decryption
58
+ serializer.deserialize(event.data)
59
+ else
60
+ EventStoreClient::DataDecryptor.new(
61
+ data: serializer.deserialize(event.data),
62
+ schema: encryption_schema,
63
+ repository: key_repository
64
+ ).call
65
+ end
60
66
 
61
67
  event_class =
62
68
  begin
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '1.1.0'
4
+ VERSION = '1.1.5'
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.1.0
4
+ version: 1.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sebastian Wilgosz
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-07 00:00:00.000000000 Z
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-configurable
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: 0.2.8
111
+ - !ruby/object:Gem::Dependency
112
+ name: pry
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.14'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.14'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: rspec
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -199,6 +213,7 @@ files:
199
213
  - lib/event_store_client/adapters/http/commands/persistent_subscriptions/create.rb
200
214
  - lib/event_store_client/adapters/http/commands/persistent_subscriptions/read.rb
201
215
  - lib/event_store_client/adapters/http/commands/projections/create.rb
216
+ - lib/event_store_client/adapters/http/commands/projections/update.rb
202
217
  - lib/event_store_client/adapters/http/commands/streams/append.rb
203
218
  - lib/event_store_client/adapters/http/commands/streams/delete.rb
204
219
  - lib/event_store_client/adapters/http/commands/streams/link_to.rb
@@ -232,7 +247,7 @@ licenses:
232
247
  - MIT
233
248
  metadata:
234
249
  allowed_push_host: https://rubygems.org
235
- post_install_message:
250
+ post_install_message:
236
251
  rdoc_options: []
237
252
  require_paths:
238
253
  - lib
@@ -247,8 +262,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
262
  - !ruby/object:Gem::Version
248
263
  version: '0'
249
264
  requirements: []
250
- rubygems_version: 3.0.4
251
- signing_key:
265
+ rubygems_version: 3.1.4
266
+ signing_key:
252
267
  specification_version: 4
253
268
  summary: Ruby integration for https://eventstore.org
254
269
  test_files: []