pact_broker 2.65.0 → 2.66.0

Sign up to get free protection for your applications and to get access to all the features.
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