pact_broker 2.8.0.beta.4 → 2.8.0.beta.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: da8917d330d81bf413bf0538e48931988f66d3d2
4
- data.tar.gz: 3deb4db35fc00e414aa3b093c1cd637a97104a6d
3
+ metadata.gz: cebfc643458ec866835a2820e3dcbc3f214266ae
4
+ data.tar.gz: 85ab89a5a77b328efe497d9846110a5291de65c1
5
5
  SHA512:
6
- metadata.gz: ee960619a012edafc2c74a1bfc3a4567ba950546cf292cd67e723964925681c312524586adae4bdcd9ed1c334b2a2c25203f5efbbcccaec902a9f5fca1782f0c
7
- data.tar.gz: 929ce32af34f0e4eb44c6bffc034887aee3d2a58fa5cef634c8b5486fef9935411b01ef07653e41057a528306399260e9eb665ad5dc8b495caed19f491e06d48
6
+ metadata.gz: 76afaaa40c825514aeeae6e334274caf8d52b850b41126ae68163214810bd0d5bf9244b9e737135f0baf82339f43a48d07d5ce0df2c6372d3705a86dfaa2d71d
7
+ data.tar.gz: 4f35850adae4e26e3ed41c83e71c0509b05f5ba7af7dacb30ca37bb059abe6c62c2eba346fc0b790ec6ca6f6a0f3e7c19b1e47818b3d93939e796ae1cea2010a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
- <a name="v2.7.0.beta.4"></a>
2
- ### v2.7.0.beta.4 (2017-11-03)
1
+ <a name="v2.8.0.beta.5"></a>
2
+ ### v2.8.0.beta.5 (2017-11-06)
3
+
4
+ #### Features
5
+
6
+ * **matrix**
7
+ * fix matrix data types for MySQL ([42c5478](/../../commit/42c5478))
8
+ * improve reason when no results are found ([473abbe](/../../commit/473abbe))
9
+ * change groupby param to latestby ([a6a8ba2](/../../commit/a6a8ba2))
10
+ * ensure correct row is returned when a pact is revised, then verified ([7f37644](/../../commit/7f37644))
11
+ * return most recent rows first ([cfa2109](/../../commit/cfa2109))
12
+ * allow a limit to be specified ([2a11334](/../../commit/2a11334))
13
+ * return most recent rows first ([e896b7b](/../../commit/e896b7b))
14
+ * use views to create matrix query ([8488212](/../../commit/8488212))
15
+ * add matrix view ([852324a](/../../commit/852324a))
16
+
17
+ <a name="v2.8.0.beta.4"></a>
18
+ ### v2.8.0.beta.4 (2017-11-03)
3
19
 
4
20
  #### Bug Fixes
5
21
 
@@ -55,3 +55,7 @@ Domain classes are found in `lib/pact_broker/domain`. Many of these classes are
55
55
  * `latest_tagged_pact_publications` - This view has the same columns as `all_pact_publications`, plus a `tag_name` column. It is used to return the pact for the latest tagged version of a consumer.
56
56
 
57
57
  * `latest_verifications` - The most recent verification for each pact version.
58
+
59
+ * `matrix` - The matrix of every pact publication and verification. Includes every pact revision (eg. publishing to the same consumer version twice, or using PATCH) and every verification (including 'overwritten' ones. eg. when the same provider build runs twice.)
60
+
61
+ * `latest_matrix` - This view is a subset of, and has the same columns as, the `matrix`. It removes 'overwritten' pacts and verifications from the matrix (ie. only show latest pact revision for each consumer version and latest verification for each provider version)
data/README.md CHANGED
@@ -34,7 +34,8 @@ Features:
34
34
  #### Step 1. Consumer CI build
35
35
  1. The consumer project runs its tests using the [Pact][pact] library to provide a mock service.
36
36
  2. While the tests run, the mock service writes the requests and the expected responses to a JSON "pact" file - this is the consumer contract.
