pact_broker 2.27.6 → 2.29.0

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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +38 -0
  5. data/MATRIX.md +4 -0
  6. data/README.md +3 -2
  7. data/Rakefile +1 -2
  8. data/config/database.yml +5 -0
  9. data/lib/pact_broker/api/decorators/matrix_decorator.rb +7 -1
  10. data/lib/pact_broker/api/decorators/reason_decorator.rb +50 -0
  11. data/lib/pact_broker/api/resources/base_resource.rb +1 -1
  12. data/lib/pact_broker/api/resources/error_handler.rb +32 -9
  13. data/lib/pact_broker/app.rb +16 -1
  14. data/lib/pact_broker/domain/webhook_request.rb +1 -1
  15. data/lib/pact_broker/matrix/deployment_status_summary.rb +123 -44
  16. data/lib/pact_broker/matrix/head_row.rb +20 -0
  17. data/lib/pact_broker/matrix/integration.rb +49 -7
  18. data/lib/pact_broker/matrix/query_results.rb +3 -2
  19. data/lib/pact_broker/matrix/query_results_with_deployment_status_summary.rb +2 -2
  20. data/lib/pact_broker/matrix/reason.rb +74 -0
  21. data/lib/pact_broker/matrix/repository.rb +97 -61
  22. data/lib/pact_broker/matrix/resolved_selector.rb +126 -0
  23. data/lib/pact_broker/matrix/row.rb +8 -1
  24. data/lib/pact_broker/matrix/service.rb +2 -16
  25. data/lib/pact_broker/pacts/repository.rb +15 -5
  26. data/lib/pact_broker/repositories/helpers.rb +3 -2
  27. data/lib/pact_broker/ui/views/index/_navbar.haml +14 -0
  28. data/lib/pact_broker/ui/views/index/show-with-tags.haml +1 -12
  29. data/lib/pact_broker/ui/views/index/show.haml +1 -12
  30. data/lib/pact_broker/ui/views/layouts/main.haml +3 -0
  31. data/lib/pact_broker/version.rb +1 -1
  32. data/lib/pact_broker/webhooks/job.rb +11 -5
  33. data/lib/pact_broker/webhooks/service.rb +10 -2
  34. data/lib/pact_broker/webhooks/webhook.rb +4 -0
  35. data/lib/rack/pact_broker/database_transaction.rb +22 -0
  36. data/pact_broker.gemspec +25 -1
  37. data/script/restart.sh +18 -0
  38. data/script/watch.sh +7 -0
  39. data/spec/features/publish_verification_spec.rb +1 -1
  40. data/spec/integration/app_spec.rb +1 -1
  41. data/spec/integration/webhooks/certificate_spec.rb +10 -2
  42. data/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb +2 -1
  43. data/spec/lib/pact_broker/api/decorators/reason_decorator_spec.rb +59 -0
  44. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +84 -21
  45. data/spec/lib/pact_broker/app_spec.rb +22 -0
  46. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +1 -1
  47. data/spec/lib/pact_broker/matrix/deployment_status_summary_spec.rb +116 -28
  48. data/spec/lib/pact_broker/matrix/head_row_spec.rb +23 -0
  49. data/spec/lib/pact_broker/matrix/integration_spec.rb +242 -0
  50. data/spec/lib/pact_broker/matrix/repository_dependency_spec.rb +58 -0
  51. data/spec/lib/pact_broker/matrix/repository_spec.rb +40 -7
  52. data/spec/lib/pact_broker/matrix/service_spec.rb +0 -50
  53. data/spec/lib/pact_broker/pacts/repository_spec.rb +20 -4
  54. data/spec/lib/pact_broker/webhooks/job_spec.rb +9 -9
  55. data/spec/lib/pact_broker/webhooks/service_spec.rb +3 -3
  56. data/spec/lib/pact_broker/webhooks/webhook_spec.rb +39 -0
  57. data/spec/lib/rack/pact_broker/database_transaction_spec.rb +40 -0
  58. data/spec/service_consumers/pact_helper.rb +2 -0
  59. data/spec/spec_helper.rb +2 -3
  60. data/spec/support/jobs.rb +12 -0
  61. data/spec/support/migration_helpers.rb +1 -1
  62. data/spec/support/simplecov.rb +10 -0
  63. data/tasks/audit.rake +2 -0
  64. data/tasks/pact.rake +5 -1
  65. data/tasks/rspec.rake +14 -0
  66. metadata +50 -5
  67. data/db/pact_broker_database.sqlite3 +0 -0
  68. data/spec/lib/pact_broker/matrix/repository_find_integrations_spec.rb +0 -51
