Skip to content

Commit 615025e

Browse files
committed
feat(tagged-pact-versions): add endpoint to view and delete a collection of pact versions by tag
1 parent c4f23bc commit 615025e

14 files changed

+316
-15
lines changed

lib/pact_broker/api.rb

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module PactBroker
1111

1212
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'versions'], Api::Resources::PactVersions, {resource_name: "pact_publications"}
1313
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'versions', :consumer_version_number], Api::Resources::Pact, {resource_name: "pact_publication", deprecated: true} # Not the standard URL, but keep for backwards compatibility
14+
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'tag', :tag], Api::Resources::TaggedPactVersions, {resource_name: "tagged_pact_publications"}
1415

1516
# Pacts
1617
add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'version', :consumer_version_number], Api::Resources::Pact, {resource_name: "pact_publication"}

lib/pact_broker/api/decorators/pact_version_decorator.rb

-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,9 @@
33
require 'pact_broker/api/decorators/timestamps'
44

55
module PactBroker
6-
76
module Api
8-
97
module Decorators
10-
118
class PactVersionDecorator < BaseDecorator
12-
139
include Timestamps
1410

1511
property :consumer_version, as: :consumerVersion, embedded: true, decorator: EmbeddedVersionDecorator
@@ -20,7 +16,6 @@ class PactVersionDecorator < BaseDecorator
2016
title: represented.name
2117
}
2218
end
23-
2419
end
2520
end
2621
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
require_relative 'base_decorator'
2+
require_relative 'pact_version_decorator'
3+
4+
module PactBroker
5+
module Api
6+
module Decorators
7+
class TaggedPactVersionsDecorator < BaseDecorator
8+
9+
collection :entries, as: :pacts, embedded: true, :extend => PactBroker::Api::Decorators::PactVersionDecorator
10+
11+
link :self do | context |
12+
{
13+
href: context[:resource_url],
14+
title: "All versions of the pact between #{context[:consumer_name]} and #{context[:provider_name]} with tag #{context[:tag]}"
15+
}
16+
end
17+
18+
link :'pb:consumer' do | context |
19+
{
20+
href: pacticipant_url(context[:base_url], OpenStruct.new(name: context[:consumer_name])),
21+
title: "Consumer",
22+
name: context[:consumer_name]
23+
}
24+
end
25+
26+
link :'pb:provider' do | context |
27+
{
28+
href: pacticipant_url(context[:base_url], OpenStruct.new(name: context[:provider_name])),
29+
title: "Provider",
30+
name: context[:provider_name]
31+
}
32+
end
33+
34+
links :'pb:pact-versions' do | context |
35+
represented.collect do | pact |
36+
{
37+
:href => pact_url(context[:base_url], pact),
38+
:title => "Pact version",
39+
:name => pact.version_and_updated_date
40+
}
41+
end
42+
end
43+
end
44+
end
45+
end
46+
end

lib/pact_broker/api/resources/index.rb