37
- 3. The generated pact is then published to the Pact Broker. This is simply a `PUT` to a resource that specifies the consumer name and version, and the provider name. eg `http://my-pact-broker/pacts/provider/Animal%20Service/consumer/Zoo%20App/version/1.0.0`
37
+ 3. The generated pact is then published to the Pact Broker. Most Pact libries will make a task available for you to do this easily, however, at its simplest, it is a `PUT` to a resource that specifies the consumer name and application version, and the provider name. eg `http://my-pact-broker/pacts/provider/Animal%20Service/consumer/Zoo%20App/version/1.0.0`
38
+ (Note that you are specifying the _consumer application version_ in the URL, not the pact version. The broker will take care of versioning the pact behind the scenes when its content changes. It is expected that the consumer application version will increment with every CI build.)
38
39
  4. When a pact is published, a webhook in the Pact Broker kicks off a build of the provider project if the pact content has changed since the previous version.
39
40
 
40
41
  #### Step 2. Provider CI build
@@ -0,0 +1,39 @@
1
+ Sequel.migration do
2
+ up do
3
+ # Includes every pact revision (eg. publishing to the same consumer version twice,
4
+ # or using PATCH) and every verification
5
+ # (including 'overwritten' ones. eg. when the same provider build runs twice.)
6
+ p = :all_pact_publications
7
+ create_view(:matrix,
8
+ from(p)
9
+ .select(
10
+ Sequel[p][:consumer_id],
11
+ Sequel[p][:consumer_name],
12
+ Sequel[p][:consumer_version_id],
13
+ Sequel[p][:consumer_version_number],
14
+ Sequel[p][:consumer_version_order],
15
+ Sequel[p][:id].as(:pact_publication_id),
16
+ Sequel[p][:pact_version_id],
17
+ Sequel[p][:pact_version_sha],
18
+ Sequel[p][:revision_number].as(:pact_revision_number),
19
+ Sequel[p][:created_at].as(:pact_created_at),
20
+ Sequel[p][:provider_id],
21
+ Sequel[p][:provider_name],
22
+ Sequel[:versions][:id].as(:provider_version_id),
23
+ Sequel[:versions][:number].as(:provider_version_number),
24
+ Sequel[:versions][:order].as(:provider_version_order),
25
+ Sequel[:verifications][:id].as(:verification_id),
26
+ Sequel[:verifications][:success],
27
+ Sequel[:verifications][:number].as(:verification_number),
28
+ Sequel[:verifications][:execution_date].as(:verification_executed_at),
29
+ Sequel[:verifications][:build_url].as(:verification_build_url)
30
+ )
31
+ .left_outer_join(:verifications, { Sequel[:verifications][:pact_version_id] => Sequel[p][:pact_version_id] })
32
+ .left_outer_join(:versions, {Sequel[:versions][:id] => Sequel[:verifications][:provider_version_id]})
33
+ )
34
+ end
35
+
36
+ down do
37
+ drop_view(:matrix)
38
+ end
39
+ end
@@ -0,0 +1,17 @@
1
+ Sequel.migration do
2
+ change do
3
+ # joining with latest_pact_publication_revision_numbers gets rid of the overwritten
4
+ # pact revisions, and the max(verification_id) gets rid of the overwritten
5
+ # verifications
6
+ create_view(:latest_verification_id_for_consumer_version_and_provider_version,
7
+ "select consumer_version_id, provider_version_id, max(verification_id) as latest_verification_id
8
+ from matrix
9
+ inner join latest_pact_publication_revision_numbers lr
10
+ on matrix.consumer_id = lr.consumer_id
11
+ and matrix.provider_id = lr.provider_id
12
+ and matrix.consumer_version_order = lr.consumer_version_order
13
+ and matrix.pact_revision_number = lr.latest_revision_number
14
+ group by consumer_version_id, provider_version_id"
15
+ )
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ Sequel.migration do
2
+ change do
3
+ # Removes 'overwritten' pacts and verifications from the matrix
4
+ # (ie. only show latest pact revision for each consumer version and
5
+ # latest verification for each provider version)
6
+ # Must include lines where verification_id is null so that we don't
7
+ # lose the unverified pacts.
8
+ create_view(:latest_matrix,
9
+ "SELECT matrix.* FROM matrix
10
+ INNER JOIN latest_verification_id_for_consumer_version_and_provider_version AS lv
11
+ ON ((matrix.consumer_version_id = lv.consumer_version_id)
12
+ AND (matrix.provider_version_id = lv.provider_version_id)
13
+ AND ((matrix.verification_id = lv.latest_verification_id)))
14
+
15
+ UNION
16
+
17
+ select matrix.* from matrix
18
+ inner join latest_pact_publication_revision_numbers lr
19
+ on matrix.consumer_id = lr.consumer_id
20
+ and matrix.provider_id = lr.provider_id
21
+ and matrix.consumer_version_order = lr.consumer_version_order
22
+ and matrix.pact_revision_number = lr.latest_revision_number
23
+ where verification_id is null
24
+ "
25
+ )
26
+ end
27
+ end
@@ -25,16 +25,14 @@ module PactBroker
25
25
  }
