pact_broker 1.18.0.beta.1 → 1.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -2
  3. data/README.md +12 -12
  4. data/example/{heroku → basic_auth}/Gemfile +0 -0
  5. data/example/{heroku → basic_auth}/Procfile +0 -0
  6. data/example/{heroku → basic_auth}/README.md +1 -1
  7. data/example/basic_auth/config.ru +19 -0
  8. data/example/config.ru +0 -2
  9. data/lib/pact_broker/api/decorators/versions_decorator.rb +1 -1
  10. data/lib/pact_broker/api/resources/versions.rb +1 -1
  11. data/lib/pact_broker/app.rb +7 -6
  12. data/lib/pact_broker/configuration.rb +4 -42
  13. data/lib/pact_broker/domain/order_versions.rb +15 -5
  14. data/lib/pact_broker/domain/webhook.rb +0 -1
  15. data/lib/pact_broker/domain/webhook_request.rb +6 -4
  16. data/lib/pact_broker/logging.rb +4 -0
  17. data/lib/pact_broker/pacticipants/repository.rb +2 -1
  18. data/lib/pact_broker/pacticipants/service.rb +2 -2
  19. data/lib/pact_broker/version.rb +1 -1
  20. data/lib/pact_broker/webhooks/job.rb +46 -0
  21. data/lib/pact_broker/webhooks/service.rb +9 -8
  22. data/lib/pact_broker/webhooks/webhook.rb +1 -1
  23. data/pact_broker.gemspec +2 -1
  24. data/pact_broker_client-pact_broker.json +4 -4
  25. data/script/foo-bar.json +22 -0
  26. data/script/publish-new.sh +7 -0
  27. data/script/publish.sh +2 -2
  28. data/script/recreate-pg-db.sh +7 -0
  29. data/spec/fixtures/a_consumer-a_provider-2.json +1 -1
  30. data/spec/fixtures/a_consumer-a_provider-3.json +1 -1
  31. data/spec/fixtures/a_consumer-a_provider-conflict.json +1 -1
  32. data/spec/fixtures/a_consumer-a_provider-merged.json +2 -2
  33. data/spec/fixtures/a_consumer-a_provider.json +1 -1
  34. data/spec/fixtures/consumer-provider.json +1 -1
  35. data/spec/fixtures/renderer_pact.json +1 -1
  36. data/spec/lib/pact_broker/configuration_spec.rb +2 -22
  37. data/spec/lib/pact_broker/domain/order_versions_spec.rb +30 -10
  38. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +3 -1
  39. data/spec/lib/pact_broker/pacticipants/repository_spec.rb +16 -0
  40. data/spec/lib/pact_broker/webhooks/job_spec.rb +67 -0
  41. data/spec/lib/pact_broker/webhooks/service_spec.rb +40 -3
  42. data/spec/support/provider_state_builder.rb +36 -8
  43. metadata +29 -12
  44. data/example/heroku/config.ru +0 -12
  45. data/lib/pact_broker/configuration/configure_basic_auth.rb +0 -83
  46. data/spec/lib/pact_broker/configuration/configure_basic_auth_spec.rb +0 -267
@@ -1,6 +1,8 @@
1
1
  require 'pact_broker/repositories'
2
2
  require 'pact_broker/logging'
3
+ require 'pact_broker/webhooks/job'
3
4
  require 'base64'
5
+ require 'securerandom'
4
6
 
5
7
  module PactBroker
6
8
 
@@ -50,6 +52,7 @@ module PactBroker
50
52
 
51
53
  def self.execute_webhooks pact
52
54
  webhooks = webhook_repository.find_by_consumer_and_provider pact.consumer, pact.provider
55
+
53
56
  if webhooks.any?
54
57
  run_later(webhooks)
55
58
  else
@@ -57,15 +60,13 @@ module PactBroker
57
60
  end
58
61
  end
59
62
 
60
- # TODO background job?
61
63
  def self.run_later webhooks
62
- Thread.new do
63
- webhooks.each do | webhook |
64
- begin
65
- webhook.execute
66
- rescue StandardError => e
67
- # Exceptions are already logged, no need to log again.
68
- end
64
+ webhooks.each do | webhook |
65
+ begin
66
+ logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
67
+ Job.perform_async webhook: webhook
68
+ rescue StandardError => e
69
+ log_error e
69
70
  end