@@ -0,0 +1,126 @@
1
+ # A selector with the pacticipant id, name, version number, and version id set
2
+ # This is created from either specified or inferred data, based on the user's query
3
+ # eg.
4
+ # can-i-deploy --pacticipant Foo --version 1 (this is a specified selector)
5
+ # --to prod (this is used to create inferred selectors)
6
+ module PactBroker
7
+ module Matrix
8
+ class ResolvedSelector < Hash
9
+
10
+ # A version ID of -1 will not match any rows, which is what we want to ensure that
11
+ # no matrix rows are returned for a version that does not exist.
12
+ NULL_VERSION_ID = -1
13
+
14
+ def initialize(params = {})
15
+ merge!(params)
16
+ end
17
+
18
+ def self.for_pacticipant(pacticipant, type)
19
+ ResolvedSelector.new(
20
+ pacticipant_id: pacticipant.id,
21
+ pacticipant_name: pacticipant.name,
22
+ type: type
23
+ )
24
+ end
25
+
26
+ def self.for_pacticipant_and_version(pacticipant, version, original_selector, type)
27
+ ResolvedSelector.new(
28
+ pacticipant_id: pacticipant.id,
29
+ pacticipant_name: pacticipant.name,
30
+ pacticipant_version_id: version.id,
31
+ pacticipant_version_number: version.number,
32
+ latest: original_selector[:latest],
33
+ tag: original_selector[:tag],
34
+ type: type
35
+ )
36
+ end
37
+
38
+ def self.for_pacticipant_and_non_existing_version(pacticipant, original_selector, type)
39
+ ResolvedSelector.new(
40
+ pacticipant_id: pacticipant.id,
41
+ pacticipant_name: pacticipant.name,
42
+ pacticipant_version_id: NULL_VERSION_ID,
43
+ pacticipant_version_number: original_selector[:pacticipant_version_number],
44
+ latest: original_selector[:latest],
45
+ tag: original_selector[:tag],
46
+ type: type
47
+ )
48
+ end
49
+
50
+ def pacticipant_id
51
+ self[:pacticipant_id]
52
+ end
53
+
54
+ def pacticipant_name
55
+ self[:pacticipant_name]
56
+ end
57
+
58
+ def pacticipant_version_id
59
+ self[:pacticipant_version_id]
60
+ end
61
+
62
+ def pacticipant_version_number
63
+ self[:pacticipant_version_number]
64
+ end
65
+
66
+ def latest?
67
+ self[:latest]
68
+ end
69
+
70
+ def tag
71
+ self[:tag]
72
+ end
73
+
74
+ def latest_tagged?
75
+ latest? && tag
76
+ end
77
+
78
+ def version_does_not_exist?
79
+ !version_exists?
80
+ end
81
+
82
+ def latest_tagged_version_that_does_not_exist?
83
+ version_does_not_exist? && latest_tagged?
84
+ end
85
+
86
+ def specified_version_that_does_not_exist?
87
+ specified? && version_does_not_exist?
88
+ end
89
+
90
+ def version_exists?
91
+ pacticipant_version_id != NULL_VERSION_ID
92
+ end
93
+
94
+ # Did the user specify this selector in the user's query?
95
+ def specified?
96
+ self[:type] == :specified
97
+ end
98
+
99
+ # Was this selector inferred based on the user's query?
100
+ #(ie. the integrations were calculated because only one selector was specified)
101
+ def inferred?
102
+ self[:type] == :inferred
103
+ end
104
+
105
+ def description
106
+ if latest_tagged? && pacticipant_version_number
107
+ "the latest version of #{pacticipant_name} with tag #{tag} (#{pacticipant_version_number})"
108
+ elsif latest_tagged?
109
+ "the latest version of #{pacticipant_name} with tag #{tag} (no such version exists)"
110
+ elsif latest? && pacticipant_version_number
111
+ "the latest version of #{pacticipant_name} (#{pacticipant_version_number})"
112
+ elsif latest?
113
+ "the latest version of #{pacticipant_name} (no such version exists)"
114
+ elsif tag && pacticipant_version_number
115
+ "a version of #{pacticipant_name} with tag #{tag} (#{pacticipant_version_number})"
116
+ elsif tag
117
+ "a version of #{pacticipant_name} with tag #{tag} (no such version exists)"
118
+ elsif pacticipant_version_number
119
+ "version #{pacticipant_version_number} of #{pacticipant_name}"
120
+ else
121
+ "any version of #{pacticipant_name}"
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -14,7 +14,6 @@ module PactBroker
14
14
  TP_COLS = [ :consumer_version_number, :pact_revision_number, :provider_version_number, :verification_number]