26
26
  end
27
27
 
28
- private
29
-
30
- attr_reader :lines
31
-
32
28
  def deployable
29
+ return nil if lines.empty?
33
30
  return nil if lines.any?{ |line| line[:success].nil? }
34
31
  lines.any? && lines.all?{ |line| line[:success] }
35
32
  end
36
33
 
37
34
  def reason
35
+ return "No results matched the given query" if lines.empty?
38
36
  case deployable
39
37
  when true then "All verification results are published and successful"
40
38
  when false then "One or more verifications have failed"
@@ -43,6 +41,10 @@ module PactBroker
43
41
  end
44
42
  end
45
43
 
44
+ private
45
+
46
+ attr_reader :lines
47
+
46
48
  def matrix(lines, base_url)
47
49
  provider = nil
48
50
  consumer = nil
@@ -118,7 +120,7 @@ module PactBroker
118
120
  verifiedAt: line[:verification_executed_at].to_datetime.xmlschema,
119
121
  _links: {
120
122
  self: {
121
- href: verification_url(OpenStruct.new(line), base_url)
123
+ href: verification_url(OpenStruct.new(line.merge(number: line[:verification_number])), base_url)
122
124
  }
123
125
  }
124
126
  }
@@ -1,23 +1,26 @@
1
1
  require 'ostruct'
2
2
  require 'pact_broker/api/pact_broker_urls'
3
+ require 'pact_broker/api/decorators/matrix_decorator'
4
+
3
5
  require 'table_print'
4
6
 
5
7
  module PactBroker
6
8
  module Api
7
9
  module Decorators
8
10
  class MatrixTextDecorator
9
- Line = Struct.new(:consumer, :consumer_version, :provider, :provider_version, :success)
11
+ Line = Struct.new(:consumer, :c_version, :revision, :provider, :p_version, :number, :success)
10
12
 
11
13
  def initialize(lines)
12
14
  @lines = lines
13
15
  end
14
16
 
15
17
  def to_text(options)
18
+ json_decorator = PactBroker::Api::Decorators::MatrixDecorator.new(lines)
16
19
  data = lines.collect do | line |
17
- Line.new(line[:consumer_name], line[:consumer_version_number], line[:provider_name], line[:provider_version_number], line[:success])
20
+ Line.new(line[:consumer_name], line[:consumer_version_number], line[:pact_revision_number], line[:provider_name], line[:provider_version_number], line[:verification_number], line[:success])
18
21
  end
19
22
  printer = TablePrint::Printer.new(data)
20
- printer.table_print + "\n"
23
+ printer.table_print + "\n\nDeployable: #{json_decorator.deployable.inspect}\nReason: #{json_decorator.reason}\n"
21
24
  end
22
25
 
23
26
  private
@@ -0,0 +1,24 @@
1
+ require 'pact_broker/matrix/row'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ # Latest pact revision for each consumer version => latest verification
6
+ class LatestRow < Row
7
+ set_dataset(:latest_matrix)
8
+
9
+ # For some reason, with MySQL, the success column value
10
+ # comes back as an integer rather than a boolean
11
+ # for the latest_matrix view (but not the matrix view!)
12
+ # Maybe something to do with the union?
13
+ # Haven't investigated as this is an easy enough fix.
14
+ def success
15
+ value = super
16
+ value.nil? ? nil : value == true || value == 1
17
+ end
18
+
19
+ def values
20
+ super.merge(success: success)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -22,6 +22,15 @@ module PactBroker
22
22
  if params.key?('success') && params['success'].is_a?(String)
23
23
  options[:success] = [params['success'] == '' ? nil : params['success'] == 'true']
24
24
  end
25
+ if params.key?('scope')
26
+ options[:scope] = params['scope']
27
+ end
28
+ if params.key?('latestby')
29
+ options[:latestby] = params['latestby']
30
+ end
31
+ if params.key?('limit')
32
+ options[:limit] = params['limit']
33
+ end
25
34
  return selectors, options
