tshield 0.11.16.0 → 0.12.0.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/README.md +22 -2
- data/bin/tshield +3 -2
- data/config/tshield.yml +1 -0
- data/lib/tshield/configuration.rb +5 -0
- data/lib/tshield/controllers/helpers/session_helpers.rb +5 -0
- data/lib/tshield/controllers/requests.rb +3 -1
- data/lib/tshield/controllers/sessions.rb +3 -0
- data/lib/tshield/errors.rb +4 -0
- data/lib/tshield/grpc.rb +5 -5
- data/lib/tshield/grpc/vcr.rb +37 -20
- data/lib/tshield/matching/filters.rb +8 -0
- data/lib/tshield/request.rb +8 -8
- data/lib/tshield/request_vcr.rb +36 -23
- data/lib/tshield/sessions.rb +20 -2
- data/lib/tshield/version.rb +2 -2
- data/spec/tshield/configuration_spec.rb +27 -0
- data/spec/tshield/fixtures/matching/example.json +12 -0
- data/spec/tshield/request_matching_spec.rb +12 -0
- data/spec/tshield/request_vcr_spec.rb +49 -0
- data/spec/tshield/sessions_spec.rb +18 -0
- data/tshield.gemspec +5 -6
- metadata +34 -45
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b027cb3ab381c02f5e74668df35812a9056f2ea67be29103905d3a965168a449
|
|
4
|
+
data.tar.gz: 71c06f7a53ba24daafbf1c42f16ed02d05d00be2f663b2c52ae39e67377aa241
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a8829db78bef723d0a9afab28bcbd3d52127d4fff48ea098482ebc3fff1bb017a751b0aef1315eb4eaf67094c836168c601e9fc93af444f05f86c50cade2be6f
|
|
7
|
+
data.tar.gz: a5f981a9d81ff8415f60e1d17572d56cf3d6112899b5a2e67bd17cebe69c1e49c48818c0ba6d4009e03e64476d10b7ccf338a4171d822b316e7305cf6a777c2c
|
data/README.md
CHANGED
|
@@ -13,7 +13,7 @@ TShield is an open source proxy for mocks API responses.
|
|
|
13
13
|
* REST
|
|
14
14
|
* SOAP
|
|
15
15
|
* Session manager to separate multiple scenarios (success, error, sucess variation, ...)
|
|
16
|
-
|
|
16
|
+
* gRPC [EXPERIMENTAL]
|
|
17
17
|
* Lightweight
|
|
18
18
|
* MIT license
|
|
19
19
|
|
|
@@ -68,6 +68,17 @@ domains:
|
|
|
68
68
|
- /users
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
**Windows Compatibility:** If you need to use Tshield in Windows SO, change the config file and set the windows_compatibility to true.
|
|
72
|
+
|
|
73
|
+
Eg:
|
|
74
|
+
```yaml
|
|
75
|
+
windows_compatibility: true
|
|
76
|
+
request:
|
|
77
|
+
# wait time for real service
|
|
78
|
+
timeout: 8
|
|
79
|
+
...
|
|
80
|
+
```
|
|
81
|
+
|
|
71
82
|
## Config options for Pattern Matching
|
|
72
83
|
|
|
73
84
|
An example of file to create a stub:
|
|
@@ -250,6 +261,16 @@ _DELETE_ to http://localhost:4567/sessions
|
|
|
250
261
|
curl -X DELETE \
|
|
251
262
|
http://localhost:4567/sessions
|
|
252
263
|
```
|
|
264
|
+
### Append secondary TShield session
|
|
265
|
+
**Append session. Secondary sessions will used only for read content in VCR mode, writes will be do in the main session. Append only works if exists a current session setted.**
|
|
266
|
+
|
|
267
|
+
_POST_ to http://localhost:4567/sessions?name=<<same_name>>
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
curl -X POST \
|
|
271
|
+
'http://localhost:4567/sessions/append?name=my_valid'
|
|
272
|
+
```
|
|
273
|
+
|
|
253
274
|
## [Experimental] Config options for gRPC
|
|
254
275
|
|
|
255
276
|
```yaml
|
|
@@ -265,7 +286,6 @@ grpc:
|
|
|
265
286
|
|
|
266
287
|
### Not Implemented Yet
|
|
267
288
|
|
|
268
|
-
- Sessions
|
|
269
289
|
- Matching
|
|
270
290
|
|
|
271
291
|
### Configuration
|
data/bin/tshield
CHANGED
data/config/tshield.yml
CHANGED
|
@@ -32,6 +32,7 @@ module TShield
|
|
|
32
32
|
attr_reader :domains
|
|
33
33
|
attr_reader :tcp_servers
|
|
34
34
|
attr_reader :session_path
|
|
35
|
+
attr_reader :windows_compatibility
|
|
35
36
|
|
|
36
37
|
def initialize(attributes)
|
|
37
38
|
attributes.each { |key, value| instance_variable_set("@#{key}", value) }
|
|
@@ -103,6 +104,10 @@ module TShield
|
|
|
103
104
|
session_path || '/sessions'
|
|
104
105
|
end
|
|
105
106
|
|
|
107
|
+
def get_questionmark_char
|
|
108
|
+
windows_compatibility ? '%3f' : '?'
|
|
109
|
+
end
|
|
110
|
+
|
|
106
111
|
def grpc
|
|
107
112
|
defaults = { 'port' => 5678, 'proto_dir' => 'proto', 'services' => {} }
|
|
108
113
|
defaults.merge(@grpc || {})
|
|
@@ -10,6 +10,11 @@ module TShield
|
|
|
10
10
|
session[:name] if session
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
+
def self.secondary_sessions(request)
|
|
14
|
+
session = TShield::Sessions.current(request.ip)
|
|
15
|
+
session[:secondary_sessions] if session
|
|
16
|
+
end
|
|
17
|
+
|
|
13
18
|
def self.current_session_call(request, callid, method)
|
|
14
19
|
session = TShield::Sessions.current(request.ip)
|
|
15
20
|
session ? session[:counter].current(callid, method) : 0
|
|
@@ -43,6 +43,7 @@ module TShield
|
|
|
43
43
|
request_content_type = request.content_type
|
|
44
44
|
|
|
45
45
|
session_name = TShield::Controllers::Helpers::SessionHelpers.current_session_name(request)
|
|
46
|
+
secondary_sessions = TShield::Controllers::Helpers::SessionHelpers.secondary_sessions(request)
|
|
46
47
|
session_call = TShield::Controllers::Helpers::SessionHelpers
|
|
47
48
|
.current_session_call(request, callid, method)
|
|
48
49
|
|
|
@@ -51,6 +52,7 @@ module TShield
|
|
|
51
52
|
headers: Helpers.build_headers(request),
|
|
52
53
|
raw_query: request.env['QUERY_STRING'],
|
|
53
54
|
session: session_name,
|
|
55
|
+
secondary_sessions: secondary_sessions,
|
|
54
56
|
call: session_call,
|
|
55
57
|
ip: request.ip
|
|
56
58
|
}
|
|
@@ -67,7 +69,7 @@ module TShield
|
|
|
67
69
|
unless api_response
|
|
68
70
|
add_headers(options, path)
|
|
69
71
|
|
|
70
|
-
api_response ||= TShield::RequestVCR.new(path, options.clone).
|
|
72
|
+
api_response ||= TShield::RequestVCR.new(path, options.clone).vcr_response
|
|
71
73
|
api_response.headers.reject! do |key, _v|
|
|
72
74
|
configuration.get_excluded_headers(domain(path)).include?(key)
|
|
73
75
|
end
|
|
@@ -23,6 +23,9 @@ module TShield
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def self.register_post(app, session_path)
|
|
26
|
+
app.post "#{session_path}/append" do
|
|
27
|
+
TShield::Sessions.append(request.ip, params[:name]).to_json
|
|
28
|
+
end
|
|
26
29
|
app.post session_path do
|
|
27
30
|
TShield::Sessions.start(request.ip, params[:name]).to_json
|
|
28
31
|
end
|
data/lib/tshield/grpc.rb
CHANGED
|
@@ -3,16 +3,15 @@
|
|
|
3
3
|
require 'grpc'
|
|
4
4
|
|
|
5
5
|
require 'tshield/configuration'
|
|
6
|
-
require 'tshield/sessions'
|
|
7
6
|
require 'tshield/grpc/vcr'
|
|
8
7
|
|
|
9
8
|
module TShield
|
|
10
9
|
module Grpc
|
|
11
10
|
module RequestHandler
|
|
12
11
|
include TShield::Grpc::VCR
|
|
13
|
-
def handler(method_name, request)
|
|
12
|
+
def handler(method_name, request, parameters)
|
|
14
13
|
options = self.class.options
|
|
15
|
-
handler_in_vcr_mode(method_name, request, options)
|
|
14
|
+
handler_in_vcr_mode(method_name, request, parameters, options)
|
|
16
15
|
end
|
|
17
16
|
end
|
|
18
17
|
def self.run!
|
|
@@ -24,6 +23,7 @@ module TShield
|
|
|
24
23
|
TShield.logger.info("loading proto files from #{lib_dir}")
|
|
25
24
|
|
|
26
25
|
bind = "0.0.0.0:#{@configuration['port']}"
|
|
26
|
+
TShield.logger.info("Starting gRPC server in #{bind}")
|
|
27
27
|
|
|
28
28
|
server = GRPC::RpcServer.new
|
|
29
29
|
server.add_http2_port(bind, :this_port_is_insecure)
|
|
@@ -61,8 +61,8 @@ module TShield
|
|
|
61
61
|
descriptions.each do |service_name, description|
|
|
62
62
|
puts description
|
|
63
63
|
method_name = service_name.to_s.underscore.to_sym
|
|
64
|
-
define_method(method_name) do |request,
|
|
65
|
-
handler(__method__, request)
|
|
64
|
+
define_method(method_name) do |request, parameters|
|
|
65
|
+
handler(__method__, request, parameters)
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
end
|
data/lib/tshield/grpc/vcr.rb
CHANGED
|
@@ -1,65 +1,82 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'tshield/sessions'
|
|
4
|
+
|
|
3
5
|
module TShield
|
|
4
6
|
module Grpc
|
|
5
7
|
module VCR
|
|
6
|
-
def handler_in_vcr_mode(method_name, request, options)
|
|
8
|
+
def handler_in_vcr_mode(method_name, request, parameters, options)
|
|
9
|
+
parameters.peer =~ /ipv6:\[(.+?)\]|ipv4:(.+?):/
|
|
10
|
+
peer = Regexp.last_match(1) || Regexp.last_match(2)
|
|
11
|
+
|
|
12
|
+
TShield.logger.info("request from #{parameters.peer}")
|
|
13
|
+
@session = TShield::Sessions.current(peer)
|
|
14
|
+
|
|
15
|
+
TShield.logger.info("grpc using session #{@session || 'default'}")
|
|
7
16
|
module_name = options['module']
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
path = create_destiny(module_name, method_name, request)
|
|
19
|
+
response = saved_response(path)
|
|
20
|
+
if response
|
|
21
|
+
TShield.logger.info("returning saved response for request #{request.to_json} saved into #{hexdigest(request)}")
|
|
22
|
+
return response
|
|
23
|
+
end
|
|
11
24
|
|
|
25
|
+
TShield.logger.info("calling server to get response for #{request.to_json}")
|
|
12
26
|
client_class = Object.const_get("#{module_name}::Stub")
|
|
13
27
|
client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
|
|
14
28
|
response = client_instance.send(method_name, request)
|
|
15
|
-
save_request_and_response(request, response)
|
|
29
|
+
save_request_and_response(path, request, response)
|
|
16
30
|
response
|
|
17
31
|
end
|
|
18
32
|
|
|
19
|
-
def saved_response(
|
|
20
|
-
|
|
21
|
-
response_file = File.join(@complete_path, 'response')
|
|
33
|
+
def saved_response(path)
|
|
34
|
+
response_file = File.join(path, 'response')
|
|
22
35
|
return false unless File.exist? response_file
|
|
23
36
|
|
|
24
37
|
content = JSON.parse File.open(response_file).read
|
|
25
|
-
response_class = File.open(File.join(
|
|
38
|
+
response_class = File.open(File.join(path, 'response_class')).read.strip
|
|
26
39
|
Kernel.const_get(response_class).new(content)
|
|
27
40
|
end
|
|
28
41
|
|
|
29
|
-
def save_request_and_response(request, response)
|
|
30
|
-
save_request(request)
|
|
31
|
-
save_response(response)
|
|
42
|
+
def save_request_and_response(path, request, response)
|
|
43
|
+
save_request(path, request)
|
|
44
|
+
save_response(path, response)
|
|
32
45
|
end
|
|
33
46
|
|
|
34
|
-
def save_request(request)
|
|
35
|
-
file = File.open(File.join(
|
|
47
|
+
def save_request(path, request)
|
|
48
|
+
file = File.open(File.join(path, 'original_request'), 'w')
|
|
36
49
|
file.puts request.to_json
|
|
37
50
|
file.close
|
|
38
51
|
end
|
|
39
52
|
|
|
40
|
-
def save_response(response)
|
|
41
|
-
file = File.open(File.join(
|
|
53
|
+
def save_response(path, response)
|
|
54
|
+
file = File.open(File.join(path, 'response'), 'w')
|
|
42
55
|
file.puts response.to_json
|
|
43
56
|
file.close
|
|
44
57
|
|
|
45
|
-
response_class = File.open(File.join(
|
|
58
|
+
response_class = File.open(File.join(path, 'response_class'), 'w')
|
|
46
59
|
response_class.puts response.class.to_s
|
|
47
60
|
response_class.close
|
|
48
61
|
end
|
|
49
62
|
|
|
50
63
|
def complete_path(module_name, method_name, request)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
64
|
+
@session_name = (@session || {})[:name]
|
|
65
|
+
path = ['requests', 'grpc', @session_name, module_name, method_name.to_s, hexdigest(request)].compact
|
|
66
|
+
path
|
|
54
67
|
end
|
|
55
68
|
|
|
56
69
|
def create_destiny(module_name, method_name, request)
|
|
57
70
|
current_path = []
|
|
58
|
-
|
|
71
|
+
|
|
72
|
+
path = complete_path(module_name, method_name, request)
|
|
73
|
+
TShield.logger.info("using path #{path}")
|
|
74
|
+
path.each do |path|
|
|
59
75
|
current_path << path
|
|
60
76
|
destiny = File.join current_path
|
|
61
77
|
Dir.mkdir destiny unless File.exist? destiny
|
|
62
78
|
end
|
|
79
|
+
path
|
|
63
80
|
end
|
|
64
81
|
|
|
65
82
|
def hexdigest(request)
|
|
@@ -8,6 +8,14 @@ module TShield
|
|
|
8
8
|
result = filter_stubs(stubs[@options[:session]] || {})
|
|
9
9
|
return result if result
|
|
10
10
|
|
|
11
|
+
find_in_secondary_sessions(stubs, @options[:secondary_sessions] || [])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def find_in_secondary_sessions(stubs, sessions)
|
|
15
|
+
sessions.each do |session|
|
|
16
|
+
result = filter_stubs(stubs[session] || {})
|
|
17
|
+
return result if result
|
|
18
|
+
end
|
|
11
19
|
filter_stubs(stubs[DEFAULT_SESSION] || {}) unless @options[:session] == DEFAULT_SESSION
|
|
12
20
|
end
|
|
13
21
|
|
data/lib/tshield/request.rb
CHANGED
|
@@ -13,8 +13,8 @@ module TShield
|
|
|
13
13
|
|
|
14
14
|
protected
|
|
15
15
|
|
|
16
|
-
def session_destiny(request_path)
|
|
17
|
-
session = @options[:session]
|
|
16
|
+
def session_destiny(request_path, current_session = nil)
|
|
17
|
+
session = current_session || @options[:session]
|
|
18
18
|
return request_path unless session
|
|
19
19
|
|
|
20
20
|
request_path = File.join(request_path, session)
|
|
@@ -22,19 +22,19 @@ module TShield
|
|
|
22
22
|
request_path
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def content_destiny
|
|
26
|
-
"#{destiny}.content"
|
|
25
|
+
def content_destiny(current_session = nil)
|
|
26
|
+
"#{destiny(current_session)}.content"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def headers_destiny
|
|
30
|
-
"#{destiny}.json"
|
|
29
|
+
def headers_destiny(current_session = nil)
|
|
30
|
+
"#{destiny(current_session)}.json"
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
def destiny
|
|
33
|
+
def destiny(current_session = nil)
|
|
34
34
|
request_path = File.join('requests')
|
|
35
35
|
Dir.mkdir(request_path) unless File.exist?(request_path)
|
|
36
36
|
|
|
37
|
-
request_path = session_destiny(request_path)
|
|
37
|
+
request_path = session_destiny(request_path, current_session)
|
|
38
38
|
|
|
39
39
|
name_path = File.join(request_path, name)
|
|
40
40
|
Dir.mkdir(name_path) unless File.exist?(name_path)
|
data/lib/tshield/request_vcr.rb
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
require 'httparty'
|
|
4
4
|
require 'json'
|
|
5
|
-
require 'byebug'
|
|
6
5
|
|
|
7
6
|
require 'digest/sha1'
|
|
8
7
|
|
|
9
8
|
require 'tshield/configuration'
|
|
9
|
+
require 'tshield/logger'
|
|
10
10
|
require 'tshield/options'
|
|
11
11
|
require 'tshield/request'
|
|
12
12
|
require 'tshield/response'
|
|
@@ -14,6 +14,8 @@ require 'tshield/response'
|
|
|
14
14
|
module TShield
|
|
15
15
|
# Module to write and read saved responses
|
|
16
16
|
class RequestVCR < TShield::Request
|
|
17
|
+
attr_reader :vcr_response
|
|
18
|
+
|
|
17
19
|
def initialize(path, options = {})
|
|
18
20
|
super()
|
|
19
21
|
@path = path
|
|
@@ -35,27 +37,31 @@ module TShield
|
|
|
35
37
|
_method, @url, @options = filter.new.filter(method, @url, @options)
|
|
36
38
|
end
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
40
|
+
in_session = find_in_sessions
|
|
41
|
+
if in_session
|
|
42
|
+
# TODO: create concept of global session in vcr
|
|
43
|
+
in_session = nil if in_session == 'global'
|
|
44
|
+
@vcr_response = response(in_session)
|
|
45
|
+
@vcr_response.original = false
|
|
41
46
|
else
|
|
47
|
+
TShield.logger.info("calling original service for request with options #{@options}")
|
|
42
48
|
raw = HTTParty.send(method.to_s, @url, @options)
|
|
43
49
|
|
|
44
50
|
original_response = save(raw)
|
|
45
51
|
original_response.original = true
|
|
46
|
-
|
|
52
|
+
@vcr_response = original_response
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
configuration.get_after_filters(domain).each do |filter|
|
|
50
|
-
|
|
56
|
+
@vcr_response = filter.new.filter(@vcr_response)
|
|
51
57
|
end
|
|
52
|
-
resp
|
|
53
58
|
end
|
|
54
59
|
|
|
55
|
-
def response
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
60
|
+
def response(session)
|
|
61
|
+
response_content = saved_content(session)
|
|
62
|
+
TShield::Response.new(response_content['body'],
|
|
63
|
+
response_content['headers'] || [],
|
|
64
|
+
response_content['status'] || 200)
|
|
59
65
|
end
|
|
60
66
|
|
|
61
67
|
private
|
|
@@ -89,20 +95,27 @@ module TShield
|
|
|
89
95
|
TShield::Response.new(raw_response.body, headers, raw_response.code)
|
|
90
96
|
end
|
|
91
97
|
|
|
92
|
-
def saved_content
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
@saved_content['body'] = File.open(content_destiny).read unless @saved_content['body']
|
|
97
|
-
@saved_content
|
|
98
|
+
def saved_content(session)
|
|
99
|
+
content = JSON.parse(File.open(headers_destiny(session)).read)
|
|
100
|
+
content['body'] = File.open(content_destiny(session)).read unless content['body']
|
|
101
|
+
content
|
|
98
102
|
end
|
|
99
103
|
|
|
100
|
-
def file_exists
|
|
101
|
-
File.exist?(content_destiny)
|
|
104
|
+
def file_exists(session)
|
|
105
|
+
File.exist?(content_destiny(session))
|
|
102
106
|
end
|
|
103
107
|
|
|
104
|
-
def
|
|
105
|
-
|
|
108
|
+
def find_in_sessions
|
|
109
|
+
in_session = nil
|
|
110
|
+
|
|
111
|
+
([@options[:session]] + (@options[:secondary_sessions] || [])).each do |session|
|
|
112
|
+
if file_exists(session) && configuration.cache_request?(domain)
|
|
113
|
+
in_session = (session || 'global')
|
|
114
|
+
break
|
|
115
|
+
end
|
|
116
|
+
TShield.logger.info("saved response not found in #{session}")
|
|
117
|
+
end
|
|
118
|
+
in_session
|
|
106
119
|
end
|
|
107
120
|
|
|
108
121
|
def key
|
|
@@ -127,9 +140,9 @@ module TShield
|
|
|
127
140
|
if url.size > 225
|
|
128
141
|
path = url.gsub(/(\?.*)/, '')
|
|
129
142
|
params = Digest::SHA1.hexdigest Regexp.last_match(1)
|
|
130
|
-
"#{path.gsub(%r{/}, '-').gsub(/^-/, '')}
|
|
143
|
+
"#{path.gsub(%r{/}, '-').gsub(/^-/, '')}#{configuration.get_questionmark_char}#{params}"
|
|
131
144
|
else
|
|
132
|
-
url.gsub(%r{/}, '-').gsub(/^-/, '')
|
|
145
|
+
url.gsub(%r{/}, '-').gsub(/^-/, '').gsub('?', configuration.get_questionmark_char)
|
|
133
146
|
end
|
|
134
147
|
end
|
|
135
148
|
end
|
data/lib/tshield/sessions.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require '
|
|
3
|
+
require 'tshield/logger'
|
|
4
4
|
require 'tshield/counter'
|
|
5
|
+
require 'tshield/errors'
|
|
5
6
|
|
|
6
7
|
module TShield
|
|
7
8
|
# Manage sessions
|
|
@@ -9,17 +10,34 @@ module TShield
|
|
|
9
10
|
# Start and stop session for ip
|
|
10
11
|
module Sessions
|
|
11
12
|
def self.start(ip, name)
|
|
12
|
-
|
|
13
|
+
TShield.logger.info("starting session #{name} for ip #{normalize_ip(ip)}")
|
|
14
|
+
sessions[normalize_ip(ip)] = {
|
|
15
|
+
name: name,
|
|
16
|
+
counter: TShield::Counter.new,
|
|
17
|
+
secondary_sessions: []
|
|
18
|
+
}
|
|
13
19
|
end
|
|
14
20
|
|
|
15
21
|
def self.stop(ip)
|
|
22
|
+
TShield.logger.info("stoping session for ip #{normalize_ip(ip)}")
|
|
16
23
|
sessions[normalize_ip(ip)] = nil
|
|
17
24
|
end
|
|
18
25
|
|
|
19
26
|
def self.current(ip)
|
|
27
|
+
TShield.logger.info("fetching session for ip #{normalize_ip(ip)}")
|
|
20
28
|
sessions[normalize_ip(ip)]
|
|
21
29
|
end
|
|
22
30
|
|
|
31
|
+
def self.append(ip, name)
|
|
32
|
+
TShield.logger.info("appeding session #{name} for ip #{normalize_ip(ip)}")
|
|
33
|
+
|
|
34
|
+
current_session = sessions[normalize_ip(ip)]
|
|
35
|
+
raise AppendSessionWithoutMainSessionError, "not found main session for #{ip}" unless current_session
|
|
36
|
+
|
|
37
|
+
current_session[:secondary_sessions] << name
|
|
38
|
+
current_session
|
|
39
|
+
end
|
|
40
|
+
|
|
23
41
|
def self.sessions
|
|
24
42
|
@sessions ||= {}
|
|
25
43
|
end
|
data/lib/tshield/version.rb
CHANGED
|
@@ -64,6 +64,33 @@ describe TShield::Configuration do
|
|
|
64
64
|
expect(@configuration.get_domain_for('/api/four')).to be_nil
|
|
65
65
|
end
|
|
66
66
|
end
|
|
67
|
+
|
|
68
|
+
describe 'SO compatibility' do
|
|
69
|
+
it 'should be compatible with windows when configuration is true' do
|
|
70
|
+
allow(YAML).to receive(:safe_load).and_return({:windows_compatibility => true })
|
|
71
|
+
TShield::Configuration.clear
|
|
72
|
+
@configuration = TShield::Configuration.singleton
|
|
73
|
+
|
|
74
|
+
expect(@configuration.get_questionmark_char).to eq('%3f')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'should be compatible with Unix when configuration is false' do
|
|
78
|
+
allow(YAML).to receive(:safe_load).and_return({:windows_compatibility => false })
|
|
79
|
+
TShield::Configuration.clear
|
|
80
|
+
@configuration = TShield::Configuration.singleton
|
|
81
|
+
|
|
82
|
+
expect(@configuration.get_questionmark_char).to eq('?')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
it 'should be compatible with Unix when configuration is missing' do
|
|
86
|
+
allow(YAML).to receive(:safe_load).and_return({})
|
|
87
|
+
TShield::Configuration.clear
|
|
88
|
+
@configuration = TShield::Configuration.singleton
|
|
89
|
+
|
|
90
|
+
expect(@configuration.get_questionmark_char).to eq('?')
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
end
|
|
67
94
|
end
|
|
68
95
|
context 'on config not exist' do
|
|
69
96
|
before :each do
|
|
@@ -99,5 +99,17 @@
|
|
|
99
99
|
"body": "body content in session"
|
|
100
100
|
}
|
|
101
101
|
}]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"session": "second-session",
|
|
105
|
+
"stubs": [{
|
|
106
|
+
"method": "GET",
|
|
107
|
+
"path": "/matching/second-example",
|
|
108
|
+
"response": {
|
|
109
|
+
"status": 200,
|
|
110
|
+
"headers": {},
|
|
111
|
+
"body": "body content in second-session"
|
|
112
|
+
}
|
|
113
|
+
}]
|
|
102
114
|
}
|
|
103
115
|
]
|
|
@@ -235,6 +235,18 @@ describe TShield::RequestMatching do
|
|
|
235
235
|
expect(@response.headers).to eql({})
|
|
236
236
|
expect(@response.status).to eql(201)
|
|
237
237
|
end
|
|
238
|
+
context 'with secondary session' do
|
|
239
|
+
it 'should return response object from session settings' do
|
|
240
|
+
@request_matching = TShield::RequestMatching.new('/matching/second-example',
|
|
241
|
+
method: 'GET',
|
|
242
|
+
session: 'a-session',
|
|
243
|
+
secondary_sessions: ['second-session'])
|
|
244
|
+
@response = @request_matching.match_request
|
|
245
|
+
expect(@response.body).to eql('body content in second-session')
|
|
246
|
+
expect(@response.headers).to eql({})
|
|
247
|
+
expect(@response.status).to eql(200)
|
|
248
|
+
end
|
|
249
|
+
end
|
|
238
250
|
end
|
|
239
251
|
end
|
|
240
252
|
end
|
|
@@ -62,6 +62,8 @@ describe TShield::RequestVCR do
|
|
|
62
62
|
}
|
|
63
63
|
)
|
|
64
64
|
|
|
65
|
+
allow(@configuration).to receive(:get_questionmark_char).and_return('?')
|
|
66
|
+
|
|
65
67
|
allow(HTTParty).to receive(:send).and_return(RawResponse.new)
|
|
66
68
|
file_double = double
|
|
67
69
|
|
|
@@ -83,6 +85,53 @@ describe TShield::RequestVCR do
|
|
|
83
85
|
method: 'GET',
|
|
84
86
|
call: 0
|
|
85
87
|
end
|
|
88
|
+
|
|
89
|
+
it 'should create response directory in windows standard' do
|
|
90
|
+
|
|
91
|
+
allow(@configuration).to receive(:domains).and_return(
|
|
92
|
+
'example.org' => {
|
|
93
|
+
'skip_query_params' => []
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
allow(@configuration).to receive(:get_questionmark_char).and_return('%3f')
|
|
98
|
+
|
|
99
|
+
allow(HTTParty).to receive(:send).and_return(RawResponse.new)
|
|
100
|
+
file_double = double
|
|
101
|
+
|
|
102
|
+
allow(File).to receive(:join)
|
|
103
|
+
.with('./requests/example.org', '%3fparam=value')
|
|
104
|
+
.and_return('./requests/example.org/%3fparam=value')
|
|
105
|
+
allow(File).to receive(:join)
|
|
106
|
+
.with('./requests/example.org/%3fparam=value', 'get')
|
|
107
|
+
.and_return('./requests/example.org/%3fparam=value/get')
|
|
108
|
+
allow(File).to receive(:join)
|
|
109
|
+
.with('./requests/example.org/%3fparam=value/get', '0')
|
|
110
|
+
.and_return('./requests/example.org/%3fparam=value/get/0')
|
|
111
|
+
|
|
112
|
+
allow(file_double).to receive(:read).and_return('{}')
|
|
113
|
+
|
|
114
|
+
expect(File).to receive('open')
|
|
115
|
+
.with('./requests/example.org/%3fparam=value/get/0.content', 'w')
|
|
116
|
+
.and_return(file_double)
|
|
117
|
+
|
|
118
|
+
expect(File).to receive('open')
|
|
119
|
+
.with('./requests/example.org/%3fparam=value/get/0.json', 'w')
|
|
120
|
+
.and_return(file_double)
|
|
121
|
+
|
|
122
|
+
expect(file_double).to receive(:write).ordered.with('this is the body')
|
|
123
|
+
expect(file_double).to receive(:write)
|
|
124
|
+
.with("{\n \"status\": 200,\n \"headers\": {\n }\n}")
|
|
125
|
+
expect(file_double).to receive(:close)
|
|
126
|
+
expect(file_double).to receive(:close)
|
|
127
|
+
|
|
128
|
+
TShield::RequestVCR.new '/',
|
|
129
|
+
raw_query: 'param=value',
|
|
130
|
+
method: 'GET',
|
|
131
|
+
call: 0
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
86
135
|
end
|
|
87
136
|
end
|
|
88
137
|
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'tshield/sessions'
|
|
4
|
+
require 'spec_helper'
|
|
5
|
+
|
|
6
|
+
describe TShield::Sessions do
|
|
7
|
+
context 'on append session' do
|
|
8
|
+
it 'should raise error if not has a main session' do
|
|
9
|
+
expect { TShield::Sessions.append 'ip', 'secondary-session' }
|
|
10
|
+
.to raise_error(AppendSessionWithoutMainSessionError)
|
|
11
|
+
end
|
|
12
|
+
it 'should append if has main session' do
|
|
13
|
+
TShield::Sessions.start 'ip', 'main-session'
|
|
14
|
+
result = TShield::Sessions.append 'ip', 'secondary-session'
|
|
15
|
+
expect(result[:secondary_sessions]).to include('secondary-session')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
data/tshield.gemspec
CHANGED
|
@@ -22,19 +22,18 @@ Gem::Specification.new do |s|
|
|
|
22
22
|
|
|
23
23
|
s.test_files = Dir['spec/**/*']
|
|
24
24
|
|
|
25
|
-
s.required_ruby_version = '>= 2.
|
|
25
|
+
s.required_ruby_version = '>= 2.4'
|
|
26
26
|
|
|
27
|
-
s.add_dependency('byebug', '~> 11.0', '>= 11.0.1')
|
|
28
27
|
s.add_dependency('grpc', '~> 1.28', '>= 1.28.0')
|
|
29
28
|
s.add_dependency('grpc-tools', '~> 1.28', '>= 1.28.0')
|
|
30
29
|
s.add_dependency('httparty', '~> 0.14', '>= 0.14.0')
|
|
31
30
|
s.add_dependency('json', '~> 2.0', '>= 2.0')
|
|
32
31
|
s.add_dependency('puma', '~> 4.3', '>= 4.3.3')
|
|
33
|
-
s.add_dependency('sinatra', '~> 1
|
|
32
|
+
s.add_dependency('sinatra', '~> 2.1', '>= 2.1.0')
|
|
34
33
|
s.add_dependency('sinatra-cross_origin', '~> 0.4.0', '>= 0.4')
|
|
35
|
-
s.add_development_dependency('coveralls')
|
|
34
|
+
s.add_development_dependency('coveralls', '~> 0.8', '>= 0.8.23')
|
|
36
35
|
s.add_development_dependency('cucumber', '~> 3.1', '>= 3.1.2')
|
|
37
|
-
s.add_development_dependency('guard', '~> 2.
|
|
36
|
+
s.add_development_dependency('guard', '~> 2.16', '>= 2.16.2')
|
|
38
37
|
s.add_development_dependency('guard-rspec', '~> 4.7', '>= 4.7.3')
|
|
39
38
|
s.add_development_dependency('rake', '>= 10.0', '~> 13.0')
|
|
40
39
|
s.add_development_dependency('rdoc', '~> 6.0', '>= 6.0')
|
|
@@ -42,6 +41,6 @@ Gem::Specification.new do |s|
|
|
|
42
41
|
s.add_development_dependency('rspec', '~> 3.5', '>= 3.5.0')
|
|
43
42
|
s.add_development_dependency('rubocop', '~> 0.73.0', '>= 0.73.0')
|
|
44
43
|
s.add_development_dependency('rubocop-rails', '~> 2.2.0', '>= 2.2.1')
|
|
45
|
-
s.add_development_dependency('simplecov', '~> 0.
|
|
44
|
+
s.add_development_dependency('simplecov', '~> 0.16', '>= 0.16.1')
|
|
46
45
|
s.add_development_dependency('webmock', '~> 2.1', '>= 2.1.0')
|
|
47
46
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: tshield
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.12.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Diego Rubin
|
|
@@ -9,28 +9,8 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2020-
|
|
12
|
+
date: 2020-11-03 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
|
-
- !ruby/object:Gem::Dependency
|
|
15
|
-
name: byebug
|
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
requirements:
|
|
18
|
-
- - "~>"
|
|
19
|
-
- !ruby/object:Gem::Version
|
|
20
|
-
version: '11.0'
|
|
21
|
-
- - ">="
|
|
22
|
-
- !ruby/object:Gem::Version
|
|
23
|
-
version: 11.0.1
|
|
24
|
-
type: :runtime
|
|
25
|
-
prerelease: false
|
|
26
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
27
|
-
requirements:
|
|
28
|
-
- - "~>"
|
|
29
|
-
- !ruby/object:Gem::Version
|
|
30
|
-
version: '11.0'
|
|
31
|
-
- - ">="
|
|
32
|
-
- !ruby/object:Gem::Version
|
|
33
|
-
version: 11.0.1
|
|
34
14
|
- !ruby/object:Gem::Dependency
|
|
35
15
|
name: grpc
|
|
36
16
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -137,20 +117,20 @@ dependencies:
|
|
|
137
117
|
requirements:
|
|
138
118
|
- - ">="
|
|
139
119
|
- !ruby/object:Gem::Version
|
|
140
|
-
version: 1.
|
|
120
|
+
version: 2.1.0
|
|
141
121
|
- - "~>"
|
|
142
122
|
- !ruby/object:Gem::Version
|
|
143
|
-
version: '1
|
|
123
|
+
version: '2.1'
|
|
144
124
|
type: :runtime
|
|
145
125
|
prerelease: false
|
|
146
126
|
version_requirements: !ruby/object:Gem::Requirement
|
|
147
127
|
requirements:
|
|
148
128
|
- - ">="
|
|
149
129
|
- !ruby/object:Gem::Version
|
|
150
|
-
version: 1.
|
|
130
|
+
version: 2.1.0
|
|
151
131
|
- - "~>"
|
|
152
132
|
- !ruby/object:Gem::Version
|
|
153
|
-
version: '1
|
|
133
|
+
version: '2.1'
|
|
154
134
|
- !ruby/object:Gem::Dependency
|
|
155
135
|
name: sinatra-cross_origin
|
|
156
136
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -175,16 +155,22 @@ dependencies:
|
|
|
175
155
|
name: coveralls
|
|
176
156
|
requirement: !ruby/object:Gem::Requirement
|
|
177
157
|
requirements:
|
|
158
|
+
- - "~>"
|
|
159
|
+
- !ruby/object:Gem::Version
|
|
160
|
+
version: '0.8'
|
|
178
161
|
- - ">="
|
|
179
162
|
- !ruby/object:Gem::Version
|
|
180
|
-
version:
|
|
163
|
+
version: 0.8.23
|
|
181
164
|
type: :development
|
|
182
165
|
prerelease: false
|
|
183
166
|
version_requirements: !ruby/object:Gem::Requirement
|
|
184
167
|
requirements:
|
|
168
|
+
- - "~>"
|
|
169
|
+
- !ruby/object:Gem::Version
|
|
170
|
+
version: '0.8'
|
|
185
171
|
- - ">="
|
|
186
172
|
- !ruby/object:Gem::Version
|
|
187
|
-
version:
|
|
173
|
+
version: 0.8.23
|
|
188
174
|
- !ruby/object:Gem::Dependency
|
|
189
175
|
name: cucumber
|
|
190
176
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -209,22 +195,22 @@ dependencies:
|
|
|
209
195
|
name: guard
|
|
210
196
|
requirement: !ruby/object:Gem::Requirement
|
|
211
197
|
requirements:
|
|
212
|
-
- - ">="
|
|
213
|
-
- !ruby/object:Gem::Version
|
|
214
|
-
version: 2.15.0
|
|
215
198
|
- - "~>"
|
|
216
199
|
- !ruby/object:Gem::Version
|
|
217
|
-
version: '2.
|
|
200
|
+
version: '2.16'
|
|
201
|
+
- - ">="
|
|
202
|
+
- !ruby/object:Gem::Version
|
|
203
|
+
version: 2.16.2
|
|
218
204
|
type: :development
|
|
219
205
|
prerelease: false
|
|
220
206
|
version_requirements: !ruby/object:Gem::Requirement
|
|
221
207
|
requirements:
|
|
222
|
-
- - ">="
|
|
223
|
-
- !ruby/object:Gem::Version
|
|
224
|
-
version: 2.15.0
|
|
225
208
|
- - "~>"
|
|
226
209
|
- !ruby/object:Gem::Version
|
|
227
|
-
version: '2.
|
|
210
|
+
version: '2.16'
|
|
211
|
+
- - ">="
|
|
212
|
+
- !ruby/object:Gem::Version
|
|
213
|
+
version: 2.16.2
|
|
228
214
|
- !ruby/object:Gem::Dependency
|
|
229
215
|
name: guard-rspec
|
|
230
216
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -369,22 +355,22 @@ dependencies:
|
|
|
369
355
|
name: simplecov
|
|
370
356
|
requirement: !ruby/object:Gem::Requirement
|
|
371
357
|
requirements:
|
|
372
|
-
- - ">="
|
|
373
|
-
- !ruby/object:Gem::Version
|
|
374
|
-
version: 0.12.0
|
|
375
358
|
- - "~>"
|
|
376
359
|
- !ruby/object:Gem::Version
|
|
377
|
-
version: '0.
|
|
360
|
+
version: '0.16'
|
|
361
|
+
- - ">="
|
|
362
|
+
- !ruby/object:Gem::Version
|
|
363
|
+
version: 0.16.1
|
|
378
364
|
type: :development
|
|
379
365
|
prerelease: false
|
|
380
366
|
version_requirements: !ruby/object:Gem::Requirement
|
|
381
367
|
requirements:
|
|
382
|
-
- - ">="
|
|
383
|
-
- !ruby/object:Gem::Version
|
|
384
|
-
version: 0.12.0
|
|
385
368
|
- - "~>"
|
|
386
369
|
- !ruby/object:Gem::Version
|
|
387
|
-
version: '0.
|
|
370
|
+
version: '0.16'
|
|
371
|
+
- - ">="
|
|
372
|
+
- !ruby/object:Gem::Version
|
|
373
|
+
version: 0.16.1
|
|
388
374
|
- !ruby/object:Gem::Dependency
|
|
389
375
|
name: webmock
|
|
390
376
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -426,6 +412,7 @@ files:
|
|
|
426
412
|
- lib/tshield/controllers/requests.rb
|
|
427
413
|
- lib/tshield/controllers/sessions.rb
|
|
428
414
|
- lib/tshield/counter.rb
|
|
415
|
+
- lib/tshield/errors.rb
|
|
429
416
|
- lib/tshield/extensions/string_extensions.rb
|
|
430
417
|
- lib/tshield/grpc.rb
|
|
431
418
|
- lib/tshield/grpc/vcr.rb
|
|
@@ -452,6 +439,7 @@ files:
|
|
|
452
439
|
- spec/tshield/options_spec.rb
|
|
453
440
|
- spec/tshield/request_matching_spec.rb
|
|
454
441
|
- spec/tshield/request_vcr_spec.rb
|
|
442
|
+
- spec/tshield/sessions_spec.rb
|
|
455
443
|
- tshield.gemspec
|
|
456
444
|
homepage: https://github.com/diegorubin/tshield
|
|
457
445
|
licenses:
|
|
@@ -465,7 +453,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
465
453
|
requirements:
|
|
466
454
|
- - ">="
|
|
467
455
|
- !ruby/object:Gem::Version
|
|
468
|
-
version: '2.
|
|
456
|
+
version: '2.4'
|
|
469
457
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
470
458
|
requirements:
|
|
471
459
|
- - ">="
|
|
@@ -481,6 +469,7 @@ test_files:
|
|
|
481
469
|
- spec/tshield/request_matching_spec.rb
|
|
482
470
|
- spec/tshield/grpc_spec.rb
|
|
483
471
|
- spec/tshield/configuration_spec.rb
|
|
472
|
+
- spec/tshield/sessions_spec.rb
|
|
484
473
|
- spec/tshield/request_vcr_spec.rb
|
|
485
474
|
- spec/tshield/controllers/requests_spec.rb
|
|
486
475
|
- spec/tshield/options_spec.rb
|