pg_eventstore 0.10.2 → 1.0.0.rc1

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.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +26 -0
  4. data/db/migrations/1_create_events.sql +12 -11
  5. data/db/migrations/2_create_subscriptions.sql +6 -2
  6. data/db/migrations/3_create_subscription_commands.sql +9 -5
  7. data/db/migrations/4_create_subscriptions_set_commands.sql +1 -1
  8. data/db/migrations/5_partitions.sql +1 -0
  9. data/docs/how_it_works.md +14 -1
  10. data/lib/pg_eventstore/commands/append.rb +1 -1
  11. data/lib/pg_eventstore/commands/event_modifiers/prepare_link_event.rb +30 -8
  12. data/lib/pg_eventstore/commands/event_modifiers/prepare_regular_event.rb +8 -10
  13. data/lib/pg_eventstore/commands/link_to.rb +14 -7
  14. data/lib/pg_eventstore/errors.rb +10 -12
  15. data/lib/pg_eventstore/event.rb +4 -0
  16. data/lib/pg_eventstore/queries/event_queries.rb +27 -6
  17. data/lib/pg_eventstore/queries/links_resolver.rb +28 -6
  18. data/lib/pg_eventstore/queries/partition_queries.rb +8 -0
  19. data/lib/pg_eventstore/queries/subscription_command_queries.rb +27 -7
  20. data/lib/pg_eventstore/queries/subscription_queries.rb +58 -28
  21. data/lib/pg_eventstore/queries/subscriptions_set_command_queries.rb +13 -1
  22. data/lib/pg_eventstore/queries/subscriptions_set_queries.rb +18 -4
  23. data/lib/pg_eventstore/query_builders/events_filtering_query.rb +4 -4
  24. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_feeder_commands.rb +10 -2
  25. data/lib/pg_eventstore/subscriptions/command_handlers/subscription_runners_commands.rb +9 -7
  26. data/lib/pg_eventstore/subscriptions/commands_handler.rb +3 -2
  27. data/lib/pg_eventstore/subscriptions/subscription.rb +28 -12
  28. data/lib/pg_eventstore/subscriptions/subscription_feeder.rb +19 -15
  29. data/lib/pg_eventstore/subscriptions/subscription_runner.rb +1 -1
  30. data/lib/pg_eventstore/subscriptions/subscription_runners_feeder.rb +1 -1
  31. data/lib/pg_eventstore/subscriptions/subscriptions_set.rb +22 -1
  32. data/lib/pg_eventstore/version.rb +1 -1
  33. data/lib/pg_eventstore/web/application.rb +180 -0
  34. data/lib/pg_eventstore/web/paginator/base_collection.rb +56 -0
  35. data/lib/pg_eventstore/web/paginator/event_types_collection.rb +50 -0
  36. data/lib/pg_eventstore/web/paginator/events_collection.rb +105 -0
  37. data/lib/pg_eventstore/web/paginator/helpers.rb +119 -0
  38. data/lib/pg_eventstore/web/paginator/stream_contexts_collection.rb +48 -0
  39. data/lib/pg_eventstore/web/paginator/stream_ids_collection.rb +50 -0
  40. data/lib/pg_eventstore/web/paginator/stream_names_collection.rb +51 -0
  41. data/lib/pg_eventstore/web/public/fonts/vendor/FontAwesome.otf +0 -0
  42. data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.eot +0 -0
  43. data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.svg +685 -0
  44. data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.ttf +0 -0
  45. data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.woff +0 -0
  46. data/lib/pg_eventstore/web/public/fonts/vendor/fontawesome-webfont.woff2 +0 -0
  47. data/lib/pg_eventstore/web/public/images/favicon.ico +0 -0
  48. data/lib/pg_eventstore/web/public/javascripts/gentelella.js +334 -0
  49. data/lib/pg_eventstore/web/public/javascripts/pg_eventstore.js +162 -0
  50. data/lib/pg_eventstore/web/public/javascripts/vendor/bootstrap.bundle.min.js +7 -0
  51. data/lib/pg_eventstore/web/public/javascripts/vendor/bootstrap.bundle.min.js.map +1 -0
  52. data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.autocomplete.min.js +8 -0
  53. data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.min.js +4 -0
  54. data/lib/pg_eventstore/web/public/javascripts/vendor/jquery.min.js.map +1 -0
  55. data/lib/pg_eventstore/web/public/javascripts/vendor/select2.full.min.js +2 -0
  56. data/lib/pg_eventstore/web/public/stylesheets/pg_eventstore.css +5 -0
  57. data/lib/pg_eventstore/web/public/stylesheets/vendor/bootstrap.min.css +7 -0
  58. data/lib/pg_eventstore/web/public/stylesheets/vendor/bootstrap.min.css.map +1 -0
  59. data/lib/pg_eventstore/web/public/stylesheets/vendor/font-awesome.min.css +4 -0
  60. data/lib/pg_eventstore/web/public/stylesheets/vendor/font-awesome.min.css.map +7 -0
  61. data/lib/pg_eventstore/web/public/stylesheets/vendor/gentelella.min.css +13 -0
  62. data/lib/pg_eventstore/web/public/stylesheets/vendor/select2-bootstrap4.min.css +3 -0
  63. data/lib/pg_eventstore/web/public/stylesheets/vendor/select2.min.css +2 -0
  64. data/lib/pg_eventstore/web/subscriptions/helpers.rb +76 -0
  65. data/lib/pg_eventstore/web/subscriptions/set_collection.rb +34 -0
  66. data/lib/pg_eventstore/web/subscriptions/subscriptions.rb +33 -0
  67. data/lib/pg_eventstore/web/subscriptions/subscriptions_set.rb +33 -0
  68. data/lib/pg_eventstore/web/subscriptions/subscriptions_to_set_association.rb +32 -0
  69. data/lib/pg_eventstore/web/views/home/dashboard.erb +147 -0
  70. data/lib/pg_eventstore/web/views/home/partials/event_filter.erb +15 -0
  71. data/lib/pg_eventstore/web/views/home/partials/events.erb +22 -0
  72. data/lib/pg_eventstore/web/views/home/partials/pagination_links.erb +3 -0
  73. data/lib/pg_eventstore/web/views/home/partials/stream_filter.erb +31 -0
  74. data/lib/pg_eventstore/web/views/layouts/application.erb +116 -0
  75. data/lib/pg_eventstore/web/views/subscriptions/index.erb +220 -0
  76. data/lib/pg_eventstore/web.rb +22 -0
  77. data/lib/pg_eventstore.rb +5 -0
  78. data/pg_eventstore.gemspec +2 -1
  79. metadata +60 -2
