pact_broker 2.58.0 → 2.58.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release_gem.yml +25 -0
  3. data/.travis.yml +5 -3
  4. data/CHANGELOG.md +8 -0
  5. data/DEVELOPER_SETUP.md +46 -16
  6. data/Dockerfile +23 -2
  7. data/Gemfile +29 -2
  8. data/config.ru +32 -12
  9. data/config/database.yml +7 -0
  10. data/docker-compose-dev-postgres.yml +35 -0
  11. data/docker-compose-test.yml +100 -0
  12. data/lib/pact_broker/api/decorators/decorator_context.rb +1 -1
  13. data/lib/pact_broker/api/pact_broker_urls.rb +4 -4
  14. data/lib/pact_broker/api/paths.rb +15 -0
  15. data/lib/pact_broker/api/resources/all_webhooks.rb +3 -3
  16. data/lib/pact_broker/api/resources/base_resource.rb +2 -174
  17. data/lib/pact_broker/api/resources/default_base_resource.rb +211 -0
  18. data/lib/pact_broker/api/resources/error_handler.rb +1 -1
  19. data/lib/pact_broker/api/resources/group.rb +1 -13
  20. data/lib/pact_broker/api/resources/label.rb +5 -2
  21. data/lib/pact_broker/api/resources/latest_pact.rb +4 -0
  22. data/lib/pact_broker/api/resources/latest_pacts.rb +1 -4
  23. data/lib/pact_broker/api/resources/latest_verifications_for_consumer_version.rb +8 -2
  24. data/lib/pact_broker/api/resources/matrix.rb +2 -2
  25. data/lib/pact_broker/api/resources/matrix_for_consumer_and_provider.rb +1 -1
  26. data/lib/pact_broker/api/resources/pact.rb +5 -1
  27. data/lib/pact_broker/api/resources/pact_content_diff.rb +1 -2
  28. data/lib/pact_broker/api/resources/pact_triggered_webhooks.rb +1 -7
  29. data/lib/pact_broker/api/resources/pact_versions.rb +1 -3
  30. data/lib/pact_broker/api/resources/pact_webhooks.rb +2 -5
  31. data/lib/pact_broker/api/resources/pact_webhooks_status.rb +1 -17
  32. data/lib/pact_broker/api/resources/pacticipant.rb +7 -14
  33. data/lib/pact_broker/api/resources/pacticipants.rb +1 -1
  34. data/lib/pact_broker/api/resources/pacticipants_for_label.rb +1 -1
  35. data/lib/pact_broker/api/resources/previous_distinct_pact_version.rb +4 -0
  36. data/lib/pact_broker/api/resources/provider_pacts.rb +5 -1
  37. data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +1 -1
  38. data/lib/pact_broker/api/resources/tag.rb +5 -1
  39. data/lib/pact_broker/api/resources/tagged_pact_versions.rb +1 -2
  40. data/lib/pact_broker/api/resources/triggered_webhook_logs.rb +4 -0
  41. data/lib/pact_broker/api/resources/verification.rb +7 -3
  42. data/lib/pact_broker/api/resources/verification_triggered_webhooks.rb +5 -1
  43. data/lib/pact_broker/api/resources/verifications.rb +7 -3
  44. data/lib/pact_broker/api/resources/version.rb +4 -1
  45. data/lib/pact_broker/api/resources/versions.rb +1 -3
  46. data/lib/pact_broker/api/resources/webhook.rb +6 -2
  47. data/lib/pact_broker/api/resources/webhook_execution.rb +4 -0
  48. data/lib/pact_broker/api/resources/webhooks.rb +3 -18
  49. data/lib/pact_broker/app.rb +1 -0
  50. data/lib/pact_broker/configuration.rb +8 -1
  51. data/lib/pact_broker/db/clean.rb +0 -6
  52. data/lib/pact_broker/domain/pacticipant.rb +7 -2
  53. data/lib/pact_broker/domain/version.rb +3 -0
  54. data/lib/pact_broker/index/service.rb +5 -45
  55. data/lib/pact_broker/pacticipants/repository.rb +2 -2
  56. data/lib/pact_broker/pacts/latest_pact_publication_id_for_consumer_version.rb +2 -5
  57. data/lib/pact_broker/pacts/pact_publication.rb +11 -9
  58. data/lib/pact_broker/pacts/pact_version.rb +3 -4
  59. data/lib/pact_broker/pacts/repository.rb +53 -39
  60. data/lib/pact_broker/pacts/verifiable_pact.rb +8 -0
  61. data/lib/pact_broker/policies.rb +53 -0
  62. data/lib/pact_broker/repositories/helpers.rb +3 -20
  63. data/lib/pact_broker/ui/controllers/index.rb +0 -2
  64. data/lib/pact_broker/verifications/latest_verification_id_for_pact_version_and_provider_version.rb +3 -5
  65. data/lib/pact_broker/version.rb +1 -1
  66. data/lib/pact_broker/versions/repository.rb +2 -4
  67. data/lib/rack/pact_broker/request_target.rb +6 -1
  68. data/lib/sequel/plugins/insert_ignore.rb +69 -0
  69. data/lib/sequel/plugins/upsert.rb +103 -0
  70. data/pact_broker.gemspec +22 -41
  71. data/script/docker/db-execute-sql-file.sh +2 -0
  72. data/script/issues/consumer-version-selectors-docs/issue-text.txt +11 -0
  73. data/script/issues/consumer-version-selectors-docs/issues.txt +6 -0
  74. data/script/issues/consumer-version-selectors-docs/raise-issue-in-client-repos.sh +10 -0
  75. data/script/prod/redact-data.sql +1 -0
  76. data/script/release-via-github-action.sh +7 -0
  77. data/script/trigger-release.sh +30 -0
  78. data/spec/features/get_versions_spec.rb +1 -6
  79. data/spec/lib/pact_broker/api/pact_broker_urls_spec.rb +2 -2
  80. data/spec/lib/pact_broker/api/resources/all_webhooks_spec.rb +1 -1
  81. data/spec/lib/pact_broker/api/resources/default_base_resource_spec.rb +158 -0
  82. data/spec/lib/pact_broker/api/resources/tag_spec.rb +2 -2
  83. data/spec/lib/pact_broker/api/resources/webhook_spec.rb +1 -1
  84. data/spec/lib/pact_broker/api/resources/webhooks_spec.rb +1 -1
  85. data/spec/lib/pact_broker/db/clean_old_spec.rb +125 -0
  86. data/spec/lib/pact_broker/db/clean_spec.rb +45 -11
  87. data/spec/lib/pact_broker/index/service_spec.rb +2 -3
  88. data/spec/lib/pact_broker/pacts/repository_find_wip_pact_versions_for_provider_spec.rb +36 -0
  89. data/spec/lib/pact_broker/versions/repository_spec.rb +8 -0
  90. data/spec/lib/rack/pact_broker/request_target_spec.rb +7 -0
  91. data/spec/lib/sequel/plugins/insert_ignore_spec.rb +82 -0
  92. data/spec/lib/sequel/plugins/upsert_spec.rb +125 -0
  93. data/spec/spec_helper.rb +1 -0
  94. data/tasks/audit.rake +6 -2
  95. data/tasks/development.rake +2 -2
  96. metadata +46 -284
  97. data/spec/lib/pact_broker/api/resources/base_resource_spec.rb +0 -78
