pact_broker 2.2.0 → 2.3.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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/db/migrations/23_create_pact_versions_table.rb +1 -5
  4. data/lib/pact_broker/api.rb +6 -0
  5. data/lib/pact_broker/api/resources/badge.rb +60 -0
  6. data/lib/pact_broker/badges/service.rb +107 -0
  7. data/lib/pact_broker/configuration.rb +3 -2
  8. data/lib/pact_broker/doc/views/publish-verification-results.markdown +4 -4
  9. data/lib/pact_broker/domain/relationship.rb +7 -1
  10. data/lib/pact_broker/domain/verification.rb +12 -0
  11. data/lib/pact_broker/locale/en.yml +2 -0
  12. data/lib/pact_broker/logging.rb +4 -2
  13. data/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names.rb +11 -5
  14. data/lib/pact_broker/pacts/service.rb +1 -1
  15. data/lib/pact_broker/services.rb +5 -0
  16. data/lib/pact_broker/ui/view_models/relationship.rb +12 -14
  17. data/lib/pact_broker/verifications/repository.rb +8 -4
  18. data/lib/pact_broker/verifications/service.rb +2 -2
  19. data/lib/pact_broker/verifications/verification_status.rb +47 -0
  20. data/lib/pact_broker/version.rb +1 -1
  21. data/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb +1 -1
  22. data/public/images/pact-changed-orange.svg +1 -0
  23. data/public/images/pact-failed-red.svg +1 -0
  24. data/public/images/pact-unknown-lightgrey.svg +1 -0
  25. data/public/images/pact-verified-brightgreen.svg +1 -0
  26. data/public/images/pact_not_found-unknown-lightgrey.svg +1 -0
  27. data/spec/features/get_latest_pact_badge_spec.rb +53 -0
  28. data/spec/features/get_latest_tagged_pact_badge_spec.rb +38 -0
  29. data/spec/features/get_latest_untagged_pact_badge_spec.rb +38 -0
  30. data/spec/features/publish_verification_spec.rb +8 -1
  31. data/spec/lib/pact_broker/api/resources/badge_spec.rb +104 -0
  32. data/spec/lib/pact_broker/badges/service_spec.rb +244 -0
  33. data/spec/lib/pact_broker/messages_spec.rb +2 -0
  34. data/spec/lib/pact_broker/pacticipants/find_potential_duplicate_pacticipant_names_spec.rb +44 -62
  35. data/spec/lib/pact_broker/pacts/service_spec.rb +0 -3
  36. data/spec/lib/pact_broker/ui/view_models/relationship_spec.rb +5 -7
  37. data/spec/lib/pact_broker/verifications/repository_spec.rb +102 -28
  38. data/spec/lib/pact_broker/verifications/verification_status_spec.rb +48 -0
  39. metadata +22 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b84d9a4fa0cfe79efde8f1b39c6e630177b31a1
4
- data.tar.gz: d4c280d34723b159cc05b9ac4c5144c9aae1b381
3
+ metadata.gz: 03e1cb232f71117f88a50df3b9b46001bf5fcc4d
4
+ data.tar.gz: 72758f12fc4fb67c94bb3f21190ccf7e56e36b80
5
5
  SHA512:
6
- metadata.gz: 9cd26118f75849b539ae2ef910b107ac7a130fd4b4b6049fb50f4aebdc4e2b9d207c1a302db9108d351a80ce0642bab6f5a7c952cff9cacc3f856a2742bc35d2
7
- data.tar.gz: ca50ecbc5d7bca2977dbbd2082fb82408e72a8f8324fa9d2a2ac38cfa349167463a718cbc03b16414d6e95b7be6aa378c7e4e9c6008a9a7ec530f9fd9ea18f25
6
+ metadata.gz: a455b7e1e44018827ac663ffe87eeb4c118c082865f9cbb926f0cc2fc3ed7d30ba28686c6b58548b7f36550f30ff8a634f0dc4ec26af8238aaae5d27b96cf2f0
7
+ data.tar.gz: 805d6d8e3d035ae6543d342459f22e21dc05eb0cda99bf96c64b568c937c383678c5ce20582d0a44ffa41f626e9e40bed0fe15f5dcdcf5155fa053e47cad9cc9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@ Do this to generate your change history
2
2
 
3
3
  $ git log --pretty=format:' * %h - %s (%an, %ad)'
4
4
 
