pact_broker 2.65.0 → 2.66.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/db/ddl_statements/latest_triggered_webhooks.rb +66 -0
  4. data/db/migrations/20180119_update_latest_triggered_webhooks.rb +5 -39
  5. data/db/migrations/20200930_update_latest_triggered_webhooks.rb +15 -0
  6. data/db/migrations/migration_helper.rb +10 -2
  7. data/lib/pact_broker/api/decorators/decorator_context.rb +2 -2
  8. data/lib/pact_broker/api/decorators/pagination_links.rb +34 -0
  9. data/lib/pact_broker/api/decorators/versions_decorator.rb +5 -1
  10. data/lib/pact_broker/api/pact_broker_urls.rb +8 -0
  11. data/lib/pact_broker/api/resources/index.rb +1 -1
  12. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +11 -1
  13. data/lib/pact_broker/api/resources/versions.rb +12 -1
  14. data/lib/pact_broker/db/clean.rb +100 -43
  15. data/lib/pact_broker/doc/views/pacticipant/versions.markdown +9 -0
  16. data/lib/pact_broker/domain/pacticipant.rb +4 -0
  17. data/lib/pact_broker/domain/tag.rb +2 -0
  18. data/lib/pact_broker/domain/version.rb +1 -0
  19. data/lib/pact_broker/feature_toggle.rb +8 -4
  20. data/lib/pact_broker/matrix/service.rb +3 -0
  21. data/lib/pact_broker/pacticipants/repository.rb +6 -5
  22. data/lib/pact_broker/pacticipants/service.rb +5 -18
  23. data/lib/pact_broker/pacts/latest_tagged_pact_publications.rb +15 -1
  24. data/lib/pact_broker/tags/repository.rb +2 -5
  25. data/lib/pact_broker/test/test_data_builder.rb +22 -2
  26. data/lib/pact_broker/ui/views/matrix/show.haml +8 -0
  27. data/lib/pact_broker/version.rb +1 -1
  28. data/spec/features/get_versions_spec.rb +8 -0
  29. data/spec/lib/pact_broker/api/decorators/versions_decorator_spec.rb +14 -9
  30. data/spec/lib/pact_broker/api/resources/default_base_resource_spec.rb +10 -4
  31. data/spec/lib/pact_broker/db/clean_spec.rb +72 -4
  32. data/spec/lib/pact_broker/feature_toggle_spec.rb +9 -1
  33. data/spec/lib/pact_broker/pacticipants/repository_spec.rb +8 -1
  34. data/spec/lib/pact_broker/pacts/latest_tagged_pact_publications_spec.rb +99 -0
  35. data/spec/lib/pact_broker/webhooks/repository_spec.rb +20 -0
  36. metadata +8 -2
@@ -0,0 +1,9 @@
1
+ # Pacticipant versions
2
+
3
+ Allowed methods: `GET`
4
+
5
+ Path: `/pacticipants/{pacticipant}/versions`
6
+
7
+ A list of pacticipant versions in order from newest to oldest.
8
+
9
+ To paginate, append `?pageNumber=x&pageSize=x` and follow the `next` relation until it is no longer present.
@@ -30,6 +30,10 @@ module PactBroker
30
30
  end
31
31
 
32
32
  def before_destroy
33
+ PactBroker::Pacts::PactPublication.where(provider: self).delete
34
+ PactBroker::Domain::Verification.where(consumer: self).or(provider: self).delete
35
+ PactBroker::Domain::Version.where(pacticipant: self).delete
36
+ PactBroker::Pacts::PactVersion.where(consumer: self).or(provider: self).delete
33
37
  PactBroker::Domain::Label.where(pacticipant: self).destroy
34
38
  super
35
39
  end
@@ -10,6 +10,8 @@ module PactBroker
10
10
  include PactBroker::Repositories::Helpers
11
11
  end
12
12
 
13
+ plugin :insert_ignore, identifying_columns: [:name, :version_id]
14
+
13
15
  unrestrict_primary_key
14
16
 
15
17
  associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
