pg_eventstore 1.7.0 → 1.8.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 +5 -0
- data/db/migrations/8_improve_0_revision_partial_index.sql +2 -0
- data/docs/admin_ui.md +7 -1
- data/docs/events_and_streams.md +18 -2
- data/docs/multiple_commands.md +4 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +18 -6
- data/lib/pg_eventstore/web/paginator/events_collection.rb +2 -3
- data/lib/pg_eventstore.rb +9 -6
- data/sig/pg_eventstore/web/application.rbs +5 -0
- data/sig/pg_eventstore.rbs +2 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ac379ed7a4c1c8ca46edd8e4ec537dafd575dd50749a140d15c49c24a1ece1c1
|
4
|
+
data.tar.gz: c5f83c5aa8fccc6f7100172270c951fda96ca7926c7a1b6b37f90217024e3e09
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ffbe0ddbfff6207069a527c7f41693e59d00a3217f68697c851fb2582300fefc569be8035f30924a225b95522ee5ef29ec26c9c6134fe705b31f6b32d0c39a3
|
7
|
+
data.tar.gz: 3b1860f5f351f5ffcd56f09c557661796a98f46a49c9a9bf8945667074ad8705f7cb9998639031fe07a4a2dfb89a0bf636b1e8157a21374da869cf4e76377792
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.8.0]
|
4
|
+
- Introduce default config for admin web UI. Now if you define `:admin_web_ui` config - it will be preferred over default config
|
5
|
+
- Fix pagination of events in admin UI
|
6
|
+
- Improve partial index for `$streams` system stream
|
7
|
+
|
3
8
|
## [1.7.0]
|
4
9
|
- Implement reading from `"$streams"` system stream
|
5
10
|
- Disable Host authorization introduced in sinatra v4.1
|
data/docs/admin_ui.md
CHANGED
@@ -10,7 +10,13 @@
|
|
10
10
|
|
11
11
|
## Authorization
|
12
12
|
|
13
|
-
Admin UI is implemented as a rack application. It doesn't have any built-in authentication/authorization mechanism - it is your responsibility to take care of it.
|
13
|
+
Admin UI is implemented as a rack application. It doesn't have any built-in authentication/authorization mechanism - it is your responsibility to take care of it. Admin UI tries to look for `:admin_web_ui` config with a fallback to `:default` config. Thus, you can setup Admin UI-specific config, e.g. without some middlewares or so. Example:
|
14
|
+
|
15
|
+
```ruby
|
16
|
+
PgEventstore.configure(name: :admin_web_ui) do |config|
|
17
|
+
config.middlewares = { my_admin_ui_middleware: AdminUIMiddleware.new }
|
18
|
+
end
|
19
|
+
```
|
14
20
|
|
15
21
|
### Rails integration
|
16
22
|
|
data/docs/events_and_streams.md
CHANGED
@@ -16,7 +16,9 @@
|
|
16
16
|
- `stream_revision` - Integer(optional, read only). A revision of an event inside its stream.
|
17
17
|
- `data` - Hash(optional). Event's payload data. For example, if you have a `DescriptionChanged` event class, then you may want to have a description value in the event payload data. Example: `DescriptionChanged.new(data: { 'description' => 'Description of something', 'post_id' => SecureRandom.uuid })`
|
18
18
|
- `metadata` - Hash(optional). Event metadata. Event meta information which is not part of an events data payload. Example: `{ published_by: publishing_user.id }`
|
19
|
-
- `link_id` - String(UUIDv4, optional, read only). If an event is a link event (link events are pointers to other events), this attribute contains
|
19
|
+
- `link_id` - String(UUIDv4, optional, read only). If an event is a link event (link events are pointers to other events), this attribute contains an `id` of the original event. Manually assigning this attribute has no effect. It is internally set when appending an event to the given stream or when reading events from the database.
|
20
|
+
- `link_partition_id` - Integer(optional, read only). If an event is a link event - this attribute contains a partition `id` of original event. Manually assigning this attribute has no effect. It is internally set when appending an event to the given stream or when reading events from the database.
|
21
|
+
- `link` - PgEventstore::Event(optional, read only). When reading from a stream using `resolve_link_tos: true`, if an event is resolved from a link - this attribute contains a `PgEventstore::Event` object which corresponds to that link. Manually assigning this attribute has no effect. It is internally set when reading events from the database.
|
20
22
|
- `created_at` - Time(optional, read only). Database's timestamp when an event was appended to a stream. You may want to put your own timestamp into a `metadata` attribute - it may be useful when migrating between different databases. Manually assigning this attribute has no effect. It is internally set when appending an event to the given stream or when reading events from the database.
|
21
23
|
|
22
24
|
Example:
|
@@ -40,7 +42,21 @@ PgEventstore::Stream.new(context: 'Sales', stream_name: 'Customer', stream_id: '
|
|
40
42
|
PgEventstore::Stream.new(context: 'Sales', stream_name: 'Customer', stream_id: 'f37b82f2-4152-424d-ab6b-0cc6f0a53aae')
|
41
43
|
```
|
42
44
|
|
43
|
-
|
45
|
+
### "all" stream
|
46
|
+
|
47
|
+
There is a special stream, called the "all" stream. You can get this object by calling the `PgEventstore::Stream.all_stream` method. Read more about the "all" stream in the `Reading from the "all" stream` section of [Reading events](reading_events.md) chapter.
|
48
|
+
|
49
|
+
### System streams
|
50
|
+
|
51
|
+
System stream is a special stream, the representation of which is pre-defined by the gem. System stream object can be created in next way:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
PgEventstore::Stream.system_stream(stream_name)
|
55
|
+
```
|
56
|
+
|
57
|
+
Current list of system streams is:
|
58
|
+
|
59
|
+
- `"$streams"`. Reading from this stream will return 0 revision events. This allows effectively loop through a list of streams. Read more in [Reading events](reading_events.md#streams-stream-filtering) chapter.
|
44
60
|
|
45
61
|
## Important note
|
46
62
|
|
data/docs/multiple_commands.md
CHANGED
@@ -44,3 +44,7 @@ old_email =
|
|
44
44
|
# Sending email outside multiple block to prevent potential re-triggering of it
|
45
45
|
UserMailer.notify_email_changed(user.id, old_email: old_email, new_email: user.email).deliver_later
|
46
46
|
```
|
47
|
+
|
48
|
+
## Side effect of internal implementation
|
49
|
+
|
50
|
+
Please note that when publishing an event with a type as part of a `multiple` block that does not yet exist in the database, the block will run twice as the first attempt to publish will always fail due to the way `append_to_stream` is implemented. Consider this when writing expectations in your tests for example.
|
@@ -5,6 +5,11 @@ require 'securerandom'
|
|
5
5
|
module PgEventstore
|
6
6
|
module Web
|
7
7
|
class Application < Sinatra::Base
|
8
|
+
# @return [Symbol]
|
9
|
+
DEFAULT_ADMIN_UI_CONFIG = :admin_web_ui
|
10
|
+
# @return [String]
|
11
|
+
COOKIES_CONFIG_KEY = 'current_config'
|
12
|
+
|
8
13
|
set :static_cache_control, [:private, max_age: 86400]
|
9
14
|
set :environment, -> { (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV'])&.to_sym || :development }
|
10
15
|
set :logging, -> { environment == :development || environment == :test }
|
@@ -34,14 +39,23 @@ module PgEventstore
|
|
34
39
|
|
35
40
|
# @return [Symbol]
|
36
41
|
def current_config
|
37
|
-
|
38
|
-
|
42
|
+
resolve_config_by_name(request.cookies[COOKIES_CONFIG_KEY]&.to_s&.to_sym)
|
43
|
+
end
|
44
|
+
|
45
|
+
# @param config_name [Symbol, nil]
|
46
|
+
# @return [Symbol]
|
47
|
+
def resolve_config_by_name(config_name)
|
48
|
+
existing_config = [config_name, DEFAULT_ADMIN_UI_CONFIG].find do |name|
|
49
|
+
PgEventstore.available_configs.include?(name)
|
50
|
+
end
|
51
|
+
|
52
|
+
existing_config || PgEventstore::DEFAULT_CONFIG
|
39
53
|
end
|
40
54
|
|
41
55
|
# @param val [Object]
|
42
56
|
# @return [void]
|
43
57
|
def current_config=(val)
|
44
|
-
response.set_cookie(
|
58
|
+
response.set_cookie(COOKIES_CONFIG_KEY, { value: val.to_s, http_only: true, same_site: :lax })
|
45
59
|
end
|
46
60
|
|
47
61
|
# @return [PgEventstore::Connection]
|
@@ -137,9 +151,7 @@ module PgEventstore
|
|
137
151
|
end
|
138
152
|
|
139
153
|
post '/change_config' do
|
140
|
-
|
141
|
-
config = :default unless PgEventstore.available_configs.include?(config)
|
142
|
-
self.current_config = config
|
154
|
+
self.current_config = resolve_config_by_name(params[:config]&.to_s&.to_sym)
|
143
155
|
redirect(redirect_back_url(fallback_url: '/'))
|
144
156
|
end
|
145
157
|
|
@@ -34,8 +34,7 @@ module PgEventstore
|
|
34
34
|
def collection
|
35
35
|
@_collection ||= PgEventstore.client(config_name).read(
|
36
36
|
@stream,
|
37
|
-
options: options.merge(from_position: starting_id, max_count: per_page, direction: order)
|
38
|
-
middlewares: []
|
37
|
+
options: options.merge(from_position: starting_id, max_count: per_page, direction: order)
|
39
38
|
)
|
40
39
|
end
|
41
40
|
|
@@ -60,7 +59,7 @@ module PgEventstore
|
|
60
59
|
).to_sql_builder.unselect.select('global_position').offset(1)
|
61
60
|
sql, params = sql_builder.to_exec_params
|
62
61
|
sql = "SELECT * FROM (#{sql}) events ORDER BY global_position #{order} LIMIT 1"
|
63
|
-
|
62
|
+
connection.with do |conn|
|
64
63
|
conn.exec_params(sql, params)
|
65
64
|
end.to_a.dig(0, 'global_position')
|
66
65
|
end
|
data/lib/pg_eventstore.rb
CHANGED
@@ -18,6 +18,9 @@ require_relative 'pg_eventstore/middleware'
|
|
18
18
|
require_relative 'pg_eventstore/subscriptions/subscriptions_manager'
|
19
19
|
|
20
20
|
module PgEventstore
|
21
|
+
# @return [Symbol]
|
22
|
+
DEFAULT_CONFIG = :default
|
23
|
+
|
21
24
|
class << self
|
22
25
|
# @!attribute mutex
|
23
26
|
# @return [Thread::Mutex]
|
@@ -27,7 +30,7 @@ module PgEventstore
|
|
27
30
|
# Creates a Config if not exists and yields it to the given block.
|
28
31
|
# @param name [Symbol] a name to assign to a config
|
29
32
|
# @return [Object] a result of the given block
|
30
|
-
def configure(name:
|
33
|
+
def configure(name: DEFAULT_CONFIG)
|
31
34
|
mutex.synchronize do
|
32
35
|
@config[name] ||= Config.new(name: name)
|
33
36
|
connection_config_was = @config[name].connection_options
|
@@ -48,7 +51,7 @@ module PgEventstore
|
|
48
51
|
|
49
52
|
# @param name [Symbol]
|
50
53
|
# @return [PgEventstore::Config]
|
51
|
-
def config(name =
|
54
|
+
def config(name = DEFAULT_CONFIG)
|
52
55
|
return @config[name] if @config[name]
|
53
56
|
|
54
57
|
error_message = <<~TEXT
|
@@ -64,7 +67,7 @@ module PgEventstore
|
|
64
67
|
# thread-safe
|
65
68
|
# @param name [Symbol]
|
66
69
|
# @return [PgEventstore::Connection]
|
67
|
-
def connection(name =
|
70
|
+
def connection(name = DEFAULT_CONFIG)
|
68
71
|
mutex.synchronize do
|
69
72
|
@connection[name] ||= Connection.new(**config(name).connection_options)
|
70
73
|
end
|
@@ -76,7 +79,7 @@ module PgEventstore
|
|
76
79
|
# @param retries_interval [Integer, nil] a delay between retries of failed SubscriptionsSet
|
77
80
|
# @param force_lock [Boolean] whether to force-lock subscriptions
|
78
81
|
# @return [PgEventstore::SubscriptionsManager]
|
79
|
-
def subscriptions_manager(config_name =
|
82
|
+
def subscriptions_manager(config_name = DEFAULT_CONFIG, subscription_set:, max_retries: nil, retries_interval: nil,
|
80
83
|
force_lock: false)
|
81
84
|
SubscriptionsManager.new(
|
82
85
|
config: config(config_name),
|
@@ -89,7 +92,7 @@ module PgEventstore
|
|
89
92
|
|
90
93
|
# @param name [Symbol]
|
91
94
|
# @return [PgEventstore::Client]
|
92
|
-
def client(name =
|
95
|
+
def client(name = DEFAULT_CONFIG)
|
93
96
|
Client.new(config(name))
|
94
97
|
end
|
95
98
|
|
@@ -108,7 +111,7 @@ module PgEventstore
|
|
108
111
|
|
109
112
|
# @return [void]
|
110
113
|
def init_variables
|
111
|
-
@config = {
|
114
|
+
@config = { DEFAULT_CONFIG => Config.new(name: DEFAULT_CONFIG) }
|
112
115
|
@connection = {}
|
113
116
|
@mutex = Thread::Mutex.new
|
114
117
|
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module PgEventstore
|
2
2
|
module Web
|
3
3
|
class Application
|
4
|
+
COOKIES_CONFIG_KEY: String
|
5
|
+
DEFAULT_ADMIN_UI_CONFIG: Symbol
|
6
|
+
|
4
7
|
def asset_url: (String path) -> String
|
5
8
|
|
6
9
|
def connection: -> PgEventstore::Connection
|
@@ -17,6 +20,8 @@ module PgEventstore
|
|
17
20
|
|
18
21
|
def redirect_back_url: (fallback_url: String) -> String
|
19
22
|
|
23
|
+
def resolve_config_by_name: (Symbol? config_name)-> Symbol
|
24
|
+
|
20
25
|
def resolve_link_tos?: -> bool
|
21
26
|
|
22
27
|
def streams_filter: -> Array[Hash[untyped, untyped]]?
|
data/sig/pg_eventstore.rbs
CHANGED
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: 1.
|
4
|
+
version: 1.8.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: 2025-
|
11
|
+
date: 2025-02-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -78,6 +78,7 @@ files:
|
|
78
78
|
- db/migrations/5_partitions.sql
|
79
79
|
- db/migrations/6_add_commands_data.sql
|
80
80
|
- db/migrations/7_support_reading_streams_system_stream.sql
|
81
|
+
- db/migrations/8_improve_0_revision_partial_index.sql
|
81
82
|
- docs/admin_ui.md
|
82
83
|
- docs/appending_events.md
|
83
84
|
- docs/configuration.md
|