5
+ #### 2.3.0 (2017-07-14)
6
+ * 3ac4351 - fix(potential duplicate pacticipant names): Make duplicate logic smarter. Fixes https://github.com/pact-foundation/pact_broker/issues/35 (Beth Skurrie, Tue Jul 11 10:30:11 2017 +1000)
7
+ * 81979b1 - add basic auth example to duplicate pacticipant error/help message (Fitzgerald, Andrew, Mon Jul 10 00:11:25 2017 -0400)
8
+ * bc54321 - feat(badges): Add endpoint to retrieve badge for latest untagged pact (Beth Skurrie, Fri Jul 7 10:15:29 2017 +1000)
9
+ * 5a3b149 - feat(badges): Add endpoint to retrieve badge for latest tagged pact (Beth Skurrie, Fri Jul 7 09:32:24 2017 +1000)
10
+ * 78c888b - feat(badges): Use static images when shields.io base URL is not configured. (Beth Skurrie, Fri Jul 7 08:41:35 2017 +1000)
11
+ * b30c368 - feat(badges): Allow shields.io base URL to be configured (Beth Skurrie, Fri Jul 7 08:31:47 2017 +1000)
12
+ * d8b2cec - feat(badges): Added configuration for turning badge resources on or off (Beth Skurrie, Fri Jul 7 08:25:48 2017 +1000)
13
+ * 2e43b5f - feat(badges): Added read timeout of 1000ms for HTTP call to create badge. (Beth Skurrie, Thu Jul 6 07:48:39 2017 +1000)
14
+ * 6bdae00 - fix(publish verification): Corrected pact finding params when publishing a verification. (Beth Skurrie, Thu Jul 6 07:30:38 2017 +1000)
15
+ * 2508eba - feat(badges): Allow pacticipant initials to be used where names are too long for the badge (Beth Skurrie, Wed Jul 5 14:49:07 2017 +1000)
16
+ * f7a36b7 - feat(badges): Return static badge when there is an error creating a dynamic one (Beth Skurrie, Wed Jul 5 10:14:18 2017 +1000)
17
+ * 24860b3 - feat(badges): Add badge svg endpoint for latest pact (Beth Skurrie, Tue Jul 4 15:28:28 2017 +1000)
18
+
5
19
  #### 2.2.0 (2017-07-04)
6
20
  * 788c5d0 - chore(gems): Lock rack and red-carpet gem versions for hakiri (Beth Skurrie, Tue Jul 4 10:28:15 2017 +1000)
7
21
  * f1abebe - chore(gems): Upgrade pact gems (Beth Skurrie, Tue Jul 4 10:10:55 2017 +1000)
@@ -1,7 +1,7 @@
1
1
  require_relative 'migration_helper'
2
2
 
3
3
  Sequel.migration do
4
- up do
4
+ change do
5
5
  create_table(:pact_versions, charset: 'utf8') do
6
6
  primary_key :id
7
7
  foreign_key :consumer_id, :pacticipants
@@ -12,8 +12,4 @@ Sequel.migration do
12
12
  DateTime :created_at, null: false
13
13
  end
14
14
  end
15
-
16
- down do
17
-
18
- end
19
15
  end
@@ -26,6 +26,7 @@ require 'pact_broker/api/resources/latest_provider_pacts'
26
26
  require 'pact_broker/api/resources/verifications'
27
27
  require 'pact_broker/api/resources/verification'
28
28
  require 'pact_broker/api/resources/latest_verifications_for_consumer_version'
29
+ require 'pact_broker/api/resources/badge'
29
30
 
30
31
  require 'webmachine/adapters/rack_mapped'
31
32
 
@@ -49,6 +50,11 @@ module PactBroker
49
50
  add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'pact-version', :pact_version_sha, 'verification-results', :verification_number], Api::Resources::Verification, {resource_name: "verification_result"}
50
51
  add ['verification-results', 'consumer', :consumer_name, 'version', :consumer_version_number,'latest'], Api::Resources::LatestVerificationsForConsumerVersion, {resource_name: "verification_results_for_consumer_version"}
51
52
 
53
+ # Badges
54
+ add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest', 'badge'], Api::Resources::Badge, {resource_name: "latest_pact_badge"}
55
+ add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest', :tag, 'badge'], Api::Resources::Badge, {resource_name: "latest_tagged_pact_badge"}
56
+ add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest-untagged', 'badge'], Api::Resources::Badge, {resource_name: "latest_untagged_pact_badge", tag: :untagged}
57
+
52
58
  # Latest pacts
53
59
  add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest'], Api::Resources::LatestPact, {resource_name: "latest_pact_publication"}
54
60
  add ['pacts', 'provider', :provider_name, 'consumer', :consumer_name, 'latest', :tag], Api::Resources::LatestPact, {resource_name: "latest_tagged_pact_publication"}