@@ -50,6 +50,7 @@ module PactBroker
50
50
  end
51
51
 
52
52
  def delete
53
+ PactBroker::Pacts::PactPublication.where(consumer_version: self).delete
53
54
  PactBroker::Domain::Tag.where(version: self).delete
54
55
  super
55
56
  end
@@ -1,7 +1,11 @@
1
1
  module PactBroker
2
2
  class FeatureToggle
3
- def self.enabled?(feature)
4
- not_production? || feature_in_env_var?(feature)
3
+ def self.enabled?(feature, ignore_env)
4
+ if ignore_env
5
+ feature_in_env_var?(feature)
6
+ else
7
+ not_production? || feature_in_env_var?(feature)
8
+ end
5
9
  end
6
10
 
7
11
  def self.not_production?
@@ -17,7 +21,7 @@ module PactBroker
17
21
  end
18
22
  end
19
23
 
20
- def self.feature_enabled?(feature)
21
- FeatureToggle.enabled?(feature)
24
+ def self.feature_enabled?(feature, ignore_env = false)
25
+ FeatureToggle.enabled?(feature, ignore_env)
22
26
  end
23
27
  end
@@ -1,3 +1,4 @@
1
+ require 'pact_broker/logging'
1
2
  require 'pact_broker/repositories'
2
3
  require 'pact_broker/matrix/row'
3
4
  require 'pact_broker/matrix/deployment_status_summary'
@@ -9,8 +10,10 @@ module PactBroker
9
10
  extend self
10
11
  extend PactBroker::Repositories
11
12
  extend PactBroker::Services
13
+ include PactBroker::Logging
12
14
 
13
15
  def find selectors, options = {}
16
+ logger.info "Querying matrix", selectors: selectors, options: options
14
17
  query_results = matrix_repository.find selectors, options
15
18
  deployment_status_summary = DeploymentStatusSummary.new(query_results.rows, query_results.resolved_selectors, query_results.integrations)
16
19
  QueryResultsWithDeploymentStatusSummary.new(query_results, deployment_status_summary)
@@ -8,6 +8,7 @@ module PactBroker
8
8
  class Repository
9
9
 
10
10
  include PactBroker::Repositories::Helpers
11
+ include PactBroker::Repositories
11
12
 
12
13
  def find_by_name name
13
14
  pacticipants = PactBroker::Domain::Pacticipant.where(name_like(:name, name)).all
@@ -35,11 +36,11 @@ module PactBroker
35
36
  query.order_ignore_case(Sequel[:pacticipants][:name]).eager(:labels).eager(:latest_version).all
36
37
  end
37
38
 
38
- def find_all_pacticipant_versions_in_reverse_order name
39
- PactBroker::Domain::Version.select_all_qualified
40
- .join(:pacticipants, {id: :pacticipant_id})
41
- .where(name_like(:name, name))
42
- .reverse_order(:order)
39
+ def find_all_pacticipant_versions_in_reverse_order name, pagination_options = nil
40
+ pacticipant = pacticipant_repository.find_by_name!(name)
41
+ query = PactBroker::Domain::Version.where(pacticipant: pacticipant).reverse_order(:order)
42
+ query = query.paginate(pagination_options[:page_number], pagination_options[:page_size]) if pagination_options
43
+ query
43
44
  end
44
45
 
45
46
  def find_by_name_or_create name
@@ -52,8 +52,8 @@ module PactBroker
52
52
  pacticipant_repository.find options
53
53
  end
54
54
 
55
- def self.find_all_pacticipant_versions_in_reverse_order name
56
- pacticipant_repository.find_all_pacticipant_versions_in_reverse_order(name)
55
+ def self.find_all_pacticipant_versions_in_reverse_order name, pagination_options = nil
56
+ pacticipant_repository.find_all_pacticipant_versions_in_reverse_order(name, pagination_options)
57
57
  end
58
58
 
59
59
  def self.find_pacticipant_repository_url_by_pacticipant_name name
@@ -79,21 +79,8 @@ module PactBroker
79
79
 
