sqs_web 0.0.1
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 +7 -0
- data/Gemfile +5 -0
- data/README.markdown +121 -0
- data/Rakefile +27 -0
- data/bin/sqs_web +10 -0
- data/lib/sqs_web/application/app.rb +89 -0
- data/lib/sqs_web/application/controller_action.rb +16 -0
- data/lib/sqs_web/application/exception_handle.rb +9 -0
- data/lib/sqs_web/application/flash_message.rb +15 -0
- data/lib/sqs_web/application/navigation.rb +42 -0
- data/lib/sqs_web/application/poller_action.rb +41 -0
- data/lib/sqs_web/application/public/javascripts/application.js +58 -0
- data/lib/sqs_web/application/public/javascripts/jquery-1.7.1.min.js +4 -0
- data/lib/sqs_web/application/public/javascripts/jquery.relatize_date.js +117 -0
- data/lib/sqs_web/application/public/stylesheets/reset.css +44 -0
- data/lib/sqs_web/application/public/stylesheets/style.css +58 -0
- data/lib/sqs_web/application/sqs_action.rb +77 -0
- data/lib/sqs_web/application/views/dlq_console.erb +21 -0
- data/lib/sqs_web/application/views/error.erb +3 -0
- data/lib/sqs_web/application/views/layout.erb +33 -0
- data/lib/sqs_web/application/views/message.erb +47 -0
- data/lib/sqs_web/application/views/overview.erb +17 -0
- data/lib/sqs_web/application/views/queue_stats.erb +14 -0
- data/lib/sqs_web/application/views/working.erb +13 -0
- data/lib/sqs_web/railtie.rb +7 -0
- data/lib/sqs_web.rb +9 -0
- data/spec/integration/app_spec.rb +369 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/fake_sqs.rb +19 -0
- data/spec/support/rails_app.rb +12 -0
- data/sqs_web.gemspec +37 -0
- metadata +191 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
<li class="job" id="message_<%= message.message_id %>">
|
2
|
+
<input type="checkbox" class="bulk_check" id="batch_action_item_<%= message.message_id %>" value="<%= message.message_id %>/<%= queue[:name] %>" name="message_collection[]" form="bulk_action_form"/>
|
3
|
+
<dl>
|
4
|
+
<dt>ID</dt>
|
5
|
+
<dd>
|
6
|
+
<a name="<%= message.message_id %>"></a>
|
7
|
+
<a href="#<%= message.message_id %>"><%=h message.message_id %></a>
|
8
|
+
<div class="controls">
|
9
|
+
<form action="<%= u("remove/#{queue[:name]}/#{message.message_id}") %>" method="post">
|
10
|
+
<%= csrf_token_tag %>
|
11
|
+
<input type="submit" value="Remove" />
|
12
|
+
</form>
|
13
|
+
or
|
14
|
+
<form action="<%= u("requeue/#{queue[:name]}/#{message.message_id}") %>" method="post">
|
15
|
+
<%= csrf_token_tag %>
|
16
|
+
<input type="submit" value="Move to Source Queue" />
|
17
|
+
</form>
|
18
|
+
</div>
|
19
|
+
</dd>
|
20
|
+
<% if message.attributes["ApproximateReceiveCount"] %>
|
21
|
+
<dt>Receive Count</dt>
|
22
|
+
<dd><%=h message.attributes["ApproximateReceiveCount"] %></dd>
|
23
|
+
<% end %>
|
24
|
+
<dt>Queue Name</dt>
|
25
|
+
<dd><%=h queue[:name] %></dd>
|
26
|
+
<dt>Origin Queue</dt>
|
27
|
+
<dd><%=h queue[:source_url] %></dd>
|
28
|
+
<dt>Message Body</dt>
|
29
|
+
<dd>
|
30
|
+
<pre><%=h message.body %></pre>
|
31
|
+
</dd>
|
32
|
+
<dt>Raw Message</dt>
|
33
|
+
<dd>
|
34
|
+
<div class="backtrace">
|
35
|
+
<pre><%=h message.inspect.to_yaml[0..100] + '...' %></pre>
|
36
|
+
</div>
|
37
|
+
<a class="backtrace" href="#">Toggle full message</a>
|
38
|
+
<div class="backtrace full hide">
|
39
|
+
<pre><%=h message.inspect.to_yaml %></pre>
|
40
|
+
</div>
|
41
|
+
</dd>
|
42
|
+
<dt>Enqueued At</dt>
|
43
|
+
<dd class="time">
|
44
|
+
<%=h Time.at(message.attributes["SentTimestamp"].to_i/1000).rfc822 %>
|
45
|
+
</dd>
|
46
|
+
</dl>
|
47
|
+
</li>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
<h1>
|
2
|
+
Overview
|
3
|
+
</h1>
|
4
|
+
<p class="sub">
|
5
|
+
The list below shows an overview of messages in the SQS queues.
|
6
|
+
</p>
|
7
|
+
<table class="overview">
|
8
|
+
<tr>
|
9
|
+
<th>Queue Name</th>
|
10
|
+
<th>Visible Messages Count</th>
|
11
|
+
<th>In Flight Messages Count</th>
|
12
|
+
<th>Source Queue</th>
|
13
|
+
</tr>
|
14
|
+
<% @queues.each do |queue| %>
|
15
|
+
<%= partial :queue_stats, {:queue => queue, :stats => @stats[queue[:name]]} %>
|
16
|
+
<% end %>
|
17
|
+
</table>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<h1>Working</h1>
|
2
|
+
<p class="sub">
|
3
|
+
The list below contains jobs currently being processed.
|
4
|
+
</p>
|
5
|
+
<p class="sub">
|
6
|
+
<%= "Showing #{start} to #{start + per_page} of #{@all_jobs.count} working jobs." %>
|
7
|
+
</p>
|
8
|
+
<ul class="job">
|
9
|
+
<% @jobs.each do |job| %>
|
10
|
+
<%= partial :job, {:job => job} %>
|
11
|
+
<% end %>
|
12
|
+
</ul>
|
13
|
+
<%= partial :next_more, :start => start, :total_size => @all_jobs.count, :per_page => per_page %>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Sinatra will automatically use Rails sessions if mounted within a Rails app.
|
2
|
+
#
|
3
|
+
# If you enable sessions in a Sinatra app, and then mount it in a Rails 4 app,
|
4
|
+
# Rack::Session will blow up because ActionDispatch::Request::Session does not
|
5
|
+
# implement #each.
|
6
|
+
|
7
|
+
SqsWeb.disable :sessions
|
data/lib/sqs_web.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'sqs_web/application/flash_message'
|
2
|
+
require 'sqs_web/application/navigation'
|
3
|
+
require 'sqs_web/application/controller_action'
|
4
|
+
require 'sqs_web/application/app'
|
5
|
+
require 'sqs_web/application/sqs_action'
|
6
|
+
require 'sqs_web/application/poller_action'
|
7
|
+
require 'sqs_web/application/exception_handle'
|
8
|
+
require 'sqs_web/railtie' if defined?(Rails)
|
9
|
+
SqsAction.initialize_aws
|
@@ -0,0 +1,369 @@
|
|
1
|
+
require 'support/rails_app'
|
2
|
+
require 'support/fake_sqs'
|
3
|
+
|
4
|
+
Capybara.app = RailsApp
|
5
|
+
|
6
|
+
RSpec.describe "Mounted in Rails Application", :sqs do
|
7
|
+
|
8
|
+
SOURCE_QUEUE_NAME = "TestSourceQueue"
|
9
|
+
|
10
|
+
DLQ_QUEUE_NAME = "TestSourceQueueDLQ"
|
11
|
+
|
12
|
+
let(:sqs) { Aws::SQS::Client.new(region: "us-east-1", credentials: Aws::Credentials.new("fake", "fake")) }
|
13
|
+
|
14
|
+
let(:source_queue_url) { sqs.get_queue_url(queue_name: SOURCE_QUEUE_NAME).queue_url }
|
15
|
+
|
16
|
+
let(:dlq_queue_url) { sqs.get_queue_url(queue_name: DLQ_QUEUE_NAME).queue_url }
|
17
|
+
|
18
|
+
before do
|
19
|
+
sqs.config.endpoint = $fake_sqs.uri
|
20
|
+
[SOURCE_QUEUE_NAME, DLQ_QUEUE_NAME].each{|queue_name| sqs.create_queue(queue_name: queue_name)}
|
21
|
+
dlq_arn = sqs.get_queue_attributes(queue_url: dlq_queue_url).attributes.fetch("QueueArn")
|
22
|
+
#Set DLQ
|
23
|
+
sqs.set_queue_attributes(
|
24
|
+
queue_url: source_queue_url,
|
25
|
+
attributes: {
|
26
|
+
"RedrivePolicy" => "{\"deadLetterTargetArn\":\"#{dlq_arn}\",\"maxReceiveCount\":10}"
|
27
|
+
}
|
28
|
+
)
|
29
|
+
SqsWeb.options[:queues] = [SOURCE_QUEUE_NAME, DLQ_QUEUE_NAME]
|
30
|
+
end
|
31
|
+
|
32
|
+
# basic smoke test all the tabs
|
33
|
+
%w(overview dlq_console).each do |tab|
|
34
|
+
specify "test_#{tab}" do
|
35
|
+
visit "/sqs/#{tab}"
|
36
|
+
expect(page.status_code).to eq 200
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "Overview page" do
|
41
|
+
it "will show Visible Messages" do
|
42
|
+
default_messages
|
43
|
+
|
44
|
+
visit "/sqs/overview"
|
45
|
+
|
46
|
+
match_content(page, "#{SOURCE_QUEUE_NAME} 5 0 N/A")
|
47
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 3 0 #{source_queue_url}")
|
48
|
+
end
|
49
|
+
|
50
|
+
specify "In Flight Messages" do
|
51
|
+
default_messages
|
52
|
+
|
53
|
+
receive_messages(source_queue_url, {count: 3})
|
54
|
+
receive_messages(dlq_queue_url, {count: 2})
|
55
|
+
|
56
|
+
visit "/sqs/overview"
|
57
|
+
|
58
|
+
match_content(page, "#{SOURCE_QUEUE_NAME} 2 3 N/A")
|
59
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 1 2 #{source_queue_url}")
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "should be default page" do
|
63
|
+
visit "/sqs"
|
64
|
+
|
65
|
+
expect(current_path).to eq "/sqs/overview"
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "handle non existent queues" do
|
69
|
+
SqsWeb.options[:queues] = ["BOGUSQUEUE"]
|
70
|
+
|
71
|
+
visit "/sqs/overview"
|
72
|
+
|
73
|
+
match_content(page, "Aws::SQS::Errors::NonExistentQueue: BOGUSQUEUE")
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "DLQ Console", js: true do
|
78
|
+
|
79
|
+
it "should only show unique entries for each message" do
|
80
|
+
message_ids = generate_messages(dlq_queue_url, 5).map{|c| c.message_id}
|
81
|
+
|
82
|
+
visit "/sqs/dlq_console"
|
83
|
+
|
84
|
+
message_ids.each{|message_id| expect(page.all("#message_#{message_id}").count).to eq 1}
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should delete single message" do
|
88
|
+
messages = generate_messages(dlq_queue_url, 2)
|
89
|
+
deleted_message_id = messages.pop.message_id
|
90
|
+
retained_message_id = messages.pop.message_id
|
91
|
+
|
92
|
+
visit "/sqs/dlq_console"
|
93
|
+
|
94
|
+
first("#message_#{deleted_message_id}").hover
|
95
|
+
|
96
|
+
within("#message_#{deleted_message_id}") do
|
97
|
+
click_on "Remove"
|
98
|
+
end
|
99
|
+
|
100
|
+
success_message = "Message ID: #{deleted_message_id} in Queue #{DLQ_QUEUE_NAME} was successfully removed."
|
101
|
+
expect(first("#alert").text).to eq success_message
|
102
|
+
|
103
|
+
expect(page.all("#message_#{deleted_message_id}").count).to eq 0
|
104
|
+
expect(page.all("#message_#{retained_message_id}").count).to eq 1
|
105
|
+
|
106
|
+
visit "/sqs/overview"
|
107
|
+
|
108
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 1 0 #{source_queue_url}")
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should handle deleting single message that is already deleted" do
|
112
|
+
deleted_message_id = generate_messages(dlq_queue_url, 1).first.message_id
|
113
|
+
|
114
|
+
visit "/sqs/dlq_console"
|
115
|
+
|
116
|
+
sqs.purge_queue({ queue_url: dlq_queue_url })
|
117
|
+
|
118
|
+
first("#message_#{deleted_message_id}").hover
|
119
|
+
|
120
|
+
within("#message_#{deleted_message_id}") do
|
121
|
+
click_on "Remove"
|
122
|
+
end
|
123
|
+
|
124
|
+
error_message = "Message ID: #{deleted_message_id} in Queue #{DLQ_QUEUE_NAME} has already been deleted or is not visible."
|
125
|
+
expect(first("#alert").text).to eq error_message
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should remove multiple selected messages" do
|
129
|
+
messages = generate_messages(dlq_queue_url, 6)
|
130
|
+
deleted_message_ids = messages.pop(4).map{|c| c.message_id}
|
131
|
+
retained_message_ids = messages.pop(2).map{|c| c.message_id}
|
132
|
+
|
133
|
+
visit "/sqs/dlq_console"
|
134
|
+
|
135
|
+
deleted_message_ids.each do |message_id|
|
136
|
+
first("#batch_action_item_#{message_id}").set(true)
|
137
|
+
end
|
138
|
+
|
139
|
+
click_on "Bulk Remove"
|
140
|
+
|
141
|
+
expect(first("#alert").text).to eq "Selected messages have been removed successfully."
|
142
|
+
|
143
|
+
deleted_message_ids.each{|message_id| expect(page.all("#message_#{message_id}").count).to eq 0}
|
144
|
+
retained_message_ids.each{|message_id| expect(page.all("#message_#{message_id}").count).to eq 1}
|
145
|
+
|
146
|
+
visit "/sqs/overview"
|
147
|
+
|
148
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 2 0 #{source_queue_url}")
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should handle removing multiple selected messages where one or more is already deleted or not visible" do
|
152
|
+
generate_messages(dlq_queue_url, 3)
|
153
|
+
|
154
|
+
visit "/sqs/dlq_console"
|
155
|
+
|
156
|
+
messages = receive_messages(dlq_queue_url, {count: 3}).messages
|
157
|
+
sqs.delete_message({queue_url: dlq_queue_url, receipt_handle: messages[2].receipt_handle})
|
158
|
+
sqs.change_message_visibility_batch({
|
159
|
+
queue_url: dlq_queue_url,
|
160
|
+
entries: messages.take(2).map do |message|
|
161
|
+
{id: message.message_id, receipt_handle: message.receipt_handle, visibility_timeout: 0}
|
162
|
+
end
|
163
|
+
})
|
164
|
+
messages.each do |message|
|
165
|
+
first("#batch_action_item_#{message.message_id}").set(true)
|
166
|
+
end
|
167
|
+
|
168
|
+
click_on "Bulk Remove"
|
169
|
+
|
170
|
+
expect(first("#alert").text).to eq "One or more messages may have already been removed or is not visible."
|
171
|
+
|
172
|
+
messages.each{|message| expect(page.all("#message_#{message.message_id}").count).to eq 0}
|
173
|
+
|
174
|
+
visit "/sqs/overview"
|
175
|
+
|
176
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 0 0 #{source_queue_url}")
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should handle clicking on Bulk Remove without any selection" do
|
180
|
+
messages = generate_messages(dlq_queue_url, 3)
|
181
|
+
|
182
|
+
visit "/sqs/dlq_console"
|
183
|
+
|
184
|
+
click_on "Bulk Remove"
|
185
|
+
|
186
|
+
expect(first("#alert").text).to eq ""
|
187
|
+
|
188
|
+
messages.each{|message| expect(page.all("#message_#{message.message_id}").count).to eq 1}
|
189
|
+
end
|
190
|
+
|
191
|
+
it "should render all information related to the visible messages" do
|
192
|
+
generate_messages(dlq_queue_url, 1)
|
193
|
+
|
194
|
+
visit "/sqs/dlq_console"
|
195
|
+
|
196
|
+
message = receive_messages(dlq_queue_url).messages.first
|
197
|
+
message.attributes["ApproximateReceiveCount"] = "1"
|
198
|
+
|
199
|
+
message_metadata = <<-EOF
|
200
|
+
ID #{message.message_id} or Receive Count 1
|
201
|
+
Queue Name #{DLQ_QUEUE_NAME} Origin Queue #{source_queue_url}
|
202
|
+
Message Body Test_0
|
203
|
+
EOF
|
204
|
+
|
205
|
+
message_entry = first("#message_#{message.message_id}")
|
206
|
+
|
207
|
+
within(message_entry) do
|
208
|
+
click_on "Toggle full message"
|
209
|
+
first(".toggle_format").click
|
210
|
+
end
|
211
|
+
|
212
|
+
match_content(message_entry, normalize_whitespace(message_metadata))
|
213
|
+
match_content(message_entry, normalize_whitespace(message.inspect.to_yaml.split('receipt_handle')[0]))
|
214
|
+
match_content(message_entry, normalize_whitespace(message.inspect.to_yaml.split('md5', 2)[1]))
|
215
|
+
match_content(message_entry, normalize_whitespace("Enqueued At #{Time.at(message.attributes["SentTimestamp"].to_i/1000).rfc822}"))
|
216
|
+
end
|
217
|
+
|
218
|
+
it "should not display any messages that are not in a DLQ" do
|
219
|
+
generate_messages(source_queue_url, 1)
|
220
|
+
|
221
|
+
visit "/sqs/dlq_console"
|
222
|
+
|
223
|
+
message = receive_messages(source_queue_url).messages.first
|
224
|
+
|
225
|
+
expect(first("#message_#{message.message_id}")).to be_nil
|
226
|
+
|
227
|
+
match_content(page, "Showing 0 visible messages")
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should be able to move a single message to source queue" do
|
231
|
+
message_id = generate_messages(dlq_queue_url, 1).first.message_id
|
232
|
+
|
233
|
+
|
234
|
+
visit "/sqs/dlq_console"
|
235
|
+
|
236
|
+
first("#message_#{message_id}").hover
|
237
|
+
|
238
|
+
within("#message_#{message_id}") do
|
239
|
+
click_on "Move to Source Queue"
|
240
|
+
end
|
241
|
+
|
242
|
+
success_message = "Message ID: #{message_id} in Queue #{DLQ_QUEUE_NAME} was successfully moved to Source Queue #{source_queue_url}."
|
243
|
+
expect(first("#alert").text).to eq success_message
|
244
|
+
expect(page.all("#message_#{message_id}").count).to eq 0
|
245
|
+
|
246
|
+
visit "/sqs/overview"
|
247
|
+
|
248
|
+
match_content(page, "#{SOURCE_QUEUE_NAME} 1 0 N/A")
|
249
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 0 0 #{source_queue_url}")
|
250
|
+
|
251
|
+
moved_message = receive_messages(source_queue_url).messages.first
|
252
|
+
expect(moved_message).to_not be_nil
|
253
|
+
expect(moved_message.body).to eq "Test_0"
|
254
|
+
expect(moved_message.attributes["ApproximateReceiveCount"]).to eq "1"
|
255
|
+
expect(moved_message.message_attributes["foo_class"].to_hash).to eq({string_value: "FooWorker", data_type: "String"})
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should move multiple selected messages" do
|
259
|
+
messages = generate_messages(dlq_queue_url, 6)
|
260
|
+
retained_message_ids = messages.pop(2).map{|c| c.message_id}
|
261
|
+
moved_message_ids = messages.pop(4).map{|c| c.message_id}
|
262
|
+
|
263
|
+
visit "/sqs/dlq_console"
|
264
|
+
|
265
|
+
moved_message_ids.each do |message_id|
|
266
|
+
first("#batch_action_item_#{message_id}").set(true)
|
267
|
+
end
|
268
|
+
|
269
|
+
click_on "Bulk Move to Source Queue"
|
270
|
+
|
271
|
+
expect(first("#alert").text).to eq "Selected messages have been requeued successfully."
|
272
|
+
|
273
|
+
moved_message_ids.each{|message_id| expect(page.all("#message_#{message_id}").count).to eq 0}
|
274
|
+
retained_message_ids.each{|message_id| expect(page.all("#message_#{message_id}").count).to eq 1}
|
275
|
+
|
276
|
+
visit "/sqs/overview"
|
277
|
+
|
278
|
+
match_content(page, "#{SOURCE_QUEUE_NAME} 4 0 N/A")
|
279
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 2 0 #{source_queue_url}")
|
280
|
+
|
281
|
+
moved_messages = receive_messages(source_queue_url, {count: 4}).messages.sort_by{|c| c.body}
|
282
|
+
moved_messages.each_with_index do |moved_message, index|
|
283
|
+
expect(moved_message).to_not be_nil
|
284
|
+
expect(moved_message.body).to match "Test_#{index}"
|
285
|
+
expect(moved_message.attributes["ApproximateReceiveCount"]).to eq "1"
|
286
|
+
expect(moved_message.message_attributes["foo_class"].to_hash).to eq({string_value: "FooWorker", data_type: "String"})
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should handle moving multiple selected messages where one or more is already deleted or not visible" do
|
291
|
+
generate_messages(dlq_queue_url, 3)
|
292
|
+
|
293
|
+
visit "/sqs/dlq_console"
|
294
|
+
|
295
|
+
messages = receive_messages(dlq_queue_url, {count: 3}).messages
|
296
|
+
sqs.delete_message({queue_url: dlq_queue_url, receipt_handle: messages[2].receipt_handle})
|
297
|
+
sqs.change_message_visibility_batch({
|
298
|
+
queue_url: dlq_queue_url,
|
299
|
+
entries: messages.take(2).map do |message|
|
300
|
+
{id: message.message_id, receipt_handle: message.receipt_handle, visibility_timeout: 0}
|
301
|
+
end
|
302
|
+
})
|
303
|
+
messages.each do |message|
|
304
|
+
first("#batch_action_item_#{message.message_id}").set(true)
|
305
|
+
end
|
306
|
+
|
307
|
+
click_on "Bulk Move to Source Queue"
|
308
|
+
|
309
|
+
expect(first("#alert").text).to eq "One or more messages may have already been requeued or is not visible."
|
310
|
+
|
311
|
+
messages.each{|message| expect(page.all("#message_#{message.message_id}").count).to eq 0}
|
312
|
+
|
313
|
+
visit "/sqs/overview"
|
314
|
+
|
315
|
+
match_content(page, "#{SOURCE_QUEUE_NAME} 2 0 N/A")
|
316
|
+
match_content(page, "#{DLQ_QUEUE_NAME} 0 0 #{source_queue_url}")
|
317
|
+
end
|
318
|
+
|
319
|
+
it "should handle clicking on Bulk Move to Source Queue without any selection" do
|
320
|
+
messages = generate_messages(dlq_queue_url, 3)
|
321
|
+
|
322
|
+
visit "/sqs/dlq_console"
|
323
|
+
|
324
|
+
click_on "Bulk Move to Source Queue"
|
325
|
+
|
326
|
+
expect(first("#alert").text).to eq ""
|
327
|
+
|
328
|
+
messages.each{|message| expect(page.all("#message_#{message.message_id}").count).to eq 1}
|
329
|
+
end
|
330
|
+
|
331
|
+
it "should have a select/unselect all function" do
|
332
|
+
messages = generate_messages(dlq_queue_url, 3)
|
333
|
+
|
334
|
+
visit "/sqs/dlq_console"
|
335
|
+
|
336
|
+
first("#select_all").click
|
337
|
+
|
338
|
+
page.all(".bulk_check").each{|node| expect(node["checked"]).to eq true}
|
339
|
+
|
340
|
+
first("#select_all").click
|
341
|
+
|
342
|
+
page.all(".bulk_check").each{|node| expect(node["checked"]).to eq false}
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def receive_messages(queue_url, options = {count: 1})
|
347
|
+
sqs.receive_message({
|
348
|
+
queue_url: queue_url,
|
349
|
+
attribute_names: ["All"],
|
350
|
+
message_attribute_names: ["All"],
|
351
|
+
max_number_of_messages: options[:count],
|
352
|
+
wait_time_seconds: 1,
|
353
|
+
visibility_timeout: options[:visibility_timeout]
|
354
|
+
})
|
355
|
+
end
|
356
|
+
|
357
|
+
def default_messages
|
358
|
+
generate_messages(source_queue_url, 5) + generate_messages(dlq_queue_url, 3)
|
359
|
+
end
|
360
|
+
|
361
|
+
def generate_messages(queue_url, count=1)
|
362
|
+
messages = []
|
363
|
+
count.times do |time|
|
364
|
+
messages << sqs.send_message(queue_url: queue_url, message_body: "Test_#{time}",
|
365
|
+
message_attributes: {"foo_class"=> {string_value: "FooWorker", data_type: "String"}})
|
366
|
+
end
|
367
|
+
messages
|
368
|
+
end
|
369
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
require "byebug"
|
3
|
+
require 'capybara-webkit'
|
4
|
+
require 'capybara/rspec'
|
5
|
+
CodeClimate::TestReporter.start
|
6
|
+
|
7
|
+
ENV['RACK_ENV'] = 'development'
|
8
|
+
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.disable_monkey_patching!
|
11
|
+
config.include Capybara::DSL
|
12
|
+
end
|
13
|
+
|
14
|
+
Capybara.javascript_driver = :webkit
|
15
|
+
|
16
|
+
def match_content(actual, expected)
|
17
|
+
actual_text = actual.respond_to?(:text) ? normalize_whitespace(actual.text) : actual
|
18
|
+
expect(actual_text).to include(expected), <<-EOF
|
19
|
+
expected
|
20
|
+
"#{actual_text}"
|
21
|
+
to have content
|
22
|
+
"#{expected}"
|
23
|
+
EOF
|
24
|
+
end
|
25
|
+
|
26
|
+
def normalize_whitespace(content)
|
27
|
+
Capybara::Helpers.normalize_whitespace(content)
|
28
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "fake_sqs/test_integration"
|
2
|
+
|
3
|
+
db = ENV["SQS_DATABASE"] || ":memory:"
|
4
|
+
puts "\n\e[34mRunning specs with database \e[33m#{db}\e[0m"
|
5
|
+
$fake_sqs = FakeSQS::TestIntegration.new(
|
6
|
+
database: db,
|
7
|
+
sqs_endpoint: "localhost",
|
8
|
+
sqs_port: 4568,
|
9
|
+
)
|
10
|
+
|
11
|
+
SqsWeb.options[:aws][:access_key_id] = "fake"
|
12
|
+
SqsWeb.options[:aws][:secret_access_key] = "fake"
|
13
|
+
SqsWeb.options[:aws][:endpoint] = $fake_sqs.uri
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.before(:each, :sqs) { $fake_sqs.start }
|
17
|
+
config.before(:each, :sqs) { $fake_sqs.reset }
|
18
|
+
config.after(:suite) { $fake_sqs.stop }
|
19
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "action_controller/railtie"
|
2
|
+
require "logger"
|
3
|
+
require "sqs_web"
|
4
|
+
|
5
|
+
class RailsApp < Rails::Application
|
6
|
+
config.logger = Rails.logger = Logger.new($stdout)
|
7
|
+
config.secret_token = "a3d6cee7966878577a764ed273359d9e"
|
8
|
+
|
9
|
+
routes.draw do
|
10
|
+
match "/sqs" => SqsWeb, :anchor => false, via: [:get, :post]
|
11
|
+
end
|
12
|
+
end
|
data/sqs_web.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = "sqs_web"
|
3
|
+
gem.version = "0.0.1"
|
4
|
+
gem.author = "Nathaniel Ritholtz"
|
5
|
+
gem.email = "nritholtz@gmail.com"
|
6
|
+
gem.homepage = "https://github.com/nritholtz/sqs_web"
|
7
|
+
gem.summary = "Web interface for SQS inspired by delayed_job_web"
|
8
|
+
gem.description = gem.summary
|
9
|
+
gem.license = "MIT"
|
10
|
+
|
11
|
+
gem.executables = ["sqs_web"]
|
12
|
+
|
13
|
+
gem.files = [
|
14
|
+
"Gemfile",
|
15
|
+
"README.markdown",
|
16
|
+
"Rakefile",
|
17
|
+
"sqs_web.gemspec"
|
18
|
+
] + %x{ git ls-files }.split("\n").select { |d| d =~ %r{^(lib|spec|bin)} }
|
19
|
+
|
20
|
+
gem.extra_rdoc_files = [
|
21
|
+
"README.markdown"
|
22
|
+
]
|
23
|
+
|
24
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
25
|
+
|
26
|
+
gem.add_runtime_dependency "sinatra", [">= 1.4.4"]
|
27
|
+
gem.add_dependency 'aws-sdk', '~> 2.1.33'
|
28
|
+
|
29
|
+
|
30
|
+
gem.add_development_dependency "rspec"
|
31
|
+
gem.add_development_dependency "rails", ["~> 3.0"]
|
32
|
+
gem.add_development_dependency "capybara"
|
33
|
+
gem.add_development_dependency "codeclimate-test-reporter"
|
34
|
+
gem.add_development_dependency "byebug"
|
35
|
+
gem.add_development_dependency "capybara-webkit"
|
36
|
+
#gem.add_development_dependency "fake_sqs", ["~> 0.3.1"]
|
37
|
+
end
|