pact_broker 2.48.0 → 2.49.0

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
  SHA256:
3
- metadata.gz: bdda99358c4d4d8ca43f6e53e0e314fd24f475f1ff85fd0511743d8982224bca
4
- data.tar.gz: 4f61ab3a051164ac3db85d7eef44fa088053873b2381f4c4104f4822c47bd651
3
+ metadata.gz: 1b99bf5d9f68ee92e651f20ad2595feb55060c1720a816179f1e143d7db4dea4
4
+ data.tar.gz: 174f33323d8ecb66ec0640705273b2629b4203812f1fa4f3d68a5c997db40328
5
5
  SHA512:
6
- metadata.gz: e3d9f86596a6d4aa776c13f28e84af1976485dc9eff3bef98accf6e372d379cb591270354a252c88a6b2c08ac928391e2ddcf8ee19d3db6f9832a667b4a81184
7
- data.tar.gz: d4b2273ab789165a3f0ca7ce8f5fdb2220814d222ea172fa02fe77d19ce6e9e251277a6c621483a41b6b9911ab207b6d59861a39d2168ecdf5b0db64d1477d9f
6
+ metadata.gz: 51835924b04c884319fcc89dcc729a572645ad490245d94eb77e3b7fd58df244d4980aaeb8363f10af52fae7ea8eeeb8a96096b4f6f8f711fc86ebbc30d097e9
7
+ data.tar.gz: d9a90fecfc31add0cfb0104eae721d55e2fb695823bbd151d12f4c9f8fdb1581ced85f679be6e489a05882bc9b04c3f448e97495e538afc98d8623ce08746050
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ <a name="v2.49.0"></a>
2
+ ### v2.49.0 (2020-02-13)
3
+
4
+
5
+ #### Features
6
+
7
+ * **webhooks**
8
+ * support upsert of webhook via a PUT to /webhooks/{uuid} ([f9ba9ab5](/../../commit/f9ba9ab5))
9
+
10
+ * don't double parse the incoming JSON body when checking if it is invalid. ([bd74b82c](/../../commit/bd74b82c))
11
+ * support saving symbol configuration settings ([73db9c2b](/../../commit/73db9c2b))
12
+ * allow verification status badges to be served via a redirect instead of proxying the response ([a34d5f7f](/../../commit/a34d5f7f))
13
+
14
+
1
15
  <a name="v2.48.0"></a>
2
16
  ### v2.48.0 (2020-02-07)
3
17
 
@@ -5,11 +5,10 @@ require 'pact_broker/configuration'
5
5
  module PactBroker
6
6
  module Api
7
7
  module Resources
8
-
9
8
  class Badge < BaseResource
10
9
 
11
10
  def allowed_methods
12
- ["GET", "OPTIONS", "OPTIONS"]
11
+ ["GET", "OPTIONS"]
13
12
  end
14
13
 
15
14
  def content_types_provided
@@ -17,6 +16,11 @@ module PactBroker
17
16
  end
18
17
 
19
18
  def resource_exists?
19
+ !badge_service.can_provide_badge_using_redirect?
20
+ end
21
+
22
+ # Only called if resource_exists? returns false
23
+ def previously_existed?
20
24
  true
21
25
  end
22
26
 
@@ -28,13 +32,18 @@ module PactBroker
28
32
  false
29
33
  end
30
34
 
31
- private
32
-
33
35
  def to_svg
34
36
  response.headers['Cache-Control'] = 'no-cache'
35
37
  comment + badge_service.pact_verification_badge(pact, label, initials, pseudo_branch_verification_status)
36
38
  end
37
39
 
40
+ def moved_temporarily?
41
+ response.headers['Cache-Control'] = 'no-cache'
42
+ badge_service.pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
43
+ end
44
+
45
+ private
46
+
38
47
  def pact
39
48
  @pact ||= pact_service.find_latest_pact(identifier_from_path)
40
49
  end
@@ -84,7 +84,7 @@ module PactBroker
84
84
  end
85
85
 
86
86
  def params
87
- @params ||= JSON.parse(request.body.to_s, {symbolize_names: true}.merge(PACT_PARSING_OPTIONS))
87
+ @params ||= JSON.parse(request.body.to_s, { symbolize_names: true }.merge(PACT_PARSING_OPTIONS)) #Not load! Otherwise it will try to load Ruby classes.
88
88
  end
89
89
 
90
90
  def params_with_string_keys
@@ -123,7 +123,7 @@ module PactBroker
123
123
 
124
124
  def invalid_json?
125
125
  begin
126
- JSON.parse(request_body, PACT_PARSING_OPTIONS) #Not load! Otherwise it will try to load Ruby classes.
126
+ params
127
127
  false
128
128
  rescue StandardError => e
129
129
  logger.info "Error parsing JSON #{e} - #{request_body}"
@@ -94,6 +94,11 @@ module PactBroker
94
94
  title: 'Webhooks',
95
95
  templated: false
96
96
  },