+6
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ def links
3838
title: 'Latest pact versions',
3939
templated: false
4040
},
41+
'pb:tagged-pact-versions' =>
42+
{
43+
href: base_url + '/pacts/provider/{provider}/consumer/{consumer}/tag/{tag}',
44+
title: 'All versions of a pact for a given consumer, provider and consumer version tag',
45+
templated: false
46+
},
4147
'pb:pacticipants' =>
4248
{
4349
href: base_url + '/pacticipants',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require 'pact_broker/api/resources/base_resource'
2+
require 'pact_broker/configuration'
3+
require 'pact_broker/api/decorators/tagged_pact_versions_decorator'
4+
5+
module PactBroker
6+
module Api
7+
module Resources
8+
class TaggedPactVersions < BaseResource
9+
10+
def content_types_provided
11+
[["application/hal+json", :to_json]]
12+
end
13+
14+
def allowed_methods
15+
["GET", "DELETE", "OPTIONS"]
16+
end
17+
18+
def resource_exists?
19+
pacticipant_service.find_pacticipant_by_name(consumer_name) &&
20+
pacticipant_service.find_pacticipant_by_name(provider_name)
21+
end
22+
23+
def to_json
24+
PactBroker::Api::Decorators::TaggedPactVersionsDecorator.new(pacts).to_json(user_options: decorator_context(identifier_from_path))
25+
end
26+
27+
def delete_resource
28+
pact_service.delete_all_pact_versions_between consumer_name, and: provider_name, tag: identifier_from_path[:tag]
29+
true
30+
end
31+
32+
def pacts
33+
pact_service.find_all_pact_versions_between consumer_name, and: provider_name, tag: identifier_from_path[:tag]
34+
end
35+
end
36+
end
37+
end
38+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Tagged pact versions
2+
3+
Allowed methods: `GET`, `DELETE`
4+
5+
Lists all the pact versions with the specified consumer, provider and consumer version tag.
6+
7+
Send a `DELETE` request to the resource to batch delete all the versions.

lib/pact_broker/pacts/repository.rb

+17-4
Original file line numberDiff line numberDiff line change
@@ -75,15 +75,17 @@ def delete_by_version_id version_id
7575
end
7676

7777
def find_all_pact_versions_between consumer_name, options
78-
provider_name = options.fetch(:and)
79-
LatestPactPublicationsByConsumerVersion
78+
find_all_database_versions_between(consumer_name, options)
8079
.eager(:tags)
81-
.consumer(consumer_name)
82-
.provider(provider_name)
8380
.reverse_order(:consumer_version_order)
8481
.collect(&:to_domain)
8582
end
8683

84+
def delete_all_pact_versions_between consumer_name, options
85+
ids = find_all_database_versions_between(consumer_name, options).select_for_subquery(:id)
86+
PactPublication.where(id: ids).delete
87+
end
88+
8789
def find_latest_pact_versions_for_provider provider_name, tag = nil
8890
if tag
8991
LatestTaggedPactPublications.provider(provider_name).order_ignore_case(:consumer_name).where(tag_name: tag).collect(&:to_domain)
@@ -255,6 +257,17 @@ def create_pact_version consumer_id, provider_id, sha, json_content
255257
pact_version = PactVersion.new(consumer_id: consumer_id, provider_id: provider_id, sha: sha, content: json_content)
256258
pact_version.save
257259
end
260+
261+
def find_all_database_versions_between(consumer_name, options)
262+
provider_name = options.fetch(:and)
263+
264+
query = LatestPactPublicationsByConsumerVersion
265+
.consumer(consumer_name)
266+
.provider(provider_name)
267+
268+
query = query.tag(options[:tag]) if options[:tag]
269+
query
270+
end
258271
end
259272
end
260273
end

lib/pact_broker/pacts/service.rb

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ def find_all_pact_versions_between consumer, options
6868
pact_repository.find_all_pact_versions_between consumer, options
6969
end
7070

71+
def delete_all_pact_versions_between consumer, options
72+
pact_repository.delete_all_pact_versions_between consumer, options
73+
end
74+
7175
def find_latest_pact_versions_for_provider provider_name, options = {}
7276
pact_repository.find_latest_pact_versions_for_provider provider_name, options[:tag]
7377
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
describe "retrieving tagged pact versions" do
2+
3+
let(:path) { "/pacts/provider/Provider/consumer/Consumer/tag/prod"}
4+
5+
subject { get path; last_response }
6+
let(:json_response_body) { JSON.parse(subject.body, symbolize_names: true) }
7+
8+
before do
9+
TestDataBuilder.new
10+
.create_consumer("Consumer")
11+
.create_provider("Provider")
12+
.create_consumer_version("1.2.3")
13+
.create_consumer_version_tag("prod")
14+
.create_pact
15+
.create_consumer_version("4.5.6")
16+
.create_pact
17+
end
18+
19+
it "returns the latest tagged pact version" do
20+
expect(json_response_body[:_links][:self][:href]).to end_with("1.2.3")
21+
end
22+
end

spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb

-3
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@
22
require 'pact_broker/api/decorators/pact_version_decorator'
33

44
module PactBroker
5-
65
module Api
7-
86
module Decorators
9-
107
describe PactVersionDecorator do
118

129
let(:json_content) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
require 'pact_broker/api/decorators/tagged_pact_versions_decorator'
2+
3+
def register_fixture name
4+
yield
5+
end
6+
7+
8+
module PactBroker
9+
module Api
10+
module Decorators
11+
describe TaggedPactVersionsDecorator do
12+
13+
let(:user_options) do
14+
register_fixture(:tagged_pact_versions_decorator_args) do
15+
{
16+
consumer_name: "Foo",
17+
provider_name: "Bar"
18+
}
19+
end
20+
end
21+
22+
let(:pact_versions) { [pact_version] }
23+
let(:pact_version) do
24+
instance_double("PactBroker::Domain::Pact")
25+
end
26+
27+
let(:decorator) { TaggedPactVersionsDecorator.new(pact_versions) }
28+
let(:json) { decorator.to_json(user_options: user_options) }
29+
subject { JSON.parse(json) }
30+
31+
xit "" do
32+
subject(subject['_links']['pb:consumer']).to eq({})
33+
end
34+
35+
end
36+
end
37+
end
38+
end
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
require 'pact_broker/api/resources/tagged_pact_versions'
2+
3+
module PactBroker
4+
module Api
5+
module Resources
6+
describe TaggedPactVersions do
7+
include_context "stubbed services"
8+
9+
before do
10+
allow(pacticipant_service).to receive(:find_pacticipant_by_name).with("Foo").and_return(consumer)
11+
allow(pacticipant_service).to receive(:find_pacticipant_by_name).with("Bar").and_return(provider)
12+
end
13+
14+
let(:path) { "/pacts/provider/Bar/consumer/Foo/tag/prod" }
15+
let(:consumer) { double('Bar') }
16+
let(:provider) { double('Foo') }
17+
18+
context "GET" do
19+
before do
20+
allow(PactBroker::Api::Decorators::TaggedPactVersionsDecorator).to receive(:new).and_return(decorator)
21+
allow(pact_service).to receive(:find_all_pact_versions_between).and_return(pact_versions)
22+
end
23+
24+
let(:decorator) { instance_double(PactBroker::Api::Decorators::TaggedPactVersionsDecorator, to_json: 'json') }
25+
let(:pact_versions) { double('pacts') }
26+
27+
subject { get(path) }
28+
29+
let(:user_options) do
30+
{
31+
base_url: "http://example.org",
32+
resource_url: "http://example.org/pacts/provider/Bar/consumer/Foo/tag/prod",
33+
consumer_name: "Foo",
34+
provider_name: "Bar",
35+
tag: "prod"
36+
}
37+
end
38+
39+
it "finds all the pacts with the given consumer/provider/tag" do
40+
expect(pact_service).to receive(:find_all_pact_versions_between).with("Foo", and: "Bar", tag: "prod")
41+
subject
42+
end
43+
44+
it "returns a 200 OK hal+json response" do
45+
expect(subject).to be_a_hal_json_success_response
46+
end
47+
48+
it "creates a JSON representation of the pact versions" do
49+
expect(PactBroker::Api::Decorators::TaggedPactVersionsDecorator).to receive(:new).with(pact_versions)
50+
expect(decorator).to receive(:to_json).with(user_options: hash_including(user_options))
51+
subject
52+
end
53+
54+
it "returns the JSON representation of the pact versions" do
55+
expect(subject.body).to eq 'json'
56+
end
57+
58+
context "with the consumer or provider do not exist" do
59+
let(:consumer) { nil }
60+
61+
it "returns a 404" do
62+
expect(subject).to be_a_404_response
63+
end
64+
end
65+
end
66+
67+
context "DELETE" do
68+
before do
69+
allow(pact_service).to receive(:delete_all_pact_versions_between)
70+
end
71+
72+
subject { delete(path) }
73+
74+
it "deletes all the pacts with the given consumer/provider/tag" do
75+
expect(pact_service).to receive(:delete_all_pact_versions_between).with("Foo", and: "Bar", tag: "prod")
76+
subject
77+
end
78+
79+
it "returns a 204" do
80+
expect(subject.status).to eq 204
81+
end
82+
end
83+
end
84+
end
85+
end
86+
end

0 commit comments

Comments
 (0)