@@ -0,0 +1,60 @@
1
+ require 'pact_broker/api/resources/base_resource'
2
+ require 'pact_broker/verifications/verification_status'
3
+ require 'pact_broker/configuration'
4
+
5
+ module PactBroker
6
+ module Api
7
+ module Resources
8
+
9
+ class Badge < BaseResource
10
+
11
+ def allowed_methods
12
+ ['GET']
13
+ end
14
+
15
+ def content_types_provided
16
+ [['image/svg+xml', :to_svg]]
17
+ end
18
+
19
+ def resource_exists?
20
+ PactBroker.configuration.enable_badge_resources
21
+ end
22
+
23
+ def is_authorized?(authorization_header)
24
+ true
25
+ end
26
+
27
+ def forbidden?
28
+ false
29
+ end
30
+
31
+ private
32
+
33
+ def to_svg
34
+ badges_service.pact_verification_badge pact, label, initials, verification_status
35
+ end
36
+
37
+ def pact
38
+ @pact ||= pact_service.find_latest_pact(identifier_from_path)
39
+ end
40
+
41
+ def latest_verification
42
+ return nil unless pact
43
+ @verification ||= verification_service.find_latest_verification_for(pact.consumer, pact.provider, identifier_from_path[:tag])
44
+ end
45
+
46
+ def verification_status
47
+ @verification_status ||= PactBroker::Verifications::Status.new(pact, latest_verification).to_sym
48
+ end
49
+
50
+ def label
51
+ request.query['label']
52
+ end
53
+
54
+ def initials
55
+ request.query['initials'] == 'true'
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,107 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ require 'pact_broker/project_root'
4
+ require 'pact_broker/logging'
5
+ require 'pact_broker/configuration'
6
+
7
+ module PactBroker
8
+ module Badges
9
+ module Service
10
+
11
+ extend self
12
+ include PactBroker::Logging
13
+
14
+ SPACE_DASH_UNDERSCORE = /[\s_\-]/
15
+ LOWER_TO_UPPERCASE = /[a-zA-Z](?=[A-Z])/
16
+
17
+ def pact_verification_badge pact, label, initials, verification_status
18
+ return static_svg(pact, verification_status) unless pact
19
+
20
+ title = badge_title pact, label, initials
21
+ status = badge_status verification_status
22
+ color = badge_color verification_status
23
+
24
+ dynamic_svg(title, status, color) || static_svg(pact, verification_status)
25
+ end
26
+
27
+ private
28
+
29
+ def badge_title pact, label, initials
30
+ title = case (label || '').downcase
31
+ when 'consumer' then prepare_name(pact.consumer_name, initials)
32
+ when 'provider' then prepare_name(pact.provider_name, initials)
33
+ else "#{prepare_name(pact.consumer_name, initials)}%2F#{prepare_name(pact.provider_name, initials)}"
34
+ end
35
+ "#{title} pact".downcase
36
+ end
37
+
38
+ def prepare_name name, initials
39
+ if initials
40
+ parts = name.split(SPACE_DASH_UNDERSCORE)
41
+ parts = name.split(LOWER_TO_UPPERCASE) if parts.size == 1
42
+ return parts.collect{ |p| p[0] }.join.downcase if parts.size > 1
43
+ end
44
+ name.downcase
45
+ end
46
+
47
+ def badge_status verification_status
48
+ case verification_status
49
+ when :success then "verified"
50
+ when :failed then "failed"
51
+ when :stale then "changed"
52
+ else "unknown"
53
+ end
54
+ end
55
+
56
+ def badge_color verification_status
57
+ case verification_status
58
+ when :success then "brightgreen"
59
+ when :failed then "red"
60
+ when :stale then "orange"
61
+ else "lightgrey"
62
+ end
63
+ end
64
+
65
+ def dynamic_svg left_text, right_text, color
66
+ return nil unless PactBroker.configuration.shields_io_base_url
67
+ uri = build_uri(left_text, right_text, color)
68
+ begin
69
+ response = do_request(uri)
70
+ response.code == '200' ? response.body : nil
71
+ rescue StandardError => e
72
+ log_error e, "Error retrieving badge from #{uri}"
73
+ nil
74
+ end
75
+ end
76
+
77
+ def build_uri left_text, right_text, color
78
+ shield_base_url = PactBroker.configuration.shields_io_base_url
79
+ path = "/badge/#{escape_text(left_text)}-#{escape_text(right_text)}-#{color}.svg"
80
+ URI.parse(shield_base_url + path)
81
+ end
82
+
83
+ def escape_text text
84
+ text.gsub(" ", "%20").gsub("-", "--").gsub("_", "__")
85
+ end
86
+
87
+ def do_request(uri)
88
+ request = Net::HTTP::Get.new(uri)
89
+ Net::HTTP.start(uri.hostname, uri.port,
90
+ use_ssl: uri.scheme == 'https', read_timeout: 1000) do |http|
91
+ http.request request
92
+ end
93
+ end
94
+
95
+ def static_svg pact, verification_status
96
+ file_name = case verification_status
97
+ when :success then "pact-verified-brightgreen.svg"
98
+ when :failed then "pact-failed-red.svg"
99
+ when :stale then "pact-changed-orange.svg"
100
+ else "pact-unknown-lightgrey.svg"
101
+ end
102
+ file_name = "pact_not_found-unknown-lightgrey.svg" unless pact
103
+ File.read(PactBroker.project_root.join("public", "images", file_name))
104
+ end
105
+ end
106
+ end
107
+ end
@@ -17,6 +17,7 @@ module PactBroker
17
17
  attr_accessor :validate_database_connection_config, :enable_diagnostic_endpoints, :version_parser
