pacto-server 0.4.0.rc1

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 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: []