pact_broker 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +15 -2
  3. data/CHANGELOG.md +9 -0
  4. data/CONTRIBUTING.md +15 -0
  5. data/config/database.travis.yml +13 -0
  6. data/config/database.yml +25 -13
  7. data/db/migrations/32_create_latest_verifications.rb +6 -5
  8. data/db/migrations/33_create_config_table.rb +1 -1
  9. data/db/migrations/34_create_index_on_consumer_version_order.rb +10 -0
  10. data/db/migrations/35_create_index_on_names.rb +11 -0
  11. data/db/migrations/36_create_webhook_execution.rb +16 -0
  12. data/example/pact_broker_database.sqlite3 +0 -0
  13. data/lib/db.rb +6 -1
  14. data/lib/pact_broker/api/contracts/put_pact_params_contract.rb +1 -0
  15. data/lib/pact_broker/app.rb +1 -0
  16. data/lib/pact_broker/domain/order_versions.rb +37 -15
  17. data/lib/pact_broker/domain/relationship.rb +11 -5
  18. data/lib/pact_broker/domain/tag.rb +4 -0
  19. data/lib/pact_broker/domain/version.rb +6 -1
  20. data/lib/pact_broker/domain/webhook_execution_result.rb +7 -2
  21. data/lib/pact_broker/domain/webhook_request.rb +24 -2
  22. data/lib/pact_broker/pacticipants/service.rb +4 -2
  23. data/lib/pact_broker/pacts/all_pact_publications.rb +1 -1
  24. data/lib/pact_broker/pacts/repository.rb +3 -3
  25. data/lib/pact_broker/repositories/helpers.rb +16 -0
  26. data/lib/pact_broker/ui/view_models/relationship.rb +9 -0
  27. data/lib/pact_broker/ui/views/relationships/show.haml +9 -0
  28. data/lib/pact_broker/version.rb +1 -1
  29. data/lib/pact_broker/webhooks/execution.rb +17 -0
  30. data/lib/pact_broker/webhooks/repository.rb +20 -2
  31. data/lib/pact_broker/webhooks/service.rb +4 -1
  32. data/public/stylesheets/relationships.css +1 -0
  33. data/script/publish-2.sh +1 -0
  34. data/script/publish-new.sh +1 -0
  35. data/script/publish-not-a-pact.sh +1 -0
  36. data/script/publish.sh +1 -0
  37. data/script/record_verification.sh +1 -0
  38. data/script/recreate-pg-db.sh +2 -0
  39. data/spec/lib/pact_broker/api/contracts/put_pact_params_contract_spec.rb +20 -0
  40. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +2 -1
  41. data/spec/lib/pact_broker/domain/order_versions_spec.rb +37 -15
  42. data/spec/lib/pact_broker/domain/version_spec.rb +14 -0
  43. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +49 -6
  44. data/spec/lib/pact_broker/domain/webhook_spec.rb +1 -0
  45. data/spec/lib/pact_broker/pacticipants/service_spec.rb +28 -4
  46. data/spec/lib/pact_broker/pacts/pact_version_spec.rb +1 -1
  47. data/spec/lib/pact_broker/pacts/repository_spec.rb +10 -10
  48. data/spec/lib/pact_broker/tags/repository_spec.rb +2 -2
  49. data/spec/lib/pact_broker/ui/controllers/relationships_spec.rb +7 -7
  50. data/spec/lib/pact_broker/webhooks/repository_spec.rb +52 -4
  51. data/spec/lib/pact_broker/webhooks/service_spec.rb +6 -1
  52. data/spec/migrations/23_pact_versions_spec.rb +1 -1
  53. data/spec/support/database_cleaner.rb +5 -1
  54. data/spec/support/provider_state_builder.rb +8 -0
  55. data/spec/support/rspec_matchers.rb +9 -0
  56. data/tasks/database.rb +3 -2
  57. data/tasks/db.rake +41 -3
  58. metadata +10 -3
  59. data/lib/pact_broker/api/contracts/consumer_version_number_validation.rb +0 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c84ee6c55e2a54d6217caee612ab6497e43acda7
