event_store_client 2.0.4 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/lib/event_store_client/adapters/grpc/cluster/gossip_discover.rb +0 -4
  4. data/lib/event_store_client/adapters/grpc/commands/gossip/cluster_info.rb +0 -4
  5. data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +1 -32
  6. data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +0 -3
  7. data/lib/event_store_client/adapters/grpc/commands/streams/hard_delete.rb +0 -3
  8. data/lib/event_store_client/adapters/grpc/commands/streams/link_to.rb +2 -2
  9. data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +0 -3
  10. data/lib/event_store_client/adapters/grpc/commands/streams/subscribe.rb +0 -3
  11. data/lib/event_store_client/adapters/grpc/options/streams/read_options.rb +0 -2
  12. data/lib/event_store_client/adapters/grpc/options/streams/write_options.rb +0 -2
  13. data/lib/event_store_client/adapters/grpc/shared/options/filter_options.rb +0 -2
  14. data/lib/event_store_client/adapters/grpc/shared/streams/process_response.rb +2 -1
  15. data/lib/event_store_client/adapters/grpc/shared/streams/process_responses.rb +4 -3
  16. data/lib/event_store_client/adapters/grpc.rb +3 -1
  17. data/lib/event_store_client/data_decryptor.rb +5 -2
  18. data/lib/event_store_client/deserialized_event.rb +9 -2
  19. data/lib/event_store_client/mapper/default.rb +12 -27
  20. data/lib/event_store_client/mapper/encrypted.rb +38 -62
  21. data/lib/event_store_client/serialized_event.rb +43 -0
  22. data/lib/event_store_client/serializer/event_deserializer.rb +74 -0
  23. data/lib/event_store_client/serializer/event_serializer.rb +69 -0
  24. data/lib/event_store_client/serializer/json.rb +20 -11
  25. data/lib/event_store_client/version.rb +1 -1
  26. data/lib/event_store_client.rb +4 -1
  27. metadata +19 -32
  28. data/lib/event_store_client/adapters/grpc/shared/event_deserializer.rb +0 -52
  29. data/lib/event_store_client/event.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 857f587d240bbcb824b885cb5fa2c0b077351fcb12dead2d074ba23036252460
4
- data.tar.gz: fa56d9d29c29faf05ddc1b20d9de6f1aec46d9d80a330184131b5902238f3310
3
+ metadata.gz: 215ed29dc778024dfc7122cc295ec29e112284f67cb3ed4e74960d47a1e0a32e
4
+ data.tar.gz: 0be0bb78dfe0ce97fb3d171a9d145d82091e81978944c1dfb891d2609df12356
5
5
  SHA512:
6
- metadata.gz: c584fa5f7817f4631a6f73e91f7a2ee99b153e49b9b56980317f4a61f4d87ae197037b56e204f6f71729006f5c493404510136f131fa8efcca3c1b150cc18c5a
7
- data.tar.gz: 3f995f97559e9b305eac4d0cf7ba0bbef9c6e85a399a2109385f9cab03669791e3a2915c5a3b7564b3956f2d216370dcd8150814a38001a5219994873a8d0c3a
6
+ metadata.gz: ba84c9bd5b6148f4c8eaa4332e4a264364edcdf2642ef680a403a29d0406306e19db1f70422e711601ffe349df50c8b54a458f201d45288f711864aa6823c683
7
+ data.tar.gz: d2932967bcd5018fe330b0923231ee83062c3a8a713bcd6e6e92d8ddfa5bd633609415dd6ecf6b76d479d115b34cdd02a4de3f3f8b5c57a4010e29b40e0d8b6e
data/README.md CHANGED
@@ -44,6 +44,10 @@ See documentation chapters for the usage reference:
44
44
  - [Deleting streams](docs/deleting_streams.md)
45
45
  - [Encrypting events](docs/encrypting_events.md)
46
46
 