80
80
  def self.delete name
81
81
  pacticipant = find_pacticipant_by_name name
82
- connection = PactBroker::Domain::Pacticipant.new.db
83
- version_ids = PactBroker::Domain::Version.where(pacticipant_id: pacticipant.id).select_for_subquery(:id) #stupid mysql doesn't allow subqueries
84
- select_pacticipant = "select id from pacticipants where name = '#{name}'"
85
- tag_repository.delete_by_version_id version_ids
86
- webhook_service.delete_all_webhhook_related_objects_by_pacticipant pacticipant
87
- pact_repository.delete_by_version_id version_ids
88
- connection.run("delete from pact_publications where provider_id = #{pacticipant.id}")
89
- connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where provider_id = #{pacticipant.id})")
90
- connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where consumer_id = #{pacticipant.id})")
91
- connection.run("delete from pact_versions where provider_id = #{pacticipant.id}")
92
- connection.run("delete from pact_versions where consumer_id = #{pacticipant.id}")
93
- connection.run("delete from versions where pacticipant_id = #{pacticipant.id}")
94
- version_repository.delete_by_id version_ids
95
- label_repository.delete_by_pacticipant_id(pacticipant.id)
96
- connection.run("delete from pacticipants where id = #{pacticipant.id}")
82
+ webhook_service.delete_all_webhhook_related_objects_by_pacticipant(pacticipant)
83
+ pacticipant.destroy
97
84
  end
98
85
 
99
86
  def self.delete_if_orphan(pacticipant)
@@ -105,4 +92,4 @@ module PactBroker
105
92
  end
106
93
  end
107
94
  end
108
- end
95
+ end
@@ -3,10 +3,24 @@ require 'pact_broker/pacts/head_pact'
3
3
 
4
4
  module PactBroker
5
5
  module Pacts
6
-
7
6
  class LatestTaggedPactPublications < LatestPactPublicationsByConsumerVersion
8
7
  set_dataset(:latest_tagged_pact_publications)
9
8
 
9
+ dataset_module do
10
+ def where_age_less_than(days)
11
+ start_date = Date.today - days
12
+ where{ latest_tagged_pact_publications[:created_at] >= start_date }
13
+ end
14
+
15
+ def for_selector(selector)
16
+ query = self
17
+ query = query.where(consumer_name: selector.pacticipant_name) if selector.pacticipant_name
18
+ query = query.where(tag_name: selector.tag) if selector.tag && selector.tag.is_a?(String)
19
+ query = query.where_age_less_than(selector.max_age) if selector.max_age
20
+ query
21
+ end
22
+ end
23
+
10
24
  def to_domain
11
25
  HeadPact.new(super, consumer_version_number, tag_name)
12
26
  end
@@ -10,12 +10,9 @@ module PactBroker
10
10
  def create args
11
11
  params = {
12
12
  name: args.fetch(:name),
13
- version_id: args.fetch(:version).id,
14
- created_at: Sequel.datetime_class.now,
15
- updated_at: Sequel.datetime_class.now
13
+ version_id: args.fetch(:version).id
16
14
  }
17
- Domain::Tag.dataset.insert_ignore.insert(params)
18
- Domain::Tag.find(name: args[:name], version_id: args[:version].id)
15
+ Domain::Tag.new(params).insert_ignore
19
16
  end
20
17
 
21
18
  def find args
@@ -293,8 +293,9 @@ module PactBroker
293
293
  def create_triggered_webhook params = {}
294
294
  params.delete(:comment)
295
295
  trigger_uuid = params[:trigger_uuid] || webhook_service.next_uuid
296
+ event_name = params.key?(:event_name) ? params[:event_name] : @webhook.events.first.name # could be nil, for backwards compatibility
296
297
  verification = @webhook.trigger_on_provider_verification_published? ? @verification : nil