26
35
  end
27
36
  end
@@ -1,4 +1,6 @@
1
1
  require 'pact_broker/repositories/helpers'
2
+ require 'pact_broker/matrix/row'
3
+ require 'pact_broker/matrix/latest_row'
2
4
 
3
5
  module PactBroker
4
6
  module Matrix
@@ -6,61 +8,84 @@ module PactBroker
6
8
  include PactBroker::Repositories::Helpers
7
9
  include PactBroker::Repositories
8
10
 
11
+ # TODO move latest verification logic in to database
12
+
13
+ GROUP_BY_PROVIDER_VERSION_NUMBER = [:consumer_name, :consumer_version_number, :provider_name, :provider_version_number]
14
+ GROUP_BY_PROVIDER = [:consumer_name, :consumer_version_number, :provider_name]
15
+ GROUP_BY_PACT = [:consumer_name, :provider_name]
16
+
9
17
  # Return the latest matrix row (pact/verification) for each consumer_version_number/provider_version_number
10
18
  def find selectors, options = {}
11
19
  # The group with the nil provider_version_numbers will be the results of the left outer join
12
20
  # that don't have verifications, so we need to include them all.
13
- lines = find_all(selectors)
14
- .group_by{|line| [line[:consumer_name], line[:consumer_version_number], line[:provider_name], line[:provider_version_number]]}
15
- .values
16
- .collect{ | lines | lines.first[:provider_version_number].nil? ? lines : lines.last }
17
- .flatten
21
+ lines = find_all(selectors, options)
22
+ lines = apply_scope(options, selectors, lines)
18
23
 
19
24
  if options.key?(:success)
20
- lines = lines.select{ |l| options[:success].include?(l[:success]) }
25
+ lines = lines.select{ |l| options[:success].include?(l.success) }
21
26
  end
22
27
 
23
- lines
28
+ lines.sort.collect(&:values)
29
+ end
30
+
31
+ def all_versions_specified? selectors
32
+ selectors.all?{ |s| s[:pacticipant_version_number] }
33
+ end
34
+
35
+ def apply_scope options, selectors, lines
36
+ return lines unless options[:latestby] == 'cvp' || options[:latestby] == 'cp'
37
+
38
+ group_by_columns = case options[:latestby]
39
+ when 'cvp' then GROUP_BY_PROVIDER
40
+ when 'cp' then GROUP_BY_PACT
41
+ end
42
+
43
+ lines.group_by{|line| group_by_columns.collect{|key| line.send(key) }}
44
+ .values
45
+ .collect{ | lines | lines.first.provider_version_number.nil? ? lines : lines.first }
46
+ .flatten
24
47
  end
25
48
 
26
49
  def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
27
50
  selectors = [{ pacticipant_name: pacticipant_1_name }, { pacticipant_name: pacticipant_2_name }]
28
- find_all(selectors)
29
- .sort{|l1, l2| l2[:consumer_version_order] <=> l1[:consumer_version_order]}
51
+ find_all(selectors, {latestby: 'cvpv'}).sort.collect(&:values)
30
52
  end
31
53
 
32
54
  def find_compatible_pacticipant_versions selectors
33
- find(selectors).select{ |line | line[:success] }
55
+ find(selectors, latestby: 'cvpv').select{|line| line[:success] }
34
56
  end
35
57
 
36
58
  ##
37
59
  # If the version is nil, it means all versions for that pacticipant are to be included
38
60
  #
39
- def find_all selectors
61
+ def find_all selectors, options
40
62
  selectors = look_up_versions_for_tags(selectors)
41
-
42
- query = PactBroker::Pacts::LatestPactPublicationsByConsumerVersion
43
- .select_append(:consumer_version_number, :provider_name, :consumer_name, :provider_version_id, :provider_version_number, :success)
44
- .select_append(Sequel[:latest_pact_publications_by_consumer_versions][:created_at].as(:pact_created_at))
45
- .select_append(Sequel[:all_verifications][:number])
46
- .select_append(Sequel[:all_verifications][:id].as(:verification_id))
47
- .select_append(Sequel[:execution_date].as(:verification_executed_at))
48
- .left_outer_join(:all_verifications, pact_version_id: :pact_version_id)
63
+ query = base_table(options).select_all
49
64
 