15
15
 
16
16
  associate(:one_to_many, :latest_triggered_webhooks, :class => "PactBroker::Webhooks::LatestTriggeredWebhook", primary_key: :pact_publication_id, key: :pact_publication_id)
17
- associate(:one_to_many, :webhooks, :class => "PactBroker::Webhooks::Webhook", primary_key: [:consumer_id, :provider_id], key: [:consumer_id, :provider_id])
18
17
  associate(:one_to_many, :consumer_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :consumer_version_id, key: :version_id)
19
18
  associate(:one_to_many, :provider_version_tags, :class => "PactBroker::Tags::TagWithLatestFlag", primary_key: :provider_version_id, key: :version_id)
20
19
 
@@ -198,6 +197,14 @@ module PactBroker
198
197
  def eql?(obj)
199
198
  (obj.class == model) && (obj.values == values)
200
199
  end
200
+
201
+ def pacticipant_names
202
+ [consumer_name, provider_name]
203
+ end
204
+
205
+ def involves_pacticipant_with_name?(pacticipant_name)
206
+ pacticipant_name.include?(pacticipant_name)
207
+ end
201
208
  end
202
209
  end
203
210
  end
@@ -12,10 +12,8 @@ module PactBroker
12
12
 
13
13
  def find selectors, options = {}
14
14
  query_results = matrix_repository.find selectors, options
15
- pacticipant_names = selectors.collect{ | s| s[:pacticipant_name] }
16
- integrations = matrix_repository.find_integrations(pacticipant_names)
17
- deployment_status_summary = DeploymentStatusSummary.new(query_results.rows, query_results.resolved_selectors, integrations)
18
- QueryResultsWithDeploymentStatusSummary.new(query_results.rows, query_results.selectors, query_results.options, query_results.resolved_selectors, deployment_status_summary)
15
+ deployment_status_summary = DeploymentStatusSummary.new(query_results.rows, query_results.resolved_selectors, query_results.integrations)
16
+ QueryResultsWithDeploymentStatusSummary.new(query_results.rows, query_results.selectors, query_results.options, query_results.resolved_selectors, query_results.integrations, deployment_status_summary)
19
17
  end
20
18
 
21
19
  def find_for_consumer_and_provider params
@@ -67,18 +65,6 @@ module PactBroker
67
65
  end
68
66
  end
69
67
 
70
- if error_messages.empty?
71
- selectors.each do | s |
72
- if s[:pacticipant_version_number]
73
- version = version_service.find_by_pacticipant_name_and_number(pacticipant_name: s[:pacticipant_name], pacticipant_version_number: s[:pacticipant_version_number])
74
- error_messages << "No pact or verification found for #{s[:pacticipant_name]} version #{s[:pacticipant_version_number]}" if version.nil?
75
- elsif s[:tag]
76
- version = version_service.find_by_pacticipant_name_and_latest_tag(s[:pacticipant_name], s[:tag])
77
- error_messages << "No version of #{s[:pacticipant_name]} found with tag #{s[:tag]}" if version.nil?
78
- end
79
- end
80
- end
81
-
82
68
  if selectors.size == 0
83
69
  error_messages << "Please provide 1 or more version selectors."
84
70
  end
@@ -35,13 +35,18 @@ module PactBroker
35
35
  existing_model = PactPublication.find(id: id)
36
36
  pact_version = find_or_create_pact_version(existing_model.consumer_version.pacticipant_id, existing_model.provider_id, params[:json_content])
37
37
  if existing_model.pact_version_id != pact_version.id
38
- pact_publication = PactPublication.new(
38
+ key = {
39
39
  consumer_version_id: existing_model.consumer_version_id,
40
- consumer_id: existing_model.consumer_id,
41
40
  provider_id: existing_model.provider_id,
42
- revision_number: (existing_model.revision_number + 1),
43
- pact_version: pact_version,
44
- ).save
41
+ revision_number: next_revision_number(existing_model),
42
+ }
43
+ new_params = key.merge(
44
+ consumer_id: existing_model.consumer_id,
45
+ pact_version_id: pact_version.id,
46
+ created_at: Sequel.datetime_class.now
47
+ )
48
+ PactPublication.upsert(new_params, key.keys)
49
+ pact_publication = PactPublication.where(key).single_record
45
50
  update_latest_pact_publication_ids(pact_publication)
46
51
  pact_publication.to_domain
47
52
  else
@@ -49,6 +54,11 @@ module PactBroker
49
54
  end
50
55
  end
51
56
 
57
+ # This logic is a separate method so we can stub it to create a "conflict" scenario
58
+ def next_revision_number(existing_model)
59
+ existing_model.revision_number + 1
60
+ end
61
+
52
62
  def update_latest_pact_publication_ids(pact_publication)
53
63
  params = {
54
64
  consumer_version_id: pact_publication.consumer_version_id,
@@ -36,11 +36,12 @@ module PactBroker
36
36
  end
37
37
  end
38
38
 
39
- def upsert row, key_names
39
+ def upsert row, key_names, columns_to_update = nil
40
40
  if postgres?
41
41
  insert_conflict(update: row, target: key_names).insert(row)
42
42
  elsif mysql?
43
- on_duplicate_key_update.insert(row)
43
+ update_cols = columns_to_update || (row.keys - key_names)
44
+ on_duplicate_key_update(*update_cols).insert(row)
44
45
  else
45
46
  # Sqlite
46
47
  key = row.reject{ |k, v| !key_names.include?(k) }
@@ -0,0 +1,14 @@
1
+ %nav.navbase-default{role: "navigation", id: "navigation"}
2
+ .container-fluid
3
+ .navbar-header
4
+ %ul.navbar-right#top-left-menu
5
+ %li
6
+ - if tag_toggle
7
+ %a{href: './?tags=true'}
8
+ Show latest tags
9
+ - else
10
+ %a{href: './'}
11
+ Hide latest tags
12
+
13
+ %a{href: '/hal-browser/browser.html'}
14
+ API Browser
@@ -1,19 +1,8 @@
1
1
  %body
2
- %link{rel: 'stylesheet', href: '/css/bootstrap.min.css'}
3
2
  %link{rel: 'stylesheet', href: '/stylesheets/index.css'}
4
- %script{type: 'text/javascript', src:'/javascripts/jquery-2.1.1.min.js'}
5
3
  %script{type: 'text/javascript', src:'/javascripts/jquery.tablesorter.min.js'}
6
- %script{type: 'text/javascript', src:'/js/bootstrap.min.js'}
7
4
  .container
8
- %nav.navbase-default{role: "navigation", id: "navigation"}
9
- .container-fluid
10
- .navbar-header
11
- %ul.navbar-right#top-left-menu
12
- %li
13
- %a{href: './'}
14
- Hide latest tags
15
- %a{href: '/hal-browser/browser.html'}
16
- API Browser
5
+ = render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: false}
17
6
  %h1.page-header
18
7
  Pacts
19
8
  %table.table.table-bordered.table-striped{ id: 'relationships' }
@@ -1,19 +1,8 @@
1
1
  %body
2
- %link{rel: 'stylesheet', href: '/css/bootstrap.min.css'}
3
2
  %link{rel: 'stylesheet', href: '/stylesheets/index.css'}
4
- %script{type: 'text/javascript', src:'/javascripts/jquery-2.1.1.min.js'}
5
3
  %script{type: 'text/javascript', src:'/javascripts/jquery.tablesorter.min.js'}
6
- %script{type: 'text/javascript', src:'/js/bootstrap.min.js'}
7
4
  .container
8
- %nav.navbase-default{role: "navigation", id: "navigation"}
9
- .container-fluid
10
- .navbar-header
11
- %ul.navbar-right#top-left-menu
12
- %li
13
- %a{href: './?tags=true'}
14
- Show latest tags
15
- %a{href: '/hal-browser/browser.html'}
16
- API Browser
5
+ = render :haml, :'index/_navbar', :layout => false, locals: {tag_toggle: true}
17
6
  %h1.page-header
18
7
  Pacts
19
8
  %table.table.table-bordered.table-striped{ id: 'relationships' }
@@ -3,4 +3,7 @@
3
3
  %head
4
4
  %title= defined?(title) ? title : ""
5
5
  %link{rel: 'shortcut icon', href: '/images/favicon.ico', type:'image/x-icon'}
6
+ %link{rel: 'stylesheet', href: '/css/bootstrap.min.css'}
7
+ %script{type: 'text/javascript', src:'/javascripts/jquery-2.1.1.min.js'}
8
+ %script{type: 'text/javascript', src:'/js/bootstrap.min.js'}
6
9
  = yield
@@ -1,3 +1,3 @@
1
1
  module PactBroker
2
- VERSION = '2.27.6'
2
+ VERSION = '2.29.0'
3
3
  end
@@ -11,6 +11,16 @@ module PactBroker
11
11
  include PactBroker::Logging
12
12
 
13
13
  def perform data
14
+ data.fetch(:database_connector).call do
15
+ perform_with_connection(data)
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :triggered_webhook, :error_count
22
+
23
+ def perform_with_connection(data)
14
24
  @data = data
15
25
  @triggered_webhook = PactBroker::Webhooks::TriggeredWebhook.find(id: data[:triggered_webhook].id)
16
26
  @error_count = data[:error_count] || 0
@@ -26,10 +36,6 @@ module PactBroker
26
36
  end
27
37
  end
28
38
 
29
- private
30
-
31
- attr_reader :triggered_webhook, :error_count
32
-
33
39
  def execution_options
34
40
  {
35
41
  success_log_message: "Successfully executed webhook",
@@ -59,7 +65,7 @@ module PactBroker
59
65
  reschedule_job
60
66
  update_triggered_webhook_status TriggeredWebhook::STATUS_RETRYING
61
67
  else
62
- logger.error "Failed to execute webhook #{triggered_webhook.webhook_uuid} after #{retry_schedule.size + 1} attempts."
68
+ logger.info "Failed to execute webhook #{triggered_webhook.webhook_uuid} after #{retry_schedule.size + 1} attempts."
63
69
  update_triggered_webhook_status TriggeredWebhook::STATUS_FAILURE
64
70
  end
65
71
  end
@@ -124,14 +124,22 @@ module PactBroker
124
124
  begin
125
125
  triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, verification, RESOURCE_CREATION)
126
126
  logger.info "Scheduling job for #{webhook.description} with uuid #{webhook.uuid}"
127
- # Bit of a dodgey hack to make sure the request transaction has finished before we execute the webhook
128
- Job.perform_in(5, triggered_webhook: triggered_webhook)
127
+ job_data = {
128
+ triggered_webhook: triggered_webhook,
129
+ database_connector: job_database_connector
130
+ }
131
+ # Delay slightly to make sure the request transaction has finished before we execute the webhook
132
+ Job.perform_in(5, job_data)
129
133
  rescue StandardError => e
130
134
  log_error e
131
135
  end
132
136
  end
133
137
  end
134
138
 
139
+ def self.job_database_connector
140
+ Thread.current[:pact_broker_thread_data].database_connector
141
+ end
142
+
135
143
  def self.find_latest_triggered_webhooks_for_pact pact
136
144
  webhook_repository.find_latest_triggered_webhooks_for_pact pact
137
145
  end
@@ -71,6 +71,10 @@ module PactBroker
71
71
  end
72
72
  end
73
73
 
74
+ def is_for? relationship
75
+ (consumer_id == relationship.consumer_id || !consumer_id) && (provider_id == relationship.provider_id || !provider_id)
76
+ end
77
+
74
78
  private
75
79
 
76
80
  def self.properties_hash_from_domain webhook
@@ -1,5 +1,6 @@
1
1
  require 'pact_broker/constants'
2
2
  require 'sequel'
3
+ require 'ostruct'
3
4
 
4
5
  module Rack
5
6
  module PactBroker
@@ -11,14 +12,22 @@ module Rack
11
12
  def initialize app, database_connection
12
13
  @app = app
13
14
  @database_connection = database_connection
15
+ @default_database_connector = ->(&block) {
16
+ database_connection.synchronize do
17
+ block.call
18
+ end
19
+ }
14
20
  end
15
21
 
16
22
  def call env
23
+ set_database_connector
17
24
  if use_transaction? env
18
25
  call_with_transaction env
19
26
  else
20
27
  call_without_transaction env
21
28
  end
29
+ ensure
30
+ clear_database_connector
22
31
  end
23
32
 
24
33
  def use_transaction? env
@@ -43,6 +52,19 @@ module Rack
43
52
  def do_not_rollback? response
44
53
  response[1].delete(::PactBroker::DO_NOT_ROLLBACK)
45
54
  end
55
+
56
+ def set_database_connector
57
+ Thread.current[:pact_broker_thread_data] ||= OpenStruct.new
58
+ Thread.current[:pact_broker_thread_data].database_connector ||= @default_database_connector
59
+ end
60
+
61
+ def clear_database_connector
62
+ if thread_data = Thread.current[:pact_broker_thread_data]
63
+ if thread_data.database_connector == @default_database_connector
64
+ thread_data.database_connector = nil
65
+ end
66
+ end
67
+ end
46
68
  end
47
69
  end
48
70
  end
data/pact_broker.gemspec CHANGED
@@ -3,6 +3,28 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'pact_broker/version'
5
5
 
6
+ def gem_files
7
+ if Dir.exist?(".git")
8
+ `git ls-files`.split($/)
9
+ else
10
+ root_path = File.dirname(__FILE__)
11
+ all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") }
12
+ all_files.reject! { |file| [".", ".."].include?(File.basename(file)) || File.directory?(file)}
13
+ gitignore_path = File.join(root_path, ".gitignore")
14
+ gitignore = File.readlines(gitignore_path)
15
+ gitignore.map! { |line| line.chomp.strip }
16
+ gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ }
17
+
18
+ all_files.reject do |file|
19
+ gitignore.any? do |ignore|
20
+ file.start_with?(ignore) ||
21
+ File.fnmatch(ignore, file, File::FNM_PATHNAME) ||
22
+ File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
6
28
  Gem::Specification.new do |gem|
7
29
  gem.name = "pact_broker"
8
30
  gem.version = PactBroker::VERSION
@@ -14,7 +36,7 @@ Gem::Specification.new do |gem|
14
36
 
15
37
  gem.required_ruby_version = '>= 2.2.0'
16
38
 
17
- gem.files = `git ls-files`.split($/)
39
+ gem.files = gem_files
18
40
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
41
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
42
  gem.require_paths = ["lib"]
@@ -38,6 +60,7 @@ Gem::Specification.new do |gem|
38
60
  gem.add_runtime_dependency 'sucker_punch', '~>2.0'
39
61
  gem.add_runtime_dependency 'rack-protection', '~>2.0'
40
62
  gem.add_runtime_dependency 'dry-types', '~> 0.10.3' # https://travis-ci.org/pact-foundation/pact_broker/jobs/249448621
63
+ gem.add_runtime_dependency 'dry-logic', '0.4.2' # Later version cases ArgumentError: wrong number of arguments
41
64
  gem.add_runtime_dependency 'table_print', '~> 1.5'
42
65
  gem.add_runtime_dependency 'semantic_logger', '~> 4.3'
43
66
 
@@ -57,4 +80,5 @@ Gem::Specification.new do |gem|
57
80
  gem.add_development_dependency 'bump', '~> 0.5'
58
81
  gem.add_development_dependency 'timecop', '~> 0.9'
59
82
  gem.add_development_dependency 'sequel-annotate', '~>1.3'
83
+ gem.add_development_dependency 'faraday', '~>0.15'
60
84
  end