4
- data.tar.gz: 8c3be29c77af89f8c654373e3ec89fcbbea206d9
3
+ metadata.gz: 50c48f4aaa8d0b97a1bf1c5e22221a9f3c1f11da
4
+ data.tar.gz: ffaeb22aabb0ca377af193023e9d53c9f1e8603e
5
5
  SHA512:
6
- metadata.gz: 8d083312f78b5b4f6c4d43b8561c08fee50fc9a609ace5002053ababf191fa464398c0c67fdfa8014487a1bf187b1148e534c22a012cf30a8b99a5b1523bea47
7
- data.tar.gz: 245ab76eece33dd4fe12f74a9b9bf8e37b887c7e769536fcd24c2d404c8ec9772eb9a7168c2e77a8319c18d01ea519e2ee3cc3ff9f30f19f50600fceb978ddd0
6
+ metadata.gz: 6f78013eca52554efcaca7fdedef4ab71c8c0d5da00226b6296043a1e0880adbf17f228eec498b942b5d4ffaff091237023086f0f27acd564241a4c81f740c2d
7
+ data.tar.gz: 0f3577bf77b6f5d6fe9e035eaf9495db9bf685a8fd76ec697f97c084234e15550f8a3c1463f2d8d99fc03a94b07ef94e1239483fa14efcf2ff24aba805f435e9
data/.travis.yml CHANGED
@@ -1,5 +1,18 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.7
4
- - 2.3.4
3
+ # - 2.2.7
4
+ # - 2.3.4
5
5
  - 2.4.1
