pact_broker 2.2.0 → 2.3.0

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