putsreq 0.0.2 → 0.0.3
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/README.md +5 -2
- data/app/controllers/application_controller.rb +46 -6
- data/app/controllers/buckets_controller.rb +3 -2
- data/app/controllers/requests_controller.rb +10 -0
- data/app/interactors/forward_request.rb +0 -1
- data/app/models/bucket.rb +1 -1
- data/app/models/request.rb +5 -5
- data/app/models/response.rb +0 -5
- data/app/views/buckets/_form.html.erb +2 -2
- data/app/views/buckets/_requests.html.erb +5 -4
- data/app/views/buckets/show.html.erb +2 -1
- data/app/views/home/index.html.erb +1 -1
- data/bin/putsreq +43 -19
- data/config/routes.rb +1 -0
- data/lib/putsreq/version.rb +1 -1
- data/spec/models/request_spec.rb +21 -0
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d16ff759b8282f7cb2d4488a019d6631dac732a3
|
4
|
+
data.tar.gz: e7b1e5d40ac369a8a3060701343d772c092db96d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ac7b3f2361a504d38e929713f4a438e4fcbee4aacd15016bac9c296733e8647fadf56d438b781c0799d086d4a20ae3626317de520dfbebcc3785e1a0b04764e
|
7
|
+
data.tar.gz: 094d545c5baca272fbd54c40d1fd13d53ebb0c06c11ebcc328b260d3ad83d99f8e7552bcd4d211e4d86deae8f12e525240852d337ef77771f6307f12a7fdff9a
|
data/README.md
CHANGED
@@ -113,12 +113,15 @@ request.forwardTo = 'http://example.com/api';
|
|
113
113
|
|
114
114
|
### CLI
|
115
115
|
|
116
|
-
Want to test
|
116
|
+
Want to test Webhook calls against your localhost? PutsReq makes it easy!
|
117
117
|
|
118
|
-
|
118
|
+
You can think of it, as a kind of [ngrok](http://ngrok.io), but instead of creating a tunnel to your localhost, PutsReq polls requests from `YOUR-PUTSREQ-TOKEN` and forwards to your localhost.
|
119
|
+
|
120
|
+
```bash
|
119
121
|
gem install putsreq
|
120
122
|
|
121
123
|
putsreq forward --to http://localhost:3000 --token YOUR-TOKEN
|
124
|
+
|
122
125
|
Listening requests from YOUR-TOKEN
|
123
126
|
Forwarding to http://localhost:3000
|
124
127
|
Press CTRL+c to terminate
|
@@ -3,19 +3,57 @@ class ApplicationController < ActionController::Base
|
|
3
3
|
# For APIs, you may want to use :null_session instead.
|
4
4
|
protect_from_forgery with: :exception
|
5
5
|
|
6
|
-
helper_method :
|
6
|
+
helper_method :owner?, :body_as_string, :headers_as_string
|
7
7
|
|
8
8
|
before_action :configure_permitted_parameters, if: :devise_controller?
|
9
9
|
|
10
10
|
protected
|
11
11
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
12
|
+
def body_as_string(req_or_res)
|
13
|
+
body = req_or_res.body
|
14
|
+
|
15
|
+
if body_json?(req_or_res) && body.is_a?(String)
|
16
|
+
# See https://github.com/phstc/putsreq/issues/31#issuecomment-271681249
|
17
|
+
return JSON.pretty_generate(JSON.parse(body))
|
18
|
+
end
|
19
|
+
|
20
|
+
if body.is_a?(Hash)
|
21
|
+
# For responses body can be a hash
|
22
|
+
# body.to_h because body can be a BSON::Document
|
23
|
+
# which for some reason does format well with
|
24
|
+
# pretty_generate
|
25
|
+
return JSON.pretty_generate(body.to_h)
|
26
|
+
end
|
27
|
+
|
28
|
+
if body.is_a?(Array)
|
29
|
+
# see https://github.com/phstc/putsreq/issues/33
|
30
|
+
return JSON.pretty_generate(body.to_a)
|
31
|
+
end
|
32
|
+
|
33
|
+
body.to_s
|
34
|
+
rescue
|
35
|
+
body.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
def headers_as_string(req_or_res)
|
39
|
+
JSON.pretty_generate(req_or_res.headers.to_h)
|
40
|
+
end
|
41
|
+
|
42
|
+
def body_json?(req_or_res)
|
43
|
+
req_or_res.headers.to_h.each do |key, value|
|
44
|
+
return !!(value =~ /application\/json/i) if key =~ /^content-type$/i
|
15
45
|
end
|
46
|
+
|
47
|
+
false
|
16
48
|
end
|
17
49
|
|
18
|
-
def
|
50
|
+
def check_ownership!
|
51
|
+
return if owner?(bucket)
|
52
|
+
|
53
|
+
redirect_to bucket_path(bucket.token), alert: 'Only the bucket owner can perform this operation'
|
54
|
+
end
|
55
|
+
|
56
|
+
def owner?(bucket)
|
19
57
|
owner_token == bucket.owner_token || (user_signed_in? && bucket.user == current_user)
|
20
58
|
end
|
21
59
|
|
@@ -30,6 +68,8 @@ class ApplicationController < ActionController::Base
|
|
30
68
|
def configure_permitted_parameters
|
31
69
|
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:name, :email, :password, :password_confirmation, :remember_me) }
|
32
70
|
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:name, :email, :password, :remember_me) }
|
33
|
-
devise_parameter_sanitizer.for(:account_update)
|
71
|
+
devise_parameter_sanitizer.for(:account_update) do |u|
|
72
|
+
u.permit(:name, :email, :password, :password_confirmation, :current_password)
|
73
|
+
end
|
34
74
|
end
|
35
75
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class BucketsController < ApplicationController
|
2
2
|
skip_before_action :verify_authenticity_token, only: :record
|
3
3
|
|
4
|
-
before_filter :check_ownership!, only: %i
|
4
|
+
before_filter :check_ownership!, only: %i(clear destroy update)
|
5
5
|
|
6
6
|
def create
|
7
7
|
new_bucket = { owner_token: owner_token }
|
@@ -70,7 +70,8 @@ class BucketsController < ApplicationController
|
|
70
70
|
|
71
71
|
response.headers.merge! recorded_response.headers.to_h
|
72
72
|
|
73
|
-
render text: recorded_response
|
73
|
+
render text: body_as_string(recorded_response),
|
74
|
+
status: recorded_response.status
|
74
75
|
end
|
75
76
|
|
76
77
|
private
|
data/app/models/bucket.rb
CHANGED
data/app/models/request.rb
CHANGED
@@ -20,12 +20,12 @@ class Request
|
|
20
20
|
|
21
21
|
after_create :bump_requests_recorded
|
22
22
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
23
|
+
def path
|
24
|
+
return unless url
|
25
|
+
|
26
|
+
u = URI(url)
|
26
27
|
|
27
|
-
|
28
|
-
JSON.pretty_generate(headers.to_h)
|
28
|
+
url.gsub(/.*#{Regexp.escape(u.host)}(\:#{Regexp.escape(u.port.to_s)})?/, '')
|
29
29
|
end
|
30
30
|
|
31
31
|
private
|
data/app/models/response.rb
CHANGED
@@ -9,14 +9,9 @@ class Response
|
|
9
9
|
field :headers, type: Hash, default: { 'Content-Type' => 'text/plain' }
|
10
10
|
field :status, type: Integer, default: 200
|
11
11
|
|
12
|
-
# index created_at: 1, options { expireAfterSeconds: 604800 }
|
13
12
|
index bucket_id: 1, created_at: -1
|
14
13
|
index request_id: 1
|
15
14
|
|
16
15
|
validates :bucket, presence: true
|
17
16
|
validates :request, presence: true
|
18
|
-
|
19
|
-
def body_as_string
|
20
|
-
body.is_a?(Hash) ? JSON.pretty_generate(body) : body.to_s
|
21
|
-
end
|
22
17
|
end
|
@@ -24,10 +24,10 @@
|
|
24
24
|
<div id="editor" name="editor" style="width: 100%; height: 150px"></div>
|
25
25
|
<textarea id="response_builder" name="response_builder" style="display:none;"></textarea>
|
26
26
|
<p>
|
27
|
-
<% if
|
27
|
+
<% if owner?(@bucket) %>
|
28
28
|
<%= render 'buttons' %>
|
29
29
|
<% else %>
|
30
30
|
<%= render 'readonly_buttons' %>
|
31
31
|
<% end %>
|
32
32
|
</p>
|
33
|
-
<% end %>
|
33
|
+
<% end %>
|
@@ -1,3 +1,4 @@
|
|
1
|
+
<!-- request id: <%= recorded_request.id %> -->
|
1
2
|
<div class="panel-group request-show" id="accordion" role="tablist" aria-multiselectable="true">
|
2
3
|
<div class="panel panel-default">
|
3
4
|
<span class="pull-right label label-info" title="<%= recorded_request.created_at.utc %>"><%= time_ago_in_words recorded_request.created_at %> ago</span>
|
@@ -10,7 +11,7 @@
|
|
10
11
|
</div>
|
11
12
|
<div id="collapseOne" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingOne">
|
12
13
|
<div class="panel-body">
|
13
|
-
<pre><%=
|
14
|
+
<pre><%= headers_as_string(recorded_request) %></pre>
|
14
15
|
</div>
|
15
16
|
</div>
|
16
17
|
</div>
|
@@ -18,13 +19,13 @@
|
|
18
19
|
<div class="panel-heading" role="tab" id="headingTwo">
|
19
20
|
<h4 class="panel-title">
|
20
21
|
<a role="button" data-toggle="collapse" data-parent="#accordion" href="#collapseTwo" aria-expanded="true" aria-controls="collapseTwo">
|
21
|
-
|
22
|
+
<%= recorded_request.request_method.upcase %> <%= recorded_request.path %>
|
22
23
|
</a>
|
23
24
|
</h4>
|
24
25
|
</div>
|
25
26
|
<div id="collapseTwo" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingTwo">
|
26
27
|
<div class="panel-body">
|
27
|
-
<pre><%=
|
28
|
+
<pre><%= body_as_string(recorded_request) %></pre>
|
28
29
|
</div>
|
29
30
|
</div>
|
30
31
|
</div>
|
@@ -38,7 +39,7 @@
|
|
38
39
|
</div>
|
39
40
|
<div id="collapseThree" class="panel-collapse collapse in" role="tabpanel" aria-labelledby="headingThree">
|
40
41
|
<div class="panel-body">
|
41
|
-
<pre><%=
|
42
|
+
<pre><%= body_as_string(recorded_request.response) %></pre>
|
42
43
|
</div>
|
43
44
|
</div>
|
44
45
|
</div>
|
@@ -43,8 +43,9 @@
|
|
43
43
|
<em>First request at: <%= @bucket.first_request_at %></br>
|
44
44
|
Last request at: <%= @bucket.last_request_at %></br>
|
45
45
|
<% if @bucket.last_request_at - @bucket.first_request_at > 0 %>
|
46
|
-
From first to last: <%= distance_of_time_in_words @bucket.first_request_at, @bucket.last_request_at, include_seconds: true
|
46
|
+
From first to last: <%= distance_of_time_in_words @bucket.first_request_at, @bucket.last_request_at, include_seconds: true %></br>
|
47
47
|
<% end %>
|
48
|
+
Request ID: <%= link_to @requests.first.id, request_path(id: @requests.first.id, format: :json) %>
|
48
49
|
</em>
|
49
50
|
</p>
|
50
51
|
<% end %>
|
@@ -32,7 +32,7 @@
|
|
32
32
|
<h4 class="tm20">Fake responses</h4>
|
33
33
|
<p>PutsReq is the easiest way to fake HTTP responses. You can define the response status, headers and body you want using <a href="https://github.com/phstc/putsreq#response-builder">JavaScript</a>.
|
34
34
|
<h4 class="tm20">Forward requests</h4>
|
35
|
-
<p>You can easily <a href="https://github.com/phstc/putsreq#forwardto">forward requests to another URL</a>, including <a href="https://github.com/phstc/putsreq#
|
35
|
+
<p>You can easily <a href="https://github.com/phstc/putsreq#forwardto">forward requests to another URL</a>, including <a href="https://github.com/phstc/putsreq#cli">localhost</a>.
|
36
36
|
</section>
|
37
37
|
</div>
|
38
38
|
</div>
|
data/bin/putsreq
CHANGED
@@ -22,33 +22,42 @@ class PutsReqCLI < Thor
|
|
22
22
|
|
23
23
|
p
|
24
24
|
end
|
25
|
-
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
trap('SIGINT', 'EXIT')
|
26
|
+
def subscribe_and_forward(token, to)
|
27
|
+
puts "Listening requests from #{token}"
|
28
|
+
puts "Forwarding to #{to}"
|
29
|
+
puts 'Press CTRL+c to terminate'
|
32
30
|
|
33
|
-
|
34
|
-
|
31
|
+
PusherClient.logger.level = Logger::ERROR
|
32
|
+
|
33
|
+
options = { secure: true }
|
34
|
+
|
35
|
+
socket = PusherClient::Socket.new('3466d56fe2ef1fdd2943', options)
|
35
36
|
|
36
|
-
|
37
|
-
puts "Forwarding to #{to}"
|
38
|
-
puts "Press CTRL+c to terminate"
|
37
|
+
channel = "channel_requests_#{token}"
|
39
38
|
|
40
|
-
|
39
|
+
socket.subscribe(channel)
|
41
40
|
|
42
|
-
|
41
|
+
socket[channel].bind('new') do |data|
|
42
|
+
last_request = JSON.parse(data)['request']
|
43
43
|
|
44
|
-
|
44
|
+
options = { headers: last_request['headers'] }
|
45
45
|
|
46
|
-
|
46
|
+
options[:body] = last_request['body'] unless last_request['body'].to_s.empty?
|
47
47
|
|
48
|
-
|
48
|
+
forward_request = HTTParty.send(last_request['request_method'].downcase.to_sym,
|
49
|
+
to,
|
50
|
+
options)
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
+
puts "#{Time.now}\t#{last_request['request_method']}\t#{forward_request.code}"
|
53
|
+
end
|
54
|
+
|
55
|
+
socket.connect
|
56
|
+
end
|
57
|
+
|
58
|
+
def forward_request(token, to, id)
|
59
|
+
url = "http://putsreq.com/#{token}/requests/#{id}.json"
|
60
|
+
last_request = HTTParty.get(url)
|
52
61
|
|
53
62
|
options = { headers: last_request['headers'] }
|
54
63
|
|
@@ -60,8 +69,23 @@ class PutsReqCLI < Thor
|
|
60
69
|
|
61
70
|
puts "#{Time.now}\t#{last_request['request_method']}\t#{forward_request.code}"
|
62
71
|
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'forward', 'Forward requests from PutsReq to a given URL'
|
75
|
+
method_option :token, desc: 'PutsReq token', required: true
|
76
|
+
method_option :to, desc: 'destination URL', required: true
|
77
|
+
method_option :id, desc: 'ID to forward a single request', required: false
|
78
|
+
def forward
|
79
|
+
trap('SIGINT', 'EXIT')
|
80
|
+
|
81
|
+
token = parse_bucket_token(options[:token])
|
82
|
+
to = options[:to]
|
63
83
|
|
64
|
-
|
84
|
+
if id = options[:id]
|
85
|
+
forward_request(token, to, id)
|
86
|
+
else
|
87
|
+
subscribe_and_forward(token, to)
|
88
|
+
end
|
65
89
|
end
|
66
90
|
|
67
91
|
desc 'version', 'Show version'
|
data/config/routes.rb
CHANGED
@@ -6,6 +6,7 @@ PutsReq::Application.routes.draw do
|
|
6
6
|
post 'buckets' => 'buckets#create', as: :buckets
|
7
7
|
get ':token/inspect' => 'buckets#show', as: :bucket
|
8
8
|
get ':token/last' => 'buckets#last', as: :bucket_last
|
9
|
+
get ':token/requests/:id' => 'requests#show', as: :request
|
9
10
|
get ':token/last_response' => 'buckets#last_response', as: :bucket_last_response
|
10
11
|
put ':token/buckets' => 'buckets#update', as: :update_bucket
|
11
12
|
match ':token' => 'buckets#record', via: :all, as: :bucket_record
|
data/lib/putsreq/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Request do
|
4
|
+
describe '#path' do
|
5
|
+
let(:path) { '/12345?name=test' }
|
6
|
+
|
7
|
+
it 'returns path' do
|
8
|
+
subject.url = "https://putsreq.com#{path}"
|
9
|
+
|
10
|
+
expect(subject.path).to eq path
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when domain with port' do
|
14
|
+
it 'returns path' do
|
15
|
+
subject.url = "http://localhost:3000#{path}"
|
16
|
+
|
17
|
+
expect(subject.path).to eq path
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: putsreq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pablo Cantero
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- app/controllers/buckets_controller.rb
|
73
73
|
- app/controllers/concerns/.keep
|
74
74
|
- app/controllers/home_controller.rb
|
75
|
+
- app/controllers/requests_controller.rb
|
75
76
|
- app/helpers/application_helper.rb
|
76
77
|
- app/interactors/create_request.rb
|
77
78
|
- app/interactors/create_response.rb
|
@@ -161,6 +162,7 @@ files:
|
|
161
162
|
- spec/interactors/eval_response_builder_spec.rb
|
162
163
|
- spec/interactors/forward_request_spec.rb
|
163
164
|
- spec/models/bucket_spec.rb
|
165
|
+
- spec/models/request_spec.rb
|
164
166
|
- spec/models/user_spec.rb
|
165
167
|
- spec/spec_helper.rb
|
166
168
|
- vendor/assets/javascripts/.keep
|
@@ -307,5 +309,6 @@ test_files:
|
|
307
309
|
- spec/interactors/eval_response_builder_spec.rb
|
308
310
|
- spec/interactors/forward_request_spec.rb
|
309
311
|
- spec/models/bucket_spec.rb
|
312
|
+
- spec/models/request_spec.rb
|
310
313
|
- spec/models/user_spec.rb
|
311
314
|
- spec/spec_helper.rb
|