@@ -35,7 +35,7 @@ module PactBroker
35
35
  end
36
36
 
37
37
  def version_url base_url, version
38
- "#{pacticipant_url(base_url, version.pacticipant)}/versions/#{version.number}"
38
+ "#{pacticipant_url(base_url, version.pacticipant)}/versions/#{url_encode(version.number)}"
39
39
  end
40
40
 
41
41
  def version_url_from_params params, base_url = ''
@@ -157,7 +157,7 @@ module PactBroker
157
157
  end
158
158
 
159
159
  def latest_verifications_for_consumer_version_url version, base_url
160
- "#{base_url}/verification-results/consumer/#{url_encode(version.pacticipant.name)}/version/#{version.number}/latest"
160
+ "#{base_url}/verification-results/consumer/#{url_encode(version.pacticipant.name)}/version/#{url_encode(version.number)}/latest"
161
161
  end
162
162
 
163
163
  def latest_verification_for_pact_url pact, base_url, permalink = true
@@ -198,7 +198,7 @@ module PactBroker
198
198
  end
199
199
 
200
200
  def label_url label, base_url
201
- "#{labels_url(label.pacticipant, base_url)}/#{label.name}"
201
+ "#{labels_url(label.pacticipant, base_url)}/#{url_encode(label.name)}"
202
202
  end