97
+ 'pb:webhook' => {
98
+ href: base_url + '/webhooks/{uuid}',
99
+ title: 'Webhook',
100
+ templated: true
101
+ },
97
102
  'pb:integrations' => {
98
103
  href: base_url + '/integrations',
99
104
  title: 'Integrations',
@@ -28,7 +28,7 @@ module PactBroker
28
28
 
29
29
  def malformed_request?
30
30
  if request.put?
31
- return invalid_json? || webhook_validation_errors?(new_webhook)
31
+ return invalid_json? || webhook_validation_errors?(parsed_webhook, uuid)
32
32
  end
33
33
  false
34
34
  end
@@ -38,7 +38,9 @@ module PactBroker
38
38
  @webhook = webhook_service.update_by_uuid uuid, params_with_string_keys
39
39
  response.body = to_json
40
40
  else
41
- 404
41
+ @webhook = webhook_service.create(uuid, parsed_webhook, consumer, provider)
42
+ response.body = to_json
43
+ 201
42
44
  end
43
45
  end
44
46
 
@@ -57,8 +59,8 @@ module PactBroker
57
59
  @webhook ||= webhook_service.find_by_uuid uuid
58
60
  end
59
61
 
60
- def new_webhook
61
- @new_webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
62
+ def parsed_webhook
63
+ @parsed_webhook ||= Decorators::WebhookDecorator.new(PactBroker::Domain::Webhook.new).from_json(request_body)
62
64
  end
63
65
 
64
66
  def uuid
@@ -2,8 +2,8 @@ module PactBroker
2
2
  module Api
3
3
  module Resources
4
4
  module WebhookResourceMethods
5
- def webhook_validation_errors? webhook
6
- errors = webhook_service.errors(webhook)
5
+ def webhook_validation_errors?(webhook, uuid = nil)
6
+ errors = webhook_service.errors(webhook, uuid)
7
7
  if !errors.empty?
8
8
  set_json_validation_error_messages(errors.messages)
9
9
  true
@@ -15,14 +15,21 @@ module PactBroker
15
15
  SPACE_DASH_UNDERSCORE = /[\s_\-]/
16
16
  CACHE = {}
17
17
 
18
+ def can_provide_badge_using_redirect?
19
+ PactBroker.configuration.badge_provider_mode == :redirect && !!PactBroker.configuration.shields_io_base_url
20
+ end
21
+
18
22
  def pact_verification_badge pact, label, initials, pseudo_branch_verification_status
19
23
  return static_svg(pact, pseudo_branch_verification_status) unless pact
20
24
 
21
- title = badge_title pact, label, initials
22
- status = badge_status pseudo_branch_verification_status
23
- color = badge_color pseudo_branch_verification_status
25
+ dynamic_svg(pact, label, initials, pseudo_branch_verification_status) || static_svg(pact, pseudo_branch_verification_status)
26
+ end
24
27
 
25
- dynamic_svg(title, status, color) || static_svg(pact, pseudo_branch_verification_status)
28
+ def pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
29
+ title = badge_title(pact, label, initials)
30
+ status = badge_status(pseudo_branch_verification_status)
31
+ color = badge_color(pseudo_branch_verification_status)
32
+ build_shield_io_uri(title, status, color)
26
33
  end
27
34
 
28
35
  def clear_cache
@@ -78,9 +85,9 @@ module PactBroker
78
85
  end
79
86
  end
80
87
 
81
- def dynamic_svg left_text, right_text, color
88
+ def dynamic_svg pact, label, initials, pseudo_branch_verification_status
82
89
  return nil unless PactBroker.configuration.shields_io_base_url
83
- uri = build_uri(left_text, right_text, color)
90
+ uri = pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status)
84
91
  begin
85
92
  response = do_request(uri)
86
93
  response.code == '200' ? response.body : nil
@@ -93,7 +100,7 @@ module PactBroker
93
100
  end
94
101
  end
95
102
 
96
- def build_uri left_text, right_text, color
103
+ def build_shield_io_uri left_text, right_text, color
97
104
  shield_base_url = PactBroker.configuration.shields_io_base_url
98
105
  path = "/badge/#{escape_text(left_text)}-#{escape_text(right_text)}-#{color}.svg"
99
106
  URI.parse(shield_base_url + path)
@@ -14,6 +14,8 @@ module PactBroker
14
14
  JSON.parse(value, symbolize_names: true)
15
15
  when 'string'
16
16
  value
17
+ when 'symbol'
18
+ value.to_sym
17
19
  when 'integer'
18
20
  Integer(value)
19
21
  when 'float'
@@ -33,7 +35,7 @@ module PactBroker
33
35
  "1"
34
36
  when FalseClass
35
37
  "0"
36
- when SpaceDelimitedStringList
38
+ when SpaceDelimitedStringList, Symbol
37
39
  object.to_s
38
40
  when Array, Hash
39
41
  object.to_json
@@ -56,6 +58,8 @@ module PactBroker
56
58
  'integer'
57
59
  when Float
58
60
  'float'
61
+ when Symbol
62
+ 'symbol'
59
63
  else
60
64
  nil
61
65
  end
@@ -30,7 +30,8 @@ module PactBroker
30
30
  :webhook_scheme_whitelist,
31
31
  :webhook_host_whitelist,
32
32
  :base_equality_only_on_content_that_affects_verification_results,
33
- :seed_example_data
33
+ :seed_example_data,
34
+ :badge_provider_mode
34
35
  ]
35
36
 
