sidekiq-bus 0.8.2 → 0.9.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
  SHA1:
3
- metadata.gz: 2c633c91c5b55a5e08ec80f73ee6a174df07d59c
4
- data.tar.gz: '00095d6bcdc1407137cab9051990b91f359df304'
3
+ metadata.gz: 1f2be4d5e020e0a15769b9b1afcf3f7ed36d1d6b
4
+ data.tar.gz: 9f1d638cf9f47eb1afa87bb21b8141d2baa27f10
5
5
  SHA512:
6
- metadata.gz: a4dcb3d6a379ceda40fe1da42c6cdb00662b85f37bbfcf9010ed7303883d8e44a9f43f9ab9c587793503ce4ade15a09f1c23cbdd6ec07ae5f4d85af0ce8ced04
7
- data.tar.gz: 2631baff2bc6724686516f34cc04955fef78884228566f961b2f51dd069130bf67c65a562200299e833b241d8ed18c6e7a17636768bfd6310d318de1c8997c0f
6
+ metadata.gz: 1f9783a99bf3c8b0c6fb2df111362d77507d8d245112ec66707e82ee4c172bb78cc28f5e32026adbbb3ca4e1c0f11fc15c27f67cbd79d32545e40b1bb48a424b
7
+ data.tar.gz: 66b88100b6068b47ce2f28080eeb4eca7ce5ef669cef9bd71cf195893c43c88a976ccf4040cf0e08d14a45031f4195725079e6da29f1cc5e14a6b03eec3dfb1f
@@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.0] - 2019-12-03
10
+
11
+ ### Added
12
+ - Tab in sidekiq web ui for bus info
13
+
9
14
  ### [0.8.2] - 2019-08-06
10
15
 
11
16
  ### Fixed
@@ -3,6 +3,7 @@
3
3
  require 'queue-bus'
4
4
  require 'sidekiq_bus/adapter'
5
5
  require 'sidekiq_bus/version'
6
+ require 'sidekiq_bus/server'
6
7
  require 'sidekiq_bus/middleware/retry'
7
8
 
8
9
  module SidekiqBus