47
+ ### Subscriptions
48
+
49
+ We have written a gem that helps you to manage and handle Catch-up Subscriptions - `event_store_subscription`. You could check it [here](https://github.com/yousty/event_store_subscriptions).
50
+
47
51
  ## Contributing
48
52
 
49
53
  Do you want to contribute? Welcome!
@@ -2,10 +2,6 @@
2
2
 
3
3
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
4
4
 
5
- require 'event_store_client/adapters/grpc/generated/shared_pb'
6
- require 'event_store_client/adapters/grpc/generated/gossip_pb'
7
- require 'event_store_client/adapters/grpc/generated/gossip_services_pb'
8
-
9
5
  module EventStoreClient
10
6
  module GRPC
11
7
  module Cluster
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
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
3
  module EventStoreClient
8
4
  module GRPC
9
5
  module Commands
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/streams_pb'
4
- require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
@@ -11,8 +8,6 @@ module EventStoreClient
11
8
  use_request EventStore::Client::Streams::AppendReq
12
9
  use_service EventStore::Client::Streams::Streams::Stub
13
10
 
14
- ALLOWED_EVENT_METADATA = %w[type content-type created_at].freeze
15
-
16
11
  # @api private
17
12
  # @see {EventStoreClient::GRPC::Client#append_to_stream}
18
13
  def call(stream_name, event, options:, &blk)
@@ -26,8 +21,6 @@ module EventStoreClient
26
21
  service.append(payload, metadata: metadata)
27
22
  end
28
23
  validate_response(response)
29
- rescue ::GRPC::Unavailable => e
30
- Failure(e)
31
24
  end
32
25
 
33
26
  private
@@ -36,31 +29,7 @@ module EventStoreClient
36
29
  # @return [EventStore::Client::Streams::AppendReq::ProposedMessage]
37
30
  def proposed_message(event)
38
31
  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)
51
- end
52
-
53
- # @param event_type [String]
54
- # @param event_metadata [Hash]
55
- # @return [Hash]
56
- def custom_metadata(event_type, event_metadata)
57
- {
58
- type: event_type,
59
- created_at: Time.now.utc,
60
- encryption: event_metadata['encryption'],
61
- 'content-type': event_metadata['content-type'],
62
- transaction: event_metadata['transaction']
63
- }.compact
32
+ EventStore::Client::Streams::AppendReq::ProposedMessage.new(serialized_event.to_grpc)
64
33
  end
65
34
 
66
35
  # @param stream_name [String]
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/streams_pb'
4
- require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/streams_pb'
4
- require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
@@ -8,8 +8,8 @@ module EventStoreClient
8
8
  # @see {EventStoreClient::GRPC::Client#hard_delete_stream}
9
9
  def call(stream_name, event, options:, &blk)
10
10
  append_cmd = Append.new(**connection_options)
