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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ff514958069cbf4b819b2ad959eac80ca9f24445bca19690ed4cb3ff23989804
4
- data.tar.gz: bd91be800c3739a43edf4c17c996156642f00f0d977446201c84784a1bcc6e74
3
+ metadata.gz: ac379ed7a4c1c8ca46edd8e4ec537dafd575dd50749a140d15c49c24a1ece1c1
4
+ data.tar.gz: c5f83c5aa8fccc6f7100172270c951fda96ca7926c7a1b6b37f90217024e3e09
5
5
  SHA512:
6
- metadata.gz: 1d4946c58c314f0bfb04c0c20ca1308e4c8afe915adacee8722fcd703404b26dbc429bdda5bc5a44f94654cb366813065226b42c930620bf30fcd4a345f592b1
7
- data.tar.gz: 89738f55a626cbcd4eab9d336b7f4b9ff2e3520fc30a7fc7d87dfac5e755cd6431392438d1522cec706188aa855942db4cfd6fa96bc83c5e0b67086d6e05cef8
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
@@ -0,0 +1,2 @@
1
+ DROP INDEX IF EXISTS idx_events_0_stream_revision_global_position;
2
+ CREATE INDEX idx_events_0_stream_revision_global_position ON events USING btree (global_position) WHERE stream_revision = 0;
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
 
@@ -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 the `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.
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
- 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.
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
 
@@ -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.
@@ -2,5 +2,5 @@
2
2
 
3
3
  module PgEventstore
4
4
  # @return [String]
5
- VERSION = "1.7.0"
5
+ VERSION = "1.8.0"
6
6
  end
@@ -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
- config = request.cookies['current_config']&.to_s&.to_sym
38
- PgEventstore.available_configs.include?(config) ? config : :default
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('current_config', { value: val.to_s, http_only: true, same_site: :lax })
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
- config = params[:config]&.to_sym
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
- PgEventstore.connection.with do |conn|
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: :default)
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 = :default)
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 = :default)
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 = :default, subscription_set:, max_retries: nil, retries_interval: nil,
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 = :default)
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 = { default: Config.new(name: :default) }
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]]?
@@ -1,4 +1,6 @@
1
1
  module PgEventstore
2
+ DEFAULT_CONFIG: Symbol
3
+
2
4
  # _@param_ `name` — a name to assign to a config
3
5
  #
4
6
  # _@return_ — a result of the given block
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.7.0
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-01-10 00:00:00.000000000 Z
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