@@ -0,0 +1,139 @@
1
+ require 'sidekiq/web'
2
+
3
+ module SidekiqBus
4
+ module Server
5
+ def self.registered(app)
6
+ app.get('/bus') do
7
+ # you can define @instance_variables for passing into template
8
+ # Sidekiq uses erb for its templates so you should do it aswell
9
+ erb File.read(File.join(File.dirname(__FILE__), "server/views/bus.erb"))
10
+ end
11
+
12
+ app.post '/bus/unsubscribe' do
13
+ bus_app = ::QueueBus::Application.new(params[:name]).unsubscribe
14
+ redirect "#{root_path}bus"
15
+ end
16
+ end
17
+
18
+ class Helpers
19
+ class << self
20
+ def parse_query(query_string)
21
+ has_open_brace = query_string.include?("{")
22
+ has_close_brace = query_string.include?("}")
23
+ has_multiple_lines = query_string.include?("\n")
24
+ has_colon = query_string.include?(":")
25
+ has_comma = query_string.include?(",")
26
+ has_quote = query_string.include?("\"")
27
+
28
+ exception = nil
29
+
30
+ # first let's see if it parses
31
+ begin
32
+ query_attributes = JSON.parse(query_string)
33
+ raise "Not a JSON Object" unless query_attributes.is_a?(Hash)
34
+ rescue Exception => e
35
+ exception = e
36
+ end
37
+ return query_attributes unless exception
38
+
39
+ if query_attributes
40
+ # it parsed but it's something else
41
+ if query_attributes.is_a?(Array) && query_attributes.length == 1
42
+ # maybe it's the thing from the queue
43
+ json_string = query_attributes.first
44
+ fixed = JSON.parse(json_string) rescue nil
45
+ return fixed if fixed
46
+ end
47
+
48
+ # something else?
49
+ raise exception
50
+ end
51
+
52
+ if !has_open_brace && !has_close_brace
53
+ # maybe they just forgot the braces
54
+ fixed = JSON.parse("{ #{query_string} }") rescue nil
55
+ return fixed if fixed
56
+ end
57
+
58
+ if !has_open_brace
59
+ # maybe they just forgot the braces
60
+ fixed = JSON.parse("{ #{query_string}") rescue nil
61
+ return fixed if fixed
62
+ end
63
+
64
+ if !has_close_brace
65
+ # maybe they just forgot the braces
66
+ fixed = JSON.parse("#{query_string} }") rescue nil
67
+ return fixed if fixed
68
+ end
69
+
70
+ if !has_multiple_lines && !has_colon && !has_open_brace && !has_close_brace
71
+ # we say they just put a bus_event type here, so help them out
72
+ return {"bus_event_type" => query_string, "more_here" => true}
73
+ end
74
+
75
+ if has_colon && !has_quote
76
+ # maybe it's some search syntax like this: field: value other: true, etc
77
+ # maybe use something like this later: https://github.com/dxwcyber/search-query-parser
78
+
79
+ # quote all the strings, (simply) tries to avoid integers
80
+ test_query = query_string.gsub(/([a-zA-z]\w*)/,'"\0"')
81
+ if !has_comma
82
+ test_query.gsub!("\n", ",\n")
83
+ end
84
+ if !has_open_brace && !has_close_brace
85
+ test_query = "{ #{test_query} }"
86
+ end
87
+
88
+ fixed = JSON.parse(test_query) rescue nil
89
+ return fixed if fixed
90
+ end
91
+
92
+ if has_open_brace && has_close_brace
93
+ # maybe the whole thing is a hash output from a hash.inspect log
94
+ ruby_hash_text = query_string.clone
95
+ # https://stackoverflow.com/questions/1667630/how-do-i-convert-a-string-object-into-a-hash-object
96
+ # Transform object string symbols to quoted strings
97
+ ruby_hash_text.gsub!(/([{,]\s*):([^>\s]+)\s*=>/, '\1"\2"=>')
98
+ # Transform object string numbers to quoted strings
99
+ ruby_hash_text.gsub!(/([{,]\s*)([0-9]+\.?[0-9]*)\s*=>/, '\1"\2"=>')
100
+ # Transform object value symbols to quotes strings
101
+ ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>\s*:([^,}\s]+\s*)/, '\1\2=>"\3"')
102
+ # Transform array value symbols to quotes strings
103
+ ruby_hash_text.gsub!(/([\[,]\s*):([^,\]\s]+)/, '\1"\2"')
104
+ # fix up nil situation
105
+ ruby_hash_text.gsub!(/=>nil/, '=>null')
106
+ # Transform object string object value delimiter to colon delimiter
107
+ ruby_hash_text.gsub!(/([{,]\s*)(".+?"|[0-9]+\.?[0-9]*)\s*=>/, '\1\2:')
108
+ fixed = JSON.parse(ruby_hash_text) rescue nil
109
+ return fixed if fixed
110
+ end
111
+
112
+ raise exception
113
+ end
114
+
115
+ def sort_query(query_attributes)
116
+ query_attributes.each do |key, value|
117
+ if value.is_a?(Hash)
118
+ query_attributes[key] = sort_query(value)
119
+ end
120
+ end
121
+ query_attributes.sort_by { |key| key }.to_h
122
+ end
123
+
124
+ def query_subscriptions(app, query_attributes)
125
+ # TODO: all of this can move to method in queue-bus to replace event_display_tuples
126
+ if query_attributes
127
+ subscriptions = app.subscription_matches(query_attributes)
128
+ else
129
+ subscriptions = app.send(:subscriptions).all
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+
137
+ Sidekiq::Web.register SidekiqBus::Server
138
+ Sidekiq::Web.locales << File.expand_path(File.dirname(__FILE__) + "/web/locales")
139
+ Sidekiq::Web.tabs['Bus'] = 'bus'
@@ -0,0 +1,202 @@
1
+
2
+ <script LANGUAGE="JavaScript">
3
+ <!--
4
+ function confirmSubmit()
5
+ {
6
+ var agree=confirm("Are you sure you wish to continue?");
7
+ if (agree)
8
+ return true ;
9
+ else
10
+ return false ;
11
+ }
12
+
13
+ function setSample() {
14
+ var text = document.getElementById("query_attributes").textContent;
15
+ var textArea = document.getElementById('querytext');
16
+ textArea.value = text;
17
+ return false;
18
+ }
19
+ // -->
20
+ </script>
21
+
22
+ <%
23
+ app_hash = {}
24
+ class_hash = {}
25
+ event_hash = {}
26
+ query_string = params[:query].to_s.strip
27
+
28
+ query_attributes = nil
29
+ query_error = nil
30
+ if query_string.length > 0
31
+ begin
32
+ query_attributes = ::SidekiqBus::Server::Helpers.parse_query(query_string)
33
+ raise "Not a JSON Object" unless query_attributes.is_a?(Hash)
34
+ rescue Exception => e
35
+ query_attributes = nil
36
+ query_error = e.message
37
+ end
38
+
39
+ if query_attributes
40
+ # sort keys for display
41
+ query_attributes = ::SidekiqBus::Server::Helpers.sort_query(query_attributes)
42
+ end
43
+ end
44
+
45
+ # collect each differently
46
+ ::QueueBus::Application.all.each do |app|
47
+ app_key = app.app_key
48
+
49
+ subscriptions = ::SidekiqBus::Server::Helpers.query_subscriptions(app, query_attributes)
50
+ subscriptions.each do |sub|
51
+ class_name = sub.class_name
52
+ queue = sub.queue_name
53
+ filters = sub.matcher.filters
54
+ sub_key = sub.key
55
+
56
+ if filters["bus_event_type"]
57
+ event = filters["bus_event_type"]
58
+ else
59
+ event = "see filter"
60
+ end
61
+
62
+ app_hash[app_key] ||= []
63
+ app_hash[app_key] << [sub_key, event, class_name, queue, filters]
64
+
65
+ class_hash[class_name] ||= []
66
+ class_hash[class_name] << [app_key, sub_key, event, queue, filters]
67
+
68
+ event_hash[event] ||= []
69
+ event_hash[event] << [app_key, sub_key, class_name, queue, filters]
70
+ end
71
+ end
72
+
73
+ # sort each list item by secondary label
74
+ class_hash.each do |_, array|
75
+ array.sort!{ |a,b| a.first <=> b.first }
76
+ end
77
+ event_hash.each do |_, array|
78
+ array.sort!{ |a,b| a.first <=> b.first }
79
+ end
80
+
81
+ # helper to display either
82
+ def display_row(name, val, button=nil, first=false)
83
+ form = ""
84
+ if button
85
+ text, url = button
86
+ form = "<form method='POST' action='#{url}' style='float:left; padding:0 5px 0 0;margin:0;'>#{csrf_tag}<input type='submit' name='' value='#{h(text)}' style='padding:0;margin:0;' onclick=\"return confirmSubmit();\"/><input type='hidden' name='name' value='#{h(name)}' /></form>"
87
+ end
88
+
89
+ if !val
90
+ out = "<td>&nbsp;</td><td>&nbsp;</td>"
91
+ else
92
+ one, two, three, queue, filters = val
93
+ out = "<td>#{h(one)}</td><td>#{h(two)}</td><td>#{h(three)}</td><td><a href='#{"queues/#{queue}"}'>#{h(queue)}</a></td>"
94
+ out << "<td>#{h(::QueueBus::Util.encode(filters).gsub(/\"bus_special_value_(\w+)\"/){ "(#{$1})" }).gsub(" ", "&nbsp;").gsub('&quot;,&quot;', '&quot;, &quot;')}</td>"
95
+ end
96
+
97
+ if first
98
+ "<tr><td>#{h(name)}#{form}</td>#{out}</tr>\n"
99
+ else
100
+ "<tr><td>&nbsp;</td>#{out}</tr>\n"
101
+ end
102
+ end
103
+
104
+ def output_hash(hash, action=nil)
105
+ out = ""
106
+ hash.keys.sort.each do |item|
107
+ display = hash[item]
108
+ first = display.shift
109
+ out << display_row(item, first, action, true)
110
+ display.each do |val|
111
+ out << display_row(item, val, action)
112
+ end
113
+ end
114
+ out
115
+ end
116
+ %>
117
+
118
+ <h1 class='wi'>Sample Event</h1>
119
+ <p class='intro'>Enter JSON of an event to see applicable subscriptions.</p>
120
+ <div style="text-align: center;width:700px;">
121
+ -<form method="GET" action="<%= "bus" %>" style="float:none;padding:0;margin:0;">
122
+ <textarea id="querytext" name="query" style="padding: 10px;height:150px;width:700px;font-size:14px;font-family:monospace"><%=
123
+ h(query_string)
124
+ %></textarea>
125
+ <br/>
126
+ <button onclick="window.location.href = '<%= "bus" %>'; return false;">Clear</button>
127
+ <input type="submit" name="" value="Filter results to this event"/>
128
+ </form>
129
+ </div>
130
+
131
+ <% if query_error %>
132
+ <blockquote><pre style="text-align:left;font-family:monospace;margin:5px 0 5px 0;padding:10px;background:#f2dede;color:#a94442;"><code><%=
133
+ h(query_error.strip)
134
+ %></code></pre></blockquote>
135
+ <% end %>
136
+ <% if query_attributes %>
137
+ <blockquote><pre style="text-align:left;font-family:monospace;margin:5px 0 5px 0;padding:10px;background:#dff0d8;color:#3c763d;"><code id="query_attributes"><%=
138
+ h(JSON.pretty_generate(query_attributes).strip)
139
+ %></code></pre></blockquote>
140
+ <div style="text-align:right;">
141
+ <button onclick="return setSample();">Set Sample</button>
142
+ </div>
143
+ <% end %>
144
+
145
+ <hr/>
146
+
147
+ <h1 class='wi'>Applications</h1>
148
+ <p class='intro'>The apps below have registered the given classes and queues.</p>
149
+ <table class='table-striped table-bordered'>
150
+ <thead class="thead-dark">
151
+ <tr>
152
+ <th>App Key</th>
153
+ <th>Subscription Key</th>
154
+ <th>Event Type</th>
155
+ <th>Class Name</th>
156
+ <th>Queue</th>
157
+ <th>Filters</th>
158
+ </tr>
159
+ </thead>
160
+
161
+ <%= output_hash(app_hash, query_attributes ? false : ["Unsubscribe", "bus/unsubscribe"]) %>
162
+ </table>
163
+
164
+ <p>&nbsp;</p>
165
+
166
+ <h1 class='wi'>Events</h1>
167
+ <p class='intro'>The events below have been registered by the given applications and queues.</p>
168
+
169
+ <table class='table-striped table-bordered'>
170
+ <thead class="thead-dark">
171
+ <tr>
172
+ <th>Event Type</th>
173
+ <th>App Key</th>
174
+ <th>Subscription Key</th>
175
+ <th>Class Name</th>
176
+ <th>Queue</th>
177
+ <th>Filters</th>
178
+ </tr>
179
+ </thead>
180
+ <%= output_hash(event_hash, false) %>
181
+ </table>
182
+
183
+
184
+
185
+ <p>&nbsp;</p>
186
+
187
+ <h1 class='wi'>Classes</h1>
188
+ <p class='intro'>The classes below have been registered by the given applications and queues.</p>
189
+ <table class='table-striped table-bordered'>
190
+ <thead class="thead-dark">
191
+ <tr>
192
+ <th>Class Name</th>
193
+ <th>App Key</th>
194
+ <th>Subscription Key</th>
195
+ <th>Event Type</th>
196
+ <th>Queue</th>
197
+ <th>Filters</th>
198
+ </tr>
199
+ </thead>
200
+ <%= output_hash(class_hash, false) %>
201
+ </table>
202
+
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SidekiqBus
4
- VERSION = '0.8.2'
4
+ VERSION = '0.9.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sidekiq-bus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.2
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Leonard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-06 00:00:00.000000000 Z
11
+ date: 2019-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: queue-bus
@@ -184,6 +184,8 @@ files:
184
184
  - lib/sidekiq-bus.rb
185
185
  - lib/sidekiq_bus/adapter.rb
186
186
  - lib/sidekiq_bus/middleware/retry.rb
187
+ - lib/sidekiq_bus/server.rb
188
+ - lib/sidekiq_bus/server/views/bus.erb
187
189
  - lib/sidekiq_bus/tasks.rb
188
190
  - lib/sidekiq_bus/version.rb
189
191
  - sidekiq-bus.gemspec