pg_eventstore 0.4.0 → 0.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/CODE_OF_CONDUCT.md +1 -1
- data/README.md +1 -0
- data/db/migrations/13_remove_duplicated_index.sql +1 -0
- data/docs/linking_events.md +96 -0
- data/docs/reading_events.md +56 -0
- data/lib/pg_eventstore/client.rb +30 -0
- data/lib/pg_eventstore/commands/append.rb +3 -11
- data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +22 -0
- data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +24 -0
- data/lib/pg_eventstore/commands/link_to.rb +33 -0
- data/lib/pg_eventstore/commands/regular_stream_read_paginated.rb +63 -0
- data/lib/pg_eventstore/commands/system_stream_read_paginated.rb +62 -0
- data/lib/pg_eventstore/commands.rb +5 -0
- data/lib/pg_eventstore/errors.rb +21 -4
- data/lib/pg_eventstore/event_deserializer.rb +3 -12
- data/lib/pg_eventstore/extensions/options_extension.rb +44 -6
- data/lib/pg_eventstore/pg_connection.rb +20 -3
- data/lib/pg_eventstore/queries/event_queries.rb +15 -10
- data/lib/pg_eventstore/queries/event_type_queries.rb +11 -0
- data/lib/pg_eventstore/queries/preloader.rb +37 -0
- data/lib/pg_eventstore/queries/stream_queries.rb +14 -1
- data/lib/pg_eventstore/queries/subscription_queries.rb +7 -1
- data/lib/pg_eventstore/queries.rb +1 -0
- data/lib/pg_eventstore/query_builders/events_filtering_query.rb +3 -20
- data/lib/pg_eventstore/stream.rb +2 -1
- data/lib/pg_eventstore/tasks/setup.rake +4 -0
- data/lib/pg_eventstore/version.rb +1 -1
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e1716f5a502b55d670c94cd1950f03dfa1e307050a665435840004e1eb6e2158
|
4
|
+
data.tar.gz: d76b116150c351e00fa72b88c03472dbe987140c88b00e1211e81e7eb580a8cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 131c82d96b36d105a8b5f90829864ceca96209a5dfd09ed56b27e55f7f6dc755f30fffc10c837f62017ee557a7611518f5550b43d21af00112399e7f0fe3ede1
|
7
|
+
data.tar.gz: ce52feba0bee2e4737a9e8edae5fa880ca1d66cd15e07045bd6bd965713a7f5aed0370e119d3edf63760340f369c18c242555ae0ef0d3c94cec9447791d39ec0
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
## [Unreleased]
|
2
|
+
|
3
|
+
## [0.5.0] - 2024-02-05
|
4
|
+
|
5
|
+
- Fix event class resolving when providing `resolve_link_tos: true` option
|
6
|
+
- Return correct stream revision of the `Event#stream` object of the appended event
|
7
|
+
- Implement events linking feature
|
8
|
+
- Implement paginated read
|
9
|
+
- Remove duplicated `idx_events_event_type_id` index
|
10
|
+
|
1
11
|
## [0.4.0] - 2024-01-29
|
2
12
|
|
3
13
|
- Implement asynchronous subscriptions. Refer to the documentation for more info
|
data/CODE_OF_CONDUCT.md
CHANGED
@@ -39,7 +39,7 @@ This Code of Conduct applies within all community spaces, and also applies when
|
|
39
39
|
|
40
40
|
## Enforcement
|
41
41
|
|
42
|
-
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at
|
42
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at ivan.dzyzenko@gmail.com. All complaints will be reviewed and investigated promptly and fairly.
|
43
43
|
|
44
44
|
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
45
45
|
|
data/README.md
CHANGED
@@ -52,6 +52,7 @@ Documentation chapters:
|
|
52
52
|
- [Configuration](docs/configuration.md)
|
53
53
|
- [Events and streams definitions](docs/events_and_streams.md)
|
54
54
|
- [Appending events](docs/appending_events.md)
|
55
|
+
- [Linking events](docs/linking_events.md)
|
55
56
|
- [Reading events](docs/reading_events.md)
|
56
57
|
- [Subscriptions](docs/subscriptions.md)
|
57
58
|
- [Writing middlewares](docs/writing_middleware.md)
|
@@ -0,0 +1 @@
|
|
1
|
+
DROP INDEX idx_events_event_type_id;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# Linking Events
|
2
|
+
|
3
|
+
## Linking single event
|
4
|
+
|
5
|
+
You can create a link to an existing event. Next example demonstrates how you can link an existing event from another stream:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class SomethingHappened < PgEventstore::Event
|
9
|
+
end
|
10
|
+
|
11
|
+
event = SomethingHappened.new(
|
12
|
+
type: 'some-event', data: { title: 'Something happened' }
|
13
|
+
)
|
14
|
+
|
15
|
+
events_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeStream', stream_id: '1')
|
16
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: '1')
|
17
|
+
# Persist our event
|
18
|
+
event = PgEventstore.client.append_to_stream(events_stream, event)
|
19
|
+
|
20
|
+
# Link event into your projection
|
21
|
+
PgEventstore.client.link_to(projection_stream, event)
|
22
|
+
```
|
23
|
+
|
24
|
+
The linked event can later be fetched by providing the `:resolve_link_tos` option when reading from the stream:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: '1')
|
28
|
+
PgEventstore.client.read(projection_stream, options: { resolve_link_tos: true })
|
29
|
+
```
|
30
|
+
|
31
|
+
If you don't provide the `:resolve_link_tos` option, the linked event will be returned instead of the original one.
|
32
|
+
|
33
|
+
## Linking multiple events
|
34
|
+
|
35
|
+
You can provide an array of events to link to the target stream:
|
36
|
+
|
37
|
+
```ruby
|
38
|
+
class SomethingHappened < PgEventstore::Event
|
39
|
+
end
|
40
|
+
|
41
|
+
events = 3.times.map { |i| SomethingHappened.new(type: 'some-event', data: { title: "Something happened-#{i}" }) }
|
42
|
+
events_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeStream', stream_id: '1')
|
43
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: '1')
|
44
|
+
events = PgEventstore.client.append_to_stream(events_stream, events)
|
45
|
+
# Link events
|
46
|
+
PgEventstore.client.link_to(projection_stream, events)
|
47
|
+
```
|
48
|
+
|
49
|
+
## Handling concurrency
|
50
|
+
|
51
|
+
Linking concurrency is implemented the same way as appending concurrency. You can check [**Handling concurrency**](appending_events.md#handling-concurrency) chapter of **Appending Events** section.
|
52
|
+
|
53
|
+
Example:
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'securerandom'
|
57
|
+
class SomethingHappened < PgEventstore::Event
|
58
|
+
end
|
59
|
+
|
60
|
+
event1 = SomethingHappened.new
|
61
|
+
event2 = SomethingHappened.new
|
62
|
+
|
63
|
+
events_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeStream', stream_id: '1')
|
64
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: SecureRandom.uuid)
|
65
|
+
|
66
|
+
event1, event2 = PgEventstore.client.append_to_stream(events_stream, [event1, event2])
|
67
|
+
|
68
|
+
# Links first event
|
69
|
+
PgEventstore.client.link_to(projection_stream, event1, options: { expected_revision: :no_stream })
|
70
|
+
# Raises PgEventstore::WrongExpectedVersionError error because stream already exists
|
71
|
+
PgEventstore.client.link_to(projection_stream, event2, options: { expected_revision: :no_stream })
|
72
|
+
```
|
73
|
+
|
74
|
+
## Middlewares
|
75
|
+
|
76
|
+
If you would like to modify your link events before they get persisted - you should use the `:middlewares` argument which allows you to pass the list of middlewares you would like to use. **By default no middlewares will be applied to the link event despite on `config.middlewares` option**.
|
77
|
+
|
78
|
+
Let's say you have these registered middlewares:
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
PgEventstore.configure do |config|
|
82
|
+
config.middlewares = { foo: FooMiddleware.new, bar: BarMiddleware.new, baz: BazMiddleware.new }
|
83
|
+
end
|
84
|
+
```
|
85
|
+
|
86
|
+
And you want to use `FooMiddleware` and `BazMiddleware`. You simply have to provide an array of corresponding middleware keys you would like to use:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
events_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeStream', stream_id: '1')
|
90
|
+
projection_stream = PgEventstore::Stream.new(context: 'FooCtx', stream_name: 'MyAwesomeProjection', stream_id: '1')
|
91
|
+
|
92
|
+
event = PgEventstore.client.append_to_stream(events_stream, PgEventstore::Event.new)
|
93
|
+
PgEventstore.client.link_to(projection_stream, event, middlewares: %i[foo baz])
|
94
|
+
```
|
95
|
+
|
96
|
+
See [Writing middleware](writing_middleware.md) chapter for info about what is middleware and how to implement it.
|
data/docs/reading_events.md
CHANGED
@@ -159,3 +159,59 @@ You can also mix filtering by stream's attributes and event types. The result wi
|
|
159
159
|
```ruby
|
160
160
|
PgEventstore.client.read(PgEventstore::Stream.all_stream, options: { filter: { streams: [{ context: 'MyAwesomeContext' }], event_types: %w[Foo Bar] } })
|
161
161
|
```
|
162
|
+
|
163
|
+
|
164
|
+
## Pagination
|
165
|
+
|
166
|
+
You can use `#read_paginated` to iterate over all (filtered) events. It yields each batch of records that was found according to the filter options:
|
167
|
+
|
168
|
+
```ruby
|
169
|
+
# Read from the specific stream
|
170
|
+
stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
171
|
+
PgEventstore.client.read_paginated(stream).each do |events|
|
172
|
+
events.each do |event|
|
173
|
+
# iterate through events
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Read from "all" stream
|
178
|
+
PgEventstore.client.read_paginated(PgEventstore::Stream.all_stream).each do |events|
|
179
|
+
events.each do |event|
|
180
|
+
# iterate through events
|
181
|
+
end
|
182
|
+
end
|
183
|
+
```
|
184
|
+
|
185
|
+
Options are the same as for `#read` method. Several examples:
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# Read "Foo" events only from the specific stream
|
189
|
+
stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'User', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
190
|
+
PgEventstore.client.read_paginated(stream, options: { filter: { event_types: ['Foo'] } }).each do |events|
|
191
|
+
events.each do |event|
|
192
|
+
# iterate through events
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# Backwards read from "all" stream
|
197
|
+
PgEventstore.client.read_paginated(PgEventstore::Stream.all_stream, options: { direction: 'Backwards' }).each do |events|
|
198
|
+
events.each do |event|
|
199
|
+
# iterate through events
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
# Set batch size to 100
|
204
|
+
PgEventstore.client.read_paginated(PgEventstore::Stream.all_stream, options: { max_count: 100 }).each do |events|
|
205
|
+
events.each do |event|
|
206
|
+
# iterate through events
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Reading from projection stream
|
211
|
+
projection_stream = PgEventstore::Stream.new(context: 'MyAwesomeContext', stream_name: 'MyAwesomeProjection', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
212
|
+
PgEventstore.client.read_paginated(projection_stream, options: { resolve_link_tos: true }).each do |events|
|
213
|
+
events.each do |event|
|
214
|
+
# iterate through events
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
data/lib/pg_eventstore/client.rb
CHANGED
@@ -109,6 +109,36 @@ module PgEventstore
|
|
109
109
|
call(stream, options: { max_count: config.max_count }.merge(options))
|
110
110
|
end
|
111
111
|
|
112
|
+
# @see {#read} for available params
|
113
|
+
# @return [Enumerator] enumerator will yield PgEventstore::Event
|
114
|
+
def read_paginated(stream, options: {}, middlewares: nil)
|
115
|
+
cmd_class = stream.system? ? Commands::SystemStreamReadPaginated : Commands::RegularStreamReadPaginated
|
116
|
+
cmd_class.
|
117
|
+
new(Queries.new(streams: stream_queries, events: event_queries(middlewares(middlewares)))).
|
118
|
+
call(stream, options: { max_count: config.max_count }.merge(options))
|
119
|
+
end
|
120
|
+
|
121
|
+
# Links event from one stream into another stream. You can later access it by providing :resolve_link_tos option
|
122
|
+
# when reading from a stream. Only existing events can be linked.
|
123
|
+
# @param stream [PgEventstore::Stream]
|
124
|
+
# @param events_or_event [PgEventstore::Event, Array<PgEventstore::Event>]
|
125
|
+
# @param options [Hash]
|
126
|
+
# @option options [Integer] :expected_revision provide your own revision number
|
127
|
+
# @option options [Symbol] :expected_revision provide one of next values: :any, :no_stream or :stream_exists
|
128
|
+
# @param middlewares [Array] provide a list of middleware names to use. Defaults to empty array, meaning no
|
129
|
+
# middlewares will be applied to the "link" event
|
130
|
+
# @return [PgEventstore::Event, Array<PgEventstore::Event>] persisted event(s)
|
131
|
+
# @raise [PgEventstore::WrongExpectedRevisionError]
|
132
|
+
def link_to(stream, events_or_event, options: {}, middlewares: [])
|
133
|
+
result =
|
134
|
+
Commands::LinkTo.new(
|
135
|
+
Queries.new(
|
136
|
+
streams: stream_queries, events: event_queries(middlewares(middlewares)), transactions: transaction_queries
|
137
|
+
)
|
138
|
+
).call(stream, *events_or_event, options: options)
|
139
|
+
events_or_event.is_a?(Array) ? result : result.first
|
140
|
+
end
|
141
|
+
|
112
142
|
private
|
113
143
|
|
114
144
|
# @param middlewares [Array, nil]
|
@@ -9,9 +9,10 @@ module PgEventstore
|
|
9
9
|
# @param options [Hash]
|
10
10
|
# @option options [Integer] :expected_revision provide your own revision number
|
11
11
|
# @option options [Symbol] :expected_revision provide one of next values: :any, :no_stream or :stream_exists
|
12
|
+
# @param event_modifier [#call]
|
12
13
|
# @return [Array<PgEventstore::Event>] persisted events
|
13
14
|
# @raise [PgEventstore::WrongExpectedRevisionError]
|
14
|
-
def call(stream, *events, options: {})
|
15
|
+
def call(stream, *events, options: {}, event_modifier: EventModifiers::PrepareRegularEvent)
|
15
16
|
raise SystemStreamError, stream if stream.system?
|
16
17
|
|
17
18
|
queries.transactions.transaction do
|
@@ -19,7 +20,7 @@ module PgEventstore
|
|
19
20
|
revision = stream.stream_revision
|
20
21
|
assert_expected_revision!(revision, options[:expected_revision]) if options[:expected_revision]
|
21
22
|
events.map.with_index(1) do |event, index|
|
22
|
-
queries.events.insert(stream,
|
23
|
+
queries.events.insert(stream, event_modifier.call(event, revision + index))
|
23
24
|
end.tap do
|
24
25
|
queries.streams.update_stream_revision(stream, revision + events.size)
|
25
26
|
end
|
@@ -28,15 +29,6 @@ module PgEventstore
|
|
28
29
|
|
29
30
|
private
|
30
31
|
|
31
|
-
# @param event [PgEventstore::Event]
|
32
|
-
# @param revision [Integer]
|
33
|
-
# @return [PgEventstore::Event]
|
34
|
-
def prepared_event(event, revision)
|
35
|
-
event.class.new(
|
36
|
-
id: event.id, data: event.data, metadata: event.metadata, type: event.type, stream_revision: revision
|
37
|
-
)
|
38
|
-
end
|
39
|
-
|
40
32
|
# @param revision [Integer]
|
41
33
|
# @param expected_revision [Symbol, Integer]
|
42
34
|
# @raise [PgEventstore::WrongExpectedRevisionError] in case if revision does not satisfy expected revision
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module Commands
|
5
|
+
module EventModifiers
|
6
|
+
# Defines how to transform regular event into a link event
|
7
|
+
# @!visibility private
|
8
|
+
class PrepareLinkEvent
|
9
|
+
class << self
|
10
|
+
# @param event [PgEventstore::Event]
|
11
|
+
# @param revision [Integer]
|
12
|
+
# @return [PgEventstore::Event]
|
13
|
+
def call(event, revision)
|
14
|
+
Event.new(link_id: event.id, type: Event::LINK_TYPE, stream_revision: revision).tap do |e|
|
15
|
+
%i[link_id type stream_revision].each { |attr| e.readonly!(attr) }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module Commands
|
5
|
+
module EventModifiers
|
6
|
+
# Defines how to transform regular event before appending it to the stream
|
7
|
+
# @!visibility private
|
8
|
+
class PrepareRegularEvent
|
9
|
+
class << self
|
10
|
+
# @param event [PgEventstore::Event]
|
11
|
+
# @param revision [Integer]
|
12
|
+
# @return [PgEventstore::Event]
|
13
|
+
def call(event, revision)
|
14
|
+
event.class.new(
|
15
|
+
id: event.id, data: event.data, metadata: event.metadata, type: event.type, stream_revision: revision
|
16
|
+
).tap do |e|
|
17
|
+
%i[link_id stream_revision].each { |attr| e.readonly!(attr) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module Commands
|
5
|
+
# @!visibility private
|
6
|
+
class LinkTo < AbstractCommand
|
7
|
+
# @param stream [PgEventstore::Stream]
|
8
|
+
# @param events [Array<PgEventstore::Event>]
|
9
|
+
# @param options [Hash]
|
10
|
+
# @option options [Integer] :expected_revision provide your own revision number
|
11
|
+
# @option options [Symbol] :expected_revision provide one of next values: :any, :no_stream or :stream_exists
|
12
|
+
# @return [Array<PgEventstore::Event>] persisted events
|
13
|
+
# @raise [PgEventstore::WrongExpectedRevisionError]
|
14
|
+
# @raise [PgEventstore::NotPersistedEventError]
|
15
|
+
def call(stream, *events, options: {})
|
16
|
+
events.each(&method(:check_id_presence))
|
17
|
+
append_cmd = Append.new(queries)
|
18
|
+
append_cmd.call(stream, *events, options: options, event_modifier: EventModifiers::PrepareLinkEvent)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Checks if Event#id is present. An event must have the #id value in order to be linked.
|
24
|
+
# @param event [PgEventstore::Event]
|
25
|
+
# @return [void]
|
26
|
+
def check_id_presence(event)
|
27
|
+
return unless event.id.nil?
|
28
|
+
|
29
|
+
raise NotPersistedEventError, event
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module Commands
|
5
|
+
# @!visibility private
|
6
|
+
class RegularStreamReadPaginated < AbstractCommand
|
7
|
+
# @see PgEventstore::Commands::Read for docs
|
8
|
+
def call(stream, options: {})
|
9
|
+
revision = calc_initial_revision(stream, options)
|
10
|
+
Enumerator.new do |yielder|
|
11
|
+
loop do
|
12
|
+
events = read_cmd.call(stream, options: options.merge(from_revision: revision))
|
13
|
+
yielder << events if events.any?
|
14
|
+
raise StopIteration if end_reached?(events, options[:max_count])
|
15
|
+
|
16
|
+
revision = calc_next_revision(events, revision, options[:direction])
|
17
|
+
raise StopIteration if revision.negative?
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @param stream [PgEventstore::Stream]
|
25
|
+
# @param options [Hash]
|
26
|
+
# @return [Integer]
|
27
|
+
def calc_initial_revision(stream, options)
|
28
|
+
return options[:from_revision] if options[:from_revision]
|
29
|
+
return 0 if forwards?(options[:direction])
|
30
|
+
|
31
|
+
read_cmd.call(stream, options: options.merge(max_count: 1)).first.stream_revision
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param events [Array<PgEventstore::Event>]
|
35
|
+
# @param max_count [Integer]
|
36
|
+
# @return [Boolean]
|
37
|
+
def end_reached?(events, max_count)
|
38
|
+
events.size < max_count
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param events [Array<PgEventstore::Event>]
|
42
|
+
# @param revision [Integer]
|
43
|
+
# @param direction [String, Symbol, nil]
|
44
|
+
# @return [Integer]
|
45
|
+
def calc_next_revision(events, revision, direction)
|
46
|
+
return revision + events.size if forwards?(direction)
|
47
|
+
|
48
|
+
revision - events.size
|
49
|
+
end
|
50
|
+
|
51
|
+
# @param direction [String, Symbol, nil]
|
52
|
+
# @return [Boolean]
|
53
|
+
def forwards?(direction)
|
54
|
+
QueryBuilders::EventsFiltering::SQL_DIRECTIONS[direction] == QueryBuilders::EventsFiltering::SQL_DIRECTIONS[:asc]
|
55
|
+
end
|
56
|
+
|
57
|
+
# @return [PgEventstore::Commands::Read]
|
58
|
+
def read_cmd
|
59
|
+
@read_cmd ||= Read.new(queries)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module Commands
|
5
|
+
# @!visibility private
|
6
|
+
class SystemStreamReadPaginated < AbstractCommand
|
7
|
+
# @see PgEventstore::Commands::Read for docs
|
8
|
+
def call(stream, options: {})
|
9
|
+
position = calc_initial_position(stream, options)
|
10
|
+
Enumerator.new do |yielder|
|
11
|
+
loop do
|
12
|
+
events = read_cmd.call(stream, options: options.merge(from_position: position))
|
13
|
+
yielder << events if events.any?
|
14
|
+
raise StopIteration if end_reached?(events, options[:max_count])
|
15
|
+
|
16
|
+
position = calc_next_position(events, options[:direction])
|
17
|
+
raise StopIteration if position <= 0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# @param stream [PgEventstore::Stream]
|
25
|
+
# @param options [Hash]
|
26
|
+
# @return [Integer]
|
27
|
+
def calc_initial_position(stream, options)
|
28
|
+
return options[:from_position] if options[:from_position]
|
29
|
+
return 1 if forwards?(options[:direction])
|
30
|
+
|
31
|
+
read_cmd.call(stream, options: options.merge(max_count: 1)).first.global_position
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param events [Array<PgEventstore::Event>]
|
35
|
+
# @param max_count [Integer]
|
36
|
+
# @return [Boolean]
|
37
|
+
def end_reached?(events, max_count)
|
38
|
+
events.size < max_count
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param events [Array<PgEventstore::Event>]
|
42
|
+
# @param direction [String, Symbol, nil]
|
43
|
+
# @return [Integer]
|
44
|
+
def calc_next_position(events, direction)
|
45
|
+
return events.last.global_position + 1 if forwards?(direction)
|
46
|
+
|
47
|
+
events.last.global_position - 1
|
48
|
+
end
|
49
|
+
|
50
|
+
# @param direction [String, Symbol, nil]
|
51
|
+
# @return [Boolean]
|
52
|
+
def forwards?(direction)
|
53
|
+
QueryBuilders::EventsFiltering::SQL_DIRECTIONS[direction] == QueryBuilders::EventsFiltering::SQL_DIRECTIONS[:asc]
|
54
|
+
end
|
55
|
+
|
56
|
+
# @return [PgEventstore::Commands::Read]
|
57
|
+
def read_cmd
|
58
|
+
@read_cmd ||= Read.new(queries)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,6 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'abstract_command'
|
4
|
+
require_relative 'commands/event_modifiers/prepare_link_event'
|
5
|
+
require_relative 'commands/event_modifiers/prepare_regular_event'
|
4
6
|
require_relative 'commands/append'
|
5
7
|
require_relative 'commands/read'
|
8
|
+
require_relative 'commands/regular_stream_read_paginated'
|
9
|
+
require_relative 'commands/system_stream_read_paginated'
|
6
10
|
require_relative 'commands/multiple'
|
11
|
+
require_relative 'commands/link_to'
|
data/lib/pg_eventstore/errors.rb
CHANGED
@@ -137,8 +137,8 @@ module PgEventstore
|
|
137
137
|
|
138
138
|
# @return [String]
|
139
139
|
def user_friendly_message
|
140
|
-
<<~TEXT
|
141
|
-
Could not lock
|
140
|
+
<<~TEXT.strip
|
141
|
+
Could not lock subscription from #{set.inspect} set with #{name.inspect} name. It is already locked by \
|
142
142
|
#{lock_id.inspect} set.
|
143
143
|
TEXT
|
144
144
|
end
|
@@ -161,10 +161,27 @@ module PgEventstore
|
|
161
161
|
|
162
162
|
# @return [String]
|
163
163
|
def user_friendly_message
|
164
|
-
<<~TEXT
|
165
|
-
Failed to unlock
|
164
|
+
<<~TEXT.strip
|
165
|
+
Failed to unlock subscription from #{set.inspect} set with #{name.inspect} name by \
|
166
166
|
#{expected_locked_by.inspect} lock id. It is currently locked by #{actual_locked_by.inspect} lock id.
|
167
167
|
TEXT
|
168
168
|
end
|
169
169
|
end
|
170
|
+
|
171
|
+
class NotPersistedEventError < Error
|
172
|
+
attr_reader :event
|
173
|
+
|
174
|
+
# @param event [PgEventstore::Event]
|
175
|
+
def initialize(event)
|
176
|
+
@event = event
|
177
|
+
super(user_friendly_message)
|
178
|
+
end
|
179
|
+
|
180
|
+
# @return [String]
|
181
|
+
def user_friendly_message
|
182
|
+
<<~TEXT.strip
|
183
|
+
Event#id must be present, got #{event.id.inspect} instead.
|
184
|
+
TEXT
|
185
|
+
end
|
186
|
+
end
|
170
187
|
end
|
@@ -12,18 +12,9 @@ module PgEventstore
|
|
12
12
|
@event_class_resolver = event_class_resolver
|
13
13
|
end
|
14
14
|
|
15
|
-
# @param
|
16
|
-
|
17
|
-
|
18
|
-
pg_result.map(&method(:deserialize))
|
19
|
-
end
|
20
|
-
|
21
|
-
# @param pg_result [PG::Result]
|
22
|
-
# @return [PgEventstore::Event, nil]
|
23
|
-
def deserialize_one_pg_result(pg_result)
|
24
|
-
return if pg_result.ntuples.zero?
|
25
|
-
|
26
|
-
deserialize(pg_result.first)
|
15
|
+
# @param raw_events [Array<Hash>]
|
16
|
+
def deserialize_many(raw_events)
|
17
|
+
raw_events.map(&method(:deserialize))
|
27
18
|
end
|
28
19
|
|
29
20
|
# @param attrs [Hash]
|
@@ -50,7 +50,11 @@ module PgEventstore
|
|
50
50
|
self.options = (options + Set.new([opt_name])).freeze
|
51
51
|
warn_already_defined(opt_name)
|
52
52
|
warn_already_defined(:"#{opt_name}=")
|
53
|
-
|
53
|
+
define_method "#{opt_name}=" do |value|
|
54
|
+
readonly_error(opt_name) if readonly?(opt_name)
|
55
|
+
|
56
|
+
instance_variable_set(:"@#{opt_name}", value)
|
57
|
+
end
|
54
58
|
|
55
59
|
define_method opt_name do
|
56
60
|
result = instance_variable_get(:"@#{opt_name}")
|
@@ -77,6 +81,8 @@ module PgEventstore
|
|
77
81
|
end
|
78
82
|
end
|
79
83
|
|
84
|
+
ReadonlyAttributeError = Class.new(StandardError)
|
85
|
+
|
80
86
|
def self.included(klass)
|
81
87
|
klass.singleton_class.attr_accessor(:options)
|
82
88
|
klass.options = Set.new.freeze
|
@@ -84,11 +90,8 @@ module PgEventstore
|
|
84
90
|
end
|
85
91
|
|
86
92
|
def initialize(**options)
|
87
|
-
|
88
|
-
|
89
|
-
value = options.key?(option) ? options[option] : public_send(option)
|
90
|
-
public_send("#{option}=", value)
|
91
|
-
end
|
93
|
+
@readonly = Set.new
|
94
|
+
init_default_values(options)
|
92
95
|
end
|
93
96
|
|
94
97
|
# Construct a hash from options, where key is the option's name and the value is option's
|
@@ -100,6 +103,41 @@ module PgEventstore
|
|
100
103
|
end
|
101
104
|
end
|
102
105
|
alias attributes_hash options_hash
|
106
|
+
|
107
|
+
# @param opt_name [Symbol]
|
108
|
+
# @return [Boolean]
|
109
|
+
def readonly!(opt_name)
|
110
|
+
return false unless self.class.options.include?(opt_name)
|
111
|
+
|
112
|
+
@readonly.add(opt_name)
|
113
|
+
true
|
114
|
+
end
|
115
|
+
|
116
|
+
# @param opt_name [Symbol]
|
117
|
+
# @return [Boolean]
|
118
|
+
def readonly?(opt_name)
|
119
|
+
@readonly.include?(opt_name)
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
# @param opt_name [Symbol]
|
125
|
+
# @raise [PgEventstore::Extensions::OptionsExtension::ReadOnlyError]
|
126
|
+
def readonly_error(opt_name)
|
127
|
+
raise(
|
128
|
+
ReadonlyAttributeError, "#{opt_name.inspect} attribute was marked as read only. You can no longer modify it."
|
129
|
+
)
|
130
|
+
end
|
131
|
+
|
132
|
+
# @param options [Hash]
|
133
|
+
# @return [void]
|
134
|
+
def init_default_values(options)
|
135
|
+
self.class.options.each do |option|
|
136
|
+
# init default values of options
|
137
|
+
value = options.key?(option) ? options[option] : public_send(option)
|
138
|
+
public_send("#{option}=", value)
|
139
|
+
end
|
140
|
+
end
|
103
141
|
end
|
104
142
|
end
|
105
143
|
end
|
@@ -19,11 +19,28 @@ module PgEventstore
|
|
19
19
|
|
20
20
|
sql = sql.gsub(/\$\d+/).each do |matched|
|
21
21
|
value = params[matched[1..].to_i - 1]
|
22
|
-
|
23
|
-
|
24
|
-
value.is_a?(String) ? "'#{value}'" : value
|
22
|
+
value = encode_value(value)
|
23
|
+
normalize_value(value)
|
25
24
|
end unless params&.empty?
|
26
25
|
PgEventstore.logger.debug(sql)
|
27
26
|
end
|
27
|
+
|
28
|
+
def encode_value(value)
|
29
|
+
encoder = type_map_for_queries[value.class]
|
30
|
+
return type_map_for_queries.send(encoder, value).encode(value) if encoder.is_a?(Symbol)
|
31
|
+
|
32
|
+
type_map_for_queries[value.class]&.encode(value) || value
|
33
|
+
end
|
34
|
+
|
35
|
+
def normalize_value(value)
|
36
|
+
case value
|
37
|
+
when String
|
38
|
+
"'#{value}'"
|
39
|
+
when NilClass
|
40
|
+
'NULL'
|
41
|
+
else
|
42
|
+
value
|
43
|
+
end
|
44
|
+
end
|
28
45
|
end
|
29
46
|
end
|
@@ -22,10 +22,11 @@ module PgEventstore
|
|
22
22
|
def stream_events(stream, options)
|
23
23
|
options = event_type_queries.include_event_types_ids(options)
|
24
24
|
exec_params = events_filtering(stream, options).to_exec_params
|
25
|
-
|
25
|
+
raw_events = connection.with do |conn|
|
26
26
|
conn.exec_params(*exec_params)
|
27
|
-
end
|
28
|
-
|
27
|
+
end.to_a
|
28
|
+
preloader.preload_related_objects(raw_events)
|
29
|
+
deserializer.deserialize_many(raw_events)
|
29
30
|
end
|
30
31
|
|
31
32
|
# @param stream [PgEventstore::Stream] persisted stream
|
@@ -44,10 +45,10 @@ module PgEventstore
|
|
44
45
|
RETURNING *, $#{attributes.values.size + 1} as type
|
45
46
|
SQL
|
46
47
|
|
47
|
-
|
48
|
+
raw_event = connection.with do |conn|
|
48
49
|
conn.exec_params(sql, [*attributes.values, event.type])
|
49
|
-
end
|
50
|
-
deserializer.without_middlewares.
|
50
|
+
end.to_a.first
|
51
|
+
deserializer.without_middlewares.deserialize(raw_event).tap do |persisted_event|
|
51
52
|
persisted_event.stream = stream
|
52
53
|
end
|
53
54
|
end
|
@@ -56,17 +57,21 @@ module PgEventstore
|
|
56
57
|
|
57
58
|
# @param stream [PgEventstore::Stream]
|
58
59
|
# @param options [Hash]
|
59
|
-
# @param offset [Integer]
|
60
60
|
# @return [PgEventstore::EventsFilteringQuery]
|
61
|
-
def events_filtering(stream, options
|
62
|
-
return QueryBuilders::EventsFiltering.all_stream_filtering(options
|
61
|
+
def events_filtering(stream, options)
|
62
|
+
return QueryBuilders::EventsFiltering.all_stream_filtering(options) if stream.all_stream?
|
63
63
|
|
64
|
-
QueryBuilders::EventsFiltering.specific_stream_filtering(stream, options
|
64
|
+
QueryBuilders::EventsFiltering.specific_stream_filtering(stream, options)
|
65
65
|
end
|
66
66
|
|
67
67
|
# @return [PgEventstore::EventTypeQueries]
|
68
68
|
def event_type_queries
|
69
69
|
EventTypeQueries.new(connection)
|
70
70
|
end
|
71
|
+
|
72
|
+
# @return [PgEventstore::Preloader]
|
73
|
+
def preloader
|
74
|
+
Preloader.new(connection)
|
75
|
+
end
|
71
76
|
end
|
72
77
|
end
|
@@ -33,6 +33,17 @@ module PgEventstore
|
|
33
33
|
end.to_a.dig(0, 'id')
|
34
34
|
end
|
35
35
|
|
36
|
+
# @param ids [Array<Integer>]
|
37
|
+
# @return [Array<Hash>]
|
38
|
+
def find_by_ids(ids)
|
39
|
+
return [] if ids.empty?
|
40
|
+
|
41
|
+
builder = SQLBuilder.new.from('event_types').where('id = ANY(?)', ids.uniq)
|
42
|
+
connection.with do |conn|
|
43
|
+
conn.exec_params(*builder.to_exec_params)
|
44
|
+
end.to_a
|
45
|
+
end
|
46
|
+
|
36
47
|
# @param types [Array<String>]
|
37
48
|
# @return [Array<Integer, nil>]
|
38
49
|
def find_event_types(types)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
# @!visibility private
|
5
|
+
class Preloader
|
6
|
+
attr_reader :connection
|
7
|
+
private :connection
|
8
|
+
|
9
|
+
# @param connection [PgEventstore::Connection]
|
10
|
+
def initialize(connection)
|
11
|
+
@connection = connection
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param raw_events [Array<Hash>]
|
15
|
+
# @return [Array<Hash>]
|
16
|
+
def preload_related_objects(raw_events)
|
17
|
+
streams = stream_queries.find_by_ids(raw_events.map { _1['stream_id'] }).to_h { [_1['id'], _1] }
|
18
|
+
types = event_type_queries.find_by_ids(raw_events.map { _1['event_type_id'] }).to_h { [_1['id'], _1] }
|
19
|
+
raw_events.each do |event|
|
20
|
+
event['stream'] = streams[event['stream_id']]
|
21
|
+
event['type'] = types[event['event_type_id']]['type']
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# @return [PgEventstore::EventTypeQueries]
|
28
|
+
def event_type_queries
|
29
|
+
EventTypeQueries.new(connection)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [PgEventstore::StreamQueries]
|
33
|
+
def stream_queries
|
34
|
+
StreamQueries.new(connection)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -26,6 +26,17 @@ module PgEventstore
|
|
26
26
|
deserialize(pg_result) if pg_result.ntuples == 1
|
27
27
|
end
|
28
28
|
|
29
|
+
# @param ids [Array<Integer>]
|
30
|
+
# @return [Array<Hash>]
|
31
|
+
def find_by_ids(ids)
|
32
|
+
return [] if ids.empty?
|
33
|
+
|
34
|
+
builder = SQLBuilder.new.from('streams').where('id = ANY(?)', ids.uniq.sort)
|
35
|
+
connection.with do |conn|
|
36
|
+
conn.exec_params(*builder.to_exec_params)
|
37
|
+
end.to_a
|
38
|
+
end
|
39
|
+
|
29
40
|
# @param stream [PgEventstore::Stream]
|
30
41
|
# @return [PgEventstore::RawStream] persisted stream
|
31
42
|
def create_stream(stream)
|
@@ -44,13 +55,15 @@ module PgEventstore
|
|
44
55
|
end
|
45
56
|
|
46
57
|
# @param stream [PgEventstore::Stream] persisted stream
|
47
|
-
# @return [
|
58
|
+
# @return [PgEventstore::Stream]
|
48
59
|
def update_stream_revision(stream, revision)
|
49
60
|
connection.with do |conn|
|
50
61
|
conn.exec_params(<<~SQL, [revision, stream.id])
|
51
62
|
UPDATE streams SET stream_revision = $1 WHERE id = $2
|
52
63
|
SQL
|
53
64
|
end
|
65
|
+
stream.stream_revision = revision
|
66
|
+
stream
|
54
67
|
end
|
55
68
|
|
56
69
|
private
|
@@ -79,9 +79,10 @@ module PgEventstore
|
|
79
79
|
return [] if query_options.empty?
|
80
80
|
|
81
81
|
final_builder = union_builders(query_options.map { |id, opts| query_builder(id, opts) })
|
82
|
-
connection.with do |conn|
|
82
|
+
raw_events = connection.with do |conn|
|
83
83
|
conn.exec_params(*final_builder.to_exec_params)
|
84
84
|
end.to_a
|
85
|
+
preloader.preload_related_objects(raw_events)
|
85
86
|
end
|
86
87
|
|
87
88
|
# @param id [Integer] subscription's id
|
@@ -151,6 +152,11 @@ module PgEventstore
|
|
151
152
|
EventTypeQueries.new(connection)
|
152
153
|
end
|
153
154
|
|
155
|
+
# @return [PgEventstore::Preloader]
|
156
|
+
def preloader
|
157
|
+
Preloader.new(connection)
|
158
|
+
end
|
159
|
+
|
154
160
|
# @param hash [Hash]
|
155
161
|
# @return [Hash]
|
156
162
|
def deserialize(hash)
|
@@ -10,6 +10,7 @@ require_relative 'queries/subscription_queries'
|
|
10
10
|
require_relative 'queries/subscriptions_set_queries'
|
11
11
|
require_relative 'queries/subscription_command_queries'
|
12
12
|
require_relative 'queries/subscriptions_set_command_queries'
|
13
|
+
require_relative 'queries/preloader'
|
13
14
|
|
14
15
|
module PgEventstore
|
15
16
|
# @!visibility private
|
@@ -4,7 +4,6 @@ module PgEventstore
|
|
4
4
|
module QueryBuilders
|
5
5
|
# @!visibility private
|
6
6
|
class EventsFiltering
|
7
|
-
DEFAULT_OFFSET = 0
|
8
7
|
DEFAULT_LIMIT = 1_000
|
9
8
|
SQL_DIRECTIONS = {
|
10
9
|
'asc' => 'ASC',
|
@@ -26,14 +25,12 @@ module PgEventstore
|
|
26
25
|
end
|
27
26
|
|
28
27
|
# @param options [Hash]
|
29
|
-
# @param offset [Integer]
|
30
28
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
31
|
-
def all_stream_filtering(options
|
29
|
+
def all_stream_filtering(options)
|
32
30
|
event_filter = new
|
33
31
|
options in { filter: { event_type_ids: Array => event_type_ids } }
|
34
32
|
event_filter.add_event_types(event_type_ids)
|
35
33
|
event_filter.add_limit(options[:max_count])
|
36
|
-
event_filter.add_offset(offset)
|
37
34
|
event_filter.resolve_links(options[:resolve_link_tos])
|
38
35
|
options in { filter: { streams: Array => streams } }
|
39
36
|
streams&.each { |attrs| event_filter.add_stream_attrs(**attrs) }
|
@@ -44,14 +41,12 @@ module PgEventstore
|
|
44
41
|
|
45
42
|
# @param stream [PgEventstore::Stream]
|
46
43
|
# @param options [Hash]
|
47
|
-
# @param offset [Integer]
|
48
44
|
# @return [PgEventstore::QueryBuilders::EventsFiltering]
|
49
|
-
def specific_stream_filtering(stream, options
|
45
|
+
def specific_stream_filtering(stream, options)
|
50
46
|
event_filter = new
|
51
47
|
options in { filter: { event_type_ids: Array => event_type_ids } }
|
52
48
|
event_filter.add_event_types(event_type_ids)
|
53
49
|
event_filter.add_limit(options[:max_count])
|
54
|
-
event_filter.add_offset(offset)
|
55
50
|
event_filter.resolve_links(options[:resolve_link_tos])
|
56
51
|
event_filter.add_stream(stream)
|
57
52
|
event_filter.add_revision(options[:from_revision], options[:direction])
|
@@ -64,13 +59,10 @@ module PgEventstore
|
|
64
59
|
@sql_builder =
|
65
60
|
SQLBuilder.new.
|
66
61
|
select('events.*').
|
67
|
-
select('row_to_json(streams.*) as stream').
|
68
|
-
select('event_types.type as type').
|
69
62
|
from('events').
|
70
63
|
join('JOIN streams ON streams.id = events.stream_id').
|
71
64
|
join('JOIN event_types ON event_types.id = events.event_type_id').
|
72
|
-
limit(DEFAULT_LIMIT)
|
73
|
-
offset(DEFAULT_OFFSET)
|
65
|
+
limit(DEFAULT_LIMIT)
|
74
66
|
end
|
75
67
|
|
76
68
|
# @param context [String, nil]
|
@@ -144,14 +136,6 @@ module PgEventstore
|
|
144
136
|
@sql_builder.limit(limit)
|
145
137
|
end
|
146
138
|
|
147
|
-
# @param offset [Integer, nil]
|
148
|
-
# @return [void]
|
149
|
-
def add_offset(offset)
|
150
|
-
return unless offset
|
151
|
-
|
152
|
-
@sql_builder.offset(offset)
|
153
|
-
end
|
154
|
-
|
155
139
|
# @param should_resolve [Boolean]
|
156
140
|
# @return [void]
|
157
141
|
def resolve_links(should_resolve)
|
@@ -160,7 +144,6 @@ module PgEventstore
|
|
160
144
|
@sql_builder.
|
161
145
|
unselect.
|
162
146
|
select("(COALESCE(original_events.*, events.*)).*").
|
163
|
-
select('row_to_json(streams.*) as stream').
|
164
147
|
join("LEFT JOIN events original_events ON original_events.id = events.link_id")
|
165
148
|
end
|
166
149
|
|
data/lib/pg_eventstore/stream.rb
CHANGED
@@ -18,7 +18,8 @@ module PgEventstore
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
attr_reader :context, :stream_name, :stream_id, :id
|
21
|
+
attr_reader :context, :stream_name, :stream_id, :id
|
22
|
+
attr_accessor :stream_revision
|
22
23
|
|
23
24
|
# @param context [String]
|
24
25
|
# @param stream_name [String]
|
@@ -59,6 +59,10 @@ namespace :pg_eventstore do
|
|
59
59
|
DROP TABLE IF EXISTS public.streams;
|
60
60
|
DROP TABLE IF EXISTS public.event_types;
|
61
61
|
DROP TABLE IF EXISTS public.migrations;
|
62
|
+
DROP TABLE IF EXISTS public.subscriptions_set;
|
63
|
+
DROP TABLE IF EXISTS public.subscriptions;
|
64
|
+
DROP TABLE IF EXISTS public.subscription_commands;
|
65
|
+
DROP TABLE IF EXISTS public.subscriptions_set_commands;
|
62
66
|
DROP EXTENSION IF EXISTS "uuid-ossp";
|
63
67
|
DROP EXTENSION IF EXISTS pgcrypto;
|
64
68
|
SQL
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_eventstore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Dzyzenko
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-02-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -57,6 +57,7 @@ files:
|
|
57
57
|
- db/migrations/10_create_subscription_commands.sql
|
58
58
|
- db/migrations/11_create_subscriptions_set_commands.sql
|
59
59
|
- db/migrations/12_improve_events_indexes.sql
|
60
|
+
- db/migrations/13_remove_duplicated_index.sql
|
60
61
|
- db/migrations/1_improve_specific_stream_indexes.sql
|
61
62
|
- db/migrations/2_adjust_global_position_index.sql
|
62
63
|
- db/migrations/3_extract_type_into_separate_table.sql
|
@@ -69,6 +70,7 @@ files:
|
|
69
70
|
- docs/appending_events.md
|
70
71
|
- docs/configuration.md
|
71
72
|
- docs/events_and_streams.md
|
73
|
+
- docs/linking_events.md
|
72
74
|
- docs/multiple_commands.md
|
73
75
|
- docs/reading_events.md
|
74
76
|
- docs/subscriptions.md
|
@@ -79,8 +81,13 @@ files:
|
|
79
81
|
- lib/pg_eventstore/client.rb
|
80
82
|
- lib/pg_eventstore/commands.rb
|
81
83
|
- lib/pg_eventstore/commands/append.rb
|
84
|
+
- lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb
|
85
|
+
- lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb
|
86
|
+
- lib/pg_eventstore/commands/link_to.rb
|
82
87
|
- lib/pg_eventstore/commands/multiple.rb
|
83
88
|
- lib/pg_eventstore/commands/read.rb
|
89
|
+
- lib/pg_eventstore/commands/regular_stream_read_paginated.rb
|
90
|
+
- lib/pg_eventstore/commands/system_stream_read_paginated.rb
|
84
91
|
- lib/pg_eventstore/config.rb
|
85
92
|
- lib/pg_eventstore/connection.rb
|
86
93
|
- lib/pg_eventstore/errors.rb
|
@@ -96,6 +103,7 @@ files:
|
|
96
103
|
- lib/pg_eventstore/queries.rb
|
97
104
|
- lib/pg_eventstore/queries/event_queries.rb
|
98
105
|
- lib/pg_eventstore/queries/event_type_queries.rb
|
106
|
+
- lib/pg_eventstore/queries/preloader.rb
|
99
107
|
- lib/pg_eventstore/queries/stream_queries.rb
|
100
108
|
- lib/pg_eventstore/queries/subscription_command_queries.rb
|
101
109
|
- lib/pg_eventstore/queries/subscription_queries.rb
|