Skip to content

Commit 5aca966

Browse files
committed
feat: Dynamically retrieve pacts for a given provider
1 parent 233d860 commit 5aca966

File tree

11 files changed

+803
-76
lines changed

11 files changed

+803
-76
lines changed

lib/pact/hal/entity.rb

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
require 'uri'
2+
require 'delegate'
3+
require 'pact/hal/link'
4+
5+
module Pact
6+
module Hal
7+
class Entity
8+
def initialize(data, http_client, response = nil)
9+
@data = data
10+
@links = @data.fetch("_links", {})
11+
@client = http_client
12+
@response = response
13+
end
14+
15+
def get(key, *args)
16+
_link(key).get(*args)
17+
end
18+
19+
def post(key, *args)
20+
_link(key).post(*args)
21+
end
22+
23+
def put(key, *args)
24+
_link(key).put(*args)
25+
end
26+
27+
def can?(key)
28+
@links.key? key.to_s
29+
end
30+
31+
def follow(key, http_method, *args)
32+
Link.new(@links[key].merge(method: http_method), @client).run(*args)
33+
end
34+
35+
def _link(key)
36+
if @links[key]
37+
Link.new(@links[key], @client)
38+
else
39+
nil
40+
end
41+
end
42+
43+
def success?
44+
true
45+
end
46+
47+
def response
48+
@response
49+
end
50+
51+
def fetch(key)
52+
@links[key]
53+
end
54+
55+
def method_missing(method_name, *args, &block)
56+
if @data.key?(method_name.to_s)
57+
@data[method_name.to_s]
58+
elsif @links.key?(method_name)
59+
Link.new(@links[method_name], @client).run(*args)
60+
else
61+
super
62+
end
63+
end
64+
65+
def respond_to_missing?(method_name, include_private = false)
66+
@data.key?(method_name) || @links.key?(method_name)
67+
end
68+
end
69+
70+
class ErrorEntity < Entity
71+
def success?
72+
false
73+
end
74+
end
75+
end
76+
end

lib/pact/hal/http_client.rb

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
require 'pact/retry'
2+
3+
module Pact
4+
module Hal
5+
class HttpClient
6+
attr_accessor :username, :password
7+
8+
def initialize options
9+
@username = options[:username]
10+
@password = options[:password]
11+
end
12+
13+
def get href, params = {}
14+
uri = URI(href)
15+
perform_request(create_request(uri, 'Get'), uri)
16+
end
17+
18+
def put href, body = nil
19+
uri = URI(href)
20+
perform_request(create_request(uri, 'Put', body), uri)
21+
end
22+
23+
def post href, body = nil
24+
uri = URI(href)
25+
perform_request(create_request(uri, 'Post', body), uri)
26+
end
27+
28+
def create_request uri, http_method, body = nil
29+
path = uri.path.size == 0 ? "/" : uri.path
30+
request = Net::HTTP.const_get(http_method).new(path)
31+
request['Content-Type'] = "application/json"
32+
request['Accept'] = "application/hal+json"
33+
request.body = body if body
34+
request.basic_auth username, password if username
35+
request
36+
end
37+
38+
def perform_request request, uri
39+
options = {:use_ssl => uri.scheme == 'https'}
40+
response = Retry.until_true do
41+
Net::HTTP.start(uri.host, uri.port, :ENV, options) do |http|
42+
http.request request
43+
end
44+
end
45+
Response.new(response)
46+
end
47+
48+
class Response < SimpleDelegator
49+
def body
50+
bod = __getobj__().body
51+
if bod && bod != ''
52+
JSON.parse(bod)
53+
else
54+
nil
55+
end
56+
end
57+
58+
def success?
59+
__getobj__().code.start_with?("2")
60+
end
61+
end
62+
end
63+
end
64+
end

