pact_broker 2.0.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/lib/pact_broker/api/decorators/version_decorator.rb +1 -1
  4. data/lib/pact_broker/api/decorators/webhook_decorator.rb +3 -3
  5. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +10 -2
  6. data/lib/pact_broker/api/resources/version.rb +6 -1
  7. data/lib/pact_broker/app.rb +6 -1
  8. data/lib/pact_broker/doc/views/webhooks-create.markdown +38 -0
  9. data/lib/pact_broker/doc/views/webhooks-webhooks.markdown +15 -0
  10. data/lib/pact_broker/domain/order_versions.rb +15 -1
  11. data/lib/pact_broker/pacticipants/service.rb +4 -2
  12. data/lib/pact_broker/pacts/repository.rb +4 -0
  13. data/lib/pact_broker/tags/repository.rb +4 -0
  14. data/lib/pact_broker/version.rb +1 -1
  15. data/lib/pact_broker/versions/repository.rb +4 -0
  16. data/lib/pact_broker/versions/service.rb +7 -1
  17. data/lib/rack/pact_broker/database_transaction.rb +42 -0
  18. data/lib/rack/pact_broker/invalid_uri_protection.rb +36 -0
  19. data/pact_broker.gemspec +1 -0
  20. data/script/publish-new.sh +3 -1
  21. data/script/recreate-pg-db.sh +1 -0
  22. data/spec/features/delete_version_spec.rb +23 -0
  23. data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +1 -1
  24. data/spec/lib/pact_broker/api/decorators/webhook_decorator_spec.rb +3 -3
  25. data/spec/lib/pact_broker/api/decorators/webhooks_decorator_spec.rb +3 -3
  26. data/spec/lib/pact_broker/app_spec.rb +35 -1
  27. data/spec/lib/pact_broker/domain/order_versions_spec.rb +22 -0
  28. data/spec/lib/pact_broker/pacts/repository_spec.rb +23 -0
  29. data/spec/lib/pact_broker/tags/repository_spec.rb +22 -1
  30. data/spec/lib/pact_broker/versions/repository_spec.rb +16 -0
  31. data/spec/lib/pact_broker/versions/service_spec.rb +35 -0
  32. data/spec/lib/rack/pact_broker/database_transaction_spec.rb +45 -0
  33. data/spec/lib/rack/pact_broker/invalid_uri_protection_spec.rb +30 -0
  34. data/spec/spec_helper.rb +2 -1
  35. metadata +28 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 22d05f1fc364395a7232c0f95a4566816533c476
4
- data.tar.gz: acd6dbb2fc0f05ba3cfe47029b187435213fa5bb
3
+ metadata.gz: 880db0a792e03e1d623978995043fd1658e9674e
4
+ data.tar.gz: 0dca6bd356860e3392292b10bc3c23d18f51cd65
5
5
  SHA512:
6
- metadata.gz: 4c70846fdbdff418c6a9f231384e8796cabc7003b4b8d631ebac9b6e9a306f7f6b9a48b4adf45fd49e471d5b8e5260609401b4581a806de0edbcedaa37b995a5
7
- data.tar.gz: 7d9c77cda110fea93028b43ca5e7f489e8258b2f6132a6b6eb964d34af8267547a064b51f2d4118eddca2571b165e97282f9b97623a2c71301b66a4cba8d9693
6
+ metadata.gz: 2eab4b5d78400d982467ccd00f9e1e382a99cc145ee02d6721147a91de925f67f97c4a9cf3d998cef126ab73b1f41a85e308d64f5743aa8eef76b846052adf92
7
+ data.tar.gz: 9cd39079ca7a9ff6a595aee30933fd74dbc3c899a3fb65daf9522c040fa8a5979ccb3c5f2c295a3f7398c85d7010987812c24b43bd53aafe967690fbfe0dc238
@@ -2,6 +2,13 @@ Do this to generate your change history
2
2
 
3
3
  $ git log --pretty=format:' * %h - %s (%an, %ad)' vX.Y.Z..HEAD
4
4
 
5
+ #### 2.0.1 (2017-05-17)
6
+ * 8d105aa - Allow an application version to be deleted via the API. (Beth Skurrie, Fri May 19 10:39:16 2017 +1000)
7
+ * 025b0f7 - Ensure version numbers that don't conform to the semver2 spec don't cause errors when sorting versions. #103 (Beth Skurrie, Fri May 19 09:58:50 2017 +1000)
8
+ * ca6d88e - Corrected hal link rels that had missing curies (prepended "pb:") (Beth Skurrie, Thu May 18 10:20:06 2017 +1000)
9
+ * 1cabd5e - Use Rack::Protection. (Beth Skurrie, Tue May 16 10:13:40 2017 +1000)
10
+ * 2a3bbd1 - Return 404 instead of 500 when Ruby standard URI lib can't parse the URI. https://github.com/pact-foundation/pact_broker/issues/101 (Beth Skurrie, Tue May 16 09:45:37 2017 +1000)
11
+
5
12
  #### 2.0.0 (2017-05-16)