297
- @triggered_webhook = webhook_repository.create_triggered_webhook trigger_uuid, @webhook, @pact, verification, PactBroker::Webhooks::Service::RESOURCE_CREATION, @webhook.events.first.name
298
+ @triggered_webhook = webhook_repository.create_triggered_webhook trigger_uuid, @webhook, @pact, verification, PactBroker::Webhooks::Service::RESOURCE_CREATION, event_name
298
299
  @triggered_webhook.update(status: params[:status]) if params[:status]
299
300
  set_created_at_if_set params[:created_at], :triggered_webhooks, {id: @triggered_webhook.id}
300
301
  self
@@ -329,7 +330,7 @@ module PactBroker
329
330
 
330
331
  if tag_names.any?
331
332
  tag_names.each do | tag_name |
332
- PactBroker::Domain::Tag.create(name: tag_name, version: @provider_version)
333
+ PactBroker::Domain::Tag.new(name: tag_name, version: @provider_version).insert_ignore
333
334
  set_created_at_if_set(parameters[:created_at], :tags, version_id: @provider_version.id, name: tag_name)
334
335
  end
335
336
  end
@@ -407,6 +408,25 @@ module PactBroker
407
408
  end
408
409
  end
409
410
 
411
+ def random_json_content(consumer_name, provider_name)
412
+ {
413
+ "consumer" => {
414
+ "name" => consumer_name
415
+ },
416
+ "provider" => {
417
+ "name" => provider_name
418
+ },
419
+ "interactions" => [{
420
+ "request" => {
421
+ "method" => "GET",
422
+ "path" => "/things/#{rand}"
423
+ },
424
+ "response" => {
425
+ "status" => 200
426
+ }
427
+ }],
428
+ }.to_json
429
+ end
410
430
 
411
431
  private
412
432
 
@@ -51,6 +51,14 @@
51
51
  %input{name: 'q[]latest', value: 'true', hidden: true, class: 'latest-flag'}
52
52
 
53
53
 
54
+ - if options.latest || options.tag
55
+ .selector
56
+ %label{for: 'to'}
57
+ = options.latest ? 'To' : 'With all'
58
+ %input{name: 'tag', id: 'to', value: options.tag }
59
+ %input{name: 'latest', value: options.latest.to_s, hidden: true}
60
+
61
+
54
62
  %div.top-of-group
55
63
  .input-group
56
64
  %input{type: 'radio', name: "latestby", class: '', value: '', id: 'all_rows', checked: options.all_rows_checked}
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.65.0'
2
+ VERSION = '2.66.0'
3
3
  end
@@ -21,6 +21,14 @@ describe "Get versions" do
21
21
  it "returns a list of links to the versions" do
22
22
  expect(last_response_body[:_links][:"versions"].size).to eq 2
23
23
  end
24
+
25
+ context "with pagination options" do
26
+ subject { get(path, { 'pageSize' => '1', 'pageNumber' => '1' }) }
27
+
28
+ it "paginates the response" do
29
+ expect(last_response_body[:_links][:"versions"].size).to eq 1
30
+ end
31
+ end
24
32
  end
25
33
 
26
34
  context "when the pacticipant does not exist" do
@@ -3,32 +3,38 @@ require 'pact_broker/api/decorators/versions_decorator'
3
3
  require 'pact_broker/domain/version'
4
4
 
5
5
  module PactBroker
6
-
7
6
  module Api
8
-
9
7
  module Decorators
10
-
11
8
  describe VersionsDecorator do
12
9
 
13
- let(:options) { {base_url: 'http://example.org', pacticipant_name: "Consumer" }}
10
+ let(:options) { { resource_url: 'http://versions', base_url: 'http://example.org', pacticipant_name: "Consumer", query_string: query_string}}
11
+ let(:query_string) { nil }
12
+ let(:versions) { [] }
14
13
 
15
14
  subject { JSON.parse VersionsDecorator.new(versions).to_json(user_options: options), symbolize_names: true }
16
15
 
17
- context "with no versions" do
18
- let(:versions) { [] }
16
+ context "with no query string" do
17
+ its([:_links, :self, :href]) { is_expected.to eq 'http://versions' }
18
+ end
19
19
 