50
65
  if selectors.size == 1
51
66
  query = where_consumer_or_provider_is(selectors.first, query)
52
67
  else
53
- query = where_consumer_and_provider_within(selectors, query)
68
+ query = where_consumer_and_provider_in(selectors, query)
54
69
  end
55
70
 
56
- query.order(:execution_date, :verification_id)
57
- .collect(&:values)
71
+ query = query.limit(options[:limit]) if options[:limit]
72
+ query.order(
73
+ Sequel.asc(:consumer_name),
74
+ Sequel.desc(:consumer_version_order),
75
+ Sequel.desc(:pact_revision_number),
76
+ Sequel.asc(:provider_name),
77
+ Sequel.desc(:provider_version_order),
78
+ Sequel.desc(:verification_id)).all
79
+ end
80
+
81
+ def base_table(options)
82
+ return Row unless options[:latestby]
83
+ return LatestRow
58
84
  end
59
85
 
60
86
  def look_up_versions_for_tags(selectors)
61
87
  selectors.collect do | selector |
62
88
  # resource validation currently stops tag being specified without latest=true
63
-
64
89
  if selector[:tag] && selector[:latest]
65
90
  version = version_repository.find_by_pacticpant_name_and_latest_tag(selector[:pacticipant_name], selector[:tag])
66
91
  # validation in resource should ensure we always have a version
@@ -80,7 +105,7 @@ module PactBroker
80
105
  end
81
106
  end
82
107
 
83
- def where_consumer_and_provider_within selectors, query
108
+ def where_consumer_and_provider_in selectors, query
84
109
  query.where{
85
110
  Sequel.&(
86
111
  Sequel.|(
@@ -0,0 +1,46 @@
1
+ require 'pact_broker/repositories/helpers'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ class Row < Sequel::Model(:matrix)
6
+
7
+ dataset_module do
8
+ include PactBroker::Repositories::Helpers
9
+ end
10
+
11
+ def summary
12
+ "#{consumer_name}#{consumer_version_number} #{provider_name}#{provider_version_number || '?'} (r#{pact_revision_number}n#{verification_number || '?'})"
13
+ end
14
+
15
+ # Add logic for ignoring case
16
+ def <=> other
17
+
18
+ comparisons = [
19
+ compare_name_asc(consumer_name, other.consumer_name),
20
+ compare_number_desc(consumer_version_order, other.consumer_version_order),
21
+ compare_number_desc(pact_revision_number, other.pact_revision_number),
22
+ compare_name_asc(provider_name, other.provider_name),
23
+ compare_number_desc(provider_version_order, other.provider_version_order),
24
+ compare_number_desc(verification_id, other.verification_id)
25
+ ]
26
+
27
+ comparisons.find{|c| c != 0 } || 0
28
+
29
+ end
30
+
31
+ def compare_name_asc name1, name2
32
+ name1 <=> name2
33
+ end
34
+
35
+ def compare_number_desc number1, number2
36
+ if number1 && number2
37
+ number2 <=> number1
38
+ elsif number1
39
+ 1
40
+ else
41
+ -1
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -11,7 +11,7 @@ module PactBroker
11
11
 
12
12
  get "/provider/:provider_name/consumer/:consumer_name" do
13
13
  selectors = [{ pacticipant_name: params[:consumer_name] }, { pacticipant_name: params[:provider_name] } ]
14
- lines = matrix_service.find(selectors)
14
+ lines = matrix_service.find(selectors, {latestby: 'cvpv', limit: 1000})
15
15
  lines = lines.collect{|line| PactBroker::UI::ViewDomain::MatrixLine.new(line) }.sort
16
16
  locals = {
17
17
  lines: lines,
@@ -71,11 +71,11 @@ module PactBroker
71
71
  end
72
72
 
73
73
  def orderable_fields
74
- [consumer_name, provider_name, consumer_version_order, provider_version_order]
74
+ [consumer_name, consumer_version_order, @line[:pact_revision_number], provider_name, @line[:verification_id]]
75
75
  end
76
76
 
77
77
  def <=> other
78
- self.orderable_fields <=> other.orderable_fields
78
+ (self.orderable_fields <=> other.orderable_fields) * -1
79
79
  end
80
80
 
81
81
  def verification_status