18
18
  attr_accessor :use_case_sensitive_resource_names, :order_versions_by_date
19
19
  attr_accessor :semver_formats
20
+ attr_accessor :enable_badge_resources, :shields_io_base_url
20
21
  attr_writer :logger
21
22
 
22
23
  def initialize
@@ -38,6 +39,8 @@ module PactBroker
38
39
  config.use_hal_browser = true
39
40
  config.validate_database_connection_config = true
40
41
  config.enable_diagnostic_endpoints = true
42
+ config.enable_badge_resources = false # For security
43
+ config.shields_io_base_url = "https://img.shields.io".freeze
41
44
  config.use_case_sensitive_resource_names = true
42
45
  config.html_pact_renderer = default_html_pact_render
43
46
  config.version_parser = PactBroker::Versions::ParseSemanticVersion
@@ -127,7 +130,5 @@ module PactBroker
127
130
  def log_path
128
131
  log_dir + "/pact_broker.log"
129
132
  end
130
-
131
133
  end
132
-
133
134
  end
@@ -6,9 +6,9 @@ Use the `pb:publish-verification-results` link in the pact resource to publish t
6
6
 
7
7
  POST http://broker/pacts/provider/Foo/consumer/Bar/pact-version/1234
8
8
  {
9
- success: true,
10
- providerApplicationVersion: "4.5.6",
11
- buildUrl: "http://my-ci.org/build/3456"
9
+ "success": true,
10
+ "providerApplicationVersion": "4.5.6",
11
+ "buildUrl": "http://my-ci.org/build/3456"
12
12
  }
13
13
 
14
- Multiple verification results may be published for the same pact resource.
14
+ Multiple verification results may be published for the same pact resource. The most recently published one will be considered to reflect the current status of verification.
@@ -1,3 +1,5 @@
1
+ require 'pact_broker/verifications/verification_status'
2
+
1
3
  module PactBroker
2
4
  module Domain
3
5
 
@@ -13,7 +15,7 @@ module PactBroker
13
15
  @webhooks = webhooks
14
16
  end
15
17
 
16
- def self.create consumer, provider, latest_pact, latest_verification, webhooks
18
+ def self.create consumer, provider, latest_pact, latest_verification, webhooks = []
17
19
  new consumer, provider, latest_pact, latest_verification, webhooks
18
20
  end
19
21
 
@@ -43,6 +45,10 @@ module PactBroker
43
45
  @webhooks.any?
44
46
  end
45
47
 
48
+ def verification_status
49
+ @verification_status ||= PactBroker::Verifications::Status.new(@latest_pact, @latest_verification).to_sym
50
+ end
51
+
46
52
  def ever_verified?
47
53
  !!latest_verification
48
54
  end
@@ -41,6 +41,18 @@ module PactBroker
41
41
  where(Sequel.qualify("verifications", "number") => number)
42
42
  end
43
43
 
44
+ def tag tag_name
45
+ filter = name_like(Sequel.qualify(:tags, :name), tag_name)
46
+ join(:pact_publications, {pact_version_id: :pact_version_id})
47
+ .join(:tags, {version_id: :consumer_version_id}).where(filter)
48
+ end
49
+
50
+ def untagged
51
+ join(:pact_publications, {pact_version_id: :pact_version_id})
52
+ .left_outer_join(:tags, {version_id: :consumer_version_id})
53
+ .where(Sequel.qualify(:tags, :name) => nil)
54
+ end
55
+
44
56
  def latest
45
57
  reverse_order(:consumer_version_order, :number).limit(1)
46
58
  end