6
13
 
7
14
  #### 2.0.0.beta.8 (2017-05-15)
@@ -25,7 +25,7 @@ module PactBroker
25
25
  }
26
26
  end
27
27
 
28
- link :pacticipant do | options |
28
+ link :'pb:pacticipant' do | options |
29
29
  {
30
30
  title: 'Pacticipant',
31
31
  name: represented.pacticipant.name,
@@ -24,21 +24,21 @@ module PactBroker
24
24
 
25
25
  end
26
26
 
27
- link :'pact-webhooks' do | options |
27
+ link :'pb:pact-webhooks' do | options |
28
28
  {
29
29
  title: "All webhooks for the pact between #{represented.consumer.name} and #{represented.provider.name}",
30
30
  href: webhooks_for_pact_url(represented.consumer, represented.provider, options[:base_url])
31
31
  }
32
32
  end
33
33
 
34
- link :'webhooks' do | options |
34
+ link :'pb:webhooks' do | options |
35
35
  {
36
36
  title: "All webhooks",
37
37
  href: webhooks_url(options[:base_url])
38
38
  }
39
39
  end
40
40
 
41
- link :execute do | options |
41
+ link :'pb:execute' do | options |
42
42
  {
43
43
  title: "Test the execution of the webhook by sending a POST request to this URL",
44
44
  href: webhook_execution_url(represented, options[:base_url])
@@ -14,7 +14,14 @@ module PactBroker
14
14
  }
15
15
  end
16
16
 
17
- links :webhooks do | context |
17
+ link :'pb:create' do | context |
18
+ {
19
+ title: "POST to create a webhook",
20
+ href: context[:resource_url]
21
+ }
22
+ end
23
+
24
+ links :'pb:webhooks' do | context |
18
25
  represented.entries.collect do | webhook |
19
26
  {
20
27
  title: webhook.description,
@@ -27,7 +34,8 @@ module PactBroker
27
34
  curies do | context |
28
35
  [{
29
36
  name: :pb,
30
- href: context[:base_url] + '/doc/webhooks',
37
+ href: context[:base_url] + '/doc/webhooks-{rel}',
38
+ templated: true
31
39
  }]
32
40
  end
33
41
 
@@ -12,7 +12,7 @@ module PactBroker
12
12
  end
13
13
 
14
14
  def allowed_methods
15
- ["GET"]
15
+ ["GET", "DELETE"]
16
16
  end
17
17
 
18
18
  def resource_exists?
@@ -23,6 +23,11 @@ module PactBroker
23
23
  Decorators::VersionDecorator.new(version).to_json(user_options: {base_url: base_url})
24
24
  end
25
25
 
26
+ def delete_resource
27
+ version_service.delete version
28
+ true
29
+ end
30
+
26
31
  private
27
32
 
28
33
  def version
@@ -1,9 +1,12 @@
1
1
  require 'pact_broker/configuration'
2
2
  require 'pact_broker/db'
3
3
  require 'pact_broker/project_root'
4
+ require 'rack-protection'
4
5
  require 'rack/hal_browser'
5
6
  require 'rack/pact_broker/add_pact_broker_version_header'
6
7
  require 'rack/pact_broker/convert_file_extension_to_accept_header'
8
+ require 'rack/pact_broker/database_transaction'
9
+ require 'rack/pact_broker/invalid_uri_protection'
7
10
  require 'sucker_punch'
8
11
 
9
12
  module PactBroker
@@ -54,6 +57,8 @@ module PactBroker
54
57
  def build_app
55
58
  @app = Rack::Builder.new
56
59
 
60
+ @app.use Rack::Protection, except: [:remote_token, :session_hijacking]
61
+ @app.use Rack::PactBroker::InvalidUriProtection
57
62
  @app.use Rack::PactBroker::AddPactBrokerVersionHeader
58
63
  @app.use Rack::Static, :urls => ["/stylesheets", "/css", "/fonts", "/js", "/javascripts", "/images"], :root => PactBroker.project_root.join("public")
59
64
  @app.use Rack::PactBroker::ConvertFileExtensionToAcceptHeader
@@ -79,7 +84,7 @@ module PactBroker
79
84
  end
80
85
 
81
86
  apps << PactBroker::UI::App.new
82
- apps << PactBroker::API
87
+ apps << Rack::PactBroker::DatabaseTransaction.new(PactBroker::API, configuration.database_connection)
83
88
 
84
89
  @app.map "/" do
85
90
  run Rack::Cascade.new(apps)
@@ -0,0 +1,38 @@
1
+ # Create a webhook
2
+
3
+ Allowed methods: POST
4
+
5
+ 1. Click the "NON-GET" button for the "pb:create" relation.
6
+ 3. Paste in the webhook JSON (example shown below) in the body section and click "Make Request".
7
+
8
+ An example webhook to trigger a Bamboo job.
9
+
10
+ {
11
+ "request": {
12
+ "method": "POST",
13
+ "url": "http://master.ci.my.domain:8085/rest/api/latest/queue/SOME-PROJECT?os_authType=basic",
14
+ "username": "username",
15
+ "password": "password",
16
+ "headers": {
17
+ "Accept": "application/json"
18
+ }
19
+ }
20
+ }
21
+
22
+ A request body can be specified as well.
23
+
24
+ {
25
+ "request": {
26
+ "method": "POST",
27
+ "url": "http://example.org/something",
28
+ "body": {
29
+ "some" : "json"
30
+ }
31
+ }
32
+ }
33
+
34
+ **BEWARE** The password can be reverse engineered from the database, so make a separate account for the Pact Broker to use, don't use your personal account!
35
+
36
+ ### Testing
37
+
38
+ To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
@@ -0,0 +1,15 @@
1
+ # Webhooks
2
+
3
+ Allowed methods: GET, DELETE
4
+
5
+ ### Testing
6
+
7
+ To test a webhook, navigate to the webhook in the HAL browser, then make a POST request to the "execute" relation. The response or error will be shown in the window.
8
+
9
+ ### Deleting
10
+
11
+ Send a DELETE request to the webhook URL.
12
+
13
+ ### Updating
14
+
15
+ Currently not implemented. You will need to delete and re-create the webhook.
@@ -26,8 +26,22 @@ module PactBroker
26
26
  @sortable_number = PactBroker.configuration.version_parser.call version_model.number
27
27
  end
28
28
 
29
+ # Incoming version numbers are rejected if they can't be parsed by the version parser,
30
+ # however, the change from Versionomy to SemVer for version parsing means that some
31
+ # existing version numbers cannot be parsed and are returning nil.
32
+ # The main reason to sort the versions is to that we can get the "latest" pact.
33
+ # Any existing version with a number that cannot be parsed will almost definitely not
34
+ # be the "latest", so sort them first.
29
35
  def <=> other
30
- self.sortable_number <=> other.sortable_number
36
+ if sortable_number.nil? && other.sortable_number.nil?
37
+ 0
38
+ elsif sortable_number.nil?
39
+ -1
40
+ elsif other.sortable_number.nil?
41
+ 1
42
+ else
43
+ self.sortable_number <=> other.sortable_number
44
+ end
31
45
  end
32
46
 
33
47
  def update_model new_order
@@ -75,15 +75,17 @@ module PactBroker
75
75
  def self.delete name
76
76
  pacticipant = find_pacticipant_by_name name
77
77
  connection = PactBroker::Domain::Pacticipant.new.db
78
+ version_ids = PactBroker::Domain::Version.where(pacticipant_id: pacticipant.id).select(:id)
78
79
  select_pacticipant = "select id from pacticipants where name = '#{name}'"
79
- connection.run("delete from tags where version_id IN (select id from versions where pacticipant_id = #{pacticipant.id})")
80
- connection.run("delete from pact_publications where consumer_version_id IN (select id from versions where pacticipant_id = #{pacticipant.id})")
80
+ tag_repository.delete_by_version_id version_ids
81
+ pact_repository.delete_by_version_id version_ids
81
82
  connection.run("delete from pact_publications where provider_id = #{pacticipant.id}")
82
83
  connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where provider_id = #{pacticipant.id})")
83
84
  connection.run("delete from verifications where pact_version_id IN (select id from pact_versions where consumer_id = #{pacticipant.id})")
84
85
  connection.run("delete from pact_versions where provider_id = #{pacticipant.id}")
85
86
  connection.run("delete from pact_versions where consumer_id = #{pacticipant.id}")
86
87
  connection.run("delete from versions where pacticipant_id = #{pacticipant.id}")
88
+ version_repository.delete_by_id version_ids
87
89
  webhook_service.delete_by_pacticipant pacticipant
88
90
  connection.run("delete from pacticipants where id = #{pacticipant.id}")
89
91
  end
@@ -48,6 +48,10 @@ module PactBroker
48
48
  PactPublication.where(id: id).delete
49
49
  end
50
50
 
51
+ def delete_by_version_id version_id
52
+ Sequel::Model.db[:pact_publications].where(consumer_version_id: version_id).delete
53
+ end
54
+
51
55
  def find_all_pact_versions_between consumer_name, options
52
56
  provider_name = options.fetch(:and)
53
57
  LatestPactPublicationsByConsumerVersion
@@ -22,6 +22,10 @@ module PactBroker
22
22
  .where(name_like(Sequel.qualify("pacticipants", "name"), args.fetch(:pacticipant_name)))
23
23
  .single_record
24
24
  end
25
+
26
+ def delete_by_version_id version_id
27
+ Sequel::Model.db[:tags].where(version_id: version_id).delete
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.0.0'
2
+ VERSION = '2.0.1'
3
3
  end
@@ -34,6 +34,10 @@ module PactBroker
34
34
  create(pacticipant_id: pacticipant_id, number: number)
35
35
  end
36
36
  end
37
+
38
+ def delete_by_id version_ids
39
+ Sequel::Model.db[:versions].where(id: version_ids).delete
40
+ end
37
41
  end
38
42
  end
39
43
  end
@@ -10,6 +10,12 @@ module PactBroker
10
10
  def self.find_by_pacticipant_name_and_number params
11
11
  version_repository.find_by_pacticipant_name_and_number params.fetch(:pacticipant_name), params.fetch(:pacticipant_version_number)
12
12
  end
13
+
14
+ def self.delete version
15
+ tag_repository.delete_by_version_id version.id
16
+ pact_repository.delete_by_version_id version.id
17
+ version_repository.delete_by_id version.id
18
+ end
13
19
  end
14
20
  end
15
- end
21
+ end
@@ -0,0 +1,42 @@
1
+ require 'pact_broker/version'
2
+ require 'sequel'
3
+
4
+ module Rack
5
+ module PactBroker
6
+ class DatabaseTransaction
7
+
8
+ REQUEST_METHOD = "REQUEST_METHOD".freeze
9
+ TRANS_METHODS = %{POST PUT PATCH DELETE}.freeze
10
+
11
+ def initialize app, database_connection
12
+ @app = app
13
+ @database_connection = database_connection
14
+ end
15
+
16
+ def call env
17
+ if use_transaction? env
18
+ call_with_transaction env
19
+ else
20
+ call_without_transaction env
21
+ end
22
+ end
23
+
24
+ def use_transaction? env
25
+ TRANS_METHODS.include? env[REQUEST_METHOD]
26
+ end
27
+
28
+ def call_without_transaction env
29
+ @app.call(env)
30
+ end
31
+
32
+ def call_with_transaction env
33
+ response = nil
34
+ @database_connection.transaction do
35
+ response = @app.call(env)
36
+ raise Sequel::Rollback if response.first == 500
37
+ end
38
+ response
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ require 'uri'
2
+
3
+ # This class is for https://github.com/pact-foundation/pact_broker/issues/101
4
+ # curl -i "http://127.0.0.1:9292/<script>"
5
+
6
+ module Rack
7
+ module PactBroker
8
+ class InvalidUriProtection
9
+
10
+ def initialize app
11
+ @app = app
12
+ end
13
+
14
+ def call env
15
+ if valid_uri? env
16
+ @app.call(env)
17
+ else
18
+ [404, {}, []]
19
+ end
20
+ end
21
+
22
+ def valid_uri? env
23
+ begin
24
+ parse(::Rack::Request.new(env).url)
25
+ true
26
+ rescue URI::InvalidURIError
27
+ false
28
+ end
29
+ end
30
+
31
+ def parse uri
32
+ URI.parse(uri)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -36,6 +36,7 @@ Gem::Specification.new do |gem|
36
36
  gem.add_runtime_dependency 'padrino-core', '~>0.12.4'
37
37
  gem.add_runtime_dependency 'haml', '~>4.0'
38
38
  gem.add_runtime_dependency 'sucker_punch', '~>2.0'
39
+ gem.add_runtime_dependency 'rack-protection', '~>1.5'
39
40
 
40
41
  gem.add_development_dependency 'sqlite3'
41
42
  gem.add_development_dependency 'pry-byebug'
@@ -1,7 +1,9 @@
1
1
  # set -x
2
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
+ latest_url=$(curl http://localhost:9292/pacts/provider/Bar/consumer/Foo/latest | jq -r ._links.self.href)
4
+ next_version=$(echo ${latest_url} | ruby -e "require 'semver'; puts SemVer.parse(ARGF.read[/\d+\.\d\.\d+/]).tap{ | v| v.minor = v.minor + 1}.format('%M.%m.%p')")
3
5
  echo ${BODY} >> tmp.json
4
6
  curl -v -XPUT \-H "Content-Type: application/json" \
5
7
  -d@tmp.json \
6
- http://127.0.0.1:9292/pacts/provider/Bar/consumer/Foo/version/1.0.0
8
+ http://127.0.0.1:9292/pacts/provider/Bar/consumer/Foo/version/${next_version}
7
9
  rm tmp.json
@@ -1,3 +1,4 @@
1
+ set -e
1
2
  psql postgres -c "DROP DATABASE pact_broker;"
2
3
  psql postgres -c "CREATE DATABASE pact_broker;"
3
4
  psql postgres -c "GRANT ALL PRIVILEGES ON DATABASE pact_broker TO pact_broker;"
@@ -0,0 +1,23 @@
1
+ require 'spec/support/provider_state_builder'
2
+
3
+ describe "Delete version" do
4
+
5
+ let(:path) { "/pacticipants/Consumer/versions/1.2.3" }
6
+ let(:last_response_body) { JSON.parse(subject.body, symbolize_names: true) }
7
+
8
+ subject { delete path; last_response }
9
+
10
+ before do
11
+ ProviderStateBuilder.new
12
+ .create_consumer("Another Consumer")
13
+ .create_consumer("Consumer")
14
+ .create_consumer_version("1.2.3")
15
+ .create_consumer_version_tag("prod")
16
+ .create_consumer_version("1.2.4")
17
+ end
18
+
19
+
20
+ it "returns a 200 HAL JSON response" do
21
+ expect(subject.status).to eq 204
22
+ end
23
+ end
@@ -35,7 +35,7 @@ module PactBroker
35
35
  end
36
36
 
37
37
  it "includes a link to the pacticipant" do
38
- expect(subject[:_links][:pacticipant]).to eq title: "Pacticipant", name: "Consumer", href: "http://example.org/pacticipants/Consumer"
38
+ expect(subject[:_links][:'pb:pacticipant']).to eq title: "Pacticipant", name: "Consumer", href: "http://example.org/pacticipants/Consumer"
39
39
  end
40
40
 
41
41
  it "includes a list of the tags" do
@@ -72,15 +72,15 @@ module PactBroker
72
72
  end
73
73
 
74
74
  it 'includes a link to its parent collection' do
75
- expect(parsed_json[:_links][:'pact-webhooks'][:href]).to_not be_nil
75
+ expect(parsed_json[:_links][:'pb:pact-webhooks'][:href]).to_not be_nil
76
76
  end
77
77
 
78
78
  it 'includes a link to the webhooks resource' do
79
- expect(parsed_json[:_links][:webhooks][:href]).to_not be_nil
79
+ expect(parsed_json[:_links][:'pb:webhooks'][:href]).to_not be_nil
80
80
  end
81
81
 
82
82
  it 'includes a link to execute the webhook directly' do
83
- expect(parsed_json[:_links][:execute][:href]).to eq 'http://example.org/webhooks/some-uuid/execute'
83
+ expect(parsed_json[:_links][:'pb:execute'][:href]).to eq 'http://example.org/webhooks/some-uuid/execute'
84
84
  end
85
85
 
86
86
  it 'includes timestamps' do
@@ -30,12 +30,12 @@ module PactBroker
30
30
  end
31
31
 
32
32
  it "includes a list of links to the webhooks" do
33
- expect(subject[:_links][:webhooks]).to be_instance_of(Array)
34
- expect(subject[:_links][:webhooks].first).to eq title: 'description', name: 'request description', href: 'http://example.org/webhooks/some-uuid'
33
+ expect(subject[:_links][:'pb:webhooks']).to be_instance_of(Array)
34
+ expect(subject[:_links][:'pb:webhooks'].first).to eq title: 'description', name: 'request description', href: 'http://example.org/webhooks/some-uuid'
35
35
  end
36
36
 
37
37
  it "includes curies" do
38
- expect(subject[:_links][:curies]).to eq [{:name=>"pb", :href=>"http://example.org/doc/webhooks"}]
38
+ expect(subject[:_links][:curies]).to eq [{:name=>"pb", :href=>"http://example.org/doc/webhooks-{rel}", templated: true}]
39
39
  end
40
40
 
41
41
  end
@@ -3,9 +3,22 @@ require 'pact_broker/app'
3
3
  module PactBroker
4
4
  describe App do
5
5
 
6
+ class TestApp < PactBroker::App
7
+
8
+ def configure_database_connection
9
+ # do nothing
10
+ end
11
+
12
+ def post_configure
13
+ # do nothing
14
+ end
15
+
16
+ end
17
+
6
18
  let(:app) do
7
- PactBroker::App.new do | configuration |
19
+ TestApp.new do | configuration |
8
20
  configuration.database_connection = PactBroker::DB.connection
21
+ configuration.auto_migrate_db = false
9
22
  end
10
23
  end
11
24
 
@@ -14,5 +27,26 @@ module PactBroker
14
27
  expect(last_response.headers['X-Pact-Broker-Version']).to match /\d/
15
28
  end
16
29
 
30
+ describe "transactions", no_db_clean: true do
31
+ let(:pact_content) { load_fixture('a_consumer-a_provider.json') }
32
+ let(:path) { "/pacts/provider/A%20Provider/consumer/A%20Consumer/versions/1.2.3" }
33
+ let(:response_body_json) { JSON.parse(subject.body) }
34
+
35
+ before do
36
+ PactBroker::Database.truncate
37
+ allow_any_instance_of(PactBroker::Pacts::Repository).to receive(:create).and_raise("an error")
38
+ end
39
+
40
+ after do
41
+ PactBroker::Database.truncate
42
+ end
43
+
44
+ subject { put path, pact_content, {'CONTENT_TYPE' => 'application/json' }; last_response }
45
+
46
+ it "wraps the API with a database transaction" do
47
+ expect { subject }.to_not change { PactBroker::Domain::Pacticipant.count }
48
+ end
49
+ end
50
+
17
51
  end
18
52
  end
@@ -40,4 +40,26 @@ describe PactBroker::Domain::OrderVersions do
40
40
 
41
41
  end
42
42
 
43
+ context "when an existing version number in the database that Versionomy could parse cannot be parsed by SemVer" do
44
+ let!(:consumer) do
45
+ ProviderStateBuilder.new
46
+ .create_consumer
47
+ .create_consumer_version('1')
48
+ .create_consumer_version('2')
49
+ .create_consumer_version('3')
50
+ .create_consumer_version('4')
51
+ .and_return(:consumer)
52
+ end
53
+
54
+ let(:ordered_versions) { PactBroker::Domain::Version.order(:order).all.collect(&:number) }
55
+
56
+ it "sorts the unparseable version as being first and maintains their relative order" do
57
+ Sequel::Model.db[:versions].where(number: '1').update(number: 'z')
58
+ Sequel::Model.db[:versions].where(number: '2').update(number: 'a')
59
+ Sequel::Model.db[:versions].where(number: '4').update(number: 'h')
60
+ PactBroker::Domain::Version.create(number: '5', pacticipant_id: consumer.id)
61
+ expect(ordered_versions).to eq(['z', 'a', 'h', '3', '5'])
62
+ end
63
+ end
64
+
43
65
  end
@@ -191,6 +191,29 @@ module PactBroker
191
191
 
192
192
  end
193
193
 
194
+ describe "delete_by_version_id" do
195
+ let!(:version) do
196
+ ProviderStateBuilder.new
197
+ .create_consumer
198
+ .create_provider
199
+ .create_consumer_version("4.5.6")
200
+ .create_pact
201
+ .create_consumer_version("1.2.3")
202
+ .create_pact
203
+ .and_return(:consumer_version)
204
+ end
205
+
206
+ subject { Repository.new.delete_by_version_id(version.id) }
207
+
208
+ it "deletes the pact publication" do
209
+ expect{ subject }.to change { PactPublication.count }.by(-1)
210
+ end
211
+
212
+ it "does not delete the content because it may be used by another pact" do
213
+ expect { subject }.to change { PactVersion.count }.by(0)
214
+ end
215
+ end
216
+
194
217
  describe "#find_all_pact_versions_between" do
195
218
 
196
219
  before do
@@ -62,6 +62,27 @@ module PactBroker
62
62
  end
63
63
  end
64
64
 
65
+ describe "delete_by_version_id" do
66
+ let!(:version) do
67
+ ProviderStateBuilder.new
68
+ .create_consumer
69
+ .create_provider
70
+ .create_consumer_version("4.5.6")
71
+ .create_consumer_version_tag("prod")
72
+ .create_consumer_version("1.2.3")
73
+ .create_consumer_version_tag("prod")
74
+ .create_consumer_version_tag("foo")
75
+ .and_return(:consumer_version)
76
+ end
77
+
78
+ subject { Repository.new.delete_by_version_id(version.id) }
79
+
80
+ it "deletes the tag" do
81
+ expect{ subject }.to change { PactBroker::Domain::Tag.count }.by(-2)
82
+ end
83
+
84
+ end
85
+
65
86
  end
66
87
  end
67
- end
88
+ end
@@ -24,6 +24,22 @@ module PactBroker
24
24
  end
25
25
  end
26
26
 
27
+ describe "#delete_by_id" do
28
+ let!(:version) do
29
+ ProviderStateBuilder.new
30
+ .create_consumer
31
+ .create_consumer_version("1.2.3")
32
+ .create_consumer_version("4.5.6")
33
+ .and_return(:consumer_version)
34
+ end
35
+
36
+ subject { Repository.new.delete_by_id version.id }
37
+
38
+ it "deletes the version" do
39
+ expect { subject }.to change{ PactBroker::Domain::Version.count }.by(-1)
40
+ end
41
+ end
42
+
27
43
  describe "#find_by_pacticipant_name_and_number" do
28
44
 
29
45
  subject { described_class.new.find_by_pacticipant_name_and_number pacticipant_name, version_number }
@@ -0,0 +1,35 @@
1
+ require 'pact_broker/versions/service'
2
+
3
+ module PactBroker
4
+
5
+ module Versions
6
+ describe Service do
7
+
8
+ describe ".delete" do
9
+ let!(:version) do
10
+ ProviderStateBuilder.new
11
+ .create_consumer
12
+ .create_provider
13
+ .create_consumer_version("1.2.3")
14
+ .create_consumer_version_tag("prod")
15
+ .create_pact
16
+ .and_return(:consumer_version)
17
+ end
18
+
19
+ subject { Service.delete(version) }
20
+
21
+ it "deletes the pact publication" do
22
+ expect{ subject }.to change { PactBroker::Pacts::PactPublication.count }.by(-1)
23
+ end
24
+
25
+ it "deletes the tags" do
26
+ expect{ subject }.to change { PactBroker::Domain::Tag.count }.by(-1)
27
+ end
28
+
29
+ it "deletes the version" do
30
+ expect{ subject }.to change { PactBroker::Domain::Version.count }.by(-1)
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ require 'tasks/database'
2
+ require 'rack/pact_broker/database_transaction'
3
+
4
+ module Rack
5
+ module PactBroker
6
+ describe DatabaseTransaction, no_db_clean: true do
7
+
8
+ before do
9
+ ::PactBroker::Database.truncate
10
+ end
11
+
12
+ after do
13
+ ::PactBroker::Database.truncate
14
+ end
15
+
16
+ let(:api) do
17
+ ->(env) { ::PactBroker::Domain::Pacticipant.create(name: 'Foo'); [500, {}, []] }
18
+ end
19
+
20
+ let(:api_with_transaction) do
21
+ ::Rack::PactBroker::DatabaseTransaction.new(api, ::PactBroker::DB.connection)
22
+ end
23
+
24
+ subject { self.send(http_method, "/") }
25
+
26
+ context "for get requests" do
27
+ let(:app) { api_with_transaction }
28
+
29
+ let(:http_method) { :get }
30
+ it "does not use a transaction" do
31
+ expect { subject }.to change { ::PactBroker::Domain::Pacticipant.count }.by(1)
32
+ end
33
+ end
34
+
35
+ [:post, :put, :patch, :delete].each do | http_meth |
36
+ let(:http_method) { http_meth }
37
+ context "for #{http_meth} requests" do
38
+ it "uses a transaction and rollsback if there is a 500 error" do
39
+ expect { subject }.to change { ::PactBroker::Domain::Pacticipant.count }.by(0)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,30 @@
1
+ require 'rack/pact_broker/invalid_uri_protection'
2
+
3
+ module Rack
4
+ module PactBroker
5
+ describe InvalidUriProtection do
6
+
7
+ let(:app) { InvalidUriProtection.new(->(env){ [200,{},[]] }) }
8
+
9
+ subject { get "/badpath"; last_response }
10
+
11
+ context "with a URI that the Ruby default URI library cannot parse" do
12
+
13
+ before do
14
+ # Can't use or stub URI.parse because rack test uses it to execute the actual test
15
+ allow_any_instance_of(InvalidUriProtection).to receive(:parse).and_raise(URI::InvalidURIError)
16
+ end
17
+
18
+ it "returns a 404" do
19
+ expect(subject.status).to eq 404
20
+ end
21
+ end
22
+
23
+ context "when the URI can be parsed" do
24
+ it "passes the request to the underlying app" do
25
+ expect(subject.status).to eq 200
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -5,6 +5,7 @@ $: << File.expand_path("../../", __FILE__)
5
5
  require 'rack/test'
6
6
  require 'db'
7
7
  require 'pact_broker/api'
8
+ require 'tasks/database'
8
9
  require 'rspec/its'
9
10
 
10
11
  Dir.glob("./spec/support/**/*.rb") { |file| require file }
@@ -14,7 +15,7 @@ I18n.config.enforce_available_locales = false
14
15
  RSpec.configure do | config |
15
16
  config.before :suite do
16
17
  raise "Wrong environment!!! Don't run this script!! ENV['RACK_ENV'] is #{ENV['RACK_ENV']} and RACK_ENV is #{RACK_ENV}" if ENV['RACK_ENV'] != 'test' || RACK_ENV != 'test'
17
- PactBroker::DB.connection = DB::PACT_BROKER_DB
18
+ PactBroker::DB.connection = PactBroker::Database.database = DB::PACT_BROKER_DB
18
19
  end
19
20
 
20
21
  config.include Rack::Test::Methods
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.0.0
4
+ version: 2.0.1
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: 2017-05-15 00:00:00.000000000 Z
13
+ date: 2017-05-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: httparty
@@ -234,6 +234,20 @@ dependencies:
234
234
  - - "~>"
235
235
  - !ruby/object:Gem::Version
236
236
  version: '2.0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: rack-protection
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '1.5'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - "~>"
249
+ - !ruby/object:Gem::Version
250
+ version: '1.5'
237
251
  - !ruby/object:Gem::Dependency
238
252
  name: sqlite3
239
253
  requirement: !ruby/object:Gem::Requirement
@@ -534,6 +548,8 @@ files:
534
548
  - lib/pact_broker/doc/views/self.markdown
535
549
  - lib/pact_broker/doc/views/tag-prod-version.markdown
536
550
  - lib/pact_broker/doc/views/tag-version.markdown
551
+ - lib/pact_broker/doc/views/webhooks-create.markdown
552
+ - lib/pact_broker/doc/views/webhooks-webhooks.markdown
537
553
  - lib/pact_broker/doc/views/webhooks.markdown
538
554
  - lib/pact_broker/domain.rb
539
555
  - lib/pact_broker/domain/group.rb
@@ -607,6 +623,8 @@ files:
607
623
  - lib/rack/hal_browser/redirect.rb
608
624
  - lib/rack/pact_broker/add_pact_broker_version_header.rb
609
625
  - lib/rack/pact_broker/convert_file_extension_to_accept_header.rb
626
+ - lib/rack/pact_broker/database_transaction.rb
627
+ - lib/rack/pact_broker/invalid_uri_protection.rb
610
628
  - pact_broker.gemspec
611
629
  - pact_broker_client-pact_broker.json
612
630
  - public/Network Graph_files/d3.v3.js
@@ -643,6 +661,7 @@ files:
643
661
  - script/update-hal-browser
644
662
  - spec/features/create_webhook_spec.rb
645
663
  - spec/features/delete_pact_spec.rb
664
+ - spec/features/delete_version_spec.rb
646
665
  - spec/features/get_diff_spec.rb
647
666
  - spec/features/get_pact_spec.rb
648
667
  - spec/features/get_pact_versions_spec.rb
@@ -744,11 +763,14 @@ files:
744
763
  - spec/lib/pact_broker/verifications/summary_for_consumer_version_spec.rb
745
764
  - spec/lib/pact_broker/versions/parse_semantic_version_spec.rb
746
765
  - spec/lib/pact_broker/versions/repository_spec.rb
766
+ - spec/lib/pact_broker/versions/service_spec.rb
747
767
  - spec/lib/pact_broker/webhooks/job_spec.rb
748
768
  - spec/lib/pact_broker/webhooks/repository_spec.rb
749
769
  - spec/lib/pact_broker/webhooks/service_spec.rb
750
770
  - spec/lib/rack/hal_browser/redirect_spec.rb
751
771
  - spec/lib/rack/pact_broker/add_pact_broker_version_header_spec.rb
772
+ - spec/lib/rack/pact_broker/database_transaction_spec.rb
773
+ - spec/lib/rack/pact_broker/invalid_uri_protection_spec.rb
752
774
  - spec/migrations/23_pact_versions_spec.rb
753
775
  - spec/migrations/24_populate_pact_contents_spec.rb
754
776
  - spec/migrations/34_latest_tagged_pacts_spec.rb
@@ -830,6 +852,7 @@ summary: See description
830
852
  test_files:
831
853
  - spec/features/create_webhook_spec.rb
832
854
  - spec/features/delete_pact_spec.rb
855
+ - spec/features/delete_version_spec.rb
833
856
  - spec/features/get_diff_spec.rb
834
857
  - spec/features/get_pact_spec.rb
835
858
  - spec/features/get_pact_versions_spec.rb
@@ -931,11 +954,14 @@ test_files:
931
954
  - spec/lib/pact_broker/verifications/summary_for_consumer_version_spec.rb
932
955
  - spec/lib/pact_broker/versions/parse_semantic_version_spec.rb
933
956
  - spec/lib/pact_broker/versions/repository_spec.rb
957
+ - spec/lib/pact_broker/versions/service_spec.rb
934
958
  - spec/lib/pact_broker/webhooks/job_spec.rb
935
959
  - spec/lib/pact_broker/webhooks/repository_spec.rb
936
960
  - spec/lib/pact_broker/webhooks/service_spec.rb
937
961
  - spec/lib/rack/hal_browser/redirect_spec.rb
938
962
  - spec/lib/rack/pact_broker/add_pact_broker_version_header_spec.rb
963
+ - spec/lib/rack/pact_broker/database_transaction_spec.rb
964
+ - spec/lib/rack/pact_broker/invalid_uri_protection_spec.rb
939
965
  - spec/migrations/23_pact_versions_spec.rb
940
966
  - spec/migrations/24_populate_pact_contents_spec.rb
941
967
  - spec/migrations/34_latest_tagged_pacts_spec.rb