tshield 0.11.15.0 → 0.11.20.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
  SHA256:
3
- metadata.gz: e6ed543aa8bab1fff2c4d75d7479b7af0b3e2f06432330a697b6808048466228
4
- data.tar.gz: d14d318efb407d1a927cb2c5ac745081531c307f011d873965793c77ec0fd1b6
3
+ metadata.gz: 1880df41008713100967696d1bf1695b1e830851877f0ed5340f1f3f75cabd5e
4
+ data.tar.gz: 2a540ed72557ad9f9ba32073a5e98ed9b22812c1d0b96feaec00eeed35494386
5
5
  SHA512:
6
- metadata.gz: 7a46edf78463d75e523fd842397bae66e749dc58cd31d5fab8500dab706028850963d8e84f366706e0aef7807f45283d74ad785379f38b9b144d40b801681155
7
- data.tar.gz: 2eafe43d7acd092e04ff7b7dc1f2fcdd6dc7de2789a7f10e441bda3b72e838e13df86b8193ec7f3cbbd30a7f43a5880ce4c1629faf313ee3db3363695858aee0
6
+ metadata.gz: 7187004fa9e86545dcf81ebf70ea3059e04a0a8dcfe0143be3714ead2df3b9fc7d3e981641514f38192d69e57f826c85b14c46965f5d8171e3813e1bd9188574
7
+ data.tar.gz: d1e5e07ff88b722a944d78e22f1d3089214fb1ae9bed74c7bd42943700fc63bc096fc7916368513c83e660cb7936044dffd52e0f5c5b927d2618364a1411074e
data/Gemfile CHANGED
@@ -1,5 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  source 'https://rubygems.org'
4
- gem 'sinatra-cross_origin'
5
4
  gemspec
data/README.md CHANGED
@@ -13,9 +13,10 @@ 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
+ * gRPC [EXPERIMENTAL]
16
17
  * Lightweight
17
18
  * MIT license
18
-
19
+
19
20
  ## Table of Contents
20
21
 