lib/pact/hal/link.rb

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
require 'uri'
2+
require 'delegate'
3+
4+
module Pact
5+
module Hal
6+
class Link
7+
attr_reader :request_method, :href
8+
9+
def initialize(attrs, http_client)
10+
@attrs = attrs
11+
@request_method = attrs.fetch(:method, :get).to_sym
12+
@href = attrs.fetch('href')
13+
@http_client = http_client
14+
end
15+
16+
def run(payload = nil)
17+
response = case request_method
18+
when :get
19+
get(payload)
20+
when :put
21+
put(payload)
22+
when :post
23+
post(payload)
24+
end
25+
end
26+
27+
def get(payload = {})
28+
wrap_response(@http_client.get(href, payload))
29+
end
30+
31+
def put(payload = nil)
32+
wrap_response(@http_client.put(href, payload ? JSON.dump(payload) : nil))
33+
end
34+
35+
def post(payload = nil)
36+
wrap_response(@http_client.post(href, payload ? JSON.dump(payload) : nil))
37+
end
38+
39+
def expand(params)
40+
expanded_url = expand_url(params, href)
41+
new_attrs = @attrs.merge('href' => expanded_url)
42+
Link.new(new_attrs, @http_client)
43+
end
44+
45+
private
46+
47+
def wrap_response(http_response)
48+
require 'pact/hal/entity' # avoid circular reference
49+
if http_response.success?
50+
Entity.new(http_response.body, @http_client, http_response)
51+
else
52+
ErrorEntity.new(http_response.body, @http_client, http_response)
53+
end
54+
end
55+
56+
def expand_url(params, url)
57+
new_url = url
58+
params.each do | key, value |
59+
new_url = new_url.gsub('{' + key.to_s + '}', value)
60+
end
61+
new_url
62+
end
63+
end
64+
end
65+
end

lib/pact/pact_broker/fetch_pacts.rb

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
require 'pact/hal/entity'
2+
require 'pact/hal/http_client'
3+
4+
module Pact
5+
module PactBroker
6+
class FetchPacts
7+
attr_reader :provider, :tags, :broker_base_url, :basic_auth_options, :http_client, :pact_entity
8+
9+
LATEST_PROVIDER_TAG_RELATION = 'pb:latest-provider-pacts-with-tag'.freeze
10+
LATEST_PROVIDER_RELATION = 'pb:latest-provider-pacts'.freeze
11+
PACTS = 'pacts'.freeze
12+
HREF = 'href'.freeze
13+
14+
def initialize(provider, tags, broker_base_url, basic_auth_options)
15+
@provider = provider
16+
@tags = tags
17+
18+
@http_client = Pact::Hal::HttpClient.new(basic_auth_options)
19+
@response = @http_client.get(broker_base_url)
20+
@pact_entity = Pact::Hal::Entity.new(@response.body, http_client)
21+
end
22+
23+
def self.call(provider, tags = nil, broker_base_url, basic_auth_options)
24+
new(provider, tags, broker_base_url, basic_auth_options).call
25+
end
26+
27+
def call
28+
pact_urls = []
29+
if tags
30+
link = pact_entity._link(LATEST_PROVIDER_TAG_RELATION)
31+
tags.each do |tag|
32+
link_by_tag = link.expand(provider: provider, tag: tag).get
33+
get_pact_urls(link_by_tag, pact_urls)
34+
end
35+
else
36+
link = pact_entity._link(LATEST_PROVIDER_RELATION)
37+
link_by_provider = link.expand(provider: provider).get
38+
get_pact_urls(link_by_provider, pact_urls)
39+
end
40+
pact_urls
41+
end
42+
43+
private
44+
45+
def get_pact_urls(link_by_provider, pact_urls)
46+
pacts = link_by_provider.fetch(PACTS)
47+
pacts.each do |pact|
48+
pact_urls.push(pact[HREF])
49+
end
50+
end
51+
end
52+
end
53+
end

0 commit comments

Comments
 (0)