20
+ context "with a query string" do
21
+ let(:query_string) { 'foo=bar' }
22
+ its([:_links, :self, :href]) { is_expected.to eq 'http://versions?foo=bar' }
23
+ end
24
+
25
+ context "with no versions" do
20
26
  it "doesn't blow up" do
21
27
  subject
22
28
  end
23
29
  end
24
30
 
25
31
  context "with versions" do
26
- let(:version) do
32
+ let!(:version) do
27
33
  TestDataBuilder.new
28
34
  .create_consumer("Consumer")
29
35
  .create_consumer_version("1.2.3")
30
36
  .create_consumer_version_tag("prod")
31
- PactBroker::Versions::Repository.new.find_by_pacticipant_name_and_number "Consumer", "1.2.3"
37
+ .and_return(:consumer_version)
32
38
  end
33
39
  let(:versions) { [version] }
34
40
 
@@ -37,7 +43,6 @@ module PactBroker
37
43
  expect(subject[:_embedded][:versions].size).to eq 1
38
44
  end
39
45
  end
40
-
41
46
  end
42
47
  end
43
48
  end
@@ -5,11 +5,11 @@ module PactBroker
5
5
  module Resources
6
6
  describe DefaultBaseResource do
7
7
  let(:request) { double('request', body: body, uri: uri, base_uri: URI("http://example.org/"), env: env).as_null_object }
8
- let(:response) { double('response') }
8
+ let(:response) { double('response').as_null_object }
9
9
  let(:uri) { URI('http://example.org/path?query') }
10
10
  let(:body) { double('body', to_s: body_string) }
11
11
  let(:body_string) { '' }
12
- let(:env) { double('env') }
12
+ let(:env) { double('env').as_null_object }
13
13
 
14
14
  subject { BaseResource.new(request, response) }
15
15
 
@@ -100,6 +100,10 @@ module PactBroker
100
100
  end
101
101
 
102
102
  describe "decorator_options" do
103
+ before do
104
+ allow(env).to receive(:[]).with("QUERY_STRING").and_return("foo=bar")
105
+ end
106
+
103
107
  context "with no overrides" do
104
108
  it "returns the default decorator options" do
105
109
  expect(subject.decorator_options).to eq(
@@ -107,7 +111,8 @@ module PactBroker
107
111
  base_url: "http://example.org",
108
112
  resource_url: "http://example.org/path",
109
113
  env: env,
110
- resource_title: nil
114
+ resource_title: nil,
115
+ query_string: "foo=bar"
111
116
  }
112
117
  )
113
118
  end
@@ -121,7 +126,8 @@ module PactBroker
121
126
  resource_url: "http://example.org/path",
122
127
  env: env,
123
128
  resource_title: "foo",
124
- something: "else"
129
+ something: "else",
130
+ query_string: "foo=bar"
125
131
  }
126
132
  )
127
133
  end
@@ -7,6 +7,11 @@ module PactBroker
7
7
  module DB
8
8
  # Inner queries don't work on MySQL. Seriously, MySQL???
9
9
  describe Clean, pending: IS_MYSQL do
10
+
11
+ def pact_publication_count_for(consumer_name, version_number)
12
+ PactBroker::Pacts::PactPublication.where(consumer_version: PactBroker::Domain::Version.where_pacticipant_name(consumer_name).where(number: version_number)).count
13
+ end
14
+
10
15
  let(:options) { {} }
11
16
  let(:db) { PactBroker::DB.connection }
12
17
 
@@ -47,6 +52,66 @@ module PactBroker
47
52
  end
48
53
  end
49
54
 