36
37
  attr_accessor :base_url, :log_dir, :database_connection, :auto_migrate_db, :auto_migrate_db_data, :example_data_seeder, :seed_example_data, :use_hal_browser, :html_pact_renderer, :use_rack_protection
@@ -40,7 +41,7 @@ module PactBroker
40
41
  attr_accessor :webhook_retry_schedule
41
42
  attr_reader :webhook_http_method_whitelist, :webhook_scheme_whitelist, :webhook_host_whitelist
42
43
  attr_accessor :semver_formats
43
- attr_accessor :enable_public_badge_access, :shields_io_base_url
44
+ attr_accessor :enable_public_badge_access, :shields_io_base_url, :badge_provider_mode
44
45
  attr_accessor :disable_ssl_verification
45
46
  attr_accessor :base_equality_only_on_content_that_affects_verification_results
46
47
  attr_reader :api_error_reporters
@@ -69,6 +70,7 @@ module PactBroker
69
70
  config.enable_diagnostic_endpoints = true
70
71
  config.enable_public_badge_access = false # For security
71
72
  config.shields_io_base_url = "https://img.shields.io".freeze
73
+ config.badge_provider_mode = :proxy # other option is :redirect
72
74
  config.use_case_sensitive_resource_names = true
73
75
  config.html_pact_renderer = default_html_pact_render
74
76
  config.version_parser = PactBroker::Versions::ParseSemanticVersion
@@ -14,6 +14,7 @@ module PactBroker
14
14
  MAPPINGS = {
15
15
  'webhooks-create' => 'webhooks',
16
16
  'webhooks-webhooks' => 'webhooks',
17
+ 'webhook' => 'webhooks',
17
18
  'can-i-deploy-pacticipant-version-to-tag' => 'can-i-deploy',
18
19
  'pacticipant' => 'pacticipants'
19
20
  }.freeze
@@ -8,7 +8,7 @@ Allowed methods: `GET`, `POST`
8
8
 
9
9
  *Individual resource*
10
10
 
11
- Path: `/webhook/UUID`
11
+ Path: `/webhook/{uuid}`
12
12
 
13
13
  Allowed methods: `GET`, `PUT`, `DELETE`
14
14
 
@@ -16,11 +16,16 @@ Webhooks are HTTP requests that are executed asynchronously after certain events
16
16
 
17
17
  ### Creating
18
18
 