21
22
  * [Basic Usage](#basic-usage)
@@ -26,7 +27,7 @@ TShield is an open source proxy for mocks API responses.
26
27
  * [Features](#features)
27
28
  * [Examples](#examples)
28
29
  * [Contributing](#contributing)
29
-
30
+
30
31
  ## Basic Usage
31
32
  ### Install
32
33
 
@@ -37,7 +38,7 @@ TShield is an open source proxy for mocks API responses.
37
38
  To run server execute this command
38
39
 
39
40
  tshield
40
-
41
+
41
42
  Default port is `4567`
42
43
 
43
44
  #### Command Line Options
@@ -102,7 +103,7 @@ To register stub into a session create an object with following attributes:
102
103
  * **session**: name of session.
103
104
  * **stubs**: an array with objects described above.
104
105
 
105
- ### Example of matching configuration
106
+ ### Example of HTTP matching configuration
106
107
 
107
108
  ```json
108
109
  [
@@ -163,7 +164,7 @@ To register stub into a session create an object with following attributes:
163
164
  ]
164
165
  ```
165
166
 
166
- ## Config options for VCR
167
+ ## Config options for HTTP VCR
167
168
  ```yaml
168
169
  request:
169
170
  timeout: 8
@@ -227,7 +228,7 @@ You can use TShield sessions to separate multiple scenarios for your mocks
227
228
  By default TShield save request/response into
228
229
 
229
230
  requests/<<domain_name>>/<<resource_with_param>>/<<http_verb>>/<<index_based.content and json>>
230
-
231
+
231
232
  If you start a session a folder with de **session_name** will be placed between **"requests/"** and **"<<domain_name>>"**
232
233
 
233
234
  ### Start TShield session
@@ -249,6 +250,45 @@ _DELETE_ to http://localhost:4567/sessions
249
250
  curl -X DELETE \
250
251
  http://localhost:4567/sessions
251
252
  ```
253
+ ### Append secondary TShield session
254
+ **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.**
255
+
256
+ _POST_ to http://localhost:4567/sessions?name=<<same_name>>
257
+
258
+ ```
259
+ curl -X POST \
260
+ 'http://localhost:4567/sessions/append?name=my_valid'
261
+ ```
262
+
263
+ ## [Experimental] Config options for gRPC
264
+
265
+ ```yaml
266
+ grpc:
267
+ port: 5678
268
+ proto_dir: 'proto'
269
+ services:
270
+ 'helloworld_services_pb':
271
+ module: 'Helloworld::Greeter'
272
+ hostname: '0.0.0.0:50051'
273
+ ```
274
+
275
+
276
+ ### Not Implemented Yet
277
+
278
+ - Matching
279
+
280
+ ### Configuration
281
+
282
+ First, generate ruby files from proto files. Use `grpc_tools_ruby_protoc`
283
+ present in the gem `grpc-tools`. Example:
284
+
285
+ `grpc_tools_ruby_protoc -I proto --ruby_out=proto --grpc_out=proto proto/<INPUT>.proto`
286
+
287
+ Call example in component_tests using [grpcurl](https://github.com/fullstorydev/grpcurl):
288
+
289
+ `grpcurl -plaintext -import-path component_tests/proto -proto helloworld.proto -d '{"name": "teste"}' localhost:5678 helloworld.Greeter/SayHello`
290
+
291
+ ### Using in VCR mode
252
292
 
253
293
  ## Custom controllers
254
294
 
data/Rakefile CHANGED
@@ -32,5 +32,5 @@ end
32
32
 
33
33
  task :server do
34
34
  $LOAD_PATH.unshift File.dirname('./lib/tshield.rb')
35
- exec 'bin/tshield'
35
+ Thread.new { exec 'bin/tshield' }
36
36
  end
@@ -5,14 +5,6 @@ require 'tshield/options'
5
5
  TShield::Options.init
6
6
 
7
7
  require 'tshield'
8
- tshield = Thread.new { TShield::Server.run! }
9
8
 
10
- configuration = TShield::Configuration.load_configuration
11
- (configuration.tcp_servers || []).each do |tcp_server|
12
- puts "initializing #{tcp_server['name']}"
13
- require "./servers/#{tcp_server['file']}"
14
- klass = Object.const_get(tcp_server['name'])
15
- Thread.new { klass.new.listen(tcp_server['port']) }
16
- end
17
-
18
- tshield.join
9
+ Thread.new { TShield::Grpc.run! }
10
+ TShield::Server.run!
@@ -0,0 +1,19 @@
1
+ ---
2
+ grpc:
3
+ port: 5678
4
+ proto_dir: 'proto'
5
+ services:
6
+ 'helloworld_services_pb':
7
+ module: 'Helloworld::Greeter'
8
+ hostname: '0.0.0.0:50051'
9
+ request:
10
+ timeout: 10
11
+ domains:
12
+ 'http://localhost:9090':
13
+ name: 'components'
14
+ skip_query_params:
15
+ - b
16
+ paths:
17
+ - /fake
18
+ - /users
19
+ - /resources
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'tshield/extensions/string_extensions'
4
4
  require 'tshield/options'
5
- require 'tshield/simple_tcp_server'
6
5
  require 'tshield/server'
6
+ require 'tshield/grpc'
7
7
 
8
8
  # TShield: API mocks for development and testing
9
9
  module TShield
@@ -103,6 +103,11 @@ module TShield
103
103
  session_path || '/sessions'
104
104
  end
105
105
 
106
+ def grpc
107
+ defaults = { 'port' => 5678, 'proto_dir' => 'proto', 'services' => {} }
108
+ defaults.merge(@grpc || {})
109
+ end
110
+
106
111
  def self.get_url_for_domain_by_path(path, config)
107
112
  config['paths'].select { |pattern| path =~ Regexp.new(pattern) }[0]
108
113
  end
@@ -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).response
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
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AppendSessionWithoutMainSessionError < RuntimeError
4
+ end
@@ -5,6 +5,14 @@ module StringExtensions
5
5
  def to_rack_name
6
6
  "HTTP_#{upcase.tr('-', '_')}"
7
7
  end
8
+
9
+ def underscore
10
+ gsub(/::/, '/')
11
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
12
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
13
+ .tr('-', '_')
14
+ .downcase
15
+ end
8
16
  end
9
17
 
10
18
  String.include StringExtensions
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: false
2
+
3
+ require 'grpc'
4
+
5
+ require 'tshield/configuration'
6
+ require 'tshield/grpc/vcr'
7
+
8
+ module TShield
9
+ module Grpc
10
+ module RequestHandler
11
+ include TShield::Grpc::VCR
12
+ def handler(method_name, request, parameters)
13
+ options = self.class.options
14
+ handler_in_vcr_mode(method_name, request, parameters, options)
15
+ end
16
+ end
17
+ def self.run!
18
+ @configuration = TShield::Configuration.singleton.grpc
19
+
20
+ lib_dir = File.join(Dir.pwd, @configuration['proto_dir'])
21
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
22
+
23
+ TShield.logger.info("loading proto files from #{lib_dir}")
24
+
25
+ bind = "0.0.0.0:#{@configuration['port']}"
26
+ TShield.logger.info("Starting gRPC server in #{bind}")
27
+
28
+ server = GRPC::RpcServer.new
29
+ server.add_http2_port(bind, :this_port_is_insecure)
30
+
31
+ services = load_services(@configuration['services'])
32
+ services.each do |class_service|
33
+ class_service.include RequestHandler
34
+ server.handle(class_service)
35
+ end
36
+
37
+ server.run_till_terminated_or_interrupted([1, 'int', 'SIGQUIT']) unless services.empty?
38
+ end
39
+
40
+ def self.load_services(services)
41
+ handlers = []
42
+ number_of_handlers = 0
43
+ services.each do |file, options|
44
+ require file
45
+
46
+ base = Object.const_get("#{options['module']}::Service")
47
+ number_of_handlers += 1
48
+
49
+ implementation = build_handler(base, base.rpc_descs, number_of_handlers, options)
50
+ handlers << implementation
51
+ end
52
+ handlers
53
+ end
54
+
55
+ def self.build_handler(base, descriptions, number_of_handlers, options)
56
+ handler = Class.new(base) do
57
+ class << self
58
+ attr_writer :options
59
+ attr_reader :options
60
+ end
61
+ descriptions.each do |service_name, description|
62
+ puts description
63
+ method_name = service_name.to_s.underscore.to_sym
64
+ define_method(method_name) do |request, parameters|
65
+ handler(__method__, request, parameters)
66
+ end
67
+ end
68
+ end
69
+ handler.options = options
70
+ TShield::Grpc.const_set "GrpcService#{number_of_handlers}", handler
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tshield/sessions'
4
+
5
+ module TShield
6
+ module Grpc
7
+ module VCR
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'}")
16
+ module_name = options['module']
17
+
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
24
+
25
+ TShield.logger.info("calling server to get response for #{request.to_json}")
26
+ client_class = Object.const_get("#{module_name}::Stub")
27
+ client_instance = client_class.new(options['hostname'], :this_channel_is_insecure)
28
+ response = client_instance.send(method_name, request)
29
+ save_request_and_response(path, request, response)
30
+ response
31
+ end
32
+
33
+ def saved_response(path)
34
+ response_file = File.join(path, 'response')
35
+ return false unless File.exist? response_file
36
+
37
+ content = JSON.parse File.open(response_file).read
38
+ response_class = File.open(File.join(path, 'response_class')).read.strip
39
+ Kernel.const_get(response_class).new(content)
40
+ end
41
+
42
+ def save_request_and_response(path, request, response)
43
+ save_request(path, request)
44
+ save_response(path, response)
45
+ end
46
+
47
+ def save_request(path, request)
48
+ file = File.open(File.join(path, 'original_request'), 'w')
49
+ file.puts request.to_json
50
+ file.close
51
+ end
52
+
53
+ def save_response(path, response)
54
+ file = File.open(File.join(path, 'response'), 'w')
55
+ file.puts response.to_json
56
+ file.close
57
+
58
+ response_class = File.open(File.join(path, 'response_class'), 'w')
59
+ response_class.puts response.class.to_s
60
+ response_class.close
61
+ end
62
+
63
+ def complete_path(module_name, method_name, request)
64
+ @session_name = (@session || {})[:name]
65
+ path = ['requests', 'grpc', @session_name, module_name, method_name.to_s, hexdigest(request)].compact
66
+ path
67
+ end
68
+
69
+ def create_destiny(module_name, method_name, request)
70
+ current_path = []
71
+
72
+ path = complete_path(module_name, method_name, request)
73
+ TShield.logger.info("using path #{path}")
74
+ path.each do |path|
75
+ current_path << path
76
+ destiny = File.join current_path
77
+ Dir.mkdir destiny unless File.exist? destiny
78
+ end
79
+ path
80
+ end
81
+
82
+ def hexdigest(request)
83
+ Digest::SHA1.hexdigest request.to_json
84
+ end
85
+ end
86
+ end
87
+ end
@@ -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
 
@@ -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)
@@ -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
- if exists
39
- response.original = false
40
- resp = response
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
- resp = original_response
52
+ @vcr_response = original_response
47
53
  end
48
54
 
49
55
  configuration.get_after_filters(domain).each do |filter|
50
- resp = filter.new.filter(resp)
56
+ @vcr_response = filter.new.filter(@vcr_response)
51
57
  end
52
- resp
53
58
  end
54
59
 
55
- def response
56
- @response ||= TShield::Response.new(saved_content['body'],
57
- saved_content['headers'] || [],
58
- saved_content['status'] || 200)
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
- return @saved_content if @saved_content
94
-
95
- @saved_content = JSON.parse(File.open(headers_destiny).read)
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 exists
105
- file_exists && configuration.cache_request?(domain)
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
@@ -57,7 +57,6 @@ module TShield
57
57
 
58
58
  def self.run!
59
59
  register_resources
60
- require 'byebug'
61
60
  super
62
61
  end
63
62
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'byebug'
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
- sessions[normalize_ip(ip)] = { name: name, counter: TShield::Counter.new }
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
@@ -5,7 +5,7 @@ module TShield
5
5
  class Version
6
6
  MAJOR = 0
7
7
  MINOR = 11
8
- PATCH = 15
8
+ PATCH = 20
9
9
  PRE = 0
10
10
 
11
11
  class << self
@@ -15,9 +15,4 @@ require 'webmock/rspec'
15
15
  require 'tshield/extensions/string_extensions'
16
16
 
17
17
  RSpec.configure do |config|
18
- config.before(:each) do
19
- allow(File).to receive(:join).and_return(
20
- 'spec/tshield/fixtures/config/tshield.yml'
21
- )
22
- end
23
18
  end
@@ -35,6 +35,12 @@ describe TShield::Configuration do
35
35
  )
36
36
  end
37
37
 
38
+ context 'on grpc configuration' do
39
+ it 'recover server port' do
40
+ expect(@configuration.grpc['port']).to(eq(5678))
41
+ end
42
+ end
43
+
38
44
  context 'on load filters' do
39
45
  it 'recover filters for a domain' do
40
46
  expect(@configuration.get_filters('example.org')).to eq([ExampleFilter])
@@ -72,4 +78,17 @@ describe TShield::Configuration do
72
78
  expect { TShield::Configuration.singleton }.to raise_error RuntimeError
73
79
  end
74
80
  end
81
+
82
+ context 'on config exists without grpc entry' do
83
+ before :each do
84
+ options_instance = double
85
+ allow(options_instance).to receive(:configuration_file)
86
+ .and_return('spec/tshield/fixtures/config/tshield-without-grpc.yml')
87
+ allow(TShield::Options).to receive(:instance).and_return(options_instance)
88
+ @configuration = TShield::Configuration.singleton
89
+ end
90
+ it 'should set default value for port' do
91
+ expect(@configuration.grpc).to eql('port' => 5678, 'proto_dir' => 'proto', 'services' => {})
92
+ end
93
+ end
75
94
  end
@@ -0,0 +1,17 @@
1
+ ---
2
+ request:
3
+ timeout: 0
4
+ domains:
5
+ 'example.org':
6
+ name: 'example.org'
7
+ filters:
8
+ - 'ExampleFilter'
9
+ paths:
10
+ - '/api/one'
11
+ - '/api/two'
12
+ skip_query_params:
13
+ - 'a'
14
+ 'example.com':
15
+ name: 'example.com'
16
+ paths:
17
+ - '/api/three'
@@ -1,4 +1,6 @@
1
1
  ---
2
+ grpc:
3
+ port: 5678
2
4
  request:
3
5
  timeout: 0
4
6
  domains:
@@ -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
  ]
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TestServices
4
+ class Service
5
+ def self.rpc_descs
6
+ { 'ServiceMethod' => {} }
7
+ end
8
+ end
9
+
10
+ class Stub
11
+ def initialize(attributes, options = {}); end
12
+ end
13
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ require 'tshield/grpc'
6
+
7
+ describe TShield::Grpc do
8
+ context 'on load services' do
9
+ before :each do
10
+ lib_dir = File.join(__dir__, 'fixtures/proto')
11
+ $LOAD_PATH.unshift(lib_dir) unless $LOAD_PATH.include?(lib_dir)
12
+
13
+ @services = {
14
+ 'test_services_pb' => { 'module' => 'TestServices', 'hostname' => '0.0.0.0:5678' }
15
+ }
16
+ end
17
+
18
+ it 'should implement a service from options' do
19
+ implementation = TShield::Grpc.load_services(@services).first
20
+ instance = implementation.new
21
+ expect(instance.respond_to?(:service_method)).to be_truthy
22
+ end
23
+ end
24
+ end
@@ -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
@@ -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
@@ -25,6 +25,8 @@ Gem::Specification.new do |s|
25
25
  s.required_ruby_version = '>= 2.3'
26
26
 
27
27
  s.add_dependency('byebug', '~> 11.0', '>= 11.0.1')
28
+ s.add_dependency('grpc', '~> 1.28', '>= 1.28.0')
29
+ s.add_dependency('grpc-tools', '~> 1.28', '>= 1.28.0')
28
30
  s.add_dependency('httparty', '~> 0.14', '>= 0.14.0')
29
31
  s.add_dependency('json', '~> 2.0', '>= 2.0')
30
32
  s.add_dependency('puma', '~> 4.3', '>= 4.3.3')
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.11.15.0
4
+ version: 0.11.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Diego Rubin
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-07-08 00:00:00.000000000 Z
12
+ date: 2020-07-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: byebug
@@ -31,6 +31,46 @@ dependencies:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
33
  version: 11.0.1
34
+ - !ruby/object:Gem::Dependency
35
+ name: grpc
36
+ requirement: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.28.0
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.28'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: 1.28.0
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.28'
54
+ - !ruby/object:Gem::Dependency
55
+ name: grpc-tools
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.28.0
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '1.28'
64
+ type: :runtime
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: 1.28.0
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.28'
34
74
  - !ruby/object:Gem::Dependency
35
75
  name: httparty
36
76
  requirement: !ruby/object:Gem::Requirement
@@ -376,6 +416,7 @@ files:
376
416
  - README.md
377
417
  - Rakefile
378
418
  - bin/tshield
419
+ - config/tshield.yml
379
420
  - lib/tshield.rb
380
421
  - lib/tshield/after_filter.rb
381
422
  - lib/tshield/before_filter.rb
@@ -385,7 +426,10 @@ files:
385
426
  - lib/tshield/controllers/requests.rb
386
427
  - lib/tshield/controllers/sessions.rb
387
428
  - lib/tshield/counter.rb
429
+ - lib/tshield/errors.rb
388
430
  - lib/tshield/extensions/string_extensions.rb
431
+ - lib/tshield/grpc.rb
432
+ - lib/tshield/grpc/vcr.rb
389
433
  - lib/tshield/logger.rb
390
434
  - lib/tshield/matching/filters.rb
391
435
  - lib/tshield/options.rb
@@ -395,18 +439,21 @@ files:
395
439
  - lib/tshield/response.rb
396
440
  - lib/tshield/server.rb
397
441
  - lib/tshield/sessions.rb
398
- - lib/tshield/simple_tcp_server.rb
399
442
  - lib/tshield/version.rb
400
443
  - spec/spec_helper.rb
401
444
  - spec/tshield/after_filter_spec.rb
402
445
  - spec/tshield/configuration_spec.rb
403
446
  - spec/tshield/controllers/requests_spec.rb
447
+ - spec/tshield/fixtures/config/tshield-without-grpc.yml
404
448
  - spec/tshield/fixtures/config/tshield.yml
405
449
  - spec/tshield/fixtures/filters/example_filter.rb
406
450
  - spec/tshield/fixtures/matching/example.json
451
+ - spec/tshield/fixtures/proto/test_services_pb.rb
452
+ - spec/tshield/grpc_spec.rb
407
453
  - spec/tshield/options_spec.rb
408
454
  - spec/tshield/request_matching_spec.rb
409
455
  - spec/tshield/request_vcr_spec.rb
456
+ - spec/tshield/sessions_spec.rb
410
457
  - tshield.gemspec
411
458
  homepage: https://github.com/diegorubin/tshield
412
459
  licenses:
@@ -434,11 +481,15 @@ summary: Proxy for mocks API responses
434
481
  test_files:
435
482
  - spec/spec_helper.rb
436
483
  - spec/tshield/request_matching_spec.rb
484
+ - spec/tshield/grpc_spec.rb
437
485
  - spec/tshield/configuration_spec.rb
486
+ - spec/tshield/sessions_spec.rb
438
487
  - spec/tshield/request_vcr_spec.rb
439
488
  - spec/tshield/controllers/requests_spec.rb
440
489
  - spec/tshield/options_spec.rb
441
490
  - spec/tshield/after_filter_spec.rb
442
491
  - spec/tshield/fixtures/matching/example.json
492
+ - spec/tshield/fixtures/proto/test_services_pb.rb
443
493
  - spec/tshield/fixtures/filters/example_filter.rb
444
494
  - spec/tshield/fixtures/config/tshield.yml
495
+ - spec/tshield/fixtures/config/tshield-without-grpc.yml
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'socket'
4
-
5
- module TShield
6
- class SimpleTCPServer
7
- def initialize
8
- @running = true
9
- end
10
-
11
- def on_connect(_client)
12
- raise 'should implement method on_connect'
13
- end
14
-
15
- def close
16
- @running = false
17
- end
18
-
19
- def listen(port)
20
- puts "listening #{port}"
21
- @server = TCPServer.new(port)
22
- while @running
23
- client = @server.accept
24
- on_connect(client)
25
- end
26
- end
27
- end
28
- end