203
203
 
204
204
  def labels_url pacticipant, base_url
@@ -281,7 +281,7 @@ module PactBroker
281
281
  end
282
282
 
283
283
  def group_url(pacticipant_name, base_url = '')
284
- "#{base_url}/groups/#{pacticipant_name}"
284
+ "#{base_url}/groups/#{url_encode(pacticipant_name)}"
285
285
  end
286
286
 
287
287
  def hal_browser_url target_url, base_url = ''
@@ -0,0 +1,15 @@
1
+ module PactBroker
2
+ module Api
3
+ module Paths
4
+ PACT_BADGE_PATH = %r{^/pacts/provider/[^/]+/consumer/.*/badge(?:\.[A-Za-z]+)?$}.freeze
5
+ MATRIX_BADGE_PATH = %r{^/matrix/provider/[^/]+/latest/[^/]+/consumer/[^/]+/latest/[^/]+/badge(?:\.[A-Za-z]+)?$}.freeze
6
+
7
+ extend self
8
+
9
+ def is_badge_path?(path)
10
+ # Optimise by checking include? first - regexp slow
11
+ path.include?('/badge') && (path =~ PACT_BADGE_PATH || path =~ MATRIX_BADGE_PATH)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -37,12 +37,12 @@ module PactBroker
37
37
  end
38
38
 
39
39
  def to_json
40
- Decorators::WebhooksDecorator.new(webhooks).to_json(user_options: decorator_context(resource_title: "Webhooks"))
40
+ Decorators::WebhooksDecorator.new(webhooks).to_json(decorator_options(resource_title: "Webhooks"))
41
41
  end
42
42
 
43
43
  def from_json
44
- saved_webhook = webhook_service.create next_uuid, webhook, consumer, provider
45
- response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(user_options: { base_url: base_url })
44
+ saved_webhook = webhook_service.create(next_uuid, webhook, consumer, provider)
45
+ response.body = Decorators::WebhookDecorator.new(saved_webhook).to_json(decorator_options)
46
46
  end
47
47
 
48
48
  private
@@ -1,181 +1,9 @@
1
- require 'webmachine'
2
- require 'pact_broker/api/resources/error_handler'
3
- require 'pact_broker/services'
4
- require 'pact_broker/api/decorators'
5
- require 'pact_broker/logging'
6
- require 'pact_broker/api/pact_broker_urls'
7
- require 'pact_broker/api/decorators/decorator_context'
8
- require 'pact_broker/json'
9
- require 'pact_broker/pacts/pact_params'
10
- require 'pact_broker/api/resources/authentication'
1
+ require 'pact_broker/configuration'
11
2
 
12
3
  module PactBroker
13
-
14
4
  module Api
15
5
  module Resources
