pacto-server 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 118b6d643b8f3a10e313cf65837f2f58d03f0d44
4
+ data.tar.gz: 063e07bd7f75b897ffe798c9591f43d67ac624bd
5
+ SHA512:
6
+ metadata.gz: cef2e211096315d6b92487bddc64bb3401f2ece8fe4cc777314ab798125117073fe0084189f4b0c09be572f77033bc83a6a1630872a8d6f934f5ed820950eb68
7
+ data.tar.gz: 54a2f790dbe2489fc0470b285f65cf6e2de8ca584e7f0a1067be6bf77e03af208530cf28dda580e19d953cdbd578f9fc24510378b8ed06907d45111ae05f3e81
data/bin/pacto-server ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'goliath/api'
3
+ require 'goliath/runner'
4
+ require 'pacto/server'
5
+
6
+ runner = Goliath::Runner.new(ARGV, Pacto::Server::API.new(:pwd => Dir.pwd))
7
+ runner.log_file = File.expand_path(runner.log_file, Dir.pwd) if runner.log_file
8
+ runner.app = Goliath::Rack::Builder.build(Pacto::Server::API, runner.api)
9
+ runner.run
@@ -0,0 +1,102 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'pacto/server/settings'
3
+
4
+ module Pacto
5
+ module Server
6
+ class API < Goliath::API
7
+ include Pacto::Server::Settings
8
+ use ::Rack::ContentLength
9
+
10
+ def initialize(*args)
11
+ @original_pwd = Dir.pwd
12
+ super
13
+ end
14
+
15
+ def on_headers(env, headers)
16
+ env.logger.debug 'receiving headers: ' + headers.inspect
17
+ env['client-headers'] = headers
18
+ end
19
+
20
+ def on_body(env, data)
21
+ env.logger.debug 'received data: ' + data
22
+ (env['async-body'] ||= '') << data
23
+ end
24
+
25
+ def response(env)
26
+ log_request(env)
27
+ req = prepare_pacto_request(env)
28
+ env.logger.info "sending: #{req}"
29
+ resp = Pacto::Consumer::FaradayDriver.new.execute(req)
30
+ process_pacto_response resp, env
31
+ rescue => e
32
+ backtrace = e.backtrace.join("\n")
33
+ env.logger.warn "responding with error: #{e.message}, backtrace: #{backtrace}"
34
+ [500, {}, e.message]
35
+ end
36
+
37
+ private
38
+
39
+ def log_request(env)
40
+ method = env['REQUEST_METHOD'].upcase
41
+ env.logger.info "received: #{method} #{env['REQUEST_URI']} with headers #{env['client-headers']}"
42
+ end
43
+
44
+ def prepare_pacto_request(env)
45
+ PactoRequest.new(
46
+ body: env['async-body'],
47
+ headers: filter_request_headers(env),
48
+ method: env['REQUEST_METHOD'].downcase.to_sym,
49
+ uri: determine_proxy_uri(env)
50
+ )
51
+ end
52
+
53
+ def process_pacto_response(resp, _env)
54
+ code = resp.status
55
+ safe_response_headers = normalize_headers(resp.headers).reject { |k, _v| %w(connection content-encoding content-length transfer-encoding).include? k.downcase }
56
+ body = proxy_rewrite(resp.body)
57
+ env.logger.info "received response: #{resp.inspect}"
58
+ env.logger.debug "response body: #{body}"
59
+ [code, safe_response_headers, body]
60
+ end
61
+
62
+ def determine_proxy_uri(env)
63
+ path = env[Goliath::Request::REQUEST_PATH]
64
+ if env.config[:backend_host]
65
+ uri = Addressable::URI.heuristic_parse("#{env.config[:backend_host]}#{path}")
66
+ else
67
+ host = env['HTTP_HOST']
68
+ host.gsub!('.dev', '.com') if env.config[:strip_dev]
69
+ uri = Addressable::URI.heuristic_parse("https://#{host}#{path}")
70
+ uri.port = nil if env.config[:strip_port]
71
+ end
72
+ uri
73
+ end
74
+
75
+ def filter_request_headers(env)
76
+ headers = env['client-headers']
77
+ safe_headers = headers.reject { |k, _v| %w(host content-length transfer-encoding).include? k.downcase }
78
+ env.logger.debug "filtered headers: #{safe_headers}"
79
+ safe_headers
80
+ end
81
+
82
+ def port
83
+ env.config[:port]
84
+ end
85
+
86
+ def normalize_headers(headers)
87
+ headers.each_with_object({}) do |elem, res|
88
+ key = elem.first.dup
89
+ value = elem.last
90
+ key.gsub!('_', '-')
91
+ key = key.split('-').map(&:capitalize).join '-'
92
+ res[key] = value
93
+ end
94
+ end
95
+
96
+ def proxy_rewrite(body)
97
+ # FIXME: How I usually deal with rels, but others may not want this behavior.
98
+ body.gsub('.com', ".dev:#{port}").gsub(/https\:([\w\-\.\\\/]+).dev/, 'http:\1.dev')
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,51 @@
1
+ # -*- encoding : utf-8 -*-
2
+ def token_map
3
+ if File.readable? '.tokens.json'
4
+ MultiJson.load(File.read '.tokens.json')
5
+ else
6
+ {}
7
+ end
8
+ end
9
+
10
+ def prepare_contracts(contracts)
11
+ contracts.stub_providers if options[:stub]
12
+ end
13
+
14
+ config[:backend_host] = options[:backend_host]
15
+ config[:strip_port] = options[:strip_port]
16
+ config[:strip_dev] = options[:strip_dev]
17
+ config[:port] = port
18
+ contracts_path = options[:directory] || File.expand_path('contracts', Dir.pwd)
19
+ Pacto.configure do |pacto_config|
20
+ pacto_config.logger = options[:pacto_logger] || logger
21
+ pacto_config.loggerl.log_level = config[:pacto_log_level] if config[:pacto_log_level]
22
+ pacto_config.contracts_path = contracts_path
23
+ pacto_config.strict_matchers = options[:strict]
24
+ pacto_config.generator_options = {
25
+ schema_version: :draft3,
26
+ token_map: token_map
27
+ }
28
+ pacto_config.stenographer_log_file = options[:stenographer_log_file]
29
+ end
30
+
31
+ if options[:generate]
32
+ Pacto.generate!
33
+ logger.info 'Pacto generation mode enabled'
34
+ end
35
+
36
+ if options[:recursive_loading]
37
+ Dir["#{contracts_path}/*"].each do |host_dir|
38
+ host = File.basename host_dir
39
+ prepare_contracts Pacto.load_contracts(host_dir, "https://#{host}", options[:format])
40
+ end
41
+ else
42
+ host_pattern = options[:backend_host] || 'https://{server}'
43
+ prepare_contracts Pacto.load_contracts(contracts_path, host_pattern, options[:format])
44
+ end
45
+
46
+ Pacto.validate! if options[:validate]
47
+
48
+ if options[:live]
49
+ # WebMock.reset!
50
+ WebMock.allow_net_connect!
51
+ end
@@ -0,0 +1,29 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Pacto
3
+ module Server
4
+ module Settings
5
+ def options_parser(opts, options) # rubocop:disable MethodLength
6
+ options[:format] ||= :default
7
+ options[:strict] ||= false
8
+ options[:directory] ||= File.expand_path('contracts', @original_pwd)
9
+ options[:config] ||= File.expand_path('../config.rb', __FILE__)
10
+ options[:stenographer_log_file] ||= File.expand_path('pacto_stenographer.log', @original_pwd)
11
+ options[:strip_port] ||= true
12
+
13
+ opts.on('-l', '--live', 'Send requests to live services (instead of stubs)') { |_val| options[:live] = true }
14
+ opts.on('-f', '--format FORMAT', 'Contract format') { |val| options[:format] = val }
15
+ opts.on('--stub', 'Stub responses based on contracts') { |_val| options[:stub] = true }
16
+ opts.on('-g', '--generate', 'Generate Contracts from requests') { |_val| options[:generate] = true }
17
+ opts.on('-V', '--validate', 'Validate requests/responses against Contracts') { |_val| options[:validate] = true }
18
+ opts.on('-m', '--match-strict', 'Enforce strict request matching rules') { |_val| options[:strict] = true }
19
+ opts.on('-x', '--contracts_dir DIR', 'Directory that contains the contracts to be registered') { |val| options[:directory] = File.expand_path(val, @original_pwd) }
20
+ opts.on('-H', '--host HOST', 'Host of the real service, for generating or validating live requests') { |val| options[:backend_host] = val }
21
+ opts.on('-r', '--recursive-loading', 'Load contracts from folders named after the host to be stubbed') { |_val| options[:recursive_loading] = true }
22
+ opts.on('--strip-port', 'Strip the port from the request URI to build the proxied URI') { |_val| options[:strip_port] = true }
23
+ opts.on('--strip-dev', 'Strip .dev from the request domain to build the proxied URI') { |_val| options[:strip_dev] = true }
24
+ opts.on('--stenographer-log-file', 'Location for the stenographer log file') { |val| options[:stenographer_log_file] = val }
25
+ opts.on('--log-level [LEVEL]', [:debug, :info, :warn, :error, :fatal], 'Pacto log level ( debug, info, warn, error or fatal)') { |val| options[:pacto_log_level] = val }
26
+ end
27
+ end
28
+ end
29
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pacto-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.0.rc1
5
+ platform: ruby
6
+ authors:
7
+ - ThoughtWorks
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pacto
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0.rc1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0.rc1
27
+ - !ruby/object:Gem::Dependency
28
+ name: goliath
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: em-synchrony
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: em-http-request
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.1'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.1'
69
+ description: Pacto Server let's you run Pacto as a standalone server to arbitrate
70
+ contract disputes between a service provider and one or more consumers in any programming
71
+ language. It's Pacto beyond Ruby
72
+ email:
73
+ - pacto-gem@googlegroups.com
74
+ executables:
75
+ - pacto-server
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - bin/pacto-server
80
+ - lib/pacto/server/api.rb
81
+ - lib/pacto/server/config.rb
82
+ - lib/pacto/server/settings.rb
83
+ homepage: http://thoughtworks.github.io/pacto/
84
+ licenses:
85
+ - MIT
86
+ metadata: {}
87
+ post_install_message:
88
+ rdoc_options: []
89
+ require_paths:
90
+ - lib
91
+ required_ruby_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ required_rubygems_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">"
99
+ - !ruby/object:Gem::Version
100
+ version: 1.3.1
101
+ requirements: []
102
+ rubyforge_project:
103
+ rubygems_version: 2.4.2
104
+ signing_key:
105
+ specification_version: 4
106
+ summary: Polyglot Integration Contract Testing server
107
+ test_files: []