event_store_client 0.2.8 → 1.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 (80) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +29 -37
  3. data/lib/event_store_client.rb +0 -2
  4. data/lib/event_store_client/adapters/grpc.rb +21 -0
  5. data/lib/event_store_client/adapters/grpc/Protos/cluster.proto +149 -0
  6. data/lib/event_store_client/adapters/grpc/Protos/gossip.proto +44 -0
  7. data/lib/event_store_client/adapters/grpc/Protos/operations.proto +45 -0
  8. data/lib/event_store_client/adapters/grpc/Protos/persistent.proto +180 -0
  9. data/lib/event_store_client/adapters/grpc/Protos/projections.proto +174 -0
  10. data/lib/event_store_client/adapters/grpc/Protos/shared.proto +22 -0
  11. data/lib/event_store_client/adapters/grpc/Protos/streams.proto +242 -0
  12. data/lib/event_store_client/adapters/grpc/Protos/users.proto +119 -0
  13. data/lib/event_store_client/adapters/grpc/client.rb +119 -0
  14. data/lib/event_store_client/adapters/grpc/command_registrar.rb +32 -0
  15. data/lib/event_store_client/adapters/grpc/commands/command.rb +43 -0
  16. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/create.rb +46 -0
  17. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/delete.rb +34 -0
  18. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/read.rb +66 -0
  19. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/settings_schema.rb +38 -0
  20. data/lib/event_store_client/adapters/grpc/commands/persistent_subscriptions/update.rb +48 -0
  21. data/lib/event_store_client/adapters/grpc/commands/projections/create.rb +45 -0
  22. data/lib/event_store_client/adapters/grpc/commands/projections/delete.rb +34 -0
  23. data/lib/event_store_client/adapters/grpc/commands/projections/update.rb +42 -0
  24. data/lib/event_store_client/adapters/grpc/commands/streams/append.rb +57 -0
  25. data/lib/event_store_client/adapters/grpc/commands/streams/delete.rb +35 -0
  26. data/lib/event_store_client/adapters/grpc/commands/streams/link_to.rb +53 -0
  27. data/lib/event_store_client/adapters/grpc/commands/streams/read.rb +80 -0
  28. data/lib/event_store_client/adapters/grpc/commands/streams/read_all.rb +43 -0
  29. data/lib/event_store_client/adapters/grpc/commands/streams/tombstone.rb +35 -0
  30. data/lib/event_store_client/adapters/grpc/connection.rb +50 -0
  31. data/lib/event_store_client/adapters/grpc/generated/cluster_pb.rb +140 -0
  32. data/lib/event_store_client/adapters/grpc/generated/cluster_services_pb.rb +46 -0
  33. data/lib/event_store_client/adapters/grpc/generated/gossip_pb.rb +53 -0
  34. data/lib/event_store_client/adapters/grpc/generated/gossip_services_pb.rb +26 -0
  35. data/lib/event_store_client/adapters/grpc/generated/operations_pb.rb +49 -0
  36. data/lib/event_store_client/adapters/grpc/generated/operations_services_pb.rb +31 -0
  37. data/lib/event_store_client/adapters/grpc/generated/persistent_pb.rb +213 -0
  38. data/lib/event_store_client/adapters/grpc/generated/persistent_services_pb.rb +29 -0
  39. data/lib/event_store_client/adapters/grpc/generated/projections_pb.rb +193 -0
  40. data/lib/event_store_client/adapters/grpc/generated/projections_services_pb.rb +34 -0
  41. data/lib/event_store_client/adapters/grpc/generated/shared_pb.rb +35 -0
  42. data/lib/event_store_client/adapters/grpc/generated/streams_pb.rb +283 -0
  43. data/lib/event_store_client/adapters/grpc/generated/streams_services_pb.rb +29 -0
  44. data/lib/event_store_client/adapters/grpc/generated/users_pb.rb +126 -0
  45. data/lib/event_store_client/adapters/grpc/generated/users_services_pb.rb +33 -0
  46. data/lib/event_store_client/adapters/http.rb +16 -0
  47. data/lib/event_store_client/adapters/http/README.md +16 -0
  48. data/lib/event_store_client/adapters/http/client.rb +160 -0
  49. data/lib/event_store_client/adapters/http/commands/command.rb +27 -0
  50. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/ack.rb +15 -0
  51. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/create.rb +31 -0
  52. data/lib/event_store_client/adapters/http/commands/persistent_subscriptions/read.rb +57 -0
  53. data/lib/event_store_client/adapters/http/commands/projections/create.rb +30 -0
  54. data/lib/event_store_client/adapters/http/commands/streams/append.rb +49 -0
  55. data/lib/event_store_client/adapters/http/commands/streams/delete.rb +16 -0
  56. data/lib/event_store_client/adapters/http/commands/streams/link_to.rb +49 -0
  57. data/lib/event_store_client/adapters/http/commands/streams/read.rb +53 -0
  58. data/lib/event_store_client/adapters/http/commands/streams/tombstone.rb +17 -0
  59. data/lib/event_store_client/adapters/http/connection.rb +46 -0
  60. data/lib/event_store_client/adapters/http/request_method.rb +28 -0
  61. data/lib/event_store_client/adapters/in_memory.rb +139 -0
  62. data/lib/event_store_client/broker.rb +16 -5
  63. data/lib/event_store_client/client.rb +17 -69
  64. data/lib/event_store_client/configuration.rb +28 -14
  65. data/lib/event_store_client/data_decryptor.rb +13 -8
  66. data/lib/event_store_client/data_encryptor.rb +7 -6
  67. data/lib/event_store_client/deserialized_event.rb +4 -1
  68. data/lib/event_store_client/event.rb +2 -2
  69. data/lib/event_store_client/mapper/default.rb +0 -1
  70. data/lib/event_store_client/serializer/json.rb +2 -0
  71. data/lib/event_store_client/subscriptions.rb +4 -13
  72. data/lib/event_store_client/types.rb +3 -1
  73. data/lib/event_store_client/value_objects/read_direction.rb +43 -0
  74. data/lib/event_store_client/version.rb +1 -1
  75. metadata +97 -15
  76. data/lib/event_store_client/store_adapter.rb +0 -10
  77. data/lib/event_store_client/store_adapter/api/client.rb +0 -224
  78. data/lib/event_store_client/store_adapter/api/connection.rb +0 -43
  79. data/lib/event_store_client/store_adapter/api/request_method.rb +0 -30
  80. data/lib/event_store_client/store_adapter/in_memory.rb +0 -160
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EventStoreClient
4
- module StoreAdapter
5
- end
6
- end
7
-
8
- require 'event_store_client/store_adapter/api/request_method'
9
- require 'event_store_client/store_adapter/api/connection'
10
- require 'event_store_client/store_adapter/api/client'
@@ -1,224 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EventStoreClient
4
- module StoreAdapter
5
- module Api
6
- class Client
7
- WrongExpectedEventVersion = Class.new(StandardError)
8
-
9
- def append_to_stream(stream_name, events, expected_version: nil)
10
- serialized_events = events.map { |event| mapper.serialize(event) }
11
- headers = {
12
- 'ES-ExpectedVersion' => expected_version&.to_s
13
- }.reject { |_key, val| val.nil? || val.empty? }
14
-
15
- data = build_events_data(serialized_events)
16
- response = make_request(:post, "/streams/#{stream_name}", body: data, headers: headers)
17
- validate_response(response, expected_version)
18
- response
19
- serialized_events
20
- end
21
-
22
- def delete_stream(stream_name, hard_delete: false)
23
- headers = {
24
- 'ES-HardDelete' => hard_delete.to_s
25
- }.reject { |_key, val| val.empty? }
26
-
27
- make_request(:delete, "/streams/#{stream_name}", body: {}, headers: headers)
28
- end
29
-
30
- def read(stream_name, direction: 'forward', start: 0, count: per_page, resolve_links: true)
31
- headers = {
32
- 'ES-ResolveLinkTos' => resolve_links.to_s,
33
- 'Accept' => 'application/vnd.eventstore.atom+json'
34
- }
35
-
36
- response = make_request(
37
- :get,
38
- "/streams/#{stream_name}/#{start}/#{direction}/#{count}",
39
- headers: headers
40
- )
41
- return [] if response.body.nil? || response.body.empty?
42
- JSON.parse(response.body)['entries'].map do |entry|
43
- deserialize_event(entry)
44
- end.reverse
45
- end
46
-
47
- def read_all_from_stream(stream, direction: 'forward', start: 0, resolve_links: true)
48
- count = per_page
49
- events = []
50
- entries = []
51
- failed_requests_count = 0
52
-
53
- while failed_requests_count < 3
54
- begin
55
- entries =
56
- read(stream, start: start, direction: direction, resolve_links: resolve_links)
57
- rescue Faraday::ConnectionFailed
58
- failed_requests_count += 1
59
- next
60
- end
61
- failed_requests_count = 0
62
- break if entries.empty?
63
- events += entries.reverse
64
- start += count
65
- end
66
- events
67
- end
68
-
69
- def join_streams(name, streams)
70
- data = <<~STRING
71
- fromStreams(#{streams})
72
- .when({
73
- $any: function(s,e) {
74
- linkTo("#{name}", e)
75
- }
76
- })
77
- STRING
78
-
79
- make_request(
80
- :post,
81
- "/projections/continuous?name=#{name}&type=js&enabled=true&emit=true%26trackemittedstreams=true", # rubocop:disable Metrics/LineLength
82
- body: data,
83
- headers: {}
84
- )
85
- end
86
-
87
- def subscribe_to_stream(
88
- stream_name, subscription_name, stats: true, start_from: 0, retries: 5
89
- )
90
- make_request(
91
- :put,
92
- "/subscriptions/#{stream_name}/#{subscription_name}",
93
- body: {
94
- extraStatistics: stats,
95
- startFrom: start_from,
96
- maxRetryCount: retries,
97
- resolveLinkTos: true
98
- },
99
- headers: {
100
- 'Content-Type' => 'application/json'
101
- }
102
- )
103
- end
104
-
105
- def consume_feed(
106
- stream_name,
107
- subscription_name,
108
- count: 1,
109
- long_poll: 0,
110
- resolve_links: true,
111
- per_page: 20
112
- )
113
- headers = long_poll.positive? ? { 'ES-LongPoll' => long_poll.to_s } : {}
114
- headers['Content-Type'] = 'application/vnd.eventstore.competingatom+json'
115
- headers['Accept'] = 'application/vnd.eventstore.competingatom+json'
116
- headers['ES-ResolveLinktos'] = resolve_links.to_s
117
-
118
- response = make_request(
119
- :get,
120
- "/subscriptions/#{stream_name}/#{subscription_name}/#{count}",
121
- headers: headers
122
- )
123
-
124
- return { events: [] } if response.body || response.body.empty?
125
-
126
- body = JSON.parse(response.body)
127
-
128
- ack_info = body['links'].find { |link| link['relation'] == 'ackAll' }
129
- return unless ack_info
130
- ack_uri = ack_info['uri']
131
- events = body['entries'].map do |entry|
132
- deserialize_event(entry)
133
- end
134
- { ack_uri: ack_uri, events: events }
135
- end
136
-
137
- def link_to(stream_name, events, expected_version: nil)
138
- data = build_linkig_data(events)
139
- headers = {
140
- 'ES-ExpectedVersion' => expected_version&.to_s
141
- }.reject { |_key, val| val.nil? || val.empty? }
142
-
143
- response = make_request(
144
- :post,
145
- "/streams/#{stream_name}",
146
- body: data,
147
- headers: headers
148
- )
149
- validate_response(response, expected_version)
150
- true
151
- end
152
-
153
- def ack(url)
154
- make_request(:post, url)
155
- end
156
-
157
- private
158
-
159
- attr_reader :uri, :per_page, :connection_options, :mapper
160
-
161
- def initialize(uri, per_page: 20, mapper:, connection_options: {})
162
- @uri = uri
163
- @per_page = per_page
164
- @mapper = mapper
165
- @connection_options = connection_options
166
- end
167
-
168
- def build_events_data(events)
169
- [events].flatten.map do |event|
170
- {
171
- eventId: event.id,
172
- eventType: event.type,
173
- data: event.data,
174
- metadata: event.metadata
175
- }
176
- end
177
- end
178
-
179
- def build_linkig_data(events)
180
- [events].flatten.map do |event|
181
- {
182
- eventId: event.id,
183
- eventType: '$>',
184
- data: event.title,
185
- }
186
- end
187
- end
188
-
189
- def make_request(method_name, path, body: {}, headers: {})
190
- method = RequestMethod.new(method_name)
191
- connection.send(method.to_s, path) do |req|
192
- req.headers = req.headers.merge(headers)
193
- req.body = body.is_a?(String) ? body : body.to_json
194
- req.params['embed'] = 'body' if method == :get
195
- end
196
- end
197
-
198
- def connection
199
- @connection ||= Api::Connection.new(uri, connection_options).call
200
- end
201
-
202
- def validate_response(resp, expected_version)
203
- return unless resp.status == 400 && resp.reason_phrase == 'Wrong expected EventNumber'
204
- raise WrongExpectedEventVersion.new(
205
- "current version: #{resp.headers.fetch('es-currentversion')} | "\
206
- "expected: #{expected_version}"
207
- )
208
- end
209
-
210
- def deserialize_event(entry)
211
- event = EventStoreClient::Event.new(
212
- id: entry['eventId'],
213
- title: entry['title'],
214
- type: entry['eventType'],
215
- data: entry['data'] || '{}',
216
- metadata: entry['isMetaData'] ? entry['metaData'] : '{}'
217
- )
218
-
219
- mapper.deserialize(event)
220
- end
221
- end
222
- end
223
- end
224
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'faraday'
4
-
5
- module EventStoreClient
6
- module StoreAdapter
7
- module Api
8
- class Connection
9
- def call
10
- Faraday.new(
11
- {
12
- url: uri.to_s,
13
- headers: DEFAULT_HEADERS
14
- }.merge(options)
15
- ) do |conn|
16
- conn.basic_auth(config.eventstore_user, config.eventstore_password)
17
- conn.adapter Faraday.default_adapter
18
- end
19
- end
20
-
21
- private
22
-
23
- def config
24
- EventStoreClient.config
25
- end
26
-
27
- def initialize(uri, options = {})
28
- @uri = uri
29
- @options = options
30
- end
31
-
32
- attr_reader :uri, :options
33
-
34
- DEFAULT_HEADERS = {
35
- 'Content-Type' => 'application/vnd.eventstore.events+json'
36
- # 'Accept' => 'application/vnd.eventstore.atom+json',
37
- # 'ES-EventType' => 'UserRegistered',
38
- # 'ES-EventId' => SecureRandom.uuid
39
- }.freeze
40
- end
41
- end
42
- end
43
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EventStoreClient
4
- module StoreAdapter
5
- module Api
6
- class RequestMethod
7
- InvalidMethodError = Class.new(StandardError)
8
- def ==(other)
9
- name == other.to_s
10
- end
11
-
12
- def to_s
13
- name
14
- end
15
-
16
- private
17
-
18
- attr_reader :name
19
-
20
- SUPPORTED_METHODS = %w[get post put delete].freeze
21
-
22
- def initialize(name)
23
- raise InvalidMethodError unless SUPPORTED_METHODS.include?(name.to_s)
24
-
25
- @name = name.to_s
26
- end
27
- end
28
- end
29
- end
30
- end
@@ -1,160 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EventStoreClient
4
- module StoreAdapter
5
- class InMemory
6
- Response = Struct.new(:body, :status) do
7
- def success?
8
- status == 200
9
- end
10
- end
11
-
12
- attr_reader :event_store
13
-
14
- def append_to_stream(stream_name, events, expected_version: nil) # rubocop:disable Lint/UnusedMethodArgument,Metrics/LineLength
15
- event_store[stream_name] = [] unless event_store.key?(stream_name)
16
- [events].flatten.each do |event|
17
- event_store[stream_name].unshift(
18
- 'eventId' => event.id,
19
- 'data' => event.data,
20
- 'eventType' => event.type,
21
- 'metaData' => event.metadata,
22
- 'positionEventNumber' => event_store[stream_name].length
23
- )
24
- end
25
- end
26
-
27
- def read(stream_name, direction: 'forward', start: 0, resolve_links: nil)
28
- response =
29
- if direction == 'forward'
30
- read_stream_forward(stream_name, start: start)
31
- else
32
- read_stream_backward(stream_name, start: start)
33
- end
34
-
35
- res = Response.new(response.to_json, 200)
36
-
37
- return [] if res.body.nil? || res.body.empty?
38
- JSON.parse(res.body)['entries'].map do |entry|
39
- deserialize_event(entry)
40
- end.reverse
41
- end
42
-
43
- def read_all_from_stream(stream_name, direction: 'forward', start: 0, resolve_links: true)
44
- response =
45
- if direction == 'forward'
46
- read_stream_forward(stream_name, start: start)
47
- else
48
- read_stream_backward(stream_name, start: start)
49
- end
50
- res = Response.new(response.to_json, 200)
51
-
52
- return [] if res.body.nil? || res.body.empty?
53
- JSON.parse(res.body)['entries'].map do |entry|
54
- deserialize_event(entry)
55
- end.reverse
56
- end
57
-
58
- def subscribe_to_stream(stream_name, subscription_name, **)
59
- # TODO: implement method body
60
- end
61
-
62
- def consume_feed(
63
- stream_name,
64
- subscription_name,
65
- **
66
- )
67
- # TODO: implement method body
68
- end
69
-
70
- def delete_stream(stream_name, hard_delete: false) # rubocop:disable Lint/UnusedMethodArgument
71
- event_store.delete(stream_name)
72
- end
73
-
74
- def link_to(stream_name, events, **)
75
- append_to_stream(stream_name, events)
76
- events
77
- end
78
-
79
- def ack(url)
80
- # TODO: implement method body
81
- end
82
-
83
- def join_streams(name, streams); end
84
-
85
- private
86
-
87
- attr_reader :per_page, :mapper
88
-
89
- def initialize(mapper:, per_page: 20)
90
- @per_page = per_page
91
- @mapper = mapper
92
- @event_store = {}
93
- end
94
-
95
- def read_stream_backward(stream_name, start: 0)
96
- response = {
97
- 'entries' => [],
98
- 'links' => []
99
- }
100
- return response unless event_store.key?(stream_name)
101
-
102
- start = start == 'head' ? event_store[stream_name].length - 1 : start
103
- last_index = start - per_page
104
- response['entries'] = event_store[stream_name].select do |event|
105
- event['positionEventNumber'] > last_index &&
106
- event['positionEventNumber'] <= start
107
- end
108
- response['links'] = links(stream_name, last_index, 'next', response['entries'], per_page)
109
-
110
- response
111
- end
112
-
113
- def read_stream_forward(stream_name, start: 0)
114
- response = {
115
- 'entries' => [],
116
- 'links' => []
117
- }
118
-
119
- return response unless event_store.key?(stream_name)
120
-
121
- last_index = start + per_page
122
- response['entries'] = event_store[stream_name].select do |event|
123
- event['positionEventNumber'] < last_index &&
124
- event['positionEventNumber'] >= start
125
- end
126
- response['links'] =
127
- links(stream_name, last_index, 'previous', response['entries'], per_page)
128
-
129
- response
130
- end
131
-
132
- def links(stream_name, batch_size, direction, entries, count)
133
- if entries.empty? || batch_size.negative?
134
- []
135
- else
136
- [{
137
- 'uri' =>
138
- "/streams/#{stream_name}/#{batch_size}/#{direction}/#{count}",
139
- 'relation' => direction
140
- }]
141
- end
142
- end
143
-
144
- private
145
-
146
- def deserialize_event(entry)
147
- data = (entry['data'].is_a?(Hash) ? entry['data'].to_json.presence : entry['data']) || '{}'
148
- event = EventStoreClient::Event.new(
149
- id: entry['eventId'],
150
- title: entry['title'],
151
- type: entry['eventType'],
152
- data: data,
153
- metadata: entry['isMetaData'] ? entry['metaData'] : '{}'
154
- )
155
-
156
- mapper.deserialize(event)
157
- end
158
- end
159
- end
160
- end