55
+ context "when the latest pact for a tag does not belong to the latest version for a tag" do
56
+ before do
57
+ td.create_pact_with_hierarchy("Foo", "1", "Bar").comment("delete, not latest pact for dev")
58
+ .create_consumer_version_tag("dev")
59
+ .create_pact_with_hierarchy("Foo", "2", "Bar").comment("keep, latest pact for dev")
60
+ .create_consumer_version_tag("dev")
61
+ .create_consumer_version("3").comment("keep, latest version for dev")
62
+ .create_consumer_version_tag("dev")
63
+ end
64
+
65
+ it "deletes the not-latest pact" do
66
+ expect { subject }.to change { pact_publication_count_for("Foo", "1") }.by(-1)
67
+ end
68
+
69
+ it "does not delete the pact latest" do
70
+ expect { subject }.to_not change { pact_publication_count_for("Foo", "2") }
71
+ end
72
+ end
73
+
74
+ context "when a verification for the latest tagged version belongs to a pact that is not the latest tagged version" do
75
+ before do
76
+ td.create_pact_with_hierarchy("Foo", "1", "Bar", td.random_json_content("Foo", "Bar"))
77
+ .create_consumer_version_tag("dev").comment("delete, not latest, not verified by latest Bar")
78
+ .create_verification(provider_version: "3", tag_names: "dev").comment("delete, not latest")
79
+ .create_pact_with_hierarchy("Foo", "2", "Bar", td.random_json_content("Foo", "Bar"))
80
+ .create_consumer_version_tag("dev").comment("can't delete because it is verified by the latest dev version of the provider")
81
+ .create_verification(provider_version: "4", tag_names: "dev").comment("keep")
82
+ .create_pact_with_hierarchy("Foo", "3", "Bar", td.random_json_content("Foo", "Bar"))
83
+ .create_consumer_version_tag("dev").comment("keep")
84
+ end
85
+
86
+ let(:options) { { keep: [latest_dev_selector] } }
87
+
88
+ it "does not delete the latest verification" do
89
+ expect{ subject }.to_not change {
90
+ PactBroker::Domain::Verification.where(provider_version: PactBroker::Domain::Version.where_pacticipant_name("Bar").where(number: "4")).count
91
+ }
92
+ end
93
+
94
+ it "deletes the non-latest verification" do
95
+ expect{ subject }.to change {
96
+ PactBroker::Domain::Verification.where(provider_version: PactBroker::Domain::Version.where_pacticipant_name("Bar").where(number: "3")).count
97
+ }.by(-1)
98
+ end
99
+
100
+ it "does not delete the pact publication that belongs to the latest verification" do
101
+ expect{ subject }.to_not change {
102
+ PactBroker::Pacts::PactPublication.where(consumer_version: PactBroker::Domain::Version.where_pacticipant_name("Foo").where(number: "2")).count
103
+ }
104
+ end
105
+
106
+ it "deletes the pact publication that does not belongs to the latest verification" do
107
+ expect{ subject }.to change {
108
+ PactBroker::Pacts::PactPublication.where(consumer_version: PactBroker::Domain::Version.where_pacticipant_name("Foo").where(number: "1")).count
109
+ }.by(-1)
110
+ end
111
+ end
112
+
113
+
114
+
50
115
  context "with orphan pact versions" do
51
116
  before do
52
117
  # Create a pact that will not be deleted
@@ -69,20 +134,23 @@ module PactBroker
69
134
  end
70
135
  end
71
136
 
72
- context "with triggered and executed" do
137
+ context "with triggered and executed webhooks" do
73
138
  before do
74
139
  td.create_pact_with_hierarchy("Foo", "1", "Bar")
75
- .create_consumer_version_tag("dev").comment("keep")
140
+ .create_consumer_version_tag("dev").comment("delete")
76
141
  .create_webhook
77
142
  .create_triggered_webhook
78
143
  .create_webhook_execution
79
144
  .add_day
145
+ .create_pact_with_hierarchy("Foo", "2", "Bar")
146
+ .create_consumer_version_tag("dev").comment("keep")
147
+ .create_webhook
80
148
  .create_triggered_webhook
81
149
  .create_webhook_execution
82
150
  end
83
151
 
84
- xit "deletes all but the most recent triggered webhook" do
85
-
152
+ it "deletes the ones associated with the deleted pacts" do
153
+ expect { subject }.to change { PactBroker::Webhooks::TriggeredWebhook.count }.by(-1)
86
154
  end
87
155
  end
88
156
  end