11
- link_event = EventStoreClient::DeserializedEvent.new(
12
- id: event.id, type: '$>', data: event.title
11
+ link_event = DeserializedEvent.new(
12
+ id: event.id, type: DeserializedEvent::LINK_TYPE, data: event.title
13
13
  )
14
14
  append_cmd.call(stream_name, link_event, options: options, &blk)
15
15
  end
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/streams_pb'
4
- require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
@@ -1,8 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/streams_pb'
4
- require 'event_store_client/adapters/grpc/generated/streams_services_pb'
5
-
6
3
  module EventStoreClient
7
4
  module GRPC
8
5
  module Commands
@@ -2,8 +2,6 @@
2
2
 
3
3
  # rubocop:disable Metrics/AbcSize
4
4
 
5
- require 'event_store_client/adapters/grpc/generated/shared_pb'
6
-
7
5
  module EventStoreClient
8
6
  module GRPC
9
7
  module Options
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/shared_pb'
4
-
5
3
  module EventStoreClient
6
4
  module GRPC
7
5
  module Options
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'event_store_client/adapters/grpc/generated/shared_pb'
4
-
5
3
  module EventStoreClient
6
4
  module GRPC
7
5
  module Shared
@@ -6,6 +6,7 @@ module EventStoreClient
6
6
  module Streams
7
7
  class ProcessResponse
8
8
  include Dry::Monads[:result]
9
+ include Configuration
9
10
 
10
11
  # @api private
11
12
  # @param response [EventStore::Client::Streams::ReadResp]
@@ -18,7 +19,7 @@ module EventStoreClient
18
19
  return unless response.event&.event
19
20
 
20
21
  Success(
21
- EventDeserializer.new.call(response.event.event, skip_decryption: skip_decryption)
22
+ config.mapper.deserialize(response.event.event, skip_decryption: skip_decryption)
22
23
  )
23
24
  end
24
25
  end
@@ -6,6 +6,7 @@ module EventStoreClient
6
6
  module Streams
7
7
  class ProcessResponses
8
8
  include Dry::Monads[:result]
9
+ include Configuration
9
10
 
10
11
  # @api private
11
12
  # @param responses [Array<EventStore::Client::Streams::ReadResp>]
@@ -17,12 +18,12 @@ module EventStoreClient
17
18
  return Success(responses) if skip_deserialization
18
19
 
19
20
  events =
20
- responses.map do |read_resp|
21
+ responses.map do |response|
21
22
  # It could be <EventStore::Client::Streams::ReadResp: last_stream_position: 39> for
22
23
  # example. Such responses should be skipped. See generated files for more info.
23
- next unless read_resp.event&.event
24
+ next unless response.event&.event
24
25
 
25
- EventDeserializer.new.call(read_resp.event.event, skip_decryption: skip_decryption)
26
+ config.mapper.deserialize(response.event.event, skip_decryption: skip_decryption)
26
27
  end
27
28
  Success(events.compact)
28
29
  end
@@ -3,10 +3,12 @@
3
3
  require 'grpc'
4
4
  require 'dry-monads'
5
5
 
6
+ # Load all generated by google-protobuf files
7
+ Dir[File.expand_path('grpc/generated/*.rb', __dir__)].each { |f| require f }
8
+
6
9
  require 'event_store_client/adapters/grpc/options/streams/read_options'
7
10
  require 'event_store_client/adapters/grpc/options/streams/write_options'
8
11
 
9
- require 'event_store_client/adapters/grpc/shared/event_deserializer'
10
12
  require 'event_store_client/adapters/grpc/shared/options/stream_options'
11
13
  require 'event_store_client/adapters/grpc/shared/options/filter_options'
12
14
  require 'event_store_client/adapters/grpc/shared/streams/process_response'
@@ -10,9 +10,11 @@ module EventStoreClient
10
10
 
11
11
  def call
12
12
  return encrypted_data if encryption_metadata.empty?
13
+ result = find_key(encryption_metadata['key'])
14
+ return encrypted_data unless result.success?
13
15
 
14
16
  decrypt_attributes(
15
- key: find_key(encryption_metadata['key']),
17
+ key: result.value!,
16
18
  data: encrypted_data,
17
19
  attributes: encryption_metadata['attributes']
18
20
  )
@@ -49,8 +51,9 @@ module EventStoreClient
49
51
  dupl
50
52
  end
51
53
 
54
+ # @return [Dry::Monads::Result]
52
55
  def find_key(identifier)
53
- key_repository.find(identifier).value!
56
+ key_repository.find(identifier)
54
57
  end
55
58
  end
56
59
  end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'dry/schema'
4
-
5
3
  module EventStoreClient
6
4
  class DeserializedEvent
5
+ LINK_TYPE = '$>'
6
+
7
7
  InvalidDataError = Class.new(StandardError)
8
8
  private_constant :InvalidDataError
9
9
 
@@ -12,6 +12,7 @@ module EventStoreClient
12
12
 
13
13
  # @args [Hash] opts
14
14
  # @option opts [Boolean] :skip_validation
15
+ # @option opts [UUID] :id
15
16
  # @option opts [Hash] :data
16
17
  # @option opts [Hash] :metadata
17
18
  # @option opts [String] :type
@@ -68,6 +69,12 @@ module EventStoreClient
68
69
  end
69
70
  end
70
71
 
72
+ # Detect whether an event is a link event
73
+ # @return [Boolean]
74
+ def link?
75
+ type == LINK_TYPE
76
+ end
77
+
71
78
  private
72
79
 
73
80
  def validate(data)
@@ -1,47 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Layout/LineLength
4
+
3
5
  module EventStoreClient
4
6
  module Mapper
5
7
  class Default
6
8
  attr_reader :serializer
7
9
  private :serializer
8
10
 
11
+ # @param serializer [#serialize, #deserialize]
9
12
  def initialize(serializer: Serializer::Json)
10
13
  @serializer = serializer
11
14
  end
12
15
 
16
+ # @param event [EventStoreClient::DeserializedEvent]
17
+ # @return [EventStoreClient::SerializedEvent]
13
18
  def serialize(event)
14
- Event.new(
15
- id: event.respond_to?(:id) ? event.id : nil,
16
- type: (event.respond_to?(:type) ? event.type : nil) || event.class.to_s,
17
- data: serializer.serialize(event.data),
18
- metadata: serializer.serialize(event.metadata)
19
- )
19
+ Serializer::EventSerializer.call(event, serializer: serializer)
20
20
  end
21
21
 
22
- def deserialize(event, **)
23
- metadata = serializer.deserialize(event.metadata)
24
- data = serializer.deserialize(event.data)
22
+ # @param event_or_raw_event [EventStoreClient::DeserializedEvent, EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
23
+ # @return event [EventStoreClient::DeserializedEvent]
24
+ def deserialize(event_or_raw_event, **)
25
+ return event_or_raw_event if event_or_raw_event.is_a?(EventStoreClient::DeserializedEvent)
25
26
 
26
- event_class =
27
- begin
28
- Object.const_get(event.type)
29
- rescue NameError
30
- EventStoreClient.config.default_event_class
31
- end
32
- event_class.new(
33
- skip_validation: true,
34
- id: event.id,
35
- type: event.type,
36
- title: event.title,
37
- data: data,
38
- metadata: metadata,
39
- stream_revision: event.stream_revision,
40
- commit_position: event.commit_position,
41
- prepare_position: event.prepare_position,
42
- stream_name: event.stream_name
43
- )
27
+ Serializer::EventDeserializer.call(event_or_raw_event, serializer: serializer)
44
28
  end
45
29
  end
46
30
  end
47
31
  end
32
+ # rubocop:enable Layout/LineLength
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/AbcSize, Layout/LineLength, Style/IfUnlessModifier
4
+
3
5
  require 'event_store_client/encryption_metadata'
4
6
  require 'event_store_client/data_encryptor'
5
7
  require 'event_store_client/data_decryptor'
@@ -15,88 +17,62 @@ module EventStoreClient
15
17
  attr_reader :key_repository, :serializer
16
18
  private :key_repository, :serializer
17
19
 
18
- ##
19
- # Initializes the mapper with required dependencies. Accepts:
20
- # * +key_repoistory+ - repository stored encryption keys. Passed down to the +DataEncryptor+
21
- # * +serializer+ - object used to serialize data. By default JSON serializer is used.
20
+ # @param key_repository [#find, #create, #encrypt, #decrypt]
21
+ # See spec/support/dummy_repository.rb for the example of simple in-memory implementation
22
+ # @param serializer [#serialize, #deserialize]
22
23
  def initialize(key_repository, serializer: Serializer::Json)
23
24
  @key_repository = key_repository
24
25
  @serializer = serializer
25
26
  end
26
27
 
27
- ##
28
- # Encrypts the given event's subset of data.
29
- # Accepts specific event class instance with:
30
- # * +#data+ - hash with non-encrypted values.
31
- # * encryption_schema - hash with information which data to encrypt and
32
- # which key should be used as an identifier.
33
- # *Returns*: General +Event+ instance with encrypted data
28
+ # @param event [EventStoreClient::DeserializedEvent]
29
+ # @return [Hash]
34
30
  def serialize(event)
35
- # Links does not need to be encrypted
36
- return Default.new(serializer: serializer).serialize(event) if event.type == '$>'
31
+ # Links don't need to be encrypted
32
+ return Default.new(serializer: serializer).serialize(event) if event.link?
33
+
34
+ serialized = Serializer::EventSerializer.call(event, serializer: serializer)
35
+ encryption_schema =
36
+ if event.class.respond_to?(:encryption_schema)
37
+ event.class.encryption_schema
38
+ end
37
39
 
38
- encryption_schema = (
39
- event.class.respond_to?(:encryption_schema) &&
40
- event.class.encryption_schema
41
- )
42
40
  encryptor = EventStoreClient::DataEncryptor.new(
43
- data: event.data,
41
+ data: serialized.data,
44
42
  schema: encryption_schema,
45
43
  repository: key_repository
46
44
  )
47
45
  encryptor.call
48
- EventStoreClient::Event.new(
49
- id: event.respond_to?(:id) ? event.id : nil,
50
- data: serializer.serialize(encryptor.encrypted_data),
51
- metadata: serializer.serialize(
52
- event.metadata.merge(encryption: encryptor.encryption_metadata)
53
- ),
54
- type: event.class.to_s
55
- )
46
+ serialized.data = encryptor.encrypted_data
47
+ serialized.custom_metadata['encryption'] = encryptor.encryption_metadata
48
+ serialized
56
49
  end
57
50
 
58
- ##
59
51
  # Decrypts the given event's subset of data.
60
- # General +Event+ instance with encrypted data
61
- # * +#data+ - hash with encrypted values.
62
- # * encryption_metadata - hash with information which data to encrypt and
63
- # which key should be used as an identifier.
64
- # *Returns*: Specific event class with all data decrypted
65
- def deserialize(event, skip_decryption: false)
66
- metadata = serializer.deserialize(event.metadata)
67
- encryption_schema = metadata['encryption']
52
+ # @param event_or_raw_event [EventStoreClient::DeserializedEvent, EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
53
+ # @param skip_decryption [Boolean]
54
+ # @return event [EventStoreClient::DeserializedEvent]
55
+ def deserialize(event_or_raw_event, skip_decryption: false)
56
+ if skip_decryption
57
+ return Default.new(serializer: serializer).deserialize(event_or_raw_event)
58
+ end
68
59
 
69
- decrypted_data =
70
- if skip_decryption
71
- serializer.deserialize(event.data)
60
+ event =
61
+ if event_or_raw_event.is_a?(EventStoreClient::DeserializedEvent)
62
+ event_or_raw_event
72
63
  else
73
- EventStoreClient::DataDecryptor.new(
74
- data: serializer.deserialize(event.data),
75
- schema: encryption_schema,
76
- repository: key_repository
77
- ).call
78
- end
79
-
80
- event_class =
81
- begin
82
- Object.const_get(event.type)
83
- rescue NameError
84
- EventStoreClient.config.default_event_class
64
+ Serializer::EventDeserializer.call(event_or_raw_event, serializer: serializer)
85
65
  end
86
66
 
87
- event_class.new(
88
- skip_validation: true,
89
- id: event.id,
90
- type: event.type,
91
- title: event.title,
92
- data: decrypted_data,
93
- metadata: metadata,
94
- stream_revision: event.stream_revision,
95
- commit_position: event.commit_position,
96
- prepare_position: event.prepare_position,
97
- stream_name: event.stream_name
98
- )
67
+ decrypted_data =
68
+ EventStoreClient::DataDecryptor.new(
69
+ data: event.data,
70
+ schema: event.metadata['encryption'],
71
+ repository: key_repository
72
+ ).call
73
+ event.class.new(**event.to_h.merge(data: decrypted_data, skip_validation: true))
99
74
  end
100
75
  end
101
76
  end
102
77
  end
78
+ # rubocop:enable Metrics/AbcSize, Layout/LineLength, Style/IfUnlessModifier
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreClient
4
+ class SerializedEvent
5
+ include Extensions::OptionsExtension
6
+
7
+ option(:id)
8
+ option(:data)
9
+ option(:custom_metadata)
10
+ option(:metadata)
11
+ option(:serializer)
12
+
13
+ # Constructs a hash that can be passed directly in the proposed_message attribute of the append
14
+ # request, or it can be used to instantiate the raw EventStore event.
15
+ # Example:
16
+ # ```ruby
17
+ # serialized_event = EventStoreClient::SerializedEvent.new(
18
+ # id: 'some id',
19
+ # data: { foo: :bar },
20
+ # custom_metadata: { bar: :baz },
21
+ # metadata: { baz: :foo },
22
+ # serializer: EventStoreClient::Serializer::Json
23
+ # )
24
+ # # Compute proposed_message
25
+ # EventStore::Client::Streams::AppendReq::ProposedMessage.new(
26
+ # serialized_event.to_grpc
27
+ # )
28
+ # # Compute raw event
29
+ # EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent.new(
30
+ # serialized_event.to_grpc
31
+ # )
32
+ # ```
33
+ # @return [Hash]
34
+ def to_grpc
35
+ {
36
+ id: { string: id },
37
+ data: serializer.serialize(data),
38
+ custom_metadata: serializer.serialize(custom_metadata),
39
+ metadata: metadata
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Metrics/AbcSize, Layout/LineLength
4
+
5
+ module EventStoreClient
6
+ module Serializer
7
+ class EventDeserializer
8
+ class << self
9
+ # @param raw_event [EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
10
+ # @param serializer [#serialize, #deserialize]
11
+ # @return [EventStoreClient::DeserializedEvent]
12
+ def call(raw_event, serializer: Serializer::Json)
13
+ new(serializer: serializer).call(raw_event)
14
+ end
15
+ end
16
+
17
+ attr_reader :serializer
18
+ private :serializer
19
+
20
+ # @param serializer [#serialize, #deserialize]
21
+ def initialize(serializer:)
22
+ @serializer = serializer
23
+ end
24
+
25
+ # @param raw_event [EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent, EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent]
26
+ # @return [EventStoreClient::DeserializedEvent]
27
+ def call(raw_event)
28
+ data = serializer.deserialize(normalize_serialized(raw_event.data))
29
+ custom_metadata = serializer.deserialize(normalize_serialized(raw_event.custom_metadata))
30
+ metadata = custom_metadata.merge(raw_event.metadata.to_h)
31
+
32
+ event_class(metadata['type']).new(
33
+ skip_validation: true,
34
+ id: raw_event.id.string,
35
+ title: "#{raw_event.stream_revision}@#{raw_event.stream_identifier.stream_name}",
36
+ type: metadata['type'],
37
+ data: data,
38
+ metadata: metadata,
39
+ stream_revision: raw_event.stream_revision,
40
+ commit_position: raw_event.commit_position,
41
+ prepare_position: raw_event.prepare_position,
42
+ stream_name: raw_event.stream_identifier.stream_name
43
+ )
44
+ end
45
+
46
+ private
47
+
48
+ # @param event_type [String]
49
+ # @return [Class<EventStoreClient::DeserializedEvent>]
50
+ def event_class(event_type)
51
+ Object.const_get(event_type)
52
+ rescue NameError, TypeError
53
+ config.logger&.debug(<<~TEXT.strip)
54
+ Unable to resolve class by `#{event_type}' event type. \
55
+ Picking default `#{config.default_event_class}' event class to instantiate the event.
56
+ TEXT
57
+ config.default_event_class
58
+ end
59
+
60
+ # @param raw_data [String]
61
+ # @return [String]
62
+ def normalize_serialized(raw_data)
63
+ return serializer.serialize({}) if raw_data.empty?
64
+
65
+ raw_data
66
+ end
67
+
68
+ def config
69
+ EventStoreClient.config
70
+ end
71
+ end
72
+ end
73
+ end
74
+ # rubocop:enable Metrics/AbcSize, Layout/LineLength
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EventStoreClient
4
+ module Serializer
5
+ class EventSerializer
6
+ ALLOWED_EVENT_METADATA = %w[type content-type created_at].freeze
7
+
8
+ class << self
9
+ # @param event [EventStoreClient::DeserializedEvent]
10
+ # @param serializer [#serialize, #deserialize]
11
+ # @return [EventStoreClient::SerializedEvent]
12
+ def call(event, serializer: Serializer::Json)
13
+ new(serializer: serializer).call(event)
14
+ end
15
+ end
16
+
17
+ attr_reader :serializer
18
+ private :serializer
19
+
20
+ # @param serializer [#serialize, #deserialize]
21
+ def initialize(serializer:)
22
+ @serializer = serializer
23
+ end
24
+
25
+ # @param event [EventStoreClient::DeserializedEvent]
26
+ # @return [EventStoreClient::SerializedEvent]
27
+ def call(event)
28
+ event_metadata = metadata(event)
29
+ event_custom_metadata = custom_metadata(event, event_metadata)
30
+ SerializedEvent.new(
31
+ id: event.id || SecureRandom.uuid,
32
+ data: data(event),
33
+ custom_metadata: event_custom_metadata,
34
+ metadata: event_metadata.slice(*ALLOWED_EVENT_METADATA),
35
+ serializer: serializer
36
+ )
37
+ end
38
+
39
+ private
40
+
41
+ # @param event [EventStoreClient::DeserializedEvent]
42
+ # @return [Hash]
43
+ def metadata(event)
44
+ metadata = serializer.deserialize(serializer.serialize(event.metadata))
45
+ metadata['created_at'] ||= Time.now.utc.to_s
46
+ metadata
47
+ end
48
+
49
+ # @param event [EventStoreClient::DeserializedEvent]
50
+ # @param metadata [Hash]
51
+ # @return [Hash]
52
+ def custom_metadata(event, metadata)
53
+ metadata.
54
+ slice('created_at', 'encryption', 'content-type', 'transaction').
55
+ merge('type' => event.type.to_s)
56
+ end
57
+
58
+ # @param event [EventStoreClient::DeserializedEvent]
59
+ # @return [Hash, String]
60
+ def data(event)
61
+ # Link events are special events. They contain special string value which shouldn't be
62
+ # serialized.
63
+ return event.data if event.link?
64
+
65
+ serializer.deserialize(serializer.serialize(event.data))
66
+ end
67
+ end
68
+ end
69
+ end
@@ -1,19 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module Serializer
4
- module Json
5
- def self.deserialize(data)
6
- return data if data.is_a?(Hash)
3
+ module EventStoreClient
4
+ module Serializer
5
+ module Json
6
+ # @param data [String, Hash]
7
+ # @return [Hash]
8
+ def self.deserialize(data)
9
+ return data if data.is_a?(Hash)
7
10
 
8
- JSON.parse(data)
9
- rescue JSON::ParserError
10
- { 'message' => data }
11
- end
11
+ result = JSON.parse(data)
12
+ return result if result.is_a?(Hash)
13
+
14
+ { 'message' => result }
15
+ rescue JSON::ParserError
16
+ { 'message' => data }
17
+ end
12
18
 
13
- def self.serialize(data)
14
- return data if data.is_a?(String)
19
+ # @param data [String, Object]
20
+ # @return [String]
21
+ def self.serialize(data)
22
+ return data if data.is_a?(String)
15
23
 
16
- JSON.generate(data)
24
+ JSON.generate(data)
25
+ end
17
26
  end
18
27
  end
19
28
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module EventStoreClient
4
- VERSION = '2.0.4'
4
+ VERSION = '2.1.0'
5
5
  end
@@ -3,9 +3,12 @@
3
3
  module EventStoreClient
4
4
  end
5
5
 
6
+ require 'json'
6
7
  require 'event_store_client/types'
7
8
 
8
9
  require 'event_store_client/serializer/json'
10
+ require 'event_store_client/serializer/event_serializer'
11
+ require 'event_store_client/serializer/event_deserializer'
9
12
 
10
13
  require 'event_store_client/mapper'
11
14
 
@@ -15,8 +18,8 @@ require 'event_store_client/utils'
15
18
 
16
19
  require 'event_store_client/connection/url'
17
20
  require 'event_store_client/connection/url_parser'
18
- require 'event_store_client/event'
19
21
  require 'event_store_client/deserialized_event'
22
+ require 'event_store_client/serialized_event'
20
23
  require 'event_store_client/configuration'
21
24
 
22
25
  require 'event_store_client/adapters/grpc'
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: 2.0.4
4
+ version: 2.1.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: 2022-10-27 00:00:00.000000000 Z
11
+ date: 2022-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-configurable
@@ -38,34 +38,6 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1'
41
- - !ruby/object:Gem::Dependency
42
- name: dry-schema
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '1'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '1'
55
- - !ruby/object:Gem::Dependency
56
- name: dry-struct
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1'
69
41
  - !ruby/object:Gem::Dependency
70
42
  name: grpc
71
43
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +150,20 @@ dependencies:
178
150
  - - "~>"
179
151
  - !ruby/object:Gem::Version
180
152
  version: 0.9.5
153
+ - !ruby/object:Gem::Dependency
154
+ name: dry-schema
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: '1'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: '1'
181
167
  description: Easy to use client for event-sources applications written in ruby
182
168
  email:
183
169
  - sebastian@driggl.com
@@ -240,7 +226,6 @@ files:
240
226
  - lib/event_store_client/adapters/grpc/generated/users_services_pb.rb
241
227
  - lib/event_store_client/adapters/grpc/options/streams/read_options.rb
242
228
  - lib/event_store_client/adapters/grpc/options/streams/write_options.rb
243
- - lib/event_store_client/adapters/grpc/shared/event_deserializer.rb
244
229
  - lib/event_store_client/adapters/grpc/shared/options/filter_options.rb
245
230
  - lib/event_store_client/adapters/grpc/shared/options/stream_options.rb
246
231
  - lib/event_store_client/adapters/grpc/shared/streams/process_response.rb
@@ -252,11 +237,13 @@ files:
252
237
  - lib/event_store_client/data_encryptor.rb
253
238
  - lib/event_store_client/deserialized_event.rb
254
239
  - lib/event_store_client/encryption_metadata.rb
255
- - lib/event_store_client/event.rb
256
240
  - lib/event_store_client/extensions/options_extension.rb
257
241
  - lib/event_store_client/mapper.rb
258
242
  - lib/event_store_client/mapper/default.rb
259
243
  - lib/event_store_client/mapper/encrypted.rb
244
+ - lib/event_store_client/serialized_event.rb
245
+ - lib/event_store_client/serializer/event_deserializer.rb
246
+ - lib/event_store_client/serializer/event_serializer.rb
260
247
  - lib/event_store_client/serializer/json.rb
261
248
  - lib/event_store_client/types.rb
262
249
  - lib/event_store_client/utils.rb
@@ -1,52 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # rubocop:disable Metrics/AbcSize
4
-
5
- module EventStoreClient
6
- module GRPC
7
- module Shared
8
- class EventDeserializer
9
- include Configuration
10
-
11
- # @param raw_event [
12
- # Array<EventStore::Client::Streams::ReadResp::ReadEvent::RecordedEvent>,
13
- # Array<EventStore::Client::PersistentSubscriptions::ReadResp::ReadEvent::RecordedEvent>
14
- # ]
15
- # @param skip_decryption [Boolean]
16
- # @return [EventStoreClient::DeserializedEvent]
17
- def call(raw_event, skip_decryption: false)
18
- data = normalize_serialized(raw_event.data)
19
- custom_metadata = normalize_serialized(raw_event.custom_metadata)
20
-
21
- metadata =
22
- JSON.parse(custom_metadata).merge(raw_event.metadata.to_h).to_json
23
-
24
- event = EventStoreClient::Event.new(
25
- id: raw_event.id.string,
26
- title: "#{raw_event.stream_revision}@#{raw_event.stream_identifier.stream_name}",
27
- type: raw_event.metadata['type'],
28
- data: data,
29
- metadata: metadata,
30
- stream_revision: raw_event.stream_revision,
31
- commit_position: raw_event.commit_position,
32
- prepare_position: raw_event.prepare_position,
33
- stream_name: raw_event.stream_identifier.stream_name
34
- )
35
-
36
- config.mapper.deserialize(event, skip_decryption: skip_decryption)
37
- end
38
-
39
- private
40
-
41
- # @param raw_data [String, nil]
42
- # @return [String]
43
- def normalize_serialized(raw_data)
44
- return {}.to_json if raw_data.nil? || raw_data.empty?
45
-
46
- raw_data
47
- end
48
- end
49
- end
50
- end
51
- end
52
- # rubocop:enable Metrics/AbcSize
@@ -1,27 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'dry-struct'
4
- require 'securerandom'
5
- require 'json'
6
-
7
- module EventStoreClient
8
- class Event < Dry::Struct
9
- attribute :id, Types::Strict::String.optional.default(nil)
10
- attribute :type, Types::Strict::String
11
- attribute :title, Types::Strict::String.optional.default(nil)
12
- attribute :data, Types::Strict::String.default('{}')
13
- attribute :metadata, Types::Strict::String.default('{}')
14
- attribute :stream_name, Types::Strict::String.optional.default(nil)
15
- attribute :stream_revision, Types::Strict::Integer.optional.default(nil)
16
- attribute :commit_position, Types::Strict::Integer.optional.default(nil)
17
- attribute :prepare_position, Types::Strict::Integer.optional.default(nil)
18
-
19
- def initialize(args = {})
20
- args[:id] = SecureRandom.uuid if args[:id].nil?
21
- hash_meta = JSON.parse(args[:metadata] || '{}')
22
- hash_meta['created_at'] ||= Time.now
23
- args[:metadata] = JSON.generate(hash_meta)
24
- super(args)
25
- end
26
- end
27
- end