@@ -0,0 +1,147 @@
1
+ <script type="text/html" id="stream-filter-tmpl">
2
+ <%= erb :'home/partials/stream_filter', { layout: false }, { stream: {} } %>
3
+ </script>
4
+ <script type="text/html" id="event-type-filter-tmpl">
5
+ <%= erb :'home/partials/event_filter', { layout: false }, { event_type: '' } %>
6
+ </script>
7
+
8
+ <div class="right_col" role="main">
9
+ <div class="">
10
+ <div class="page-title">
11
+ <div class="title_left">
12
+ <h3>Events lookup</h3>
13
+ </div>
14
+ </div>
15
+
16
+ <div class="clearfix"></div>
17
+
18
+ <div class="row" style="display: block;">
19
+ <div class="col-md-12 col-sm-12 ">
20
+ <div class="x_panel">
21
+ <div class="x_title">
22
+ <h2>Filters</h2>
23
+ <div class="clearfix"></div>
24
+ </div>
25
+ <div class="x_content">
26
+ <br/>
27
+ <form id="filters-form" action="<%= url('/') %>" method="GET" data-parsley-validate class="form-horizontal form-label-left">
28
+ <input type="hidden" name="per_page" value="<%= params[:per_page].to_i %>">
29
+ <div class="stream-filters">
30
+ <% streams_filter&.each do |attrs| %>
31
+ <%= erb :'home/partials/stream_filter', { layout: false }, { stream: attrs } %>
32
+ <% end %>
33
+ </div>
34
+ <div class="event-filters">
35
+ <% events_filter&.each do |event_type| %>
36
+ <%= erb :'home/partials/event_filter', { layout: false }, { event_type: event_type } %>
37
+ <% end %>
38
+ </div>
39
+ <div class="ln_solid"></div>
40
+ <div class="item form-group">
41
+ <div class="col-md-6 col-sm-6 offset-md-5">
42
+ <div class="btn-group">
43
+ <button type="submit" class="btn btn-success">Search</button>
44
+ </div>
45
+ <div class="btn-group">
46
+ <div class="dropdown">
47
+ <button class="btn btn-secondary dropdown-toggle" type="button"
48
+ data-toggle="dropdown" aria-expanded="false">
49
+ Add filter
50
+ </button>
51
+ <div class="dropdown-menu">
52
+ <a class="dropdown-item add-stream-filter" href="javascript: void(0)">Add stream filter</a>
53
+ <a class="dropdown-item add-event-filter" href="javascript: void(0)">Add event filter</a>
54
+ </div>
55
+ </div>
56
+ </div>
57
+ </div>
58
+ </div>
59
+ </form>
60
+ </div>
61
+ </div>
62
+ </div>
63
+ <div class="clearfix"></div>
64
+ </div>
65
+
66
+ <div class="row">
67
+ <div class="col-md-12 col-sm-12">
68
+ <div class="x_panel">
69
+ <div class="x_title">
70
+ <div class="row">
71
+ <div class="col-md-3 col-sm-12">
72
+ <h2>
73
+ Events
74
+ <small id="total-count"><%= total_count(@collection.total_count) %></small>
75
+ </h2>
76
+ <div class="clearfix"></div>
77
+ </div>
78
+ <div class="col-md-6 col-sm-12">
79
+ <form class="form-horizontal form-label-left float-md-right">
80
+ <div class="form-group float-md-right">
81
+ <label class="control-label text-nowrap float-left mr-2" for="per_page_select">Per page</label>
82
+ <select class="page-link text-dark float-left" name="per_page" id="per_page_select">
83
+ <% PgEventstore::Web::Paginator::EventsCollection::PER_PAGE.keys.each do |per_page| %>
84
+ <option <%= 'selected="selected"' if params[:per_page] == per_page %> data-url="<%= per_page_url(per_page) %>">
85
+ <%= per_page %>
86
+ </option>
87
+ <% end %>
88
+ </select>
89
+ <button type="submit" class="d-none"></button>
90
+ </div>
91
+ </form>
92
+ <div class="float-md-right mt-2 mr-2">
93
+ <div class="form-group">
94
+ <div class="checkbox">
95
+ <label for="auto-refresh" class="control-label">
96
+ Auto refresh
97
+ <input type="checkbox" id="auto-refresh" value="true" autocomplete="off">
98
+ </label>
99
+ </div>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ <div class="col-md-3 col-sm-12">
104
+ <nav aria-label="Page navigation">
105
+ <ul class="pagination justify-content-md-end" id="pagination">
106
+ <%= erb :'home/partials/pagination_links', { layout: false }, { collection: @collection } %>
107
+ </ul>
108
+ </nav>
109
+ <div class="clearfix"></div>
110
+ </div>
111
+ <div class="clearfix"></div>
112
+ </div>
113
+ </div>
114
+ <div class="x_content table-responsive">
115
+ <table class="table table-hover" id="events-table">
116
+ <thead>
117
+ <tr>
118
+ <th>
119
+ Global position
120
+ <% if params[:order] == 'asc' %>
121
+ <a href="<%= sort_url('desc') %>"><i class="fa fa-sort-desc"></i></a>
122
+ <% else %>
123
+ <a href="<%= sort_url('asc') %>"><i class="fa fa-sort-asc"></i></a>
124
+ <% end %>
125
+ </th>
126
+ <th>Stream revision</th>
127
+ <th>Context</th>
128
+ <th>Stream name</th>
129
+ <th>Stream ID</th>
130
+ <th>Event type</th>
131
+ <th>Created at(UTC)</th>
132
+ <th>Event ID</th>
133
+ <th></th>
134
+ </tr>
135
+ </thead>
136
+ <tbody>
137
+ <%= erb :'home/partials/events', { layout: false }, { events: @collection.collection } %>
138
+ </tbody>
139
+ </table>
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <div class="clearfix"></div>
145
+ </div>
146
+ </div>
147
+ </div>
@@ -0,0 +1,15 @@
1
+ <div class="form-row align-items-center">
2
+ <div class="col-11">
3
+ <select name="filter[events][]" class="form-control mb-2" data-placeholder="Event type" data-url="<%= url('/event_types_filtering') %>">
4
+ <option></option>
5
+ <% if event_type %>
6
+ <option value="<%= event_type %>" selected><%= event_type %></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>
@@ -0,0 +1,22 @@
1
+ <% events.each do |event| %>
2
+ <tr>
3
+ <td><%= event.global_position %></td>
4
+ <td><%= event.stream_revision %></td>
5
+ <td><%= event.stream.context %></td>
6
+ <td><%= event.stream.stream_name %></td>
7
+ <td><a href="<%= stream_path(event) %>"><%= event.stream.stream_id %></a></td>
8
+ <td><%= event.type %></td>
9
+ <td><%= event.created_at.strftime('%F %T') %></td>
10
+ <td><%= event.id %></td>
11
+ <td><a href="javascript: void(0);" class="d-inline-block text-nowrap toggle-event-data">JSON <i
12
+ class="fa fa-eye"></i> </a></td>
13
+ </tr>
14
+ <tr class="event-payload d-none">
15
+ <td colspan="8">
16
+ <strong>Data:</strong>
17
+ <pre><%= JSON.pretty_generate(event.data) %></pre>
18
+ <strong>Metadata:</strong>
19
+ <pre><%= JSON.pretty_generate(event.metadata) %></pre>
20
+ </td>
21
+ </tr>
22
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <%= first_page_link %>
2
+ <%= previous_page_link(collection) %>
3
+ <%= next_page_link(collection) %>
@@ -0,0 +1,31 @@
1
+ <div class="form-row align-items-center">
2
+ <div class="col-3">
3
+ <select name="filter[streams][][context]" class="form-control mb-2" data-placeholder="Context" data-url="<%= url('/stream_contexts_filtering') %>">
4
+ <option></option>
5
+ <% if stream[:context] %>
6
+ <option value="<%= stream[:context] %>" selected><%= stream[:context] %></option>
7
+ <% end %>
8
+ </select>
9
+ </div>
10
+ <div class="col-4">
11
+ <select name="filter[streams][][stream_name]" class="form-control mb-2" data-placeholder="Stream name" data-url="<%= url('/stream_names_filtering') %>">
12
+ <option></option>
13
+ <% if stream[:stream_name] %>
14
+ <option value="<%= stream[:stream_name] %>" selected><%= stream[:stream_name] %></option>
15
+ <% end %>
16
+ </select>
17
+ </div>
18
+ <div class="col-4">
19
+ <select name="filter[streams][][stream_id]" class="form-control mb-2" data-placeholder="Stream ID" data-url="<%= url('/stream_ids_filtering') %>">
20
+ <option></option>
21
+ <% if stream[:stream_id] %>
22
+ <option value="<%= stream[:stream_id] %>" selected><%= stream[:stream_id] %></option>
23
+ <% end %>
24
+ </select>
25
+ </div>
26
+ <div class="col-1">
27
+ <a class="btn btn-default remove-filter" href="javascript: void(0);">
28
+ <i class="fa fa-minus-circle"></i>
29
+ </a>
30
+ </div>
31
+ </div>
@@ -0,0 +1,116 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
5
+ <!-- Meta, title, CSS, favicons, etc. -->
6
+ <meta charset="utf-8">
7
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <link rel="icon" href="images/favicon.ico" type="image/ico"/>
10
+
11
+ <title>PG EventStore admin</title>
12
+
13
+ <!-- Bootstrap -->
14
+ <link href="stylesheets/vendor/bootstrap.min.css" rel="stylesheet">
15
+ <!-- Font Awesome -->
16
+ <link href="stylesheets/vendor/font-awesome.min.css" rel="stylesheet">
17
+
18
+ <!-- Custom Theme Style -->
19
+ <link href="stylesheets/vendor/gentelella.min.css" rel="stylesheet">
20
+ <link href="stylesheets/vendor/select2.min.css" rel="stylesheet">
21
+ <link href="stylesheets/vendor/select2-bootstrap4.min.css" rel="stylesheet">
22
+ <link href="stylesheets/pg_eventstore.css" rel="stylesheet">
23
+ </head>
24
+
25
+ <body class="nav-md">
26
+ <div class="container body">
27
+ <div class="main_container">
28
+ <div class="col-md-3 left_col">
29
+ <div class="left_col scroll-view">
30
+ <div class="navbar nav_title" style="border: 0;">
31
+ <a href="<%= url('/') %>" class="site_title"><i class="fa fa-server"></i> <span>PG EventStore</span></a>
32
+ </div>
33
+
34
+ <div class="clearfix"></div>
35
+
36
+ <br/>
37
+
38
+ <!-- sidebar menu -->
39
+ <div id="sidebar-menu" class="main_menu_side hidden-print main_menu">
40
+ <div class="menu_section">
41
+ <h3>General</h3>
42
+ <ul class="nav side-menu">
43
+ <li>
44
+ <a href="<%= url('/') %>">
45
+ <i class="fa fa-search-plus"></i>
46
+ Events lookup
47
+ </a>
48
+ </li>
49
+ <li>
50
+ <a href="<%= url('/subscriptions') %>">
51
+ <i class="fa fa-pencil-square-o"></i>
52
+ Subscriptions
53
+ </a>
54
+ </li>
55
+ </ul>
56
+ </div>
57
+ </div>
58
+ <!-- /sidebar menu -->
59
+ </div>
60
+ </div>
61
+
62
+ <!-- top navigation -->
63
+ <div class="top_nav">
64
+ <div class="nav_menu">
65
+ <div class="nav toggle">
66
+ <a id="menu_toggle"><i class="fa fa-bars"></i></a>
67
+ </div>
68
+ <nav class="nav navbar-nav">
69
+ <ul class="navbar-right" style="height: 29px;">
70
+ <li class="nav-item dropdown open" style="padding-left: 15px;">
71
+ <a href="javascript: void(0);" class="user-profile dropdown-toggle" aria-haspopup="true" id="navbarDropdown" data-toggle="dropdown" aria-expanded="false">
72
+ <strong>Current config:</strong> <%= current_config.inspect %>
73
+ </a>
74
+ <div class="dropdown-menu dropdown-usermenu pull-right" aria-labelledby="navbarDropdown">
75
+ <% PgEventstore.available_configs.each do |config| %>
76
+ <form action="<%= url('/change_config') %>" method="POST">
77
+ <input type="hidden" name="config" value="<%= config %>">
78
+ <button type="submit" class="dropdown-item"><%= config.inspect %></button>
79
+ </form>
80
+ <% end %>
81
+ </div>
82
+ </li>
83
+ </ul>
84
+ </nav>
85
+ </div>
86
+ </div>
87
+ <!-- /top navigation -->
88
+
89
+ <!-- page content -->
90
+ <%= yield %>
91
+ <!-- /page content -->
92
+
93
+ <!-- footer content -->
94
+ <footer>
95
+ <div class="pull-right">
96
+ Gentelella - Bootstrap Admin Template by <a href="https://colorlib.com" rel="nofollow noreferrer noopener">Colorlib</a>
97
+ </div>
98
+ <div class="clearfix"></div>
99
+ </footer>
100
+ <!-- /footer content -->
101
+ </div>
102
+ </div>
103
+
104
+ <!-- jQuery -->
105
+ <script src="javascripts/vendor/jquery.min.js"></script>
106
+ <!-- Bootstrap -->
107
+ <script src="javascripts/vendor/bootstrap.bundle.min.js"></script>
108
+ <!--<script src="javascripts/jquery.autocomplete.min.js"></script>-->
109
+ <script src="javascripts/vendor/select2.full.min.js"></script>
110
+
111
+ <!-- Custom Theme Scripts -->
112
+ <script src="javascripts/gentelella.js"></script>
113
+ <script src="javascripts/pg_eventstore.js"></script>
114
+
115
+ </body>
116
+ </html>
@@ -0,0 +1,220 @@
1
+ <div class="right_col" role="main">
2
+ <div class="">
3
+ <div class="page-title">
4
+ <div class="title_left">
5
+ <h3>Subscriptions</h3>
6
+ </div>
7
+ </div>
8
+
9
+ <div class="clearfix"></div>
10
+
11
+ <ul class="nav nav-tabs bg-white">
12
+ <% @set_collection.names.each do |set_name| %>
13
+ <li class="nav-item">
14
+ <a class="nav-link <%= "active bg-dark text-white" if @current_set == set_name %>" href="<%= subscriptions_url(set_name: set_name) %>">
15
+ <%= set_name %>
16
+ </a>
17
+ </li>
18
+ <% end %>
19
+ </ul>
20
+
21
+ <div class="row">
22
+ <div class="col-md-12 col-sm-12">
23
+ <div class="x_panel">
24
+ <div class="x_title">
25
+ <h2><i class="fa fa-bars"></i> Tabs <small>Float left</small></h2>
26
+ <div class="clearfix"></div>
27
+ </div>
28
+ <div class="x_content">
29
+ <ul class="nav nav-tabs bar_tabs" role="tablist">
30
+ <% @association.association.each.with_index do |(subscriptions_set, _), index| %>
31
+ <li class="nav-item">
32
+ <a class="nav-link set-tab <%= "active" if index.zero? %>" id="tab-<%= subscriptions_set.id || 'shutdown' %>" data-toggle="tab" href="#set-<%= subscriptions_set.id || 'shutdown' %>" role="tab" aria-controls="set-<%= subscriptions_set.id || 'shutdown' %>" aria-selected="true">
33
+ <%= subscriptions_set.id.nil? ? "Shutdown" : "Set ##{subscriptions_set.id}" %>
34
+ </a>
35
+ </li>
36
+ <% end %>
37
+ </ul>
38
+ <div class="tab-content">
39
+ <% @association.association.each.with_index do |(subscriptions_set, subscriptions), index| %>
40
+ <div class="table-responsive tab-pane fade show <%= "active" if index.zero? %>" id="set-<%= subscriptions_set.id || 'shutdown' %>" role="tabpanel" aria-labelledby="tab-<%= subscriptions_set.id || 'shutdown' %>">
41
+ <% if subscriptions_set.id %>
42
+ <div>
43
+ <h2>Subscriptions Set</h2>
44
+ <table class="table table-hover">
45
+ <thead>
46
+ <tr>
47
+ <th>ID</th>
48
+ <th>State</th>
49
+ <th>Restarts count</th>
50
+ <th>Max restarts number</th>
51
+ <th>Time between restarts</th>
52
+ <th>Last restarted at</th>
53
+ <th>Last error</th>
54
+ <th>Last error occurred at</th>
55
+ <th></th>
56
+ </tr>
57
+ </thead>
58
+ <tbody>
59
+ <tr>
60
+ <td><%= subscriptions_set.id %></td>
61
+ <td><%= colored_state(subscriptions_set.state, subscriptions_set.updated_at) %></td>
62
+ <td><%= subscriptions_set.restart_count %></td>
63
+ <td><%= subscriptions_set.max_restarts_number %></td>
64
+ <td><%= subscriptions_set.time_between_restarts %>s</td>
65
+ <td><%= subscriptions_set.last_restarted_at %></td>
66
+ <td>
67
+ <% if subscriptions_set.last_error %>
68
+ <a data-toggle="collapse" href="#last-set-error-<%= subscriptions_set.id %>" aria-controls="last-set-error-<%= subscriptions_set.id %>" role="button" aria-expanded="false" class="d-inline-block text-nowrap">
69
+ JSON <i class="fa fa-eye"></i>
70
+ </a>
71
+ <% end %>
72
+ </td>
73
+ <td><%= subscriptions_set.last_error_occurred_at %></td>
74
+ <td>
75
+ <% if subscriptions_set.state == PgEventstore::RunnerState::STATES[:dead] %>
76
+ <a class="btn btn-success" data-method="post" href="<%= subscriptions_set_cmd_url(subscriptions_set.id, subscriptions_set_cmds[:Restore]) %>">
77
+ Restore
78
+ </a>
79
+ <% end %>
80
+ <% if PgEventstore::RunnerState::STATES.values_at(:running, :dead).include?(subscriptions_set.state) %>
81
+ <a class="btn btn-warning" data-method="post" href="<%= subscriptions_set_cmd_url(subscriptions_set.id, subscriptions_set_cmds[:Stop]) %>" data-toggle="tooltip" title="This action will delete Subscriptions Set and release all related Subscriptions.">
82
+ Stop
83
+ </a>
84
+ <% end %>
85
+ <a class="btn btn-danger" data-method="post" href="<%= url("/delete_subscriptions_set/#{subscriptions_set.id}") %>" data-toggle="tooltip" title="Use this action only on stuck Subscriptions Set - to clean it up.">
86
+ Delete
87
+ </a>
88
+ </td>
89
+ </tr>
90
+ <% if subscriptions_set.last_error %>
91
+ <tr class="collapse" id="last-set-error-<%= subscriptions_set.id %>">
92
+ <td colspan="9">
93
+ <pre><%= JSON.pretty_generate(subscriptions_set.last_error) %></pre>
94
+ </td>
95
+ </tr>
96
+ <% end %>
97
+ </tbody>
98
+ </table>
99
+ </div>
100
+ <% end %>
101
+ <div>
102
+ <div>
103
+ <div class="float-left">
104
+ <h2>Subscriptions list</h2>
105
+ </div>
106
+ <div class="float-right">
107
+ <% if subscriptions_set.id %>
108
+ <a class="btn btn-success" data-method="post" href="<%= subscriptions_set_cmd_url(subscriptions_set.id, subscriptions_set_cmds[:StartAll]) %>">
109
+ Start All
110
+ </a>
111
+ <a class="btn btn-danger" data-method="post" href="<%= subscriptions_set_cmd_url(subscriptions_set.id, subscriptions_set_cmds[:StopAll]) %>">
112
+ Stop All
113
+ </a>
114
+ <% else %>
115
+ <a class="btn btn-danger" data-method="post" href="<%= delete_all_subscriptions_url(subscriptions.map(&:id)) %>" data-toggle="tooltip" title="Delete all Subscriptions, displayed on the current page.">
116
+ Delete All
117
+ </a>
118
+ <% end %>
119
+ </div>
120
+ </div>
121
+ <table class="table table-hover">
122
+ <thead>
123
+ <tr>
124
+ <th>ID</th>
125
+ <th>Name</th>
126
+ <th>Total processed events</th>
127
+ <th>Options</th>
128
+ <th>Current position</th>
129
+ <th>Pull interval</th>
130
+ <th>Last event arrived at</th>
131
+ <th>State</th>
132
+ <th>Performance</th>
133
+ <th>Restarts count</th>
134
+ <th>Max restarts number</th>
135
+ <th>Time between restarts</th>
136
+ <th>Last restarted at</th>
137
+ <th>Last error</th>
138
+ <th>Last error occurred at</th>
139
+ <th></th>
140
+ </tr>
141
+ </thead>
142
+ <tbody>
143
+ <% subscriptions.each do |subscription| %>
144
+ <tr>
145
+ <td><%= subscription.id %></td>
146
+ <td><%= subscription.name %></td>
147
+ <td><%= subscription.total_processed_events %></td>
148
+ <td>
149
+ <a data-toggle="collapse" href="#options-<%= subscription.id %>" aria-controls="options-<%= subscription.id %>" role="button" aria-expanded="false" class="d-inline-block text-nowrap">
150
+ JSON <i class="fa fa-eye"></i>
151
+ </a>
152
+ </td>
153
+ <td><%= subscription.current_position %></td>
154
+ <td><%= subscription.chunk_query_interval %>s</td>
155
+ <td><%= subscription.last_chunk_fed_at %></td>
156
+ <td><%= colored_state(subscription.state, subscription.updated_at) %></td>
157
+ <td><%= subscription.average_event_processing_time %></td>
158
+ <td><%= subscription.restart_count %></td>
159
+ <td><%= subscription.max_restarts_number %></td>
160
+ <td><%= subscription.time_between_restarts %>s</td>
161
+ <td><%= subscription.last_restarted_at %></td>
162
+ <td>
163
+ <% if subscription.last_error %>
164
+ <a data-toggle="collapse" href="#last-error-<%= subscription.id %>" aria-controls="last-error-<%= subscription.id %>" role="button" aria-expanded="false" class="d-inline-block text-nowrap">
165
+ JSON <i class="fa fa-eye"></i>
166
+ </a>
167
+ <% end %>
168
+ </td>
169
+ <td><%= subscription.last_error_occurred_at %></td>
170
+ <td>
171
+ <% if subscriptions_set.id %>
172
+ <% if subscription.state == PgEventstore::RunnerState::STATES[:stopped] %>
173
+ <a class="btn btn-success" data-method="post" href="<%= subscription_cmd_url(subscriptions_set.id, subscription.id, subscriptions_cmds[:Start]) %>">
174
+ Start
175
+ </a>
176
+ <% end %>
177
+ <% if PgEventstore::RunnerState::STATES.values_at(:running, :dead).include?(subscription.state) %>
178
+ <a class="btn btn-warning" data-method="post" href="<%= subscription_cmd_url(subscriptions_set.id, subscription.id, subscriptions_cmds[:Stop]) %>">
179
+ Stop
180
+ </a>
181
+ <% end %>
182
+ <% if subscription.state == PgEventstore::RunnerState::STATES[:dead] %>
183
+ <a class="btn btn-success" data-method="post" href="<%= subscription_cmd_url(subscriptions_set.id, subscription.id, subscriptions_cmds[:Restore]) %>">
184
+ Restore
185
+ </a>
186
+ <% end %>
187
+ <% end %>
188
+ <% unless subscriptions_set.id %>
189
+ <a class="btn btn-danger" data-method="post" href="<%= url("/delete_subscription/#{subscription.id}") %>" data-toggle="tooltip" title="You will lose the Subscription's position as well.">
190
+ Delete
191
+ </a>
192
+ <% end %>
193
+ </td>
194
+ </tr>
195
+ <tr class="collapse" id="options-<%= subscription.id %>">
196
+ <td colspan="16">
197
+ <pre><%= JSON.pretty_generate(subscription.options) %></pre>
198
+ </td>
199
+ </tr>
200
+ <% if subscription.last_error %>
201
+ <tr class="collapse" id="last-error-<%= subscription.id %>">
202
+ <td colspan="16">
203
+ <pre><%= JSON.pretty_generate(subscription.last_error) %></pre>
204
+ </td>
205
+ </tr>
206
+ <% end %>
207
+ <% end %>
208
+ </tbody>
209
+ </table>
210
+ </div>
211
+ </div>
212
+ <% end %>
213
+ </div>
214
+ </div>
215
+ </div>
216
+ </div>
217
+ <div class="clearfix"></div>
218
+ </div>
219
+ </div>
220
+ </div>
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pg_eventstore'
4
+ require 'sinatra/base'
5
+ require_relative 'web/paginator/helpers'
6
+ require_relative 'web/paginator/base_collection'
7
+ require_relative 'web/paginator/events_collection'
8
+ require_relative 'web/paginator/stream_contexts_collection'
9
+ require_relative 'web/paginator/stream_names_collection'
10
+ require_relative 'web/paginator/stream_ids_collection'
11
+ require_relative 'web/paginator/event_types_collection'
12
+ require_relative 'web/subscriptions/set_collection'
13
+ require_relative 'web/subscriptions/subscriptions'
14
+ require_relative 'web/subscriptions/subscriptions_set'
15
+ require_relative 'web/subscriptions/subscriptions_to_set_association'
16
+ require_relative 'web/subscriptions/helpers'
17
+ require_relative 'web/application'
18
+
19
+ module PgEventstore
20
+ module Web
21
+ end
22
+ end
data/lib/pg_eventstore.rb CHANGED
@@ -38,6 +38,11 @@ module PgEventstore
38
38
  end
39
39
  end
40
40
 
41
+ # @return [Array<Symbol>]
42
+ def available_configs
43
+ @config.keys
44
+ end
45
+
41
46
  # @param name [Symbol]
42
47
  # @return [PgEventstore::Config]
43
48
  def config(name = :default)
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.files = Dir.chdir(__dir__) do
26
26
  paths_to_exclude = %w[
27
27
  bin/ test/ spec/ features/ .git .circleci appveyor Gemfile .ruby-version .ruby-gemset .rspec docker-compose.yml
28
- Rakefile benchmark/ .yardopts db/structure.sql
28
+ Rakefile benchmark/ .yardopts db/structure.sql config.ru
29
29
  ]
30
30
  `git ls-files -z`.split("\x0").reject do |f|
31
31
  (File.expand_path(f) == __FILE__) || f.start_with?(*paths_to_exclude)
@@ -37,4 +37,5 @@ Gem::Specification.new do |spec|
37
37
 
38
38
  spec.add_dependency "pg", "~> 1.5"
39
39
  spec.add_dependency "connection_pool", "~> 2.4"
40
+ spec.add_dependency "sinatra", "~> 4.0"
40
41
  end