19
- To create a webhook, send a `POST` request to `/webhooks` with the body described below. You can do this through the API Browser by clicking on the `NON-GET` button for the `pb:webhooks` relation on the index, pasting in the JSON body, and clicking "Make Request".
19
+ To create a webhook, send a `POST` request to `/webhooks` with the body described below. You can also do a `PUT` to `/webhooks/{uuid}`, where the UUID is a identifier that you generate yourself. This supports upserting webhooks, allowing you to automate your Pact Broker set up.
20
+
21
+ The easiest way to create a webhook is by using the [Pact Broker Client CLI](https://github.com/pact-foundation/pact_broker-client/#create-webhook). It is available as a docker container and a standalone executable for all platforms.
22
+
23
+ You can also create webhooks through the API Browser by clicking on the `NON-GET` button for the `pb:webhooks` relation on the index, pasting in the JSON body, and clicking "Make Request".
20
24
 
21
25
  Below is an example webhook to trigger a Bamboo job when any contract for the provider "Foo" has changed. Both provider and consumer are optional - omitting either indicates that any pacticipant in that role will be matched. Webhooks with neither provider nor consumer specified are "global" webhooks that will trigger for any consumer/provider pair.
22
26
 
23
27
  {
28
+ "description": "Trigger SomeProject CI",
24
29
  "provider": {
25
30
  "name": "Bar"
26
31
  },
@@ -90,6 +95,7 @@ To specify an XML body, you will need to use a correctly escaped string (or use
90
95
 
91
96
  The following variables may be used in the request path, parameters or body, and will be replaced with their appropriate values at runtime.
92
97
 
98
+ * `${pactbroker.pactUrl}`: the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format.)
93
99
  * `${pactbroker.consumerName}`: the consumer name
94
100
  * `${pactbroker.providerName}`: the provider name
95
101
  * `${pactbroker.consumerVersionNumber}`: the version number of the most recent consumer version associated with the pact content.
@@ -100,7 +106,6 @@ The following variables may be used in the request path, parameters or body, and
100
106
  * `${pactbroker.providerLabels}`: the list of labels for the provider associated with the pact content, separated by ", ".
101
107
  * `${pactbroker.githubVerificationStatus}`: the verification status using the correct keywords for posting to the the [Github commit status API](https://developer.github.com/v3/repos/statuses).
102
108
  * `${pactbroker.bitbucketVerificationStatus}`: the verification status using the correct keywords for posting to the the [Bitbucket commit status API](https://developer.atlassian.com/server/bitbucket/how-tos/updating-build-status-for-commits/).
103
- * `${pactbroker.pactUrl}`: the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format.)
104
109
  * `${pactbroker.verificationResultUrl}`: the URL to the relevant verification result.
105
110
 
106
111
  Example usage:
@@ -43,6 +43,7 @@ en:
43
43
  consumer_version_number_invalid: "Consumer version number '%{consumer_version_number}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
44
44
  pacticipant_name_mismatch: "in pact ('%{name_in_pact}') does not match %{pacticipant} name in path ('%{name}')."
45
45
  connection_encoding_not_utf8: "The Sequel connection encoding (%{encoding}) is strongly recommended to be \"utf8\". If you need to set it to %{encoding} for some particular reason, then disable this check by setting config.validate_database_connection_config = false"
46
+ invalid_webhook_uuid: The UUID can only contain the characters A-Z, a-z, 0-9, _ and -, and must be 16 or more characters.
46
47
  duplicate_pacticipant: |
47
48
  This is the first time a pact has been published for "%{new_name}".
48
49
  The name "%{new_name}" is very similar to the following existing consumers/providers:
@@ -23,7 +23,6 @@ module PactBroker
23
23
  Selectors.new(super)
24
24
  end
25
25
 
26
- # might actually be, but this code doesn't know it.
27
26
  def overall_latest?
28
27
  any?(&:overall_latest?)
29
28
  end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.48.0'
2
+ VERSION = '2.49.0'
3
3
  end
@@ -14,6 +14,7 @@ require 'pact_broker/hash_refinements'
14
14
  require 'pact_broker/webhooks/execution_configuration'
15
15
  require 'pact_broker/messages'
16
16
  require 'pact_broker/webhooks/pact_and_verification_parameters'
17
+ require 'reform/contract/errors'
17
18
 
18
19
  module PactBroker
19
20
  module Webhooks
@@ -28,14 +29,24 @@ module PactBroker
28
29
  include Logging
29
30
  extend PactBroker::Messages
30
31
 
32
+ # Not actually a UUID. Ah well.
33
+ def self.valid_uuid_format?(uuid)
34
+ !!(uuid =~ /^[A-Za-z0-9_\-]{16,}$/)
35
+ end
36
+
31
37
  def self.next_uuid
32
38
  SecureRandom.urlsafe_base64
33
39
  end
34
40
 
35
- def self.errors webhook
41
+ def self.errors webhook, uuid = nil
36
42
  contract = PactBroker::Api::Contracts::WebhookContract.new(webhook)
37
43
  contract.validate(webhook.attributes)
38
- contract.errors
44
+ errors = contract.errors
45
+
46
+ if uuid && !valid_uuid_format?(uuid)
47
+ errors.add("uuid", message("errors.validation.invalid_webhook_uuid"))
48
+ end
49
+ errors
39
50
  end
40
51
 
41
52
  def self.create uuid, webhook, consumer, provider
@@ -1,13 +1,10 @@
1
- require 'support/test_data_builder'
2
-
3
1
  describe "Creating a webhook" do
4
-
5
2
  before do
6
- TestDataBuilder.new.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
3
+ td.create_pact_with_hierarchy("Some Consumer", "1", "Some Provider")
7
4
  end
8
5
 
9
6
  let(:headers) { {'CONTENT_TYPE' => 'application/json'} }
10
- let(:response_body) { JSON.parse(last_response.body, symbolize_names: true)}
7
+ let(:response_body) { JSON.parse(subject.body, symbolize_names: true)}
11
8
  let(:webhook_json) { webhook_hash.to_json }
12
9
  let(:webhook_hash) do
13
10
  {
@@ -37,40 +34,29 @@ describe "Creating a webhook" do
37
34
  context "with invalid attributes" do
38
35
  let(:webhook_hash) { {} }
39
36
 
40
- it "returns a 400" do
41
- subject
42
- expect(last_response.status).to be 400
43
- end
37
+ its(:status) { is_expected.to be 400 }
44
38
 
45
- it "returns a JSON content type" do
46
- subject
47
- expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
48
- end
39
+ it "returns a JSON content type" do
40
+ expect(subject.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
41
+ end
49
42
 
50
43
  it "returns the validation errors" do
51
- subject
52
44
  expect(response_body[:errors]).to_not be_empty
53
45
  end
54
46
  end
55
47
 
56
48
  context "with valid attributes" do
57
- it "returns a 201 response" do
58
- subject
59
- expect(last_response.status).to be 201
60
- end
49
+ its(:status) { is_expected.to be 201 }
61
50
 
62
51
  it "returns the Location header" do
63
- subject
64
- expect(last_response.headers['Location']).to match(%r{http://example.org/webhooks/.+})
52
+ expect(subject.headers['Location']).to match(%r{http://example.org/webhooks/.+})
65
53
  end
66
54
 
67
55
  it "returns a JSON Content Type" do
68
- subject
69
- expect(last_response.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
56
+ expect(subject.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
70
57
  end
71
58
 
72
59
  it "returns the newly created webhook" do
73
- subject
74
60
  expect(response_body).to include webhook_hash
75
61
  end
76
62
  end
@@ -83,10 +69,7 @@ describe "Creating a webhook" do
83
69
  webhook_hash[:provider] = { name: "Some Provider" }
84
70
  end
85
71
 
86
- it "returns a 201 response" do
87
- subject
88
- expect(last_response.status).to be 201
89
- end
72
+ its(:status) { is_expected.to be 201 }
90
73
 
91
74
  it "creates a webhook without a consumer" do
92
75
  subject
@@ -101,10 +84,7 @@ describe "Creating a webhook" do
101
84
  webhook_hash[:consumer] = { name: "Some Consumer" }
102
85
  end
103
86
 
104
- it "returns a 201 response" do
105
- subject
106
- expect(last_response.status).to be 201
107
- end
87
+ its(:status) { is_expected.to be 201 }
108
88
 
109
89
  it "creates a webhook without a provider" do
110
90
  subject
@@ -116,10 +96,7 @@ describe "Creating a webhook" do
116
96
  context "with no consumer or provider" do
117
97
  let(:path) { "/webhooks" }
118
98
 
119
- it "returns a 201 response" do
120
- subject
121
- expect(last_response.status).to be 201
122
- end
99
+ its(:status) { is_expected.to be 201 }
123
100
 
124
101
  it "creates a webhook without a provider" do
125
102
  subject
@@ -127,4 +104,12 @@ describe "Creating a webhook" do
127
104
  expect(PactBroker::Webhooks::Webhook.first.provider).to be nil
128
105
  end
129
106
  end
107
+
108
+ context "with a UUID" do
109
+ let(:path) { "/webhooks/1234123412341234" }
110
+
111
+ subject { put(path, webhook_json, headers) }
112
+
113
+ its(:status) { is_expected.to be 201 }
114
+ end
130
115
  end
@@ -1,9 +1,7 @@
1
1
  require 'support/test_data_builder'
2
2
  require 'pact_broker/api/pact_broker_urls'
3
3
 
4
-
5
4
  describe "Delete a verification" do
6
-
7
5
  let!(:verification) do
8
6
  TestDataBuilder.new
9
7
  .create_pact_with_verification("Foo", "1", "Bar", "2")
@@ -13,7 +13,9 @@ module PactBroker
13
13
  allow(PactBroker::Pacts::Service).to receive(:find_latest_pact).and_return(pact)
14
14
  allow(PactBroker::Verifications::Service).to receive(:find_latest_verification_for).and_return(verification)
15
15
  allow(PactBroker::Badges::Service).to receive(:pact_verification_badge).and_return("badge")
16
+ allow(PactBroker::Badges::Service).to receive(:pact_verification_badge_url).and_return("http://badge")
16
17
  allow(PactBroker::Verifications::PseudoBranchStatus).to receive(:new).and_return(pseudo_branch_verification_status)
18
+ allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(false)
17
19
  end
18
20
 
19
21
  let(:pact) { instance_double("PactBroker::Domain::Pact", consumer: consumer, provider: provider, consumer_version_number: "2", revision_number: "1") }
@@ -47,7 +49,6 @@ module PactBroker
47
49
  end
48
50
 
49
51
  context "when enable_public_badge_access is true" do
50
-
51
52
  before do
52
53
  PactBroker.configuration.enable_public_badge_access = true
53
54
  end
@@ -67,25 +68,48 @@ module PactBroker
67
68
  subject
68
69
  end
69
70
 
70
- it "creates a badge" do
71
- expect(PactBroker::Badges::Service).to receive(:pact_verification_badge).with(pact, nil, false, :verified)
72
- subject
73
- end
71
+ context "when can_provide_badge_using_redirect? is false" do
72
+ before do
73
+ allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(false)
74
+ end
74
75
 
75
- it "returns a 200 status" do
76
- expect(subject.status).to eq 200
77
- end
76
+ it "creates a badge" do
77
+ expect(PactBroker::Badges::Service).to receive(:pact_verification_badge).with(pact, nil, false, :verified)
78
+ subject
79
+ end
78
80
 
79
- it "does not allow caching" do
80
- expect(subject.headers['Cache-Control']).to eq 'no-cache'
81
- end
81
+ it "returns a 200 status" do
82
+ expect(subject.status).to eq 200
83
+ end
82
84
 
83
- it "returns the badge" do
84
- expect(subject.body).to end_with "badge"
85
+ it "does not allow caching" do
86
+ expect(subject.headers['Cache-Control']).to eq 'no-cache'
87
+ end
88
+
89
+ it "returns the badge" do
90
+ expect(subject.body).to end_with "badge"
91
+ end
92
+
93
+ it "returns a comment with the consumer and provider numbers" do
94
+ expect(subject.body).to include "<!-- consumer version 2 revision 1 provider version 3 number 7 -->"
95
+ end
85
96
  end
86
97
 
87
- it "returns a comment with the consumer and provider numbers" do
88
- expect(subject.body).to include "<!-- consumer version 2 revision 1 provider version 3 number 7 -->"
98
+ context "when can_provide_badge_using_redirect? is true" do
99
+ before do
100
+ allow(PactBroker::Badges::Service).to receive(:can_provide_badge_using_redirect?).and_return(true)
101
+ end
102
+
103
+ it "determines the URL of the badge to redirect to" do
104
+ expect(PactBroker::Badges::Service).to receive(:pact_verification_badge_url).with(pact, nil, false, :verified)
105
+ subject
106
+ end
107
+
108
+ it "returns a 301 redirect to the badge URL" do
109
+ expect(subject.status).to eq 307
110
+ expect(subject.headers['Location']).to eq 'http://badge'
111
+ expect(subject.headers['Cache-Control']).to eq 'no-cache'
112
+ end
89
113
  end
90
114
 
91
115
  context "when the label param is specified" do
@@ -2,11 +2,8 @@ require 'spec_helper'
2
2
  require 'pact_broker/api/resources/webhook'
3
3
 
4
4
  module PactBroker::Api
5
-
6
5
  module Resources
7
-
8
6
  describe Webhook do
9
-
10
7
  before do
11
8
  allow(PactBroker::Webhooks::Service).to receive(:find_by_uuid).and_return(webhook)
12
9
  end
@@ -23,15 +20,14 @@ module PactBroker::Api
23
20
  end
24
21
 
25
22
  context "when the webhook exists" do
23
+ before do
24
+ allow(Decorators::WebhookDecorator).to receive(:new).and_return(decorator)
25
+ end
26
26
 
27
27
  let(:webhook) { double("webhook") }
28
28
  let(:decorator) { double(Decorators::WebhookDecorator, to_json: json)}
29
29
  let(:json) { {some: 'json'}.to_json }
30
30
 
31
- before do
32
- allow(Decorators::WebhookDecorator).to receive(:new).and_return(decorator)
33
- end
34
-
35
31
  it "finds the webhook by UUID" do
36
32
  expect(PactBroker::Webhooks::Service).to receive(:find_by_uuid).with('some-uuid')
37
33
  subject
@@ -56,14 +52,61 @@ module PactBroker::Api
56
52
  end
57
53
 
58
54
  describe "PUT" do
55
+ before do
56
+ allow(Decorators::WebhookDecorator).to receive(:new).and_return(decorator)
57
+ allow(PactBroker::Webhooks::Service).to receive(:create).and_return(created_webhook)
58
+ allow_any_instance_of(Webhook).to receive(:consumer).and_return(consumer)
59
+ allow_any_instance_of(Webhook).to receive(:provider).and_return(provider)
60
+ allow_any_instance_of(Webhook).to receive(:webhook_validation_errors?).and_return(false)
61
+ end
62
+
63
+ let(:consumer) { double('consumer') }
64
+ let(:provider) { double('provider') }
65
+ let(:webhook) { double("webhook") }
66
+ let(:decorator) { double(Decorators::WebhookDecorator, from_json: parsed_webhook, to_json: json)}
67
+ let(:json) { {some: 'json'}.to_json }
68
+
69
+ let(:parsed_webhook) { double('parsed_webhook') }
70
+ let(:created_webhook) { double('created_webhook') }
71
+ let(:webhook) { nil }
72
+ let(:webhook_json) { load_fixture('webhook_valid.json') }
73
+ let(:uuid) { 'some-uuid' }
74
+
75
+ subject { put("/webhooks/#{uuid}", webhook_json, 'CONTENT_TYPE' => 'application/json') }
76
+
77
+ it "validates the UUID" do
78
+ expect_any_instance_of(Webhook).to receive(:webhook_validation_errors?).with(parsed_webhook, uuid)
79
+ subject
80
+ end
81
+
59
82
  context "when the webhook does not exist" do
60
- let(:webhook) { nil }
61
- let(:webhook_json) { load_fixture('webhook_valid.json') }
83
+ it "creates the webhook" do
84
+ expect(PactBroker::Webhooks::Service).to receive(:create).with(uuid, parsed_webhook, consumer, provider)
85
+ subject
86
+ end
62
87
 
63
- subject { put '/webhooks/some-uuid', webhook_json, {'CONTENT_TYPE' => 'application/json'}; last_response }
88
+ its(:status) { is_expected.to eq 201 }
64
89
 
65
- it "returns a 404" do
66
- expect(subject).to be_a_404_response
90
+ it "returns the JSON respresentation of the webhook" do
91
+ expect(subject.body).to eq json
92
+ end
93
+ end
94
+
95
+ context "when the webhook does exist" do
96
+ before do
97
+ allow(PactBroker::Webhooks::Service).to receive(:update_by_uuid).and_return(created_webhook)
98
+ end
99
+ let(:webhook) { double('existing webhook') }
100
+
101
+ its(:status) { is_expected.to eq 200 }
102
+
103
+ it "updates the webhook" do
104
+ expect(PactBroker::Webhooks::Service).to receive(:update_by_uuid).with(uuid, JSON.parse(webhook_json))
105
+ subject
106
+ end
107
+
108
+ it "returns the JSON respresentation of the webhook" do
109
+ expect(subject.body).to eq json
67
110
  end
68
111
  end
69
112
  end
@@ -14,7 +14,7 @@ module PactBroker
14
14
  let(:expected_url) { "https://img.shields.io/badge/#{expected_left_text}-#{expected_right_text}-#{expected_color}.svg" }
15
15
  let(:expected_color) { "brightgreen" }
16
16
  let(:expected_right_text) { "verified" }
17
- let(:expected_left_text) { "foo--bar%2Fthing__blah%20pact" }
17
+ let(:expected_left_text) { "foo--bar%2fthing__blah%20pact" }
18
18
  let(:response_status) { 200 }
19
19
  let!(:http_request) do
20
20
  stub_request(:get, expected_url).to_return(:status => response_status, :body => "svg")
@@ -22,6 +22,8 @@ module PactBroker
22
22
 
23
23
  subject { PactBroker::Badges::Service.pact_verification_badge pact, label, initials, pseudo_branch_verification_status }
24
24
 
25
+ let(:pact_verification_badge_url) { PactBroker::Badges::Service.pact_verification_badge_url(pact, label, initials, pseudo_branch_verification_status) }
26
+
25
27
  before do
26
28
  Service.clear_cache
27
29
  allow(Service).to receive(:logger).and_return(logger)
@@ -41,48 +43,53 @@ module PactBroker
41
43
  it "creates a badge with the consumer and provider names" do
42
44
  subject
43
45
  expect(http_request).to have_been_made
46
+ expect(pact_verification_badge_url).to eq URI(expected_url)
44
47
  end
45
48
 
46
49
  context "when initials is true" do
47
- let(:expected_left_text) { "fb%2Ftb%20pact" }
50
+ let(:expected_left_text) { "fb%2ftb%20pact" }
48
51
  let(:initials) { true }
49
52
 
50
53
  it "creates a badge with the consumer and provider initials" do
51
54
  subject
52
55
  expect(http_request).to have_been_made
56
+ expect(pact_verification_badge_url).to eq URI(expected_url)
53
57
  end
54
58
  end
55
59
 
56
60
  context "when initials is true but the consumer and provider names only contain one word" do
57
- let(:expected_left_text) { "foo%2Fbar%20pact" }
61
+ let(:expected_left_text) { "foo%2fbar%20pact" }
58
62
  let(:initials) { true }
59
63
  let(:pact) { double("pact", consumer_name: "Foo", provider_name: "Bar") }
60
64
 
61
65
  it "creates a badge with the consumer and provider names, not initials" do
62
66
  subject
63
67
  expect(http_request).to have_been_made
68
+ expect(pact_verification_badge_url).to eq URI(expected_url)
64
69
  end
65
70
  end
66
71
 
67
72
  context "when initials is true but the consumer and provider names are one camel cased word" do
68
- let(:expected_left_text) { "fa%2Fbp%20pact" }
73
+ let(:expected_left_text) { "fa%2fbp%20pact" }
69
74
  let(:initials) { true }
70
75
  let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "barProvider") }
71
76
 
72
77
  it "creates a badge with the consumer and provider names, not initials" do
73
78
  subject
74
79
  expect(http_request).to have_been_made
80
+ expect(pact_verification_badge_url).to eq URI(expected_url)
75
81
  end
76
82
  end
77
83
 
78
84
  context "when initials is true but the consumer and provider names are one camel cased word" do
79
- let(:expected_left_text) { "fa%2Fdat%20pact" }
85
+ let(:expected_left_text) { "fa%2fdat%20pact" }
80
86
  let(:initials) { true }
81
87
  let(:pact) { double("pact", consumer_name: "FooApp", provider_name: "doAThing") }
82
88
 
83
89
  it "creates a badge with the consumer and provider names, not initials" do
84
90
  subject
85
91
  expect(http_request).to have_been_made
92
+ expect(pact_verification_badge_url).to eq URI(expected_url)
86
93
  end
87
94
  end
88
95
  end
@@ -94,6 +101,7 @@ module PactBroker
94
101
  it "creates a badge with only the consumer name" do
95
102
  subject
96
103
  expect(http_request).to have_been_made
104
+ expect(pact_verification_badge_url).to eq URI(expected_url)
97
105
  end
98
106
 
99
107
  context "when initials is true" do
@@ -103,6 +111,7 @@ module PactBroker
103
111
  it "creates a badge with only the consumer initials" do
104
112
  subject
105
113
  expect(http_request).to have_been_made
114
+ expect(pact_verification_badge_url).to eq URI(expected_url)
106
115
  end
107
116
  end
108
117
  end
@@ -114,6 +123,7 @@ module PactBroker
114
123
  it "creates a badge with only the provider name" do
115
124
  subject
116
125
  expect(http_request).to have_been_made
126
+ expect(pact_verification_badge_url).to eq URI(expected_url)
117
127
  end
118
128
 
119
129
  context "when initials is true" do
@@ -123,6 +133,7 @@ module PactBroker
123
133
  it "creates a badge with only the provider initials" do
124
134
  subject
125
135
  expect(http_request).to have_been_made
136
+ expect(pact_verification_badge_url).to eq URI(expected_url)
126
137
  end
127
138
  end
128
139
  end
@@ -131,6 +142,7 @@ module PactBroker
131
142
  it "create a green badge with left text 'verified'" do
132
143
  subject
133
144
  expect(http_request).to have_been_made
145
+ expect(pact_verification_badge_url).to eq URI(expected_url)
134
146
  end
135
147
  end
136
148
 
@@ -142,6 +154,7 @@ module PactBroker
142
154
  it "create a lightgrey badge with left text 'unknown'" do
143
155
  subject
144
156
  expect(http_request).to have_been_made
157
+ expect(pact_verification_badge_url).to eq URI(expected_url)
145
158
  end
146
159
  end
147
160
 
@@ -153,6 +166,7 @@ module PactBroker
153
166
  it "create a red badge with left text 'failed'" do
154
167
  subject
155
168
  expect(http_request).to have_been_made
169
+ expect(pact_verification_badge_url).to eq URI(expected_url)
156
170
  end
157
171
  end
158
172
 
@@ -164,6 +178,7 @@ module PactBroker
164
178
  it "create a orange badge with left text 'changed'" do
165
179
  subject
166
180
  expect(http_request).to have_been_made
181
+ expect(pact_verification_badge_url).to eq URI(expected_url)
167
182
  end
168
183
  end
169
184
 
@@ -7,7 +7,7 @@ module PactBroker
7
7
  describe ".call" do
8
8
 
9
9
  class MockConfig
10
- attr_accessor :foo, :bar, :nana, :meep, :lalala, :meow, :peebo, :whitelist
10
+ attr_accessor :foo, :bar, :nana, :meep, :lalala, :meow, :peebo, :whitelist, :blah
11
11
  end
12
12
 
13
13
  before do
@@ -20,6 +20,7 @@ module PactBroker
20
20
  Setting.create(name: 'peebo', type: 'string', value: nil)
21
21
  Setting.create(name: 'unknown', type: 'string', value: nil)
22
22
  Setting.create(name: 'whitelist', type: 'space_delimited_string_list', value: 'foo bar')
23
+ Setting.create(name: 'blah', type: 'symbol', value: 'boop')
23
24
  end
24
25
 
25
26
  let(:configuration) { MockConfig.new }
@@ -36,6 +37,11 @@ module PactBroker
36
37
  expect(configuration.bar).to eq "bar"
37
38
  end
38
39
 
40
+ it "loads a Symbol setting" do
41
+ subject
42
+ expect(configuration.blah).to eq :boop
43
+ end
44
+
39
45
  it "loads an Integer setting" do
40
46
  subject
41
47
  expect(configuration.nana).to eq 1
@@ -7,7 +7,7 @@ module PactBroker
7
7
  describe Save do
8
8
 
9
9
  describe "#call" do
10
- let(:setting_names) { [:foo, :bar, :wiffle, :meep, :flop, :peebo, :lalala, :meow, :whitelist] }
10
+ let(:setting_names) { [:foo, :bar, :wiffle, :meep, :flop, :peebo, :lalala, :meow, :whitelist, :blah] }
11
11
  let(:configuration) do
12
12
  double("PactBroker::Configuration",
13
13
  foo: true,
@@ -15,6 +15,7 @@ module PactBroker
15
15
  wiffle: ["a", "b", "c"],
16
16
  meep: {a: 'thing'},
17
17
  flop: nil,
18
+ blah: :boop,
18
19
  peebo: 1,
19
20
  lalala: 1.2,
20
21
  meow: Object.new,
@@ -23,6 +24,13 @@ module PactBroker
23
24
 
24
25
  subject { Save.call(configuration, setting_names) }
25
26
 
27
+ it "saves a Symbol" do
28
+ subject
29
+ setting = Setting.find(name: 'blah')
30
+ expect(setting.type).to eq 'symbol'
31
+ expect(setting.value).to eq 'boop'
32
+ end
33
+
26
34
  it "saves a false config setting to the database" do
27
35
  subject
28
36
  setting = Setting.find(name: 'foo')
@@ -24,7 +24,6 @@ module PactBroker
24
24
 
25
25
  subject { BuildVerifiablePactNotices.call(verifiable_pact, pact_url, options) }
26
26
 
27
-
28
27
  context "when include_pending_status is true" do
29
28
  let(:expected_notices) do
30
29
  [
@@ -14,9 +14,38 @@ module PactBroker
14
14
  allow(Service).to receive(:logger).and_return(logger)
15
15
  end
16
16
 
17
- let(:td) { TestDataBuilder.new }
18
17
  let(:logger) { double('logger').as_null_object }
19
18
 
19
+ describe "validate - integration test" do
20
+ let(:invalid_webhook) { PactBroker::Domain::Webhook.new }
21
+
22
+ context "with no uuid" do
23
+ subject { Service.errors(invalid_webhook) }
24
+
25
+ it "does not contain an error for the uuid" do
26
+ expect(subject.messages).to_not have_key('uuid')
27
+ end
28
+ end
29
+
30
+ context "with a uuid" do
31
+ subject { Service.errors(invalid_webhook, '') }
32
+
33
+ it "merges the uuid errors with the webhook errors" do
34
+ expect(subject.messages['uuid'].first).to include "can only contain"
35
+ end
36
+ end
37
+ end
38
+
39
+ describe ".valid_uuid_format?" do
40
+ it 'does something' do
41
+ expect(Service.valid_uuid_format?("_-bcdefghigHIJKLMNOP")).to be true
42
+ expect(Service.valid_uuid_format?("HIJKLMNOP")).to be false
43
+ expect(Service.valid_uuid_format?("abcdefghigHIJKLMNOP\\")).to be false
44
+ expect(Service.valid_uuid_format?("abcdefghigHIJKLMNOP#")).to be false
45
+ expect(Service.valid_uuid_format?("abcdefghigHIJKLMNOP$")).to be false
46
+ end
47
+ end
48
+
20
49
  describe ".delete_by_uuid" do
21
50
  before do
22
51
  td.create_pact_with_hierarchy
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pact_broker
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.48.0
4
+ version: 2.49.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bethany Skurrie
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2020-02-08 00:00:00.000000000 Z
13
+ date: 2020-02-13 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty