pact_broker 2.0.2 → 2.0.3

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