pg_eventstore 1.5.0 → 1.7.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 +7 -0
- data/README.md +49 -0
- data/db/migrations/7_support_reading_streams_system_stream.sql +2 -0
- data/docs/reading_events.md +16 -1
- data/docs/subscriptions.md +14 -56
- data/exe/pg-eventstore +16 -0
- data/lib/pg_eventstore/callbacks.rb +16 -0
- data/lib/pg_eventstore/cli/commands/base_command.rb +45 -0
- data/lib/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rb +38 -0
- data/lib/pg_eventstore/cli/commands/help_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands/start_subscriptions_command.rb +96 -0
- data/lib/pg_eventstore/cli/commands/stop_subscriptions_command.rb +22 -0
- data/lib/pg_eventstore/cli/commands.rb +6 -0
- data/lib/pg_eventstore/cli/exit_codes.rb +12 -0
- data/lib/pg_eventstore/cli/parser_options/base_options.rb +46 -0
- data/lib/pg_eventstore/cli/parser_options/default_options.rb +10 -0
- data/lib/pg_eventstore/cli/parser_options/metadata.rb +34 -0
- data/lib/pg_eventstore/cli/parser_options/subscription_options.rb +31 -0
- data/lib/pg_eventstore/cli/parser_options.rb +6 -0
- data/lib/pg_eventstore/cli/parsers/base_parser.rb +33 -0
- data/lib/pg_eventstore/cli/parsers/default_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers/subscription_parser.rb +24 -0
- data/lib/pg_eventstore/cli/parsers.rb +5 -0
- data/lib/pg_eventstore/cli/try_to_delete_subscriptions_set.rb +75 -0
- data/lib/pg_eventstore/cli/try_unlock_subscriptions_set.rb +16 -0
- data/lib/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rb +67 -0
- data/lib/pg_eventstore/cli.rb +42 -0
- data/lib/pg_eventstore/commands/read.rb +1 -1
- data/lib/pg_eventstore/extensions/options_extension.rb +53 -8
- data/lib/pg_eventstore/queries/event_queries.rb +1 -10
- data/lib/pg_eventstore/query_builders/events_filtering.rb +27 -0
- data/lib/pg_eventstore/rspec/has_option_matcher.rb +42 -41
- data/lib/pg_eventstore/stream.rb +8 -0
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +13 -1
- data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscription_command_queries.rb +1 -1
- data/lib/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rb +3 -1
- data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +10 -50
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rb +22 -0
- data/lib/pg_eventstore/subscriptions/subscription_feeder_commands.rb +1 -0
- data/lib/pg_eventstore/subscriptions/subscriptions_lifecycle.rb +7 -2
- data/lib/pg_eventstore/subscriptions/subscriptions_manager.rb +44 -10
- data/lib/pg_eventstore/subscriptions/subscriptions_set_lifecycle.rb +1 -1
- data/lib/pg_eventstore/utils.rb +32 -0
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +12 -2
- data/lib/pg_eventstore/web/paginator/events_collection.rb +18 -5
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +24 -2
- data/lib/pg_eventstore/web/views/home/dashboard.erb +9 -0
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +1 -1
- data/lib/pg_eventstore/web/views/home/partials/events.erb +9 -6
- data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +3 -3
- data/lib/pg_eventstore/web/views/home/partials/system_stream_filter.erb +15 -0
- data/lib/pg_eventstore/web/views/layouts/application.erb +3 -3
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +6 -6
- data/lib/pg_eventstore.rb +5 -2
- data/sig/pg_eventstore/callbacks.rbs +4 -0
- data/sig/pg_eventstore/cli/commands/base_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/callback_handlers/start_cmd_handlers.rbs +14 -0
- data/sig/pg_eventstore/cli/commands/help_command.rbs +13 -0
- data/sig/pg_eventstore/cli/commands/start_subscriptions_command.rbs +28 -0
- data/sig/pg_eventstore/cli/commands/stop_subscriptions_command.rbs +11 -0
- data/sig/pg_eventstore/cli/exit_codes.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/base_options.rbs +15 -0
- data/sig/pg_eventstore/cli/parser_options/default_options.rbs +8 -0
- data/sig/pg_eventstore/cli/parser_options/metadata.rbs +11 -0
- data/sig/pg_eventstore/cli/parser_options/subscription_options.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/base_parser.rbs +18 -0
- data/sig/pg_eventstore/cli/parsers/default_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/parsers/subscription_parser.rbs +9 -0
- data/sig/pg_eventstore/cli/try_to_delete_subscriptions_set.rbs +24 -0
- data/sig/pg_eventstore/cli/try_unlock_subscriptions_set.rbs +7 -0
- data/sig/pg_eventstore/cli/wait_for_subscriptions_set_shutdown.rbs +26 -0
- data/sig/pg_eventstore/cli.rbs +10 -0
- data/sig/pg_eventstore/extensions/options_extension.rbs +18 -1
- data/sig/pg_eventstore/queries/event_queries.rbs +0 -5
- data/sig/pg_eventstore/query_builders/events_filtering_query.rbs +6 -0
- data/sig/pg_eventstore/stream.rbs +3 -0
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rbs +8 -0
- data/sig/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rbs +7 -1
- data/sig/pg_eventstore/subscriptions/queries/subscription_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/queries/subscriptions_set_command_queries.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscription_feeder.rbs +10 -11
- data/sig/pg_eventstore/subscriptions/subscription_feeder_commands/ping.rbs +11 -0
- data/sig/pg_eventstore/subscriptions/subscriptions_lifecycle.rbs +1 -1
- data/sig/pg_eventstore/subscriptions/subscriptions_manager.rbs +13 -1
- data/sig/pg_eventstore/utils.rbs +2 -0
- data/sig/pg_eventstore/web/application.rbs +27 -0
- data/sig/pg_eventstore/web/paginator/base_collection.rbs +0 -9
- data/sig/pg_eventstore/web/paginator/event_types_collection.rbs +9 -0
- data/sig/pg_eventstore/web/paginator/events_collection.rbs +3 -1
- data/sig/pg_eventstore.rbs +2 -8
- metadata +47 -3
@@ -55,9 +55,11 @@ module PgEventstore
|
|
55
55
|
conn.exec_params(sql, [command_name, subscriptions_set_id, data])
|
56
56
|
end
|
57
57
|
deserialize(pg_result.to_a.first)
|
58
|
+
rescue PG::ForeignKeyViolation
|
59
|
+
raise RecordNotFound.new("subscriptions_set", subscriptions_set_id)
|
58
60
|
end
|
59
61
|
|
60
|
-
# @param subscriptions_set_id [Integer]
|
62
|
+
# @param subscriptions_set_id [Integer, nil]
|
61
63
|
# @return [Array<PgEventstore::SubscriptionFeederCommands::Base>]
|
62
64
|
def find_commands(subscriptions_set_id)
|
63
65
|
sql_builder =
|
@@ -6,37 +6,25 @@ module PgEventstore
|
|
6
6
|
class SubscriptionFeeder
|
7
7
|
extend Forwardable
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
attr_reader :config_name
|
10
|
+
|
11
|
+
def_delegators :@basic_runner, :start, :stop, :restore, :state, :wait_for_finish, :stop_async, :running?
|
11
12
|
|
12
13
|
# @param config_name [Symbol]
|
13
|
-
# @param
|
14
|
-
# @param
|
15
|
-
|
16
|
-
def initialize(config_name:, set_name:, max_retries:, retries_interval:)
|
14
|
+
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
15
|
+
# @param subscriptions_lifecycle [PgEventstore::SubscriptionsLifecycle]
|
16
|
+
def initialize(config_name:, subscriptions_set_lifecycle:, subscriptions_lifecycle:)
|
17
17
|
@config_name = config_name
|
18
18
|
@basic_runner = BasicRunner.new(0.2, 0)
|
19
|
-
@subscriptions_set_lifecycle =
|
20
|
-
|
21
|
-
{ name: set_name, max_restarts_number: max_retries, time_between_restarts: retries_interval }
|
22
|
-
)
|
23
|
-
@subscriptions_lifecycle = SubscriptionsLifecycle.new(@config_name, @subscriptions_set_lifecycle)
|
19
|
+
@subscriptions_set_lifecycle = subscriptions_set_lifecycle
|
20
|
+
@subscriptions_lifecycle = subscriptions_lifecycle
|
24
21
|
@commands_handler = CommandsHandler.new(@config_name, self, @subscriptions_lifecycle.runners)
|
25
22
|
attach_runner_callbacks
|
26
23
|
end
|
27
24
|
|
28
|
-
# @return [Integer]
|
25
|
+
# @return [Integer, nil]
|
29
26
|
def id
|
30
|
-
@subscriptions_set_lifecycle.
|
31
|
-
end
|
32
|
-
|
33
|
-
# Adds SubscriptionRunner to the set
|
34
|
-
# @param runner [PgEventstore::SubscriptionRunner]
|
35
|
-
# @return [PgEventstore::SubscriptionRunner]
|
36
|
-
def add(runner)
|
37
|
-
assert_proper_state!
|
38
|
-
@subscriptions_lifecycle.runners.push(runner)
|
39
|
-
runner
|
27
|
+
@subscriptions_set_lifecycle.subscriptions_set&.id
|
40
28
|
end
|
41
29
|
|
42
30
|
# Starts all SubscriptionRunners. This is only available if SubscriptionFeeder's runner is alive.
|
@@ -57,20 +45,6 @@ module PgEventstore
|
|
57
45
|
self
|
58
46
|
end
|
59
47
|
|
60
|
-
# Produces a copy of currently running Subscriptions. This is needed, because original Subscriptions objects are
|
61
|
-
# dangerous to use - users may incidentally break their state.
|
62
|
-
# @return [Array<PgEventstore::Subscription>]
|
63
|
-
def read_only_subscriptions
|
64
|
-
@subscriptions_lifecycle.subscriptions.map(&:dup)
|
65
|
-
end
|
66
|
-
|
67
|
-
# Produces a copy of current SubscriptionsSet. This is needed, because original SubscriptionsSet object is
|
68
|
-
# dangerous to use - users may incidentally break its state.
|
69
|
-
# @return [PgEventstore::SubscriptionsSet, nil]
|
70
|
-
def read_only_subscriptions_set
|
71
|
-
@subscriptions_set_lifecycle.subscriptions_set&.dup
|
72
|
-
end
|
73
|
-
|
74
48
|
private
|
75
49
|
|
76
50
|
# @return [void]
|
@@ -133,19 +107,5 @@ module PgEventstore
|
|
133
107
|
SubscriptionFeederHandlers.setup_handler(:update_subscriptions_set_restarts, @subscriptions_set_lifecycle)
|
134
108
|
)
|
135
109
|
end
|
136
|
-
|
137
|
-
# This method helps to ensure that no Subscription is added after SubscriptionFeeder's runner is working
|
138
|
-
# @return [void]
|
139
|
-
# @raise [RuntimeError]
|
140
|
-
def assert_proper_state!
|
141
|
-
return if @basic_runner.initial? || @basic_runner.stopped?
|
142
|
-
subscriptions_set = @subscriptions_set_lifecycle.persisted_subscriptions_set
|
143
|
-
|
144
|
-
error_message = <<~TEXT
|
145
|
-
Could not add subscription - #{subscriptions_set.name}##{subscriptions_set.id} must be \
|
146
|
-
either in the initial or in the stopped state, but it is in the #{@basic_runner.state} state now.
|
147
|
-
TEXT
|
148
|
-
raise error_message
|
149
|
-
end
|
150
110
|
end
|
151
111
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PgEventstore
|
4
|
+
module SubscriptionFeederCommands
|
5
|
+
# @!visibility private
|
6
|
+
class Ping < Base
|
7
|
+
# @param subscription_feeder [PgEventstore::SubscriptionFeeder]
|
8
|
+
# @return [void]
|
9
|
+
def exec_cmd(subscription_feeder)
|
10
|
+
queries(subscription_feeder.config_name).update(subscriptions_set_id, {})
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
# @param config_name [Symbol]
|
16
|
+
# @return [PgEventstore::SubscriptionsSetQueries]
|
17
|
+
def queries(config_name)
|
18
|
+
SubscriptionsSetQueries.new(PgEventstore.connection(config_name))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'subscription_feeder_commands/base'
|
4
|
+
require_relative 'subscription_feeder_commands/ping'
|
4
5
|
require_relative 'subscription_feeder_commands/restore'
|
5
6
|
require_relative 'subscription_feeder_commands/start_all'
|
6
7
|
require_relative 'subscription_feeder_commands/stop'
|
@@ -13,12 +13,12 @@ module PgEventstore
|
|
13
13
|
|
14
14
|
# @param config_name [Symbol]
|
15
15
|
# @param subscriptions_set_lifecycle [PgEventstore::SubscriptionsSetLifecycle]
|
16
|
-
def initialize(config_name, subscriptions_set_lifecycle)
|
16
|
+
def initialize(config_name, subscriptions_set_lifecycle, force_lock: false)
|
17
17
|
@config_name = config_name
|
18
18
|
@subscriptions_set_lifecycle = subscriptions_set_lifecycle
|
19
19
|
@runners = []
|
20
20
|
@subscriptions_pinged_at = Time.at(0)
|
21
|
-
@force_lock =
|
21
|
+
@force_lock = force_lock
|
22
22
|
end
|
23
23
|
|
24
24
|
# Locks all Subscriptions behind the current SubscriptionsSet
|
@@ -59,6 +59,11 @@ module PgEventstore
|
|
59
59
|
# locked by the new SubscriptionsSet.
|
60
60
|
# @return [void]
|
61
61
|
def force_lock!
|
62
|
+
message = <<~TEXT
|
63
|
+
Usage of #force_lock! is deprecated and will be removed in v2. Please pass :force_lock keyword argument when \
|
64
|
+
initializing SubscriptionsManager. Example: PgEventstore.subscriptions_manager(force_lock: true)
|
65
|
+
TEXT
|
66
|
+
Utils.deprecation_warning(message)
|
62
67
|
@force_lock = true
|
63
68
|
end
|
64
69
|
|
@@ -36,20 +36,32 @@ module PgEventstore
|
|
36
36
|
attr_reader :config
|
37
37
|
private :config
|
38
38
|
|
39
|
-
def_delegators :@subscription_feeder, :stop, :
|
39
|
+
def_delegators :@subscription_feeder, :stop, :running?
|
40
|
+
def_delegators :@subscriptions_lifecycle, :force_lock!
|
40
41
|
|
41
42
|
# @param config [PgEventstore::Config]
|
42
43
|
# @param set_name [String]
|
43
44
|
# @param max_retries [Integer, nil] max number of retries of failed SubscriptionsSet
|
44
45
|
# @param retries_interval [Integer, nil] a delay between retries of failed SubscriptionsSet
|
45
|
-
|
46
|
+
# @param force_lock [Boolean] whether to force-lock subscriptions
|
47
|
+
def initialize(config:, set_name:, max_retries: nil, retries_interval: nil, force_lock: false)
|
46
48
|
@config = config
|
47
49
|
@set_name = set_name
|
50
|
+
@subscriptions_set_lifecycle = SubscriptionsSetLifecycle.new(
|
51
|
+
config_name,
|
52
|
+
{
|
53
|
+
name: set_name,
|
54
|
+
max_restarts_number: max_retries || config.subscriptions_set_max_retries,
|
55
|
+
time_between_restarts: retries_interval || config.subscriptions_set_retries_interval
|
56
|
+
}
|
57
|
+
)
|
58
|
+
@subscriptions_lifecycle = SubscriptionsLifecycle.new(
|
59
|
+
config_name, @subscriptions_set_lifecycle, force_lock: force_lock
|
60
|
+
)
|
48
61
|
@subscription_feeder = SubscriptionFeeder.new(
|
49
|
-
config_name:
|
50
|
-
|
51
|
-
|
52
|
-
retries_interval: retries_interval || config.subscriptions_set_retries_interval
|
62
|
+
config_name: config_name,
|
63
|
+
subscriptions_set_lifecycle: @subscriptions_set_lifecycle,
|
64
|
+
subscriptions_lifecycle: @subscriptions_lifecycle
|
53
65
|
)
|
54
66
|
end
|
55
67
|
|
@@ -97,30 +109,52 @@ module PgEventstore
|
|
97
109
|
failed_subscription_notifier: failed_subscription_notifier
|
98
110
|
)
|
99
111
|
|
100
|
-
@
|
112
|
+
@subscriptions_lifecycle.runners.push(runner)
|
101
113
|
true
|
102
114
|
end
|
103
115
|
|
104
116
|
# @return [Array<PgEventstore::Subscription>]
|
105
117
|
def subscriptions
|
106
|
-
@
|
118
|
+
@subscriptions_lifecycle.subscriptions.map(&:dup)
|
107
119
|
end
|
108
120
|
|
109
121
|
# @return [PgEventstore::SubscriptionsSet, nil]
|
110
122
|
def subscriptions_set
|
111
|
-
@
|
123
|
+
@subscriptions_set_lifecycle.subscriptions_set&.dup
|
124
|
+
end
|
125
|
+
|
126
|
+
# @return [PgEventstore::BasicRunner]
|
127
|
+
# @raise [PgEventstore::SubscriptionAlreadyLockedError]
|
128
|
+
def start!
|
129
|
+
run_cli_callbacks do
|
130
|
+
@subscription_feeder.start
|
131
|
+
end
|
112
132
|
end
|
113
133
|
|
114
134
|
# @return [PgEventstore::BasicRunner, nil]
|
115
135
|
def start
|
116
|
-
|
136
|
+
start!
|
117
137
|
rescue PgEventstore::SubscriptionAlreadyLockedError => e
|
118
138
|
PgEventstore.logger&.warn(e.message)
|
119
139
|
nil
|
120
140
|
end
|
121
141
|
|
142
|
+
# @return [Symbol]
|
143
|
+
def config_name
|
144
|
+
@config.name
|
145
|
+
end
|
146
|
+
|
122
147
|
private
|
123
148
|
|
149
|
+
# @return [Object] the result of the passed block
|
150
|
+
def run_cli_callbacks
|
151
|
+
return yield unless defined?(::PgEventstore::CLI)
|
152
|
+
|
153
|
+
PgEventstore::CLI.callbacks.run_callbacks(:start_manager, self) do
|
154
|
+
yield
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
124
158
|
# @param middlewares [Array<Symbol>, nil]
|
125
159
|
# @param handler [#call]
|
126
160
|
# @return [Proc]
|
@@ -27,7 +27,7 @@ module PgEventstore
|
|
27
27
|
|
28
28
|
# @return [PgEventstore::SubscriptionsSet]
|
29
29
|
def persisted_subscriptions_set
|
30
|
-
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(
|
30
|
+
@subscriptions_set ||= SubscriptionsSet.using_connection(@config_name).create(@subscriptions_set_attrs)
|
31
31
|
end
|
32
32
|
|
33
33
|
# @return [void]
|
data/lib/pg_eventstore/utils.rb
CHANGED
@@ -69,6 +69,38 @@ module PgEventstore
|
|
69
69
|
def original_global_position(raw_event)
|
70
70
|
raw_event['link'] ? raw_event['link']['global_position'] : raw_event['global_position']
|
71
71
|
end
|
72
|
+
|
73
|
+
# @param message [String]
|
74
|
+
# @return [void]
|
75
|
+
def deprecation_warning(message)
|
76
|
+
PgEventstore.logger&.warn("\e[31m[DEPRECATED]: #{message}\e[0m")
|
77
|
+
end
|
78
|
+
|
79
|
+
# @param file_path [String]
|
80
|
+
# @param content [String]
|
81
|
+
# @return [void]
|
82
|
+
def write_to_file(file_path, content)
|
83
|
+
file = File.open(file_path, "w")
|
84
|
+
file.write(content)
|
85
|
+
file.close
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param file_path [String]
|
89
|
+
# @return [void]
|
90
|
+
def remove_file(file_path)
|
91
|
+
File.delete(file_path)
|
92
|
+
rescue Errno::ENOENT
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param file_path [String]
|
96
|
+
# @return [String, nil]
|
97
|
+
def read_pid(file_path)
|
98
|
+
file = File.open(file_path, "r")
|
99
|
+
file.readline.strip.tap do
|
100
|
+
file.close
|
101
|
+
end
|
102
|
+
rescue Errno::ENOENT
|
103
|
+
end
|
72
104
|
end
|
73
105
|
end
|
74
106
|
end
|
@@ -9,6 +9,7 @@ module PgEventstore
|
|
9
9
|
set :environment, -> { (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV'])&.to_sym || :development }
|
10
10
|
set :logging, -> { environment == :development || environment == :test }
|
11
11
|
set :erb, layout: :'layouts/application'
|
12
|
+
set :host_authorization, { allow_if: ->(_env) { true } }
|
12
13
|
|
13
14
|
helpers(Paginator::Helpers, Subscriptions::Helpers) do
|
14
15
|
# @return [Array<Hash>, nil]
|
@@ -19,6 +20,12 @@ module PgEventstore
|
|
19
20
|
end&.reject { _1.empty? }
|
20
21
|
end
|
21
22
|
|
23
|
+
# @return [String, nil]
|
24
|
+
def system_stream
|
25
|
+
params in { filter: { system_stream: String => system_stream } }
|
26
|
+
system_stream if Stream::KNOWN_SYSTEM_STREAMS.include?(system_stream)
|
27
|
+
end
|
28
|
+
|
22
29
|
# @return [Array<String>, nil]
|
23
30
|
def events_filter
|
24
31
|
params in { filter: { events: Array => events } }
|
@@ -31,6 +38,8 @@ module PgEventstore
|
|
31
38
|
PgEventstore.available_configs.include?(config) ? config : :default
|
32
39
|
end
|
33
40
|
|
41
|
+
# @param val [Object]
|
42
|
+
# @return [void]
|
34
43
|
def current_config=(val)
|
35
44
|
response.set_cookie('current_config', { value: val.to_s, http_only: true, same_site: :lax })
|
36
45
|
end
|
@@ -40,7 +49,7 @@ module PgEventstore
|
|
40
49
|
PgEventstore.connection(current_config)
|
41
50
|
end
|
42
51
|
|
43
|
-
# @param collection [PgEventstore::Paginator::BaseCollection]
|
52
|
+
# @param collection [PgEventstore::Web::Paginator::BaseCollection]
|
44
53
|
# @return [void]
|
45
54
|
def paginated_json_response(collection)
|
46
55
|
halt 200, {
|
@@ -85,7 +94,8 @@ module PgEventstore
|
|
85
94
|
options: {
|
86
95
|
filter: { event_types: events_filter, streams: streams_filter },
|
87
96
|
resolve_link_tos: resolve_link_tos?
|
88
|
-
}
|
97
|
+
},
|
98
|
+
system_stream: system_stream
|
89
99
|
)
|
90
100
|
|
91
101
|
if request.xhr?
|
@@ -19,10 +19,21 @@ module PgEventstore
|
|
19
19
|
# count instead because of the potential performance degradation.
|
20
20
|
MAX_NUMBER_TO_COUNT = 10_000
|
21
21
|
|
22
|
+
# @param config_name [Symbol]
|
23
|
+
# @param starting_id [String, Integer, nil]
|
24
|
+
# @param per_page [Integer]
|
25
|
+
# @param order [Symbol] :asc or :desc
|
26
|
+
# @param options [Hash] additional options to filter the collection
|
27
|
+
# @param system_stream [String, nil] a name of system stream
|
28
|
+
def initialize(config_name, starting_id:, per_page:, order:, options: {}, system_stream: nil)
|
29
|
+
super(config_name, starting_id: starting_id, per_page: per_page, order: order, options: options)
|
30
|
+
@stream = system_stream ? PgEventstore::Stream.system_stream(system_stream) : PgEventstore::Stream.all_stream
|
31
|
+
end
|
32
|
+
|
22
33
|
# @return [Array<PgEventstore::Event>]
|
23
34
|
def collection
|
24
35
|
@_collection ||= PgEventstore.client(config_name).read(
|
25
|
-
|
36
|
+
@stream,
|
26
37
|
options: options.merge(from_position: starting_id, max_count: per_page, direction: order),
|
27
38
|
middlewares: []
|
28
39
|
)
|
@@ -33,7 +44,8 @@ module PgEventstore
|
|
33
44
|
return unless collection.size == per_page
|
34
45
|
|
35
46
|
from_position = event_global_position(collection.first)
|
36
|
-
sql_builder = QueryBuilders::EventsFiltering.
|
47
|
+
sql_builder = QueryBuilders::EventsFiltering.events_filtering(
|
48
|
+
@stream,
|
37
49
|
options.merge(from_position: from_position, max_count: 1, direction: order)
|
38
50
|
).to_sql_builder.unselect.select('global_position').offset(per_page)
|
39
51
|
global_position(sql_builder)
|
@@ -42,7 +54,8 @@ module PgEventstore
|
|
42
54
|
# @return [Integer, nil]
|
43
55
|
def prev_page_starting_id
|
44
56
|
from_position = event_global_position(collection.first) || starting_id
|
45
|
-
sql_builder = QueryBuilders::EventsFiltering.
|
57
|
+
sql_builder = QueryBuilders::EventsFiltering.events_filtering(
|
58
|
+
@stream,
|
46
59
|
options.merge(from_position: from_position, max_count: per_page, direction: order == :asc ? :desc : :asc)
|
47
60
|
).to_sql_builder.unselect.select('global_position').offset(1)
|
48
61
|
sql, params = sql_builder.to_exec_params
|
@@ -57,7 +70,7 @@ module PgEventstore
|
|
57
70
|
@_total_count ||=
|
58
71
|
begin
|
59
72
|
sql_builder =
|
60
|
-
QueryBuilders::EventsFiltering.
|
73
|
+
QueryBuilders::EventsFiltering.events_filtering(@stream, options).
|
61
74
|
to_sql_builder.remove_limit.remove_group.remove_order
|
62
75
|
count = estimate_count(sql_builder)
|
63
76
|
return count if count > MAX_NUMBER_TO_COUNT
|
@@ -68,7 +81,7 @@ module PgEventstore
|
|
68
81
|
|
69
82
|
private
|
70
83
|
|
71
|
-
# @param event [PgEventstore::Event]
|
84
|
+
# @param event [PgEventstore::Event, nil]
|
72
85
|
# @return [Integer, nil]
|
73
86
|
def event_global_position(event)
|
74
87
|
event&.link&.global_position || event&.global_position
|
@@ -60,6 +60,13 @@ $(function(){
|
|
60
60
|
});
|
61
61
|
}
|
62
62
|
|
63
|
+
let initSystemStreamFilterAutocomplete = function($filter){
|
64
|
+
let $streamNameSelect = $filter.find('select');
|
65
|
+
$streamNameSelect.select2({
|
66
|
+
allowClear: true
|
67
|
+
});
|
68
|
+
}
|
69
|
+
|
63
70
|
let initEventTypeFilterAutocomplete = function($filter) {
|
64
71
|
let $eventTypeSelect = $filter.find('select');
|
65
72
|
$eventTypeSelect.select2({
|
@@ -86,6 +93,8 @@ $(function(){
|
|
86
93
|
let $filtersForm = $('#filters-form');
|
87
94
|
// Stream filter template
|
88
95
|
let streamFilterTmpl = $('#stream-filter-tmpl').text();
|
96
|
+
// System stream filter template
|
97
|
+
let systemStreamFilterTmpl = $('#system-stream-filter-tmpl').text();
|
89
98
|
// Event type filter template
|
90
99
|
let eventFilterTmpl = $('#event-type-filter-tmpl').text();
|
91
100
|
|
@@ -93,21 +102,34 @@ $(function(){
|
|
93
102
|
$filtersForm.on('click', '.remove-filter', function(){
|
94
103
|
$(this).parents('.form-row').remove();
|
95
104
|
});
|
96
|
-
// Add stream filter button
|
105
|
+
// "Add stream filter" button
|
97
106
|
$filtersForm.on('click', '.add-stream-filter', function(){
|
98
107
|
let filtersNum = $filtersForm.find('.stream-filters').children().length + '';
|
99
108
|
$filtersForm.find('.stream-filters').append(streamFilterTmpl.replace(/%NUM%/g, filtersNum));
|
100
109
|
initStreamFilterAutocomplete($filtersForm.find('.stream-filters').children().last());
|
101
110
|
});
|
102
|
-
// Add
|
111
|
+
// "Add system stream filter" button
|
112
|
+
$filtersForm.on('click', '.add-system-stream-filter', function(){
|
113
|
+
if($filtersForm.find('.system-stream-filter').children().length > 0)
|
114
|
+
return;
|
115
|
+
|
116
|
+
$filtersForm.find('.system-stream-filter').append(systemStreamFilterTmpl);
|
117
|
+
initSystemStreamFilterAutocomplete($filtersForm.find('.system-stream-filter').children().last());
|
118
|
+
});
|
119
|
+
// "Add event type filter" button
|
103
120
|
$filtersForm.on('click', '.add-event-filter', function(){
|
104
121
|
$filtersForm.find('.event-filters').append(eventFilterTmpl);
|
105
122
|
initEventTypeFilterAutocomplete($filtersForm.find('.event-filters').children().last());
|
106
123
|
});
|
124
|
+
|
107
125
|
// Init select2 for stream filters which were initially rendered
|
108
126
|
$filtersForm.find('.stream-filters').children().each(function(){
|
109
127
|
initStreamFilterAutocomplete($(this));
|
110
128
|
});
|
129
|
+
// Init select2 for system stream filter which were initially rendered
|
130
|
+
$filtersForm.find('.system-stream-filter').children().each(function(){
|
131
|
+
initSystemStreamFilterAutocomplete($(this));
|
132
|
+
});
|
111
133
|
// Init select2 for event type filters which were initially rendered
|
112
134
|
$filtersForm.find('.event-filters').children().each(function(){
|
113
135
|
initEventTypeFilterAutocomplete($(this));
|
@@ -1,6 +1,9 @@
|
|
1
1
|
<script type="text/html" id="stream-filter-tmpl">
|
2
2
|
<%= erb :'home/partials/stream_filter', { layout: false }, { stream: {} } %>
|
3
3
|
</script>
|
4
|
+
<script type="text/html" id="system-stream-filter-tmpl">
|
5
|
+
<%= erb :'home/partials/system_stream_filter', { layout: false }, { stream: nil } %>
|
6
|
+
</script>
|
4
7
|
<script type="text/html" id="event-type-filter-tmpl">
|
5
8
|
<%= erb :'home/partials/event_filter', { layout: false }, { event_type: '' } %>
|
6
9
|
</script>
|
@@ -27,6 +30,11 @@
|
|
27
30
|
<form id="filters-form" action="<%= url('/') %>" method="GET" data-parsley-validate class="form-horizontal form-label-left">
|
28
31
|
<input type="hidden" name="per_page" value="<%= params[:per_page].to_i %>">
|
29
32
|
<input type="hidden" name="resolve_link_tos" value="<%= resolve_link_tos? %>">
|
33
|
+
<div class="system-stream-filter">
|
34
|
+
<% if system_stream %>
|
35
|
+
<%= erb :'home/partials/system_stream_filter', { layout: false }, { stream: system_stream } %>
|
36
|
+
<% end %>
|
37
|
+
</div>
|
30
38
|
<div class="stream-filters">
|
31
39
|
<% streams_filter&.each do |attrs| %>
|
32
40
|
<%= erb :'home/partials/stream_filter', { layout: false }, { stream: attrs } %>
|
@@ -48,6 +56,7 @@
|
|
48
56
|
</button>
|
49
57
|
<div class="dropdown-menu">
|
50
58
|
<a class="dropdown-item add-stream-filter" href="javascript: void(0)">Add stream filter</a>
|
59
|
+
<a class="dropdown-item add-system-stream-filter" href="javascript: void(0)">Add system stream filter</a>
|
51
60
|
<a class="dropdown-item add-event-filter" href="javascript: void(0)">Add event filter</a>
|
52
61
|
</div>
|
53
62
|
</div>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<select name="filter[events][]" class="form-control mb-2" data-placeholder="Event type" data-url="<%= url('/event_types_filtering') %>">
|
4
4
|
<option></option>
|
5
5
|
<% if event_type %>
|
6
|
-
<option value="<%= event_type %>" selected><%= event_type %></option>
|
6
|
+
<option value="<%= h event_type %>" selected><%= h event_type %></option>
|
7
7
|
<% end %>
|
8
8
|
</select>
|
9
9
|
</div>
|
@@ -2,14 +2,17 @@
|
|
2
2
|
<tr>
|
3
3
|
<td><%= event.global_position %></td>
|
4
4
|
<td><%= event.stream_revision %></td>
|
5
|
-
<td><%= event.stream.context %></td>
|
6
|
-
<td><%= event.stream.stream_name %></td>
|
5
|
+
<td><%= h event.stream.context %></td>
|
6
|
+
<td><%= h event.stream.stream_name %></td>
|
7
7
|
<td><a href="<%= stream_path(event) %>"><%= event.stream.stream_id %></a></td>
|
8
8
|
<td>
|
9
|
-
|
9
|
+
<p class="float-left"><%= h event.type %></p>
|
10
10
|
<% if event.link %>
|
11
|
-
<
|
11
|
+
<p class="float-left ml-2">
|
12
|
+
<i class="fa fa-link"></i>
|
13
|
+
</p>
|
12
14
|
<% end %>
|
15
|
+
<div class="clearfix"></div>
|
13
16
|
</td>
|
14
17
|
<td><%= event.created_at.strftime('%F %T') %></td>
|
15
18
|
<td><%= event.id %></td>
|
@@ -19,9 +22,9 @@
|
|
19
22
|
<tr class="event-payload d-none">
|
20
23
|
<td colspan="8">
|
21
24
|
<strong>Data:</strong>
|
22
|
-
<pre><%= JSON.pretty_generate(event.data) %></pre>
|
25
|
+
<pre><%= h JSON.pretty_generate(event.data) %></pre>
|
23
26
|
<strong>Metadata:</strong>
|
24
|
-
<pre><%= JSON.pretty_generate(event.metadata) %></pre>
|
27
|
+
<pre><%= h JSON.pretty_generate(event.metadata) %></pre>
|
25
28
|
</td>
|
26
29
|
</tr>
|
27
30
|
<% end %>
|
@@ -3,7 +3,7 @@
|
|
3
3
|
<select name="filter[streams][][context]" class="form-control mb-2" data-placeholder="Context" data-url="<%= url('/stream_contexts_filtering') %>">
|
4
4
|
<option></option>
|
5
5
|
<% if stream[:context] %>
|
6
|
-
<option value="<%= stream[:context] %>" selected><%= stream[:context] %></option>
|
6
|
+
<option value="<%= h stream[:context] %>" selected><%= h stream[:context] %></option>
|
7
7
|
<% end %>
|
8
8
|
</select>
|
9
9
|
</div>
|
@@ -11,7 +11,7 @@
|
|
11
11
|
<select name="filter[streams][][stream_name]" class="form-control mb-2" data-placeholder="Stream name" data-url="<%= url('/stream_names_filtering') %>">
|
12
12
|
<option></option>
|
13
13
|
<% if stream[:stream_name] %>
|
14
|
-
<option value="<%= stream[:stream_name] %>" selected><%= stream[:stream_name] %></option>
|
14
|
+
<option value="<%= h stream[:stream_name] %>" selected><%= h stream[:stream_name] %></option>
|
15
15
|
<% end %>
|
16
16
|
</select>
|
17
17
|
</div>
|
@@ -19,7 +19,7 @@
|
|
19
19
|
<select name="filter[streams][][stream_id]" class="form-control mb-2" data-placeholder="Stream ID" data-url="<%= url('/stream_ids_filtering') %>">
|
20
20
|
<option></option>
|
21
21
|
<% if stream[:stream_id] %>
|
22
|
-
<option value="<%= stream[:stream_id] %>" selected><%= stream[:stream_id] %></option>
|
22
|
+
<option value="<%= h stream[:stream_id] %>" selected><%= h stream[:stream_id] %></option>
|
23
23
|
<% end %>
|
24
24
|
</select>
|
25
25
|
</div>
|
@@ -0,0 +1,15 @@
|
|
1
|
+
<div class="form-row align-items-center">
|
2
|
+
<div class="col-3">
|
3
|
+
<select name="filter[system_stream]" class="form-control mb-2" data-placeholder="Select system stream">
|
4
|
+
<option></option>
|
5
|
+
<% PgEventstore::Stream::KNOWN_SYSTEM_STREAMS.each do |stream_name| %>
|
6
|
+
<option value="<%= stream_name %>" <% if stream == stream_name %> selected <% end %>><%= stream_name %></option>
|
7
|
+
<% end %>
|
8
|
+
</select>
|
9
|
+
</div>
|
10
|
+
<div class="col-1">
|
11
|
+
<a class="btn btn-default remove-filter" href="javascript: void(0);">
|
12
|
+
<i class="fa fa-minus-circle"></i>
|
13
|
+
</a>
|
14
|
+
</div>
|
15
|
+
</div>
|
@@ -82,13 +82,13 @@
|
|
82
82
|
<ul class="navbar-right" style="height: 29px;">
|
83
83
|
<li class="nav-item dropdown open" style="padding-left: 15px;">
|
84
84
|
<a href="javascript: void(0);" class="user-profile dropdown-toggle" aria-haspopup="true" id="navbarDropdown" data-toggle="dropdown" aria-expanded="false">
|
85
|
-
<strong>Current config:</strong> <%= current_config.inspect %>
|
85
|
+
<strong>Current config:</strong> <%= h current_config.inspect %>
|
86
86
|
</a>
|
87
87
|
<div class="dropdown-menu dropdown-usermenu pull-right" aria-labelledby="navbarDropdown">
|
88
88
|
<% PgEventstore.available_configs.each do |config| %>
|
89
89
|
<form action="<%= url('/change_config') %>" method="POST">
|
90
|
-
<input type="hidden" name="config" value="<%= config %>">
|
91
|
-
<button type="submit" class="dropdown-item"><%= config.inspect %></button>
|
90
|
+
<input type="hidden" name="config" value="<%= h config.to_s %>">
|
91
|
+
<button type="submit" class="dropdown-item"><%= h config.inspect %></button>
|
92
92
|
</form>
|
93
93
|
<% end %>
|
94
94
|
</div>
|