16
-
17
- class InvalidJsonError < StandardError ; end
18
-
19
- class BaseResource < Webmachine::Resource
20
-
21
- include PactBroker::Services
22
- include PactBroker::Api::PactBrokerUrls
23
- include PactBroker::Api::Resources::Authentication
24
- include PactBroker::Logging
25
-
26
- attr_accessor :user
27
-
28
- def initialize
29
- PactBroker.configuration.before_resource.call(self)
30
- end
31
-
32
- def options
33
- { 'Access-Control-Allow-Methods' => allowed_methods.join(", ")}
34
- end
35
-
36
- def known_methods
37
- super + ['PATCH']
38
- end
39
-
40
- def finish_request
41
- PactBroker.configuration.after_resource.call(self)
42
- end
43
-
44
- def is_authorized?(authorization_header)
45
- authenticated?(self, authorization_header)
46
- end
47
-
48
- def forbidden?
49
- return false if PactBroker.configuration.authorize.nil?
50
- !PactBroker.configuration.authorize.call(self, {})
51
- end
52
-
53
- def identifier_from_path
54
- request.path_info.each_with_object({}) do | pair, hash|
55
- hash[pair.first] = pair.last === String ? URI.decode(pair.last) : pair.last
56
- end
57
- end
58
-
59
- alias_method :path_info, :identifier_from_path
60
-
61
- def base_url
62
- PactBroker.configuration.base_url || request.base_uri.to_s.chomp('/')
63
- end
64
-
65
- # See comments for base_url in lib/pact_broker/doc/controllers/app.rb
66
- def ui_base_url
67
- PactBroker.configuration.base_url || ''
68
- end
69
-
70
- def charsets_provided
71
- [['utf-8', :encode]]
72
- end
73
-
74
- # We only use utf-8 so leave encoding as it is
75
- def encode(body)
76
- body
77
- end
78
-
79
- def resource_url
80
- request.uri.to_s.gsub(/\?.*/, '').chomp('/')
81
- end
82
-
83
- def decorator_context options = {}
84
- Decorators::DecoratorContext.new(base_url, resource_url, request.env, options)
85
- end
86
-
87
- def handle_exception e
88
- PactBroker::Api::Resources::ErrorHandler.call(e, request, response)
89
- end
90
-
91
- def params
92
- @params ||= JSON.parse(request.body.to_s, { symbolize_names: true }.merge(PACT_PARSING_OPTIONS)) #Not load! Otherwise it will try to load Ruby classes.
93
- end
94
-
95
- def params_with_string_keys
96
- @params_with_string_keys ||= JSON.parse(request.body.to_s, {symbolize_names: false}.merge(PACT_PARSING_OPTIONS))
97
- end
98
-
99
- def pact_params
100
- @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
101
- end
102
-
103
- def set_json_error_message message
104
- response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
105
- response.body = {error: message}.to_json
106
- end
107
-
108
- def set_json_validation_error_messages errors
109
- response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
110
- response.body = {errors: errors}.to_json
111
- end
112
-
113
- def request_body
114
- @request_body ||= request.body.to_s
115
- end
116
-
117
- def consumer_name
118
- identifier_from_path[:consumer_name]
119
- end
120
-
121
- def provider_name
122
- identifier_from_path[:provider_name]
123
- end
124
-
125
- def pacticipant_name
126
- identifier_from_path[:pacticipant_name]
127
- end
128
-
129
- def invalid_json?
130
- begin
131
- params
132
- false
133
- rescue StandardError => e
134
- logger.info "Error parsing JSON #{e} - #{request_body}"
135
- set_json_error_message "Error parsing JSON - #{e.message}"
136
- response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
137
- true
138
- end
139
- end
140
-
141
- def validation_errors? model
142
- if (errors = model.validate).any?
143
- set_json_validation_error_messages errors
144
- true
145
- else
146
- false
147
- end
148
- end
149
-
150
- def contract_validation_errors? contract, params
151
- if (invalid = !contract.validate(params))
152
- set_json_validation_error_messages contract.errors.messages
153
- end
154
- invalid
155
- end
156
-
157
- def find_pacticipant name, role
158
- pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
159
- set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
160
- end
161
- end
162
-
163
- def consumer
164
- @consumer ||= identifier_from_path[:consumer_name] && find_pacticipant(identifier_from_path[:consumer_name], "consumer")
165
- end
166
-
167
- def provider
168
- @provider ||= identifier_from_path[:provider_name] && find_pacticipant(identifier_from_path[:provider_name], "provider")
169
- end
170
-
171
- def pact
172
- @pact ||= pact_service.find_pact(pact_params)
173
- end
174
-
175
- def database_connector
176
- request.env["pactbroker.database_connector"]
177
- end
178
- end
6
+ BaseResource = PactBroker.configuration.base_resource_class_factory.call
179
7
  end
