request_interceptor 0.2.1 → 0.3.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/lib/request_interceptor/application.rb +13 -9
- data/lib/request_interceptor/version.rb +1 -1
- data/lib/request_interceptor/webmock_manager.rb +60 -0
- data/lib/request_interceptor/webmock_patches.rb +19 -0
- data/lib/request_interceptor.rb +47 -163
- data/request_interceptor.gemspec +1 -0
- metadata +18 -3
- data/lib/request_interceptor/status.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15e0184221194d0cd9222006c1739e23f7c81a89
|
4
|
+
data.tar.gz: 5bb90d1e9b0b9b4d0eee2cadb6015e324db86e42
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e36776a7b15b8994100a480d5ebda2d12373d09b54d9cd48c22c2488b2623d3143b19f078f1bd067a8a90956b1d3be853388f5875616ffcbc8d8061e4c0576a2
|
7
|
+
data.tar.gz: 74d7a949efa97b601324b38ef7666741294ee5a00e6bad56d328d2e53f923a79cd3d3bdb0801bf66ec1242b0129b58ad415f0a589cccc227ccd9a3afa4e67233
|
@@ -1,18 +1,22 @@
|
|
1
1
|
require "sinatra/base"
|
2
2
|
|
3
3
|
class RequestInterceptor::Application < Sinatra::Base
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class << self
|
5
|
+
def customize(&customizations)
|
6
|
+
RequestInterceptor.define(self, &customizations)
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
def intercept(pattern, *args, &test)
|
10
|
+
RequestInterceptor.run(pattern => self.new(*args), &test)
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
13
|
+
def match(pattern)
|
14
|
+
define_singleton_method(:intercept) do |*args, &test|
|
15
|
+
super(pattern, *args, &test)
|
16
|
+
end
|
15
17
|
end
|
18
|
+
|
19
|
+
alias host match
|
16
20
|
end
|
17
21
|
|
18
22
|
configure do
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class RequestInterceptor::WebMockManager
|
2
|
+
WebMockConfigurationCache = Struct.new(:request_stubs, :callbacks, :allow_net_connect, :allow_localhost, :show_body_diff, :show_stubbing_instructions, :enabled_previously)
|
3
|
+
|
4
|
+
def initialize(applications, callback = nil)
|
5
|
+
@applications = applications
|
6
|
+
@callback = callback
|
7
|
+
end
|
8
|
+
|
9
|
+
def run_simulation
|
10
|
+
original_webmock_configuration = setup
|
11
|
+
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
teardown(original_webmock_configuration)
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
attr_reader :callback
|
20
|
+
attr_reader :applications
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def setup
|
25
|
+
original_configuration = WebMockConfigurationCache.new
|
26
|
+
original_configuration.enabled_previously = WebMock.enabled?
|
27
|
+
original_configuration.request_stubs = WebMock::StubRegistry.instance.request_stubs.dup || []
|
28
|
+
original_configuration.callbacks = WebMock::CallbackRegistry.callbacks.dup || []
|
29
|
+
original_configuration.allow_net_connect = WebMock::Config.instance.allow_net_connect
|
30
|
+
original_configuration.allow_localhost = WebMock::Config.instance.allow_localhost
|
31
|
+
original_configuration.show_body_diff = WebMock::Config.instance.show_body_diff
|
32
|
+
original_configuration.show_stubbing_instructions = WebMock::Config.instance.show_stubbing_instructions
|
33
|
+
|
34
|
+
WebMock.after_request(&callback) unless callback.nil?
|
35
|
+
|
36
|
+
applications.each do |application|
|
37
|
+
WebMock.stub_request(:any, application.pattern).to_rack(application)
|
38
|
+
end
|
39
|
+
|
40
|
+
WebMock.allow_net_connect!
|
41
|
+
WebMock.hide_body_diff!
|
42
|
+
WebMock.hide_stubbing_instructions!
|
43
|
+
WebMock.enable!
|
44
|
+
|
45
|
+
original_configuration
|
46
|
+
end
|
47
|
+
|
48
|
+
def teardown(original_configuration)
|
49
|
+
WebMock::Config.instance.allow_net_connect = original_configuration.allow_net_connect
|
50
|
+
WebMock::Config.instance.allow_localhost = original_configuration.allow_localhost
|
51
|
+
WebMock::Config.instance.show_body_diff = original_configuration.show_body_diff
|
52
|
+
WebMock::Config.instance.show_stubbing_instructions = original_configuration.show_stubbing_instructions
|
53
|
+
WebMock::CallbackRegistry.reset
|
54
|
+
original_configuration.callbacks.each do |callback_settings|
|
55
|
+
WebMock.after_request(callback_settings[:options], &callback_settings[:block])
|
56
|
+
end
|
57
|
+
WebMock::StubRegistry.instance.request_stubs = original_configuration.request_stubs
|
58
|
+
WebMock.disable! unless original_configuration.enabled_previously
|
59
|
+
end
|
60
|
+
end
|
data/lib/request_interceptor.rb
CHANGED
@@ -1,22 +1,44 @@
|
|
1
1
|
require "request_interceptor/version"
|
2
2
|
|
3
|
-
require "
|
4
|
-
require "
|
3
|
+
require "webmock"
|
4
|
+
require "uri"
|
5
5
|
|
6
6
|
class RequestInterceptor
|
7
7
|
Transaction = Struct.new(:request, :response)
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
module RequestBackwardsCompatibility
|
10
|
+
def path
|
11
|
+
uri.request_uri
|
12
|
+
end
|
13
|
+
|
14
|
+
def uri
|
15
|
+
URI.parse(super.to_s)
|
16
|
+
end
|
17
|
+
|
18
|
+
def method
|
19
|
+
super.to_s.upcase
|
13
20
|
end
|
14
21
|
end
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
23
|
+
class ApplicationWrapper < SimpleDelegator
|
24
|
+
attr_reader :pattern
|
25
|
+
|
26
|
+
def initialize(pattern, application)
|
27
|
+
@pattern =
|
28
|
+
case pattern
|
29
|
+
when String
|
30
|
+
%r{://#{Regexp.escape(pattern)}/}
|
31
|
+
else
|
32
|
+
pattern
|
33
|
+
end
|
34
|
+
|
35
|
+
super(application)
|
36
|
+
end
|
37
|
+
|
38
|
+
def intercepts?(uri)
|
39
|
+
!!pattern.match(uri.normalize.to_s)
|
40
|
+
end
|
41
|
+
end
|
20
42
|
|
21
43
|
def self.template=(template)
|
22
44
|
@template =
|
@@ -36,173 +58,35 @@ class RequestInterceptor
|
|
36
58
|
Class.new(super_class || template, &application_definition)
|
37
59
|
end
|
38
60
|
|
39
|
-
def self.run(
|
40
|
-
new(
|
61
|
+
def self.run(applications, &simulation)
|
62
|
+
new(applications).run(&simulation)
|
41
63
|
end
|
42
64
|
|
43
65
|
attr_reader :applications
|
44
66
|
attr_reader :transactions
|
45
67
|
|
46
68
|
def initialize(applications)
|
47
|
-
@applications = {}
|
69
|
+
@applications = applications.map { |pattern, application| ApplicationWrapper.new(pattern, application) }
|
48
70
|
@transactions = []
|
49
|
-
|
50
|
-
applications.each { |pattern, application| self[pattern] = application }
|
51
|
-
end
|
52
|
-
|
53
|
-
def [](pattern)
|
54
|
-
@applications[pattern]
|
55
|
-
end
|
56
|
-
|
57
|
-
def []=(pattern, application)
|
58
|
-
@applications[pattern] = application
|
59
71
|
end
|
60
72
|
|
61
73
|
def run(&simulation)
|
62
|
-
|
63
|
-
cache_original_net_http_methods
|
64
|
-
override_net_http_methods
|
65
|
-
simulation.call
|
66
|
-
transactions
|
67
|
-
ensure
|
68
|
-
restore_net_http_methods
|
69
|
-
end
|
70
|
-
|
71
|
-
def request(http_context, request, body, &block)
|
72
|
-
# use Net::HTTP set_body_internal to
|
73
|
-
# keep the same behaviour as Net::HTTP
|
74
|
-
request.set_body_internal(body)
|
75
|
-
response = nil
|
76
|
-
|
77
|
-
if mock_request = mock_request_for_application(http_context, request)
|
78
|
-
mock_response = dispatch_mock_request(request, mock_request)
|
79
|
-
|
80
|
-
# create response
|
81
|
-
status = RequestInterceptor::Status.from_code(mock_response.status)
|
82
|
-
response = status.response_class.new("1.1", status.value, status.description)
|
83
|
-
|
84
|
-
# copy header to response
|
85
|
-
mock_response.original_headers.each do |k, v|
|
86
|
-
response.add_field(k, v)
|
87
|
-
end
|
88
|
-
|
89
|
-
# copy uri
|
90
|
-
response.uri = request.uri
|
91
|
-
|
92
|
-
# copy body to response
|
93
|
-
response.body = mock_response.body
|
94
|
-
|
95
|
-
# replace Net::HTTP::Response#read_body
|
96
|
-
def response.read_body(_, &block)
|
97
|
-
block.call(@body) unless block.nil?
|
98
|
-
@body
|
99
|
-
end
|
100
|
-
|
101
|
-
# yield the response because Net::HTTP#request does
|
102
|
-
block.call(response) unless block.nil?
|
103
|
-
|
104
|
-
# log intercepted transaction
|
105
|
-
log_transaction(request, response)
|
106
|
-
else
|
107
|
-
response = real_request(http_context, request, body, &block)
|
108
|
-
end
|
109
|
-
|
110
|
-
response
|
111
|
-
end
|
74
|
+
transactions = []
|
112
75
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
http_context
|
118
|
-
end
|
119
|
-
|
120
|
-
def finish(http_context)
|
121
|
-
http_context.instance_variable_set(:@started, false)
|
122
|
-
nil
|
123
|
-
end
|
124
|
-
|
125
|
-
protected
|
126
|
-
|
127
|
-
attr_writer :restart
|
128
|
-
|
129
|
-
def restart
|
130
|
-
@restart || Restart.new(false)
|
131
|
-
end
|
132
|
-
|
133
|
-
private
|
134
|
-
|
135
|
-
def clear_transaction_log
|
136
|
-
@transactions = []
|
137
|
-
end
|
138
|
-
|
139
|
-
def cache_original_net_http_methods
|
140
|
-
@original_request_method = Net::HTTP.instance_method(:request)
|
141
|
-
@original_start_method = Net::HTTP.instance_method(:start)
|
142
|
-
@original_finish_method = Net::HTTP.instance_method(:finish)
|
143
|
-
end
|
144
|
-
|
145
|
-
def override_net_http_methods
|
146
|
-
runner = self
|
147
|
-
|
148
|
-
Net::HTTP.class_eval do
|
149
|
-
define_method(:start) { |&block| runner.start(self, &block) }
|
150
|
-
define_method(:finish) { runner.finish(self) }
|
151
|
-
define_method(:request) { |request, body = nil, &block| runner.request(self, request, body, &block) }
|
76
|
+
request_logging = ->(request, response) do
|
77
|
+
next unless applications.any? { |application| application.intercepts?(request.uri) }
|
78
|
+
request.extend(RequestBackwardsCompatibility)
|
79
|
+
transactions << Transaction.new(request, response)
|
152
80
|
end
|
153
|
-
end
|
154
81
|
|
155
|
-
|
156
|
-
http_context.finish if restart.required?
|
82
|
+
WebMockManager.new(applications, request_logging).run_simulation(&simulation)
|
157
83
|
|
158
|
-
|
159
|
-
|
160
|
-
if restart.required?
|
161
|
-
http_context.start(&restart.instructions)
|
162
|
-
self.restart = nil
|
163
|
-
end
|
164
|
-
|
165
|
-
http_context.request(request, body, &block)
|
166
|
-
end
|
167
|
-
|
168
|
-
def restore_net_http_methods(instance = nil)
|
169
|
-
if instance.nil?
|
170
|
-
Net::HTTP.send(:define_method, :request, @original_request_method)
|
171
|
-
Net::HTTP.send(:define_method, :start, @original_start_method)
|
172
|
-
Net::HTTP.send(:define_method, :finish, @original_finish_method)
|
173
|
-
else
|
174
|
-
instance.define_singleton_method(:request, @original_request_method)
|
175
|
-
instance.define_singleton_method(:start, @original_start_method)
|
176
|
-
instance.define_singleton_method(:finish, @original_finish_method)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
|
180
|
-
def mock_request_for_application(http_context, request)
|
181
|
-
_, application = applications.find { |pattern, _| pattern === http_context.address }
|
182
|
-
Rack::MockRequest.new(application) if application
|
183
|
-
end
|
184
|
-
|
185
|
-
def dispatch_mock_request(request, mock_request)
|
186
|
-
rack_env = request.to_hash
|
187
|
-
|
188
|
-
case request.method
|
189
|
-
when GET
|
190
|
-
mock_request.get(request.path, rack_env)
|
191
|
-
when POST
|
192
|
-
mock_request.post(request.path, rack_env.merge(input: request.body))
|
193
|
-
when PUT
|
194
|
-
mock_request.put(request.path, rack_env.merge(input: request.body))
|
195
|
-
when DELETE
|
196
|
-
mock_request.delete(request.path, rack_env)
|
197
|
-
else
|
198
|
-
raise NotImplementedError, "Simulating #{request.method} is not supported"
|
199
|
-
end
|
200
|
-
end
|
201
|
-
|
202
|
-
def log_transaction(request, response)
|
203
|
-
transactions << RequestInterceptor::Transaction.new(request, response)
|
84
|
+
transactions
|
204
85
|
end
|
205
86
|
end
|
206
87
|
|
207
|
-
|
208
|
-
|
88
|
+
require_relative "request_interceptor/application"
|
89
|
+
require_relative "request_interceptor/webmock_manager"
|
90
|
+
require_relative "request_interceptor/webmock_patches"
|
91
|
+
|
92
|
+
WebMock.singleton_class.prepend(RequestInterceptor::WebMockPatches)
|
data/request_interceptor.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_runtime_dependency "sinatra"
|
22
22
|
spec.add_runtime_dependency "rack"
|
23
|
+
spec.add_runtime_dependency "webmock", "~> 3.0"
|
23
24
|
|
24
25
|
spec.add_development_dependency "bundler", "~> 1.9"
|
25
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: request_interceptor
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Tennhard
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-
|
12
|
+
date: 2017-05-08 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sinatra
|
@@ -39,6 +39,20 @@ dependencies:
|
|
39
39
|
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: webmock
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '3.0'
|
49
|
+
type: :runtime
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '3.0'
|
42
56
|
- !ruby/object:Gem::Dependency
|
43
57
|
name: bundler
|
44
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,8 +129,9 @@ files:
|
|
115
129
|
- circle.yml
|
116
130
|
- lib/request_interceptor.rb
|
117
131
|
- lib/request_interceptor/application.rb
|
118
|
-
- lib/request_interceptor/status.rb
|
119
132
|
- lib/request_interceptor/version.rb
|
133
|
+
- lib/request_interceptor/webmock_manager.rb
|
134
|
+
- lib/request_interceptor/webmock_patches.rb
|
120
135
|
- request_interceptor.gemspec
|
121
136
|
homepage: http://github.com/t6d/request_interceptor
|
122
137
|
licenses:
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require 'rack/utils'
|
2
|
-
|
3
|
-
class RequestInterceptor::Status < Struct.new(:value, :description)
|
4
|
-
STATUSES = Rack::Utils::HTTP_STATUS_CODES
|
5
|
-
|
6
|
-
def self.from_code(code, description = nil)
|
7
|
-
description = STATUSES.fetch(code.to_i, "Unknown") if description.nil?
|
8
|
-
new(code.to_s, description)
|
9
|
-
end
|
10
|
-
|
11
|
-
def response_class
|
12
|
-
self.class.response_class(value)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.response_class(code)
|
16
|
-
@response_classes ||=
|
17
|
-
begin
|
18
|
-
wrapped_classes = Hash.new { Net::HTTPUnknownResponse }
|
19
|
-
Net::HTTPResponse::CODE_TO_OBJ.inject(wrapped_classes) do |classes, (code, original_class)|
|
20
|
-
new_class = Class.new(original_class) do
|
21
|
-
attr_accessor :body
|
22
|
-
end
|
23
|
-
|
24
|
-
new_class.define_singleton_method(:name) { original_class.name }
|
25
|
-
new_class.define_singleton_method(:to_s) { original_class.name }
|
26
|
-
|
27
|
-
classes[code] = new_class
|
28
|
-
classes
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
@response_classes[code]
|
33
|
-
end
|
34
|
-
end
|