6
+ services:
7
+ - postgresql
8
+ - mysql
9
+ env:
10
+ - DATABASE_ADAPTER=default RUBYOPT="-W0"
11
+ - DATABASE_ADAPTER=postgres RUBYOPT="-W0"
12
+ - DATABASE_ADAPTER=mysql RUBYOPT="-W0"
13
+ before_script:
14
+ - cp config/database.travis.yml config/database.yml
15
+ - psql -U postgres -c "CREATE DATABASE pact_broker;"
16
+ - mysql -e 'CREATE DATABASE pact_broker;'
17
+ script:
18
+ - bundle exec rake
data/CHANGELOG.md CHANGED
@@ -2,6 +2,15 @@ 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.3 (2017-05-17)
6
+ * c03b871 - Make specs pass for sqlite, postgres and mysql. At the same time. Amazing. (Beth Skurrie, Sun May 28 10:22:20 2017 +1000)
7
+ * ae2b62f - Remove inner query from latest_verifications definition for MySQL (#105) (Beth Skurrie, Sat May 27 15:11:26 2017 +1000)
8
+ * f451d35 - Add mysql build to travis for #106 (Beth Skurrie, Sat May 27 15:09:42 2017 +1000)
9
+ * 91178c2 - Altering config and travis to run against sqlite and postgres. (Beth Skurrie, Sat May 27 14:08:34 2017 +1000)
10
+ * 4c52061 - Use a simpler and more efficient algorithm for updating version orders. (Beth Skurrie, Mon May 22 13:29:07 2017 +1000)
11
+ * ba5b60c - Created indexes on pacticipant, version and tag tables. #87 (Beth Skurrie, Sun May 21 16:18:49 2017 +1000)
12
+ * 0ffad10 - Do not validate incoming consumer version number if order_versions_by_date is true. (Beth Skurrie, Sun May 21 15:46:54 2017 +1000)
13
+
5
14
  #### 2.0.2 (2017-05-17)
6
15
  * 0e4d4bf - Add missing require for migration_helper (Beth Skurrie, Fri May 19 14:16:38 2017 +1000)
7
16
 
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,15 @@
1
+ # Raising issues
2
+
3
+ Before raising an issue, make sure you have checked the [wiki] to see if an answer is provided there.
4
+ There may also be an answer to your question on [stackoverflow].
5
+
6
+ Please provide the following information with your issue to enable us to respond as quickly as possible.
7
+
8
+ * The relevant versions of the gems or packages you are using.
9
+ * The steps to recreate your issue.
10
+ * An executable code example where possible. You can use the [pact-ruby-e2e-example] codebase to quickly recreate your issue.
11
+ * Where you are using a pact broker deployed on third party infrastructure, please include the relevant details.
12
+
13
+ [wiki]: https://github.com/pact-foundation/pact_broker/wiki
14
+ [stackoverflow]: https://stackoverflow.com/questions/tagged/pact-broker
15
+ [pact-ruby-e2e-example]: https://github.com/pact-foundation/pact-ruby-e2e-example
@@ -0,0 +1,13 @@
1
+ test:
2
+ default:
3
+ adapter: sqlite
4
+ database: tmp/pact_broker_database_test.sqlite3
5
+ postgres:
6
+ adapter: postgres
7
+ database: pact_broker
8
+ encoding: utf8
9
+ mysql:
10
+ adapter: mysql2
11
+ database: pact_broker
12
+ username: travis
13
+ encoding: utf8
data/config/database.yml CHANGED
@@ -1,20 +1,32 @@
1
1
  default: &default
2
- adapter: sqlite
3
- database: db/pact_broker_database.sqlite3
2
+ database: pact_broker
3
+ username: pact_broker
4
+ password: pact_broker
5
+ encoding: utf8
4
6
 
5
7
  test:
6
- <<: *default
7
- database: tmp/pact_broker_database_test.sqlite3
8
-
9
- # test:
10
- # adapter: "postgres"
11
- # database: "pact_broker"
12
- # username: 'pact_broker'
13
- # password: 'pact_broker'
14
- # encoding: 'utf8'
8
+ default:
9
+ adapter: sqlite
10
+ database: tmp/pact_broker_database_test.sqlite3
11
+ postgres:
12
+ <<: *default
13
+ adapter: postgres
14
+ mysql:
15
+ <<: *default
16
+ adapter: mysql2
15
17
 
16
18
  development:
17
- <<: *default
19
+ default:
20
+ adapter: sqlite
21
+ database: db/pact_broker_database.sqlite3
22
+ postgres:
23
+ <<: *default
24
+ adapter: postgres
25
+ mysql:
26
+ <<: *default
27
+ adapter: mysql2
18
28
 
19
29
  production:
20
- <<: *default
30
+ default:
31
+ <<: *default
32
+ adapter: postgres
@@ -1,14 +1,15 @@
1
1
  Sequel.migration do
2
2
  up do
3
+ create_view(:latest_verification_numbers,
4
+ "SELECT pact_version_id, MAX(number) latest_number
5
+ FROM verifications
6
+ GROUP BY pact_version_id")
7
+
3
8
  # The most recent verification for each pact version
4
9
  create_or_replace_view(:latest_verifications,
5
10
  "SELECT v.id, v.number, v.success, v.provider_version, v.build_url, v.pact_version_id, v.execution_date, v.created_at
6
11
  FROM verifications v
7
- INNER JOIN (
8
- SELECT pact_version_id, MAX(number) latest_number
9
- FROM verifications
10
- GROUP BY pact_version_id
11
- ) lv ON v.pact_version_id = lv.pact_version_id AND v.number = lv.latest_number"
12
+ INNER JOIN latest_verification_numbers lv ON v.pact_version_id = lv.pact_version_id AND v.number = lv.latest_number"
12
13
  )
13
14
  end
14
15
  end
@@ -9,7 +9,7 @@ Sequel.migration do
9
9
  String :value, type: PactBroker::MigrationHelper.large_text_type
10
10
  DateTime :created_at, null: false
11
11
  DateTime :updated_at, null: false
12
- index [:name], unique: true, unique_constraint_name: 'unq_config_name'
12
+ index [:name], unique: true, name: 'unq_config_name'
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,10 @@
1
+ Sequel.migration do
2
+ up do
3
+ alter_table(:versions) do
4
+ # Not actually sure which index it will use for OrderVersions, so CREATE ALL THE INDEXES!
5
+ add_index [:number], name: 'ndx_ver_num' # Not sure if this is useful give we use LIKE not EQ
6
+ add_index [:order], name: 'ndx_ver_ord'
7
+ add_index [:pacticipant_id, :order], unique: true, name: 'uq_ver_ppt_ord'
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,11 @@
1
+ Sequel.migration do
2
+ up do
3
+ alter_table(:pacticipants) do
4
+ add_index [:name], name: 'ndx_ppt_name' # Not sure if this is useful give we use LIKE not EQ
5
+ end
6
+
7
+ alter_table(:tags) do
8
+ add_index [:name], name: 'ndx_tag_name' # Not sure if this is useful give we use LIKE not EQ
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ require_relative 'migration_helper'
2
+
3
+ Sequel.migration do
4
+ change do
5
+ create_table(:webhook_executions, charset: 'utf8') do
6
+ primary_key :id
7
+ foreign_key :webhook_id, :webhooks
8
+ foreign_key :pact_publication_id, :pact_publications
9
+ foreign_key :consumer_id, :pacticipants, null: false
10
+ foreign_key :provider_id, :pacticipants, null: false
11
+ Boolean :success, null: false
12
+ String :logs, type: PactBroker::MigrationHelper.large_text_type
13
+ DateTime :created_at, null: false
14
+ end
15
+ end
16
+ end
Binary file
data/lib/db.rb CHANGED
@@ -25,6 +25,7 @@ module DB
25
25
  # pool, as noted in the documentation for the extension.
26
26
  #
27
27
  def self.connect db_credentials
28
+ Sequel.datetime_class = DateTime
28
29
  con = Sequel.connect(db_credentials.merge(:logger => logger, :pool_class => Sequel::ThreadedConnectionPool, :encoding => 'utf8'))
29
30
  con.extension(:connection_validator)
30
31
  con.pool.connection_validation_timeout = -1 #Check the connection on every request
@@ -40,7 +41,11 @@ module DB
40
41
  def self.configuration_for_env env
41
42
  database_yml = PactBroker.project_root.join('config','database.yml')
42
43
  config = YAML.load(ERB.new(File.read(database_yml)).result)
43
- config.fetch(env)
44
+ config.fetch(env).fetch(ENV.fetch('DATABASE_ADAPTER','default'))
45
+ end
46
+
47
+ def self.mysql?
48
+ PACT_BROKER_DB.adapter_scheme.to_s =~ /mysql/
44
49
  end
45
50
 
46
51
  PACT_BROKER_DB ||= connection_for_env ENV.fetch('RACK_ENV')
@@ -34,6 +34,7 @@ module PactBroker
34
34
  config.messages_file = File.expand_path("../../../locale/en.yml", __FILE__)
35
35
 
36
36
  def valid_consumer_version_number?(value)
37
+ return true if PactBroker.configuration.order_versions_by_date
37
38
  parsed_version_number = PactBroker.configuration.version_parser.call(value)
38
39
  !parsed_version_number.nil?
39
40
  end
@@ -49,6 +49,7 @@ module PactBroker
49
49
  PactBroker::DB.connection = configuration.database_connection
50
50
  PactBroker::DB.connection.timezone = :utc
51
51
  PactBroker::DB.validate_connection_config if configuration.validate_database_connection_config
52
+ Sequel.datetime_class = DateTime
52
53
  Sequel.database_timezone = :utc # Store all dates in UTC, assume any date without a TZ is UTC
53
54
  Sequel.application_timezone = :local # Convert dates to localtime when retrieving from database
54
55
  Sequel.typecast_timezone = :utc # If no timezone specified on dates going into the database, assume they are UTC
@@ -5,16 +5,42 @@ module PactBroker
5
5
  class OrderVersions
6
6
 
7
7
  include PactBroker::Logging
8
- # TODO select for update
9
- def self.call pacticipant_id
10
-
11
- orderable_versions = PactBroker::Domain::Version.for_update.where(:pacticipant_id => pacticipant_id).order(:created_at, :id).collect{| version | OrderableVersion.new(version) }
12
- ordered_versions = if PactBroker.configuration.order_versions_by_date
13
- orderable_versions # already ordered in SQL
14
- else
15
- orderable_versions.sort
8
+
9
+ def self.call new_version
10
+ new_version.lock!
11
+ order_set = false
12
+
13
+ PactBroker::Domain::Version.for_update.where(pacticipant_id: new_version.pacticipant_id).exclude(order: nil).reverse(:order).each do | existing_version |
14
+ if new_version_after_existing_version? new_version, existing_version
15
+ set_order_after_existing_version_order new_version, existing_version
16
+ order_set = true
17
+ break
18
+ else
19
+ increment_existing_version_order existing_version
20
+ end
16
21
  end
17
- ordered_versions.each_with_index{ | version, i | version.update_model(i) }
22
+
23
+ if !order_set
24
+ set_order new_version, 0
25
+ end
26
+ end
27
+
28
+ def self.increment_existing_version_order existing_version
29
+ set_order existing_version, existing_version.order + 1
30
+ end
31
+
32
+ def self.set_order_after_existing_version_order new_version, existing_version
33
+ set_order new_version, existing_version.order + 1
34
+ end
35
+
36
+ def self.set_order version, order
37
+ # Use dataset method so we don't trigger an update to the updated_at column
38
+ Sequel::Model.db[:versions].where(id: version.id).update(order: order)
39
+ end
40
+
41
+ def self.new_version_after_existing_version? new_version, existing_version
42
+ return true if PactBroker.configuration.order_versions_by_date
43
+ return OrderableVersion.new(new_version).after?(OrderableVersion.new(existing_version))
18
44
  end
19
45
 
20
46
  class OrderableVersion
@@ -44,12 +70,8 @@ module PactBroker
44
70
  end
45
71
  end
46
72
 
47
- def update_model new_order
48
- if version_model.order != new_order
49
- # Avoid modifying the updated_at flag by doing a manual update
50
- # In 99% of cases, this will only set the order of the most recent version
51
- PactBroker::Domain::Version.where(id: version_model.id).update(order: new_order)
52
- end
73
+ def after? other
74
+ (self <=> other) == 1
53
75
  end
54
76
  end
55
77
  end
@@ -3,22 +3,24 @@ module PactBroker
3
3
 
4
4
  class Relationship
5
5
 
6
- attr_reader :consumer, :provider
6
+ attr_reader :consumer, :provider, :latest_pact, :latest_verification, :webhooks
7
7
 
8
- def initialize consumer, provider, latest_pact = nil, latest_verification = nil
8
+ def initialize consumer, provider, latest_pact = nil, latest_verification = nil, webhooks = []
9
9
  @consumer = consumer
10
10
  @provider = provider
11
11
  @latest_pact = latest_pact
12
12
  @latest_verification = latest_verification
13
+ @webhooks = webhooks
13
14
  end
14
15
 
15
- def self.create consumer, provider, latest_pact, latest_verification
16
- new consumer, provider, latest_pact, latest_verification
16
+ def self.create consumer, provider, latest_pact, latest_verification, webhooks
17
+ new consumer, provider, latest_pact, latest_verification, webhooks
17
18
  end
18
19
 
19
20
  def eq? other
20
21
  Relationship === other && other.consumer == consumer && other.provider == provider &&
21
- other.latest_pact == latest_pact && other.latest_verification == latest_verification
22
+ other.latest_pact == latest_pact && other.latest_verification == latest_verification &&
23
+ other.webhooks == webhooks
22
24
  end
23
25
 
24
26
  def == other
@@ -37,6 +39,10 @@ module PactBroker
37
39
  @latest_pact
38
40
  end
39
41
 
42
+ def any_webhooks?
43
+ @webhooks.any?
44
+ end
45
+
40
46
  def ever_verified?
41
47
  !!latest_verification
42
48
  end
@@ -9,6 +9,10 @@ module PactBroker
9
9
 
10
10
  associate(:many_to_one, :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id)
11
11
 
12
+ def <=> other
13
+ name <=> other.name
14
+ end
15
+
12
16
  end
13
17
 
14
18
  Tag.plugin :timestamps, :update_on_create=>true
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/db'
2
2
  require 'pact_broker/domain/order_versions'
3
+ require 'pact_broker/repositories/helpers'
3
4
 
4
5
  module PactBroker
5
6
 
@@ -12,8 +13,12 @@ module PactBroker
12
13
  associate(:many_to_one, :pacticipant, :class => "PactBroker::Domain::Pacticipant", :key => :pacticipant_id, :primary_key => :id)
13
14
  one_to_many :tags, :reciprocal => :version
14
15
 
16
+ dataset_module do
17
+ include PactBroker::Repositories::Helpers
18
+ end
19
+
15
20
  def after_create
16
- OrderVersions.(self.pacticipant_id)
21
+ OrderVersions.(self)
17
22
  end
18
23
 
19
24
  def to_s
@@ -4,8 +4,9 @@ module PactBroker
4
4
 
5
5
  class WebhookExecutionResult
6
6
 
7
- def initialize response, error = nil
7
+ def initialize response, logs, error = nil
8
8
  @response = response
9
+ @logs = logs
9
10
  @error = error
10
11
  end
11
12
 
@@ -21,6 +22,10 @@ module PactBroker
21
22
  @error
22
23
  end
23
24
 
25
+ def logs
26
+ @logs
27
+ end
28
+
24
29
  end
25
30
  end
26
- end
31
+ end
@@ -48,10 +48,15 @@ module PactBroker
48
48
 
49
49
  def execute
50
50
 
51
+ logs = StringIO.new
52
+ execution_logger = Logger.new(logs)
53
+
51
54
  begin
52
55
  req = http_request
56
+ execution_logger.info "HTTP/1.1 #{method.upcase} #{url_with_credentials}"
53
57
 
54
58
  headers.each_pair do | name, value |
59
+ execution_logger.info "#{name}: #{value}"
55
60
  req[name] = value
56
61
  end
57
62
 
@@ -65,20 +70,31 @@ module PactBroker
65
70
  end
66
71
  end
67
72
 
73
+ execution_logger.info req.body
74
+
68
75
  logger.info "Making webhook #{uuid} request #{to_s}"
76
+
69
77
  response = Net::HTTP.start(uri.hostname, uri.port,
70
78
  :use_ssl => uri.scheme == 'https') do |http|
71
79
  http.request req
72
80
  end
73
81
 
82
+ execution_logger.info(" ")
74
83
  logger.info "Received response for webhook #{uuid} status=#{response.code}"
84
+ execution_logger.info "HTTP/#{response.http_version} #{response.code} #{response.message}"
85
+ response.each_header do | header |
86
+ execution_logger.info "#{header.split("-").collect(&:capitalize).join('-')}: #{response[header]}"
87
+ end
75
88
  logger.debug "body=#{response.body}"
76
- WebhookExecutionResult.new(response)
89
+ execution_logger.info response.body
90
+ WebhookExecutionResult.new(response, logs.string)
77
91
 
78
92
  rescue StandardError => e
79
93
  logger.error "Error executing webhook #{uuid} #{e.class.name} - #{e.message}"
94
+ execution_logger.error "Error executing webhook #{uuid} #{e.class.name} - #{e.message}"
80
95
  logger.error e.backtrace.join("\n")
81
- WebhookExecutionResult.new(nil, e)
96
+ execution_logger.error e.backtrace.join("\n")
97
+ WebhookExecutionResult.new(nil, logs.string, e)
82
98
  end
83
99
 
84
100
  end
@@ -96,6 +112,12 @@ module PactBroker
96
112
  def uri
97
113
  URI(url)
98
114
  end
115
+
116
+ def url_with_credentials
117
+ u = URI(url)
118
+ u.userinfo = "#{username}:#{display_password}" if username
119
+ u
120
+ end
99
121
  end
100
122
 
101
123
  end