180
8
  end
181
9
  end
@@ -0,0 +1,211 @@
1
+ require 'webmachine'
2
+ require 'pact_broker/api/resources/error_handler'
3
+ require 'pact_broker/services'
4
+ require 'pact_broker/api/decorators'
5
+ require 'pact_broker/logging'
6
+ require 'pact_broker/api/pact_broker_urls'
7
+ require 'pact_broker/api/decorators/decorator_context'
8
+ require 'pact_broker/json'
9
+ require 'pact_broker/pacts/pact_params'
10
+ require 'pact_broker/api/resources/authentication'
11
+
12
+ module PactBroker
13
+ module Api
14
+ module Resources
15
+ class InvalidJsonError < PactBroker::Error ; end
16
+
17
+ class DefaultBaseResource < Webmachine::Resource
18
+ include PactBroker::Services
19
+ include PactBroker::Api::PactBrokerUrls
20
+ include PactBroker::Api::Resources::Authentication
21
+ include PactBroker::Logging
22
+
23
+ attr_accessor :user
24
+
25
+ def initialize
26
+ PactBroker.configuration.before_resource.call(self)
27
+ end
28
+
29
+ def options
30
+ { 'Access-Control-Allow-Methods' => allowed_methods.join(", ")}
31
+ end
32
+
33
+ def known_methods
34
+ super + ['PATCH']
35
+ end
36
+
37
+ def finish_request
38
+ PactBroker.configuration.after_resource.call(self)
39
+ end
40
+
41
+ def is_authorized?(authorization_header)
42
+ authenticated?(self, authorization_header)
43
+ end
44
+
45
+ def forbidden?
46
+ return false if PactBroker.configuration.authorize.nil?
47
+ !PactBroker.configuration.authorize.call(self, {})
48
+ end
49
+
50
+ def identifier_from_path
51
+ request.path_info.each_with_object({}) do | pair, hash|
52
+ hash[pair.first] = pair.last === String ? URI.decode(pair.last) : pair.last
53
+ end
54
+ end
55
+
56
+ alias_method :path_info, :identifier_from_path
57
+
58
+ def base_url
59
+ PactBroker.configuration.base_url || request.base_uri.to_s.chomp('/')
60
+ end
61
+
62
+ # See comments for base_url in lib/pact_broker/doc/controllers/app.rb
63
+ def ui_base_url
64
+ PactBroker.configuration.base_url || ''
65
+ end
66
+
67
+ def charsets_provided
68
+ [['utf-8', :encode]]
69
+ end
70
+
71
+ # We only use utf-8 so leave encoding as it is
72
+ def encode(body)
73
+ body
74
+ end
75
+
76
+ def resource_url
77
+ request.uri.to_s.gsub(/\?.*/, '').chomp('/')
78
+ end
79
+
80
+ def decorator_context options = {}
81
+ Decorators::DecoratorContext.new(base_url, resource_url, request.env, options)
82
+ end
83
+
84
+ def decorator_options options = {}
85
+ { user_options: decorator_context(options) }
86
+ end
87
+
88
+ def handle_exception e
89
+ PactBroker::Api::Resources::ErrorHandler.call(e, request, response)
90
+ end
91
+
92
+ def params(options = {})
93
+ return options[:default] if options.key?(:default) && request_body.empty?
94
+
95
+ symbolize_names = !options.key?(:symbolize_names) || options[:symbolize_names]
96
+ if symbolize_names
97
+ @params_with_symbol_keys ||= JSON.parse(request_body, { symbolize_names: true }.merge(PACT_PARSING_OPTIONS)) #Not load! Otherwise it will try to load Ruby classes.
98
+ else
99
+ @params_with_string_keys ||= JSON.parse(request_body, { symbolize_names: false }.merge(PACT_PARSING_OPTIONS)) #Not load! Otherwise it will try to load Ruby classes.
100
+ end
101
+ rescue JSON::JSONError => e
102
+ raise InvalidJsonError.new("Error parsing JSON - #{e.message}")
103
+ end
104
+
105
+ def params_with_string_keys
106
+ params(symbolize_names: false)
107
+ end
108
+
109
+ def pact_params
110
+ @pact_params ||= PactBroker::Pacts::PactParams.from_request request, path_info
111
+ end
112
+
113
+ def set_json_error_message message
114
+ response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
115
+ response.body = { error: message }.to_json
116
+ end
117
+
118
+ def set_json_validation_error_messages errors
119
+ response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
120
+ response.body = { errors: errors }.to_json
121
+ end
122
+
123
+ def request_body
124
+ @request_body ||= request.body.to_s
125
+ end
126
+
127
+ def consumer_name
128
+ identifier_from_path[:consumer_name]
129
+ end
130
+
131
+ def consumer_version_number
132
+ identifier_from_path[:consumer_version_number]
133
+ end
134
+
135
+ def consumer_specified?
136
+ identifier_from_path.key?(:consumer_name)
137
+ end
138
+
139
+ def provider_specified?
140
+ identifier_from_path.key?(:provider_name)
141
+ end
142
+
143
+ def provider_name
144
+ identifier_from_path[:provider_name]
145
+ end
146
+
147
+ def pacticipant_name
148
+ identifier_from_path[:pacticipant_name]
149
+ end
150
+
151
+ def pacticipant_specified?
152
+ identifier_from_path.key?(:pacticipant_name)
153
+ end
154
+
155
+ def invalid_json?
156
+ begin
157
+ params
158
+ false
159
+ rescue StandardError => e
160
+ logger.info "Error parsing JSON #{e} - #{request_body}"
161
+ set_json_error_message "Error parsing JSON - #{e.message}"
162
+ response.headers['Content-Type'] = 'application/hal+json;charset=utf-8'
163
+ true
164
+ end
165
+ end
166
+
167
+ def validation_errors? model
168
+ if (errors = model.validate).any?
169
+ set_json_validation_error_messages errors
170
+ true
171
+ else
172
+ false
173
+ end
174
+ end
175
+
176
+ def contract_validation_errors? contract, params
177
+ if (invalid = !contract.validate(params))
178
+ set_json_validation_error_messages contract.errors.messages
179
+ end
180
+ invalid
181
+ end
182
+
183
+ def find_pacticipant name, role
184
+ pacticipant_service.find_pacticipant_by_name(name).tap do | pacticipant |
185
+ set_json_error_message("No #{role} with name '#{name}' found") if pacticipant.nil?
186
+ end
187
+ end
188
+
189
+ def consumer
190
+ @consumer ||= identifier_from_path[:consumer_name] && find_pacticipant(identifier_from_path[:consumer_name], "consumer")
191
+ end
192
+
193
+ def provider
194
+ @provider ||= identifier_from_path[:provider_name] && find_pacticipant(identifier_from_path[:provider_name], "provider")
195
+ end
196
+
197
+ def pacticipant
198
+ @pacticipant ||= identifier_from_path[:pacticipant_name] && find_pacticipant(identifier_from_path[:pacticipant_name], "pacticipant")
199
+ end
200
+
201
+ def pact
202
+ @pact ||= pact_service.find_pact(pact_params)
203
+ end
204
+
205
+ def database_connector
206
+ request.env["pactbroker.database_connector"]
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end