70
71
  end
71
72
  end
@@ -46,7 +46,7 @@ module PactBroker
46
46
  end
47
47
 
48
48
  def request_attributes
49
- values.merge(headers: parsed_headers, body: parsed_body, password: plain_text_password)
49
+ values.merge(headers: parsed_headers, body: parsed_body, password: plain_text_password, uuid: uuid)
50
50
  end
51
51
 
52
52
  def plain_text_password
data/pact_broker.gemspec CHANGED
@@ -35,9 +35,10 @@ Gem::Specification.new do |gem|
35
35
  gem.add_runtime_dependency 'pact-support', '~>0.4', '>=0.4.2'
36
36
  gem.add_runtime_dependency 'padrino-core', '~>0.12.4'
37
37
  gem.add_runtime_dependency 'haml', '~>4.0'
38
+ gem.add_runtime_dependency 'sucker_punch', '~>2.0'
38
39
 
39
40
  gem.add_development_dependency 'sqlite3'
40
- gem.add_development_dependency 'pry'
41
+ gem.add_development_dependency 'pry-byebug'
41
42
  gem.add_development_dependency 'rake', '~>10.0'
42
43
  gem.add_development_dependency 'fakefs', '~>0.4'
43
44
  gem.add_development_dependency 'mysql2', '~>0.3.15'
@@ -35,7 +35,7 @@
35
35
  },