@@ -28,6 +28,8 @@ en:
28
28
  If you meant to specify one of the above names, please correct the pact configuration, and re-publish the pact.
29
29
  If the pact is intended to be for a new consumer or provider, please manually create "%{new_name}" using the following command, and then re-publish the pact:
30
30
  $ curl -v -XPOST -H "Content-Type: application/json" -d "{\"name\": \"%{new_name}\"}" %{create_pacticipant_url}
31
+ If the pact broker requires authentication, include the '-u' flag with the proper credentials:
32
+ $ curl -v -XPOST -u <username>:<password> -H "Content-Type: application/json" -d "{\"name\": \"%{new_name}\"}" %{create_pacticipant_url}
31
33
  "400":
32
34
  title: 400 Malformed Request
33
35
  message: The request was malformed and could not be processed.
@@ -17,8 +17,10 @@ module PactBroker
17
17
  @@logger = logger
18
18
  end
19
19
 
20
- def log_error e
21
- logger.error "#{e.class} #{e.message} #{e.backtrace.join("\n")}"
20
+ def log_error e, description = nil
21
+ message = "#{e.class} #{e.message} #{e.backtrace.join("\n")}"
22
+ message = "#{description} - #{message}" if description
23
+ logger.error message
22
24
  end
23
25
 
24
26
  def logger
@@ -21,16 +21,22 @@ module PactBroker
21
21
  return [] if existing_names.include?(new_name)
22
22
 
23
23
  existing_names.select do | existing_name |
24
- similar?(clean(new_name), clean(existing_name))
24
+ clean(new_name) == clean(existing_name)
25
25
  end
26
26
  end
27
27
 
28
- def similar?(new_name, existing_name)
29
- existing_name.include?(new_name) || new_name.include?(existing_name)
28
+ def clean name
29
+ self.class.split(name).collect{|w| w.chomp('s') } - ["api", "provider", "service"]
30
30
  end
31
31
 
32
- def clean name #TODO uppercase S
33
- name.gsub(/s\b/,'').gsub(/s([A-Z])/,'\1').gsub(/[^A-Za-z0-9]/,'').downcase
32
+ def self.split(string)
33
+ string.gsub(/\s/, '_')
34
+ .gsub(/::/, '/')
35
+ .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
36
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
37
+ .tr('-', '_')
38
+ .downcase
39
+ .split("_")
34
40
  end
35
41
 
36
42
  end
@@ -22,7 +22,7 @@ module PactBroker
22
22
  end
23
23
 
24
24
  def find_pact params
25
- pact_repository.find_pact(params[:consumer_name], params[:consumer_version_number], params[:provider_name], params[:revision_number])
25
+ pact_repository.find_pact(params[:consumer_name], params[:consumer_version_number], params[:provider_name], params[:pact_version_sha])
26
26
  end
27
27
 
28
28
  def find_by_consumer_version params
@@ -36,5 +36,10 @@ module PactBroker
36
36
  require 'pact_broker/verifications/service'
37
37
  Verifications::Service
38
38
  end
39
+
40
+ def badges_service
41
+ require 'pact_broker/badges/service'
42
+ Badges::Service
43
+ end
39
44
  end
40
45
  end
@@ -57,15 +57,11 @@ module PactBroker
57
57
  end
58
58
 
59
59
  def verification_status
60
- return "" unless @relationship.ever_verified?
61
- if @relationship.latest_verification_successful?
62
- if @relationship.pact_changed_since_last_verification?
63
- "warning"
64
- else
65
- "success"
66
- end
67
- else
68
- "danger"
60
+ case @relationship.verification_status
61
+ when :success then "success"
62
+ when :stale then "warning"
63
+ when :failed then "danger"
64
+ else ""
69
65
  end
70
66
  end
71
67
 
@@ -74,13 +70,15 @@ module PactBroker
74
70
  end
75
71
 
76
72
  def verification_tooltip
77
- return nil unless @relationship.ever_verified?
78
- if warning?
79
- "Pact has changed since last successful verification by #{provider_name} (v#{@relationship.latest_verification_provider_version})"
80
- elsif @relationship.latest_verification_successful?
73
+ case @relationship.verification_status
74
+ when :success
81
75
  "Successfully verified by #{provider_name} (v#{@relationship.latest_verification_provider_version})"
82
- elsif !@relationship.latest_verification_successful?
76
+ when :stale
77
+ "Pact has changed since last successful verification by #{provider_name} (v#{@relationship.latest_verification_provider_version})"
78
+ when :failed
83
79
  "Verification by #{provider_name} (v#{@relationship.latest_verification_provider_version}) failed"
80
+ else
81
+ nil
84
82
  end
85
83
  end
86
84