pg_eventstore 1.10.0 → 1.11.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 +6 -0
- data/lib/pg_eventstore/errors.rb +10 -0
- data/lib/pg_eventstore/subscriptions/callback_handlers/events_processor_handlers.rb +2 -2
- data/lib/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rb +3 -3
- data/lib/pg_eventstore/utils.rb +22 -4
- data/lib/pg_eventstore/version.rb +1 -1
- data/lib/pg_eventstore/web/application.rb +35 -7
- data/lib/pg_eventstore/web/paginator/helpers.rb +11 -3
- data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +41 -4
- data/lib/pg_eventstore/web/public/stylesheets/pg_eventstore.css +12 -0
- data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +5 -1
- data/lib/pg_eventstore/web/views/home/partials/events.erb +5 -5
- data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +15 -3
- data/lib/pg_eventstore/web/views/subscriptions/index.erb +2 -2
- data/sig/pg_eventstore/errors.rbs +8 -0
- data/sig/pg_eventstore/subscriptions/callback_handlers/subscription_runner_handlers.rbs +2 -2
- data/sig/pg_eventstore/utils.rbs +4 -0
- data/sig/pg_eventstore/web/application.rbs +6 -0
- data/sig/pg_eventstore/web/paginator/helpers.rbs +2 -0
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 402dc90a012b5e1ee934da4f62c4af4ba62e69693a45dac1a84d461950da5717
|
4
|
+
data.tar.gz: 6c5d1fdc2e9475eca72b5ef27d0081e0aec6331f76639b7172e676d8b6e63e3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b20c1124f2108b671bd28f3677fb3b2ae88f3b19d2470eb5a31102eb597d4bd5af1080f9bd337b1528107eef2e4bce6b882d434aa8536abc0a5a80b373bfe3a3
|
7
|
+
data.tar.gz: 9fd5af079cedc277e278b2e0b50e628601880a81ec5bf7c94700595f6472be389b2fa3d3a2ba2f5d311c8844da40de2c4a52e4dc6c2bda2ca3559e94b267f7f2
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.11.0]
|
4
|
+
|
5
|
+
- Add a global position that caused an error to the subscription's error JSON info. This will help you understand what event caused your subscription to fail.
|
6
|
+
- Improve long payloads in JSON preview in admin web UI in the way it does not moves content out of the visible area.
|
7
|
+
- Admin UI: adjust events filtering and displaying of stream context, stream name, stream id and event type when values of them contain empty strings or non-displayable characters
|
8
|
+
|
3
9
|
## [1.10.0]
|
4
10
|
- Admin UI: Adjust `SubscriptionSet` "Stop"/"Delete" buttons appearance. Now if `SubscriptionsSet` is not alive anymore(the related process is dead or does not exist anymore) - "Delete" button is shown. If `SubscriptionSet` is alive - "Stop" button is shown
|
5
11
|
- Admin IU: fixed several potential XSS vulnerabilities
|
data/lib/pg_eventstore/errors.rb
CHANGED
@@ -243,4 +243,14 @@ module PgEventstore
|
|
243
243
|
"Too many records of #{stream.to_hash.inspect} stream to lock: #{number_of_records}"
|
244
244
|
end
|
245
245
|
end
|
246
|
+
|
247
|
+
class WrappedException < Error
|
248
|
+
attr_reader :original_exception
|
249
|
+
attr_reader :extra
|
250
|
+
|
251
|
+
def initialize(original_exception, extra)
|
252
|
+
@original_exception = original_exception
|
253
|
+
@extra = extra
|
254
|
+
end
|
255
|
+
end
|
246
256
|
end
|
@@ -16,9 +16,9 @@ module PgEventstore
|
|
16
16
|
callbacks.run_callbacks(:process, Utils.original_global_position(raw_event)) do
|
17
17
|
handler.call(raw_event)
|
18
18
|
end
|
19
|
-
rescue
|
19
|
+
rescue => exception
|
20
20
|
raw_events.unshift(raw_event)
|
21
|
-
raise
|
21
|
+
raise Utils.wrap_exception(exception, global_position: Utils.original_global_position(raw_event))
|
22
22
|
end
|
23
23
|
|
24
24
|
# @param callbacks [PgEventstore::Callbacks]
|
@@ -26,7 +26,7 @@ module PgEventstore
|
|
26
26
|
end
|
27
27
|
|
28
28
|
# @param subscription [PgEventstore::Subscription]
|
29
|
-
# @param error [
|
29
|
+
# @param error [PgEventstore::WrappedException]
|
30
30
|
# @return [void]
|
31
31
|
def update_subscription_error(subscription, error)
|
32
32
|
subscription.update(last_error: Utils.error_info(error), last_error_occurred_at: Time.now.utc)
|
@@ -36,13 +36,13 @@ module PgEventstore
|
|
36
36
|
# @param restart_terminator [#call, nil]
|
37
37
|
# @param failed_subscription_notifier [#call, nil]
|
38
38
|
# @param events_processor [PgEventstore::EventsProcessor]
|
39
|
-
# @param error [
|
39
|
+
# @param error [PgEventstore::WrappedException]
|
40
40
|
# @return [void]
|
41
41
|
def restart_events_processor(subscription, restart_terminator, failed_subscription_notifier, events_processor,
|
42
42
|
error)
|
43
43
|
return if restart_terminator&.call(subscription.dup)
|
44
44
|
if subscription.restart_count >= subscription.max_restarts_number
|
45
|
-
return failed_subscription_notifier&.call(subscription.dup, error)
|
45
|
+
return failed_subscription_notifier&.call(subscription.dup, Utils.unwrap_exception(error))
|
46
46
|
end
|
47
47
|
|
48
48
|
Thread.new do
|
data/lib/pg_eventstore/utils.rb
CHANGED
@@ -46,11 +46,14 @@ module PgEventstore
|
|
46
46
|
# @param error [StandardError]
|
47
47
|
# @return [Hash]
|
48
48
|
def error_info(error)
|
49
|
+
original_error = unwrap_exception(error)
|
49
50
|
{
|
50
|
-
class:
|
51
|
-
message:
|
52
|
-
backtrace:
|
53
|
-
}
|
51
|
+
class: original_error.class,
|
52
|
+
message: original_error.message,
|
53
|
+
backtrace: original_error.backtrace
|
54
|
+
}.tap do |attrs|
|
55
|
+
attrs.merge!(error.extra) if error.is_a?(WrappedException)
|
56
|
+
end
|
54
57
|
end
|
55
58
|
|
56
59
|
# @param str [String]
|
@@ -101,6 +104,21 @@ module PgEventstore
|
|
101
104
|
end
|
102
105
|
rescue Errno::ENOENT
|
103
106
|
end
|
107
|
+
|
108
|
+
# @param exception [StandardError]
|
109
|
+
# @param extra [Hash] additional exception info
|
110
|
+
# @return [PgEventstore::WrappedException]
|
111
|
+
def wrap_exception(exception, **extra)
|
112
|
+
WrappedException.new(exception, extra)
|
113
|
+
end
|
114
|
+
|
115
|
+
# @param wrapped_exception [StandardError, PgEventstore::WrappedException]
|
116
|
+
# @return [StandardError]
|
117
|
+
def unwrap_exception(wrapped_exception)
|
118
|
+
return wrapped_exception.original_exception if wrapped_exception.is_a?(WrappedException)
|
119
|
+
|
120
|
+
wrapped_exception
|
121
|
+
end
|
104
122
|
end
|
105
123
|
end
|
106
124
|
end
|
@@ -13,6 +13,11 @@ module PgEventstore
|
|
13
13
|
# @return [String]
|
14
14
|
COOKIES_FLASH_MESSAGE_KEY = 'flash_message'
|
15
15
|
|
16
|
+
# Defines a replacement for empty string value in a stream attributes filter or in an event type filter. This
|
17
|
+
# replacement is needed to differentiate a user selection vs default placeholder value.
|
18
|
+
# @return [String]
|
19
|
+
EMPTY_STRING_SIGN = "\x00".freeze
|
20
|
+
|
16
21
|
set :static_cache_control, [:private, max_age: 86400]
|
17
22
|
set :environment, -> { (ENV['RACK_ENV'] || ENV['RAILS_ENV'] || ENV['APP_ENV'])&.to_sym || :development }
|
18
23
|
set :logging, -> { environment == :development || environment == :test }
|
@@ -69,8 +74,11 @@ module PgEventstore
|
|
69
74
|
# @param collection [PgEventstore::Web::Paginator::BaseCollection]
|
70
75
|
# @return [void]
|
71
76
|
def paginated_json_response(collection)
|
77
|
+
results = collection.collection.map do |attrs|
|
78
|
+
attrs.transform_values { escape_empty_string(_1) }
|
79
|
+
end
|
72
80
|
halt 200, {
|
73
|
-
results:
|
81
|
+
results: results,
|
74
82
|
pagination: { more: !collection.next_page_starting_id.nil?, starting_id: collection.next_page_starting_id }
|
75
83
|
}.to_json
|
76
84
|
end
|
@@ -108,9 +116,25 @@ module PgEventstore
|
|
108
116
|
COOKIES_FLASH_MESSAGE_KEY, { value: val, http_only: false, same_site: :lax, path: '/' }
|
109
117
|
)
|
110
118
|
end
|
119
|
+
|
120
|
+
# @param string [String, nil]
|
121
|
+
# @return [String, nil]
|
122
|
+
def escape_empty_string(string)
|
123
|
+
string == '' ? EMPTY_STRING_SIGN : string
|
124
|
+
end
|
125
|
+
|
126
|
+
# @param string [String, nil]
|
127
|
+
# @return [String, nil]
|
128
|
+
def unescape_empty_string(string)
|
129
|
+
string == EMPTY_STRING_SIGN ? '' : string
|
130
|
+
end
|
111
131
|
end
|
112
132
|
|
113
133
|
get '/' do
|
134
|
+
streams_filter = self.streams_filter&.map do |attrs|
|
135
|
+
attrs.transform_values { unescape_empty_string(_1) }
|
136
|
+
end
|
137
|
+
events_filter = self.events_filter&.map(&method(:unescape_empty_string))
|
114
138
|
@collection = Paginator::EventsCollection.new(
|
115
139
|
current_config,
|
116
140
|
starting_id: params[:starting_id]&.to_i,
|
@@ -169,7 +193,7 @@ module PgEventstore
|
|
169
193
|
get '/stream_contexts_filtering', provides: :json do
|
170
194
|
collection = Paginator::StreamContextsCollection.new(
|
171
195
|
current_config,
|
172
|
-
starting_id: params[:starting_id],
|
196
|
+
starting_id: unescape_empty_string(params[:starting_id]),
|
173
197
|
per_page: Paginator::StreamContextsCollection::PER_PAGE,
|
174
198
|
order: :asc,
|
175
199
|
options: { query: params[:term] }
|
@@ -180,10 +204,10 @@ module PgEventstore
|
|
180
204
|
get '/stream_names_filtering', provides: :json do
|
181
205
|
collection = Paginator::StreamNamesCollection.new(
|
182
206
|
current_config,
|
183
|
-
starting_id: params[:starting_id],
|
207
|
+
starting_id: unescape_empty_string(params[:starting_id]),
|
184
208
|
per_page: Paginator::StreamNamesCollection::PER_PAGE,
|
185
209
|
order: :asc,
|
186
|
-
options: { query: params[:term], context: params[:context] }
|
210
|
+
options: { query: params[:term], context: unescape_empty_string(params[:context]) }
|
187
211
|
)
|
188
212
|
paginated_json_response(collection)
|
189
213
|
end
|
@@ -191,10 +215,14 @@ module PgEventstore
|
|
191
215
|
get '/stream_ids_filtering', provides: :json do
|
192
216
|
collection = Paginator::StreamIdsCollection.new(
|
193
217
|
current_config,
|
194
|
-
starting_id: params[:starting_id],
|
218
|
+
starting_id: unescape_empty_string(params[:starting_id]),
|
195
219
|
per_page: Paginator::StreamIdsCollection::PER_PAGE,
|
196
220
|
order: :asc,
|
197
|
-
options: {
|
221
|
+
options: {
|
222
|
+
query: params[:term],
|
223
|
+
context: unescape_empty_string(params[:context]),
|
224
|
+
stream_name: unescape_empty_string(params[:stream_name])
|
225
|
+
}
|
198
226
|
)
|
199
227
|
paginated_json_response(collection)
|
200
228
|
end
|
@@ -202,7 +230,7 @@ module PgEventstore
|
|
202
230
|
get '/event_types_filtering', provides: :json do
|
203
231
|
collection = Paginator::EventTypesCollection.new(
|
204
232
|
current_config,
|
205
|
-
starting_id: params[:starting_id],
|
233
|
+
starting_id: unescape_empty_string(params[:starting_id]),
|
206
234
|
per_page: Paginator::EventTypesCollection::PER_PAGE,
|
207
235
|
order: :asc,
|
208
236
|
options: { query: params[:term] }
|
@@ -88,9 +88,9 @@ module PgEventstore
|
|
88
88
|
filter: {
|
89
89
|
streams: [
|
90
90
|
{
|
91
|
-
context: event.stream.context,
|
92
|
-
stream_name: event.stream.stream_name,
|
93
|
-
stream_id: event.stream.stream_id
|
91
|
+
context: escape_empty_string(event.stream.context),
|
92
|
+
stream_name: escape_empty_string(event.stream.stream_name),
|
93
|
+
stream_id: escape_empty_string(event.stream.stream_id)
|
94
94
|
}
|
95
95
|
]
|
96
96
|
}
|
@@ -98,6 +98,14 @@ module PgEventstore
|
|
98
98
|
)
|
99
99
|
end
|
100
100
|
|
101
|
+
# @param str [String]
|
102
|
+
# @return [String]
|
103
|
+
def empty_characters_fallback(str)
|
104
|
+
return str unless str.strip == ''
|
105
|
+
|
106
|
+
'<i>Non-printable characters</i>'
|
107
|
+
end
|
108
|
+
|
101
109
|
private
|
102
110
|
|
103
111
|
# @param starting_id [String, Integer, nil]
|
@@ -1,6 +1,30 @@
|
|
1
1
|
$(function(){
|
2
2
|
"use strict";
|
3
3
|
|
4
|
+
const EMPTY_STRING_SIGN = "\x00";
|
5
|
+
|
6
|
+
let templateSelection = function(state){
|
7
|
+
if (!state.id)
|
8
|
+
return state.text;
|
9
|
+
|
10
|
+
return filterValueToHuman(state.element && state.element.value, state.text);
|
11
|
+
}
|
12
|
+
|
13
|
+
let templateResult = function(item){
|
14
|
+
return filterValueToHuman(item.text);
|
15
|
+
}
|
16
|
+
|
17
|
+
let filterValueToHuman = function(str, alternativeStr){
|
18
|
+
if(str === EMPTY_STRING_SIGN)
|
19
|
+
return $("<i>Empty String</i>");
|
20
|
+
|
21
|
+
let result = alternativeStr || str;
|
22
|
+
if($.trim(result.replaceAll(EMPTY_STRING_SIGN, '')) === '')
|
23
|
+
return $("<i>Non-printable characters</i>");
|
24
|
+
|
25
|
+
return result;
|
26
|
+
}
|
27
|
+
|
4
28
|
let initStreamFilterAutocomplete = function($filter) {
|
5
29
|
let $contextSelect = $filter.find('select[name*="context"]');
|
6
30
|
let $streamNameSelect = $filter.find('select[name*="stream_name"]');
|
@@ -22,7 +46,9 @@ $(function(){
|
|
22
46
|
return data;
|
23
47
|
},
|
24
48
|
},
|
25
|
-
allowClear: true
|
49
|
+
allowClear: true,
|
50
|
+
templateSelection: templateSelection,
|
51
|
+
templateResult: templateResult,
|
26
52
|
});
|
27
53
|
$streamNameSelect.select2({
|
28
54
|
ajax: {
|
@@ -41,7 +67,9 @@ $(function(){
|
|
41
67
|
return data;
|
42
68
|
},
|
43
69
|
},
|
44
|
-
allowClear: true
|
70
|
+
allowClear: true,
|
71
|
+
templateSelection: templateSelection,
|
72
|
+
templateResult: templateResult,
|
45
73
|
});
|
46
74
|
$streamIdSelect.select2({
|
47
75
|
ajax: {
|
@@ -61,7 +89,9 @@ $(function(){
|
|
61
89
|
return data;
|
62
90
|
},
|
63
91
|
},
|
64
|
-
allowClear: true
|
92
|
+
allowClear: true,
|
93
|
+
templateSelection: templateSelection,
|
94
|
+
templateResult: templateResult,
|
65
95
|
});
|
66
96
|
$contextSelect.on('change.select2', removeDeleteBtn);
|
67
97
|
$streamNameSelect.on('change.select2', removeDeleteBtn);
|
@@ -89,7 +119,9 @@ $(function(){
|
|
89
119
|
return data;
|
90
120
|
},
|
91
121
|
},
|
92
|
-
allowClear: true
|
122
|
+
allowClear: true,
|
123
|
+
templateSelection: templateSelection,
|
124
|
+
templateResult: templateResult,
|
93
125
|
});
|
94
126
|
}
|
95
127
|
|
@@ -130,6 +162,11 @@ $(function(){
|
|
130
162
|
initEventTypeFilterAutocomplete($filtersForm.find('.event-filters').children().last());
|
131
163
|
});
|
132
164
|
|
165
|
+
// Because zero-byte character can't be rendered into HTML properly - loop through each option, marked as containing
|
166
|
+
// zero-byte value and assign zero-byte value explicitly. This must be done before we initialize select2 plugin.
|
167
|
+
$('option.zero-byte-val').each(function(){
|
168
|
+
this.value = EMPTY_STRING_SIGN;
|
169
|
+
});
|
133
170
|
// Init select2 for stream filters which were initially rendered
|
134
171
|
$filtersForm.find('.stream-filters').children().each(function(){
|
135
172
|
initStreamFilterAutocomplete($(this));
|
@@ -4,6 +4,18 @@ tr.collapsing {
|
|
4
4
|
display: none;
|
5
5
|
}
|
6
6
|
|
7
|
+
td.json-cell {
|
8
|
+
max-width: 1px;
|
9
|
+
}
|
10
|
+
|
11
|
+
td.json-cell pre {
|
12
|
+
display: block;
|
13
|
+
width: 100%;
|
14
|
+
box-sizing: border-box;
|
15
|
+
overflow-x: auto;
|
16
|
+
white-space: pre;
|
17
|
+
}
|
18
|
+
|
7
19
|
#confirmation-modal .modal-body {
|
8
20
|
font-size: 1.2rem;
|
9
21
|
}
|
@@ -3,7 +3,11 @@
|
|
3
3
|
<select name="filter[events][]" class="form-control mb-2" data-placeholder="Event type" data-url="<%= url('/event_types_filtering') %>" autocomplete="off">
|
4
4
|
<option></option>
|
5
5
|
<% if event_type %>
|
6
|
-
|
6
|
+
<% if event_type == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
|
7
|
+
<option class="zero-byte-val" selected></option>
|
8
|
+
<% else %>
|
9
|
+
<option value="<%= h event_type %>" selected><%= h event_type %></option>
|
10
|
+
<% end %>
|
7
11
|
<% end %>
|
8
12
|
</select>
|
9
13
|
</div>
|
@@ -2,17 +2,17 @@
|
|
2
2
|
<tr>
|
3
3
|
<td><%= event.global_position %></td>
|
4
4
|
<td><%= event.stream_revision %></td>
|
5
|
-
<td><%= h event.stream.context %></td>
|
6
|
-
<td><%= h event.stream.stream_name %></td>
|
5
|
+
<td><%= empty_characters_fallback(h event.stream.context) %></td>
|
6
|
+
<td><%= empty_characters_fallback(h event.stream.stream_name) %></td>
|
7
7
|
<td>
|
8
|
-
<a href="<%= stream_path(event) %>"><%= h event.stream.stream_id %></a>
|
8
|
+
<a href="<%= stream_path(event) %>"><%= empty_characters_fallback(h event.stream.stream_id) %></a>
|
9
9
|
<a role="button" href="#" data-title="Copy stream definition." class="copy-to-clipboard"
|
10
10
|
data-clipboard-content="<%= h "PgEventstore::Stream.new(context: #{event.stream.context.inspect}, stream_name: #{event.stream.stream_name.inspect}, stream_id: #{event.stream.stream_id.inspect})" %>">
|
11
11
|
<i class="fa fa-clipboard"></i>
|
12
12
|
</a>
|
13
13
|
</td>
|
14
14
|
<td>
|
15
|
-
<p class="float-left"><%= h event.type %></p>
|
15
|
+
<p class="float-left"><%= empty_characters_fallback(h event.type) %></p>
|
16
16
|
<% if event.link %>
|
17
17
|
<p class="float-left ml-2">
|
18
18
|
<i class="fa fa-link"></i>
|
@@ -32,7 +32,7 @@
|
|
32
32
|
</td>
|
33
33
|
</tr>
|
34
34
|
<tr class="event-payload d-none">
|
35
|
-
<td colspan="
|
35
|
+
<td colspan="9" class="json-cell">
|
36
36
|
<strong>Data:</strong>
|
37
37
|
<pre><%= h JSON.pretty_generate(event.data) %></pre>
|
38
38
|
<strong>Metadata:</strong>
|
@@ -3,7 +3,11 @@
|
|
3
3
|
<select name="filter[streams][][context]" class="form-control mb-2" data-placeholder="Context" data-url="<%= url('/stream_contexts_filtering') %>" autocomplete="off">
|
4
4
|
<option></option>
|
5
5
|
<% if stream[:context] %>
|
6
|
-
|
6
|
+
<% if stream[:context] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
|
7
|
+
<option class="zero-byte-val" selected></option>
|
8
|
+
<% else %>
|
9
|
+
<option value="<%= h stream[:context] %>" selected><%= h stream[:context] %></option>
|
10
|
+
<% end %>
|
7
11
|
<% end %>
|
8
12
|
</select>
|
9
13
|
</div>
|
@@ -11,7 +15,11 @@
|
|
11
15
|
<select name="filter[streams][][stream_name]" class="form-control mb-2" data-placeholder="Stream name" data-url="<%= url('/stream_names_filtering') %>" autocomplete="off">
|
12
16
|
<option></option>
|
13
17
|
<% if stream[:stream_name] %>
|
14
|
-
|
18
|
+
<% if stream[:stream_name] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
|
19
|
+
<option class="zero-byte-val" selected></option>
|
20
|
+
<% else %>
|
21
|
+
<option value="<%= h stream[:stream_name] %>" selected><%= h stream[:stream_name] %></option>
|
22
|
+
<% end %>
|
15
23
|
<% end %>
|
16
24
|
</select>
|
17
25
|
</div>
|
@@ -19,7 +27,11 @@
|
|
19
27
|
<select name="filter[streams][][stream_id]" class="form-control mb-2" data-placeholder="Stream ID" data-url="<%= url('/stream_ids_filtering') %>" autocomplete="off">
|
20
28
|
<option></option>
|
21
29
|
<% if stream[:stream_id] %>
|
22
|
-
|
30
|
+
<% if stream[:stream_id] == PgEventstore::Web::Application::EMPTY_STRING_SIGN %>
|
31
|
+
<option class="zero-byte-val" selected></option>
|
32
|
+
<% else %>
|
33
|
+
<option value="<%= h stream[:stream_id] %>" selected><%= h stream[:stream_id] %></option>
|
34
|
+
<% end %>
|
23
35
|
<% end %>
|
24
36
|
</select>
|
25
37
|
</div>
|
@@ -210,13 +210,13 @@
|
|
210
210
|
</td>
|
211
211
|
</tr>
|
212
212
|
<tr class="collapse" id="options-<%= subscription.id %>">
|
213
|
-
<td colspan="16">
|
213
|
+
<td colspan="16" class="json-cell">
|
214
214
|
<pre><%= h JSON.pretty_generate(subscription.options) %></pre>
|
215
215
|
</td>
|
216
216
|
</tr>
|
217
217
|
<% if subscription.last_error %>
|
218
218
|
<tr class="collapse" id="last-error-<%= subscription.id %>">
|
219
|
-
<td colspan="16">
|
219
|
+
<td colspan="16" class="json-cell">
|
220
220
|
<pre><%= h JSON.pretty_generate(subscription.last_error) %></pre>
|
221
221
|
</td>
|
222
222
|
</tr>
|
@@ -123,4 +123,12 @@ module PgEventstore
|
|
123
123
|
|
124
124
|
def user_friendly_message: -> String
|
125
125
|
end
|
126
|
+
|
127
|
+
class WrappedException < PgEventstore::Error
|
128
|
+
def initialize: (StandardError original_exception, Hash[Symbol, untyped] extra) -> void
|
129
|
+
|
130
|
+
attr_accessor original_exception: StandardError
|
131
|
+
|
132
|
+
attr_accessor extra: Hash[Symbol, untyped]
|
133
|
+
end
|
126
134
|
end
|
@@ -4,11 +4,11 @@ module PgEventstore
|
|
4
4
|
|
5
5
|
def self.update_subscription_stats: (PgEventstore::Subscription subscription, PgEventstore::SubscriptionHandlerPerformance stats, Integer current_position) -> void
|
6
6
|
|
7
|
-
def self.update_subscription_error: (PgEventstore::Subscription subscription,
|
7
|
+
def self.update_subscription_error: (PgEventstore::Subscription subscription, PgEventstore::WrappedException error) -> void
|
8
8
|
|
9
9
|
def self.restart_events_processor: (PgEventstore::Subscription subscription, _RestartTerminator? restart_terminator,
|
10
10
|
_FailedSubscriptionNotifier? failed_subscription_notifier, PgEventstore::EventsProcessor events_processor,
|
11
|
-
|
11
|
+
PgEventstore::WrappedException error) -> void
|
12
12
|
|
13
13
|
def self.update_subscription_chunk_stats: (PgEventstore::Subscription subscription, Integer global_position) -> void
|
14
14
|
|
data/sig/pg_eventstore/utils.rbs
CHANGED
@@ -20,5 +20,9 @@ module PgEventstore
|
|
20
20
|
def self.underscore_str: (String str) -> String
|
21
21
|
|
22
22
|
def self.original_global_position: (Hash[untyped, untyped] raw_event) -> Integer
|
23
|
+
|
24
|
+
def self.unwrap_exception: (PgEventstore::WrappedException | StandardError wrapped_exception)-> StandardError
|
25
|
+
|
26
|
+
def self.wrap_exception: (StandardError exception, **untyped extra)-> PgEventstore::WrappedException
|
23
27
|
end
|
24
28
|
end
|
@@ -5,6 +5,8 @@ module PgEventstore
|
|
5
5
|
COOKIES_FLASH_MESSAGE_KEY: String
|
6
6
|
DEFAULT_ADMIN_UI_CONFIG: Symbol
|
7
7
|
|
8
|
+
EMPTY_STRING_SIGN: String
|
9
|
+
|
8
10
|
def asset_url: (String path) -> String
|
9
11
|
|
10
12
|
def connection: -> PgEventstore::Connection
|
@@ -13,6 +15,8 @@ module PgEventstore
|
|
13
15
|
|
14
16
|
def current_config=: (untyped val) -> void
|
15
17
|
|
18
|
+
def escape_empty_string: (String? string) -> String?
|
19
|
+
|
16
20
|
def events_filter: -> Array[String]?
|
17
21
|
|
18
22
|
def flash_message=: (({ message: String, kind: String }) val)-> String
|
@@ -30,6 +34,8 @@ module PgEventstore
|
|
30
34
|
def streams_filter: -> Array[Hash[untyped, untyped]]?
|
31
35
|
|
32
36
|
def system_stream: -> String?
|
37
|
+
|
38
|
+
def unescape_empty_string: (String? string) -> String?
|
33
39
|
end
|
34
40
|
end
|
35
41
|
end
|
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.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Dzyzenko
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-05-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pg
|
@@ -368,7 +368,7 @@ metadata:
|
|
368
368
|
homepage_uri: https://github.com/yousty/pg_eventstore
|
369
369
|
source_code_uri: https://github.com/yousty/pg_eventstore
|
370
370
|
changelog_uri: https://github.com/yousty/pg_eventstore/blob/main/CHANGELOG.md
|
371
|
-
post_install_message:
|
371
|
+
post_install_message:
|
372
372
|
rdoc_options: []
|
373
373
|
require_paths:
|
374
374
|
- lib
|
@@ -383,8 +383,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
383
383
|
- !ruby/object:Gem::Version
|
384
384
|
version: '0'
|
385
385
|
requirements: []
|
386
|
-
rubygems_version: 3.5.
|
387
|
-
signing_key:
|
386
|
+
rubygems_version: 3.5.22
|
387
|
+
signing_key:
|
388
388
|
specification_version: 4
|
389
389
|
summary: EventStore implementation using PostgreSQL
|
390
390
|
test_files: []
|