36
36
  {
37
37
  "description": "a request to publish a pact",
38
- "provider_state": "an error occurs while publishing a pact",
38
+ "providerState": "an error occurs while publishing a pact",
39
39
  "request": {
40
40
  "method": "put",
41
41
  "path": "/pacticipant/Condor/versions/1.3.0/pacts/Pricing%20Service",
@@ -72,7 +72,7 @@
72
72
  },
73
73
  {
74
74
  "description": "a request to retrieve the repository URL of the 'Pricing Service'",
75
- "provider_state": "the 'Pricing Service' does not exist in the pact-broker",
75
+ "providerState": "the 'Pricing Service' does not exist in the pact-broker",
76
76
  "request": {
77
77
  "method": "get",
78
78
  "path": "/pacticipant/Pricing%20Service/repository_url",
@@ -90,7 +90,7 @@
90
90
 
91
91
  {
92
92
  "description": "a request to register the repository URL of a pacticipant",
93
- "provider_state": "the 'Pricing Service' does not exist in the pact-broker",
93
+ "providerState": "the 'Pricing Service' does not exist in the pact-broker",
94
94
  "request": {
95
95
  "method": "patch",
96
96
  "path": "/pacticipant/Pricing%20Service",
@@ -108,7 +108,7 @@
108
108
  },
109
109
  {
110
110
  "description": "a request to retrieve the repository URL of the 'Pricing Service'",
111
- "provider_state": "the 'Pricing Service' already exists in the pact-broker",
111
+ "providerState": "the 'Pricing Service' already exists in the pact-broker",
112
112
  "request": {
113
113
  "method": "get",
114
114
  "path": "/pacticipant/Pricing%20Service/repository_url",
@@ -0,0 +1,22 @@
1
+ {
2
+ "consumer": {
3
+ "name": "Foo"
4
+ },
5
+ "provider": {
6
+ "name": "Bar"
7
+ },
8
+ "interactions": [
9
+ {
10
+ "description" : "a request for something",
11
+ "providerState": null,
12
+ "request": {
13
+ "method": "get",
14
+ "path" : "/something"
15
+ },
16
+ "response": {
17
+ "status": 200,
18
+ "body" : "something"
19
+ }
20
+ }
21
+ ]
22
+ }
@@ -0,0 +1,7 @@
1
+ # set -x
2
+ BODY=$(ruby -e "require 'json'; j = JSON.parse(File.read('script/foo-bar.json')); j['interactions'][0]['providerState'] = 'it is ' + Time.now.to_s; puts j.to_json")
3
+ echo ${BODY} >> tmp.json
4
+ curl -v -XPUT \-H "Content-Type: application/json" \
5
+ -d@tmp.json \
6
+ http://127.0.0.1:9292/pacts/provider/Bar/consumer/Foo/version/1.0.0
7
+ rm tmp.json
data/script/publish.sh CHANGED
@@ -1,3 +1,3 @@
1
1
  curl -v -XPUT \-H "Content-Type: application/json" \
2
- -d@spec/fixtures/a_consumer-a_provider.json \
3
- http://localhost:9292/pacts/provider/A%20Provider/consumer/A%20Consumer/version/1.0.0
2
+ -d@script/foo-bar.json \
3
+ http://127.0.0.1:9292/pacts/provider/Bar/consumer/Foo/version/1.0.0
@@ -0,0 +1,7 @@
1
+ psql postgres -c "drop database pact_broker;"
2
+ psql postgres -c "create database pact_broker;"
3
+ psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE pact_broker to pact_broker;"
4
+ ip=$(ifconfig en0 | sed -n -e '/inet/s/.*inet \([0-9.]*\) netmask .*/\1/p')
5
+ echo ""
6
+ echo "run the following command to set your environment variables:"
7
+ echo "export PACT_BROKER_DATABASE_HOST=${ip}"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "a request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "post",
14
14
  "path" : "/something"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "another request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "get",
14
14
  "path" : "/something_else"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "a request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "post",
14
14
  "path" : "/something"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "a request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "get",
14
14
  "path" : "/something"
@@ -20,7 +20,7 @@
20
20
  },
21
21
  {
22
22
  "description" : "another request for something",
23
- "provider_state": null,
23
+ "providerState": null,
24
24
  "request": {
25
25
  "method": "get",
26
26
  "path" : "/something_else"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "a request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "get",
14
14
  "path" : "/something"
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description" : "a request for something",
11
- "provider_state": null,
11
+ "providerState": null,
12
12
  "request": {
13
13
  "method": "get",
14
14
  "path" : "/something",
@@ -8,7 +8,7 @@
8
8
  "interactions": [
9
9
  {
10
10
  "description": "a request for alligators",
11
- "provider_state": "alligators exist",
11
+ "providerState": "alligators exist",
12
12
  "request": {
13
13
  "method": "get",
14
14
  "path": "/alligators"
@@ -7,35 +7,15 @@ module PactBroker
7
7
 
8
8
  context "default configuration" do
9
9
  describe ".html_pact_renderer" do
10
+
10
11
  let(:pact) { double('pact') }
11
12
 
12
13
  it "calls the inbuilt HtmlPactRenderer" do
13
14
  expect(PactBroker::Api::Renderers::HtmlPactRenderer).to receive(:call).with(pact)
14
15
  PactBroker.configuration.html_pact_renderer.call pact
15
16
  end
16
- end
17
-
18
- describe "protect_with_basic_auth" do
19
- let(:config) do
20
- config = Configuration.new
21
- config.protect_with_basic_auth [:foo, :bar], {some: 'credentials'}
22
- config.protect_with_basic_auth :foo, {some: 'othercredentials'}
23
- config
24
- end
25
17
 
26
- it "groups credentials by scope" do
27
- expect(config.basic_auth_credentials_list_for(:foo)).to eq([{some: 'credentials'},{some: 'othercredentials'}])
28
- expect(config.basic_auth_credentials_list_for(:bar)).to eq([{some: 'credentials'}])
29
- end
30
-
31
- describe "protect_with_basic_auth?" do
32
- it "indicates whether a scope is protected" do
33
- expect(config.protect_with_basic_auth?(:foo)).to be true
34
- expect(config.protect_with_basic_auth?(:bar)).to be true
35
- expect(config.protect_with_basic_auth?(:wiffle)).to be false
36
- end
37
- end
38
18
  end
39
19
  end
40
20
  end
41
- end
21
+ end
@@ -4,20 +4,40 @@ require 'pact_broker/domain/order_versions.rb'
4
4
 
5
5
  describe PactBroker::Domain::OrderVersions do
6
6
 
7
+ context "when order_versions_by_date is false (the default)" do
8
+ before do
9
+ ProviderStateBuilder.new
10
+ .create_condor
11
+ .create_condor_version('1.3.0')
12
+ .create_condor_version('1.5.0')
13
+ .create_condor_version('1.4.0')
14
+ .create_condor_version('1.6.0')
15
+ end
7
16
 
8
- before do
9
- ProviderStateBuilder.new
10
- .create_condor
11
- .create_condor_version('1.3.0')
12
- .create_condor_version('1.5.0')
13
- .create_condor_version('1.4.0')
17
+ let(:ordered_versions) { PactBroker::Domain::Version.order(:order).all.collect(&:number) }
18
+ let(:condor) { PactBroker::Domain::Pacticipant.where(name: 'Condor').single_record }
19
+
20
+ it "orders the versions so they can be loaded from the database in order" do
21
+ expect(ordered_versions).to eq(['1.3.0', '1.4.0', '1.5.0', '1.6.0'])
22
+ end
14
23
  end
15
24
 
16
- let(:ordered_versions) { PactBroker::Domain::Version.order(:order).all.collect(&:number) }
17
- let(:condor) { PactBroker::Domain::Pacticipant.where(name: 'Condor').single_record }
25
+ context "when order_versions_by_date is true (not recommended)" do
26
+ before do
27
+ allow(PactBroker.configuration).to receive(:order_versions_by_date).and_return(true)
28
+ end
29
+ let(:consumer) { ProviderStateBuilder.new.create_consumer.and_return(:consumer) }
30
+ let!(:version_1) { PactBroker::Domain::Version.create(pacticipant_id: consumer.id, number: '2', created_at: DateTime.new(2017)) }
31
+ let!(:version_2) { PactBroker::Domain::Version.create(pacticipant_id: consumer.id, number: '1', created_at: DateTime.new(2017)) }
32
+ let!(:version_3) { PactBroker::Domain::Version.create(pacticipant_id: consumer.id, number: '3', created_at: DateTime.new(2016)) }
33
+ let!(:version_4) { PactBroker::Domain::Version.create(pacticipant_id: consumer.id, number: '4', created_at: DateTime.new(2018)) }
34
+
35
+ let(:ordered_versions) { PactBroker::Domain::Version.order(:order).all.collect(&:number) }
36
+
37
+ it "orders by date, then id" do
38
+ expect(ordered_versions).to eq(['3', '2', '1', '4'])
39
+ end
18
40
 
19
- it "orders the versions so they can be loaded from the database in order" do
20
- expect(ordered_versions).to eq(['1.3.0','1.4.0', '1.5.0'])
21
41
  end
22
42
 
23
43
  end
@@ -64,7 +64,9 @@ module PactBroker
64
64
 
65
65
  it "logs the response" do
66
66
  allow(PactBroker.logger).to receive(:info)
67
- expect(PactBroker.logger).to receive(:info).with(/response.*302.*respbod/)
67
+ allow(PactBroker.logger).to receive(:debug)
68
+ expect(PactBroker.logger).to receive(:info).with(/response.*302/)
69
+ expect(PactBroker.logger).to receive(:debug).with(/respbod/)
68
70
  subject.execute
69
71
  end
70
72
 
@@ -53,6 +53,22 @@ module PactBroker
53
53
 
54
54
  end
55
55
 
56
+ describe "#find_all_pacticipant_versions_in_reverse_order" do
57
+ before do
58
+ ProviderStateBuilder.new
59
+ .create_consumer("Foo")
60
+ .create_consumer_version("1.2.3")
61
+ .create_consumer_version("4.5.6")
62
+ .create_consumer("Bar")
63
+ .create_consumer_version("8.9.0")
64
+ end
65
+
66
+ subject { Repository.new.find_all_pacticipant_versions_in_reverse_order "Foo" }
67
+
68
+ it "returns all the application versions for the given consumer" do
69
+ expect(subject.collect(&:number)).to eq ["4.5.6", "1.2.3"]
70
+ end
71
+ end
56
72
 
57
73
  end
58
74
  end
@@ -0,0 +1,67 @@
1
+ require 'pact_broker/webhooks/job'
2
+
3
+ module PactBroker
4
+ module Webhooks
5
+ describe Job do
6
+
7
+ before do
8
+ allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now)
9
+ end
10
+
11
+ let(:webhook) { double("webhook", uuid: '1234') }
12
+
13
+ context "when an error occurs for the first time" do
14
+ before do
15
+ allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).and_raise("an error")
16
+ end
17
+
18
+ it "reschedules the job in 10 seconds" do
19
+ expect(Job).to receive(:perform_in).with(10, {webhook: webhook, error_count: 1})
20
+ Job.new.perform(webhook: webhook)
21
+ end
22
+ end
23
+
24
+ context "when the webhook execution result is not successful for the first time" do
25
+ before do
26
+ allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).and_return(instance_double("PactBroker::Domain::WebhookExecutionResult", success?: false))
27
+ end
28
+
29
+ it "reschedules the job in 10 seconds" do
30
+ expect(Job).to receive(:perform_in).with(10, {webhook: webhook, error_count: 1})
31
+ Job.new.perform(webhook: webhook)
32
+ end
33
+ end
34
+
35
+ context "when an error occurs for the second time" do
36
+ before do
37
+ allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).and_raise("an error")
38
+ end
39
+
40
+ it "reschedules the job in 60 seconds" do
41
+ expect(Job).to receive(:perform_in).with(60, {webhook: webhook, error_count: 2 })
42
+ Job.new.perform(webhook: webhook, error_count: 1)
43
+ end
44
+ end
45
+
46
+ context "when an error occurs for the last time" do
47
+ before do
48
+ allow(PactBroker::Webhooks::Service).to receive(:execute_webhook_now).and_raise("an error")
49
+ end
50
+
51
+ subject { Job.new.perform(webhook: webhook, error_count: 6) }
52
+
53
+ it "does not reschedule the job" do
54
+ expect(Job).to_not receive(:perform_in)
55
+ subject
56
+ end
57
+
58
+ it "logs that it has failed" do
59
+ allow(Job.logger).to receive(:error)
60
+ expect(Job.logger).to receive(:error).with(/Failed to execute/)
61
+ subject
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -1,5 +1,7 @@
1
1
  require 'spec_helper'
2
2
  require 'pact_broker/webhooks/service'
3
+ require 'webmock/rspec'
4
+ require 'sucker_punch/testing/inline'
3
5
 
4
6
  module PactBroker
5
7
 
@@ -12,11 +14,11 @@ module PactBroker
12
14
  let(:consumer_version) { PactBroker::Domain::Version.new(number: '1.2.3') }
13
15
  let(:consumer) { PactBroker::Domain::Pacticipant.new(name: 'Consumer') }
14
16
  let(:provider) { PactBroker::Domain::Pacticipant.new(name: 'Provider') }
15
- let(:webhooks) { [instance_double(PactBroker::Domain::Webhook)]}
17
+ let(:webhooks) { [instance_double(PactBroker::Domain::Webhook, description: 'description', uuid: '1244')]}
16
18
 
17
19
  before do
18
20
  allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_provider).and_return(webhooks)
19
- allow(Service).to receive(:run_later)
21
+ allow(Job).to receive(:perform_async)
20
22
  end
21
23
 
22
24
  subject { Service.execute_webhooks pact }
@@ -45,7 +47,42 @@ module PactBroker
45
47
  subject
46
48
  end
47
49
  end
50
+
51
+ context "when there is a scheduling error" do
52
+ before do
53
+ allow(Job).to receive(:perform_async).and_raise("an error")
54
+ end
55
+
56
+ it "logs the error" do
57
+ allow(Service.logger).to receive(:error)
58
+ expect(Service.logger).to receive(:error).with(/an error/)
59
+ subject
60
+ end
61
+ end
62
+ end
63
+
64
+ describe ".execute_webhooks integration test" do
65
+ let!(:http_request) do
66
+ stub_request(:get, "http://example.org").
67
+ to_return(:status => 200)
68
+ end
69
+
70
+ let(:pact) do
71
+ ProviderStateBuilder.new
72
+ .create_consumer
73
+ .create_provider
74
+ .create_consumer_version
75
+ .create_pact
76
+ .create_webhook(method: 'GET', url: 'http://example.org')
77
+ .and_return(:pact)
78
+ end
79
+
80
+
81
+ it "executes the HTTP request of the webhook" do
82
+ PactBroker::Webhooks::Service.execute_webhooks pact
83
+ expect(http_request).to have_been_made
84
+ end
48
85
  end
49
86
  end
50
87
  end
51
- end
88
+ end