pact_broker 2.46.0 → 2.47.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/Dockerfile +1 -0
- data/lib/pact_broker/api/resources/provider_pacts_for_verification.rb +4 -1
- data/lib/pact_broker/api/resources/verification.rb +2 -2
- data/lib/pact_broker/api/resources/verifications.rb +1 -1
- data/lib/pact_broker/domain/webhook.rb +5 -4
- data/lib/pact_broker/pacts/repository.rb +59 -14
- data/lib/pact_broker/pacts/selected_pact.rb +46 -0
- data/lib/pact_broker/pacts/selector.rb +43 -0
- data/lib/pact_broker/pacts/service.rb +2 -4
- data/lib/pact_broker/pacts/squash_pacts_for_verification.rb +30 -20
- data/lib/pact_broker/pacts/verifiable_pact_messages.rb +11 -1
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/webhooks/service.rb +3 -1
- data/lib/pact_broker/webhooks/webhook_request_logger.rb +7 -1
- data/pact_broker.gemspec +1 -1
- data/spec/lib/pact_broker/api/resources/provider_pacts_for_verification_spec.rb +14 -6
- data/spec/lib/pact_broker/domain/webhook_spec.rb +2 -2
- data/spec/lib/pact_broker/pacts/repository_find_for_verification_spec.rb +92 -45
- data/spec/lib/pact_broker/pacts/squash_pacts_for_verification_spec.rb +8 -27
- data/spec/lib/pact_broker/pacts/verifiable_pact_messages_spec.rb +2 -0
- data/spec/lib/pact_broker/webhooks/service_spec.rb +8 -1
- data/spec/lib/pact_broker/webhooks/webhook_request_logger_spec.rb +6 -1
- data/spec/support/ssl_webhook_server.rb +2 -0
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c54fb8ff19038af9dd5d0a09105244459a18febe23333bf5203ef77424eb97a3
|
4
|
+
data.tar.gz: f380f9f5e9939d9844e88f09b5807a5e1f094cc368adfa22c104ee8ad55d9f97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebfc52497d4d877c0adb715bd1bf39c1be9513893ffaf032e142d160b4aa12be04c5b01d0c698f7b94ea6aadca1a416cddd53e3a0f304bb9c2821d128cf894a2
|
7
|
+
data.tar.gz: 41184e2249f9bde504a6f9258bf44ff2f33a8374d6a2d05ebe59536c8113461c79f41319a9b337f1db58c83e31ff652951ffca7466988626150dab7e09002abb
|
data/CHANGELOG.md
CHANGED
data/Dockerfile
CHANGED
@@ -40,7 +40,10 @@ module PactBroker
|
|
40
40
|
provider_name,
|
41
41
|
parsed_query_params.provider_version_tags,
|
42
42
|
parsed_query_params.consumer_version_selectors,
|
43
|
-
{
|
43
|
+
{
|
44
|
+
include_wip_pacts_since: parsed_query_params.include_wip_pacts_since,
|
45
|
+
include_pending_status: parsed_query_params.include_pending_status
|
46
|
+
}
|
44
47
|
)
|
45
48
|
end
|
46
49
|
|
@@ -33,11 +33,11 @@ module PactBroker
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def to_json
|
36
|
-
decorator_for(verification).to_json(user_options: {base_url: base_url})
|
36
|
+
decorator_for(verification).to_json(user_options: { base_url: base_url })
|
37
37
|
end
|
38
38
|
|
39
39
|
def to_extended_json
|
40
|
-
extended_decorator_for(verification).to_json(user_options: {base_url: base_url})
|
40
|
+
extended_decorator_for(verification).to_json(user_options: { base_url: base_url })
|
41
41
|
end
|
42
42
|
|
43
43
|
private
|
@@ -49,7 +49,7 @@ module PactBroker
|
|
49
49
|
|
50
50
|
def from_json
|
51
51
|
verification = verification_service.create(next_verification_number, params_with_string_keys, pact, webhook_options)
|
52
|
-
response.body = decorator_for(verification).to_json(user_options: {base_url: base_url})
|
52
|
+
response.body = decorator_for(verification).to_json(user_options: { base_url: base_url })
|
53
53
|
true
|
54
54
|
end
|
55
55
|
|
@@ -53,14 +53,14 @@ module PactBroker
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def execute pact, verification, options
|
56
|
-
logger.info "Executing #{self}"
|
56
|
+
logger.info "Executing #{self} webhook_context=#{options.fetch(:webhook_context)}"
|
57
57
|
webhook_request = request.build(template_parameters(pact, verification, options))
|
58
58
|
http_response, error = execute_request(webhook_request)
|
59
59
|
|
60
60
|
PactBroker::Webhooks::WebhookExecutionResult.new(
|
61
61
|
webhook_request.http_request,
|
62
62
|
http_response,
|
63
|
-
generate_logs(webhook_request, http_response, error, options.fetch(:logging_options)),
|
63
|
+
generate_logs(webhook_request, http_response, error, options[:webhook_context], options.fetch(:logging_options)),
|
64
64
|
error
|
65
65
|
)
|
66
66
|
end
|
@@ -110,13 +110,14 @@ module PactBroker
|
|
110
110
|
PactBroker::Webhooks::PactAndVerificationParameters.new(pact, verification, options.fetch(:webhook_context)).to_hash
|
111
111
|
end
|
112
112
|
|
113
|
-
def generate_logs(webhook_request, http_response, error, logging_options)
|
113
|
+
def generate_logs(webhook_request, http_response, error, webhook_context, logging_options)
|
114
114
|
webhook_request_logger = PactBroker::Webhooks::WebhookRequestLogger.new(logging_options)
|
115
115
|
webhook_request_logger.log(
|
116
116
|
uuid,
|
117
117
|
webhook_request,
|
118
118
|
http_response,
|
119
|
-
error
|
119
|
+
error,
|
120
|
+
webhook_context
|
120
121
|
)
|
121
122
|
end
|
122
123
|
end
|
@@ -14,6 +14,8 @@ require 'pact_broker/matrix/head_row'
|
|
14
14
|
require 'pact_broker/pacts/latest_pact_publication_id_for_consumer_version'
|
15
15
|
require 'pact_broker/pacts/verifiable_pact'
|
16
16
|
require 'pact_broker/repositories/helpers'
|
17
|
+
require 'pact_broker/pacts/selected_pact'
|
18
|
+
require 'pact_broker/pacts/selector'
|
17
19
|
|
18
20
|
module PactBroker
|
19
21
|
module Pacts
|
@@ -127,12 +129,13 @@ module PactBroker
|
|
127
129
|
end
|
128
130
|
end
|
129
131
|
|
130
|
-
def
|
132
|
+
def find_all_pact_versions_for_provider_with_consumer_version_tags provider_name, consumer_version_tag_names
|
131
133
|
provider = pacticipant_repository.find_by_name(provider_name)
|
132
134
|
|
133
135
|
PactPublication
|
134
136
|
.select_all_qualified
|
135
137
|
.select_append(Sequel[:cv][:order].as(:consumer_version_order))
|
138
|
+
.select_append(Sequel[:ct][:name].as(:consumer_version_tag_name))
|
136
139
|
.remove_overridden_revisions
|
137
140
|
.join_consumer_versions(:cv)
|
138
141
|
.join_consumer_version_tags_with_names(consumer_version_tag_names)
|
@@ -144,8 +147,12 @@ module PactBroker
|
|
144
147
|
.all
|
145
148
|
.group_by(&:pact_version_id)
|
146
149
|
.values
|
147
|
-
.collect
|
148
|
-
|
150
|
+
.collect do | pact_publications |
|
151
|
+
selector_tag_names = pact_publications.collect{ | p| p.values.fetch(:consumer_version_tag_name) }
|
152
|
+
latest_pact_publication = pact_publications.sort_by{ |p| p.values.fetch(:consumer_version_order) }.last
|
153
|
+
selectors = selector_tag_names.collect{ | tag_name | Selector.one_of_tag(tag_name) }
|
154
|
+
SelectedPact.new(latest_pact_publication.to_domain, selectors)
|
155
|
+
end
|
149
156
|
end
|
150
157
|
|
151
158
|
# To find the work in progress pacts for this verification execution:
|
@@ -339,29 +346,67 @@ module PactBroker
|
|
339
346
|
|
340
347
|
# Returns a list of Domain::Pact objects the represent pact publications
|
341
348
|
def find_for_verification(provider_name, consumer_version_selectors)
|
342
|
-
|
349
|
+
selected_pacts = find_pacts_for_which_the_latest_version_is_required(provider_name, consumer_version_selectors) +
|
350
|
+
find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors) +
|
343
351
|
find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
|
352
|
+
selected_pacts
|
353
|
+
.group_by(&:pact_version_sha)
|
354
|
+
.values
|
355
|
+
.collect do | selected_pacts_for_pact_version_id |
|
356
|
+
SelectedPact.merge(selected_pacts_for_pact_version_id)
|
357
|
+
end
|
344
358
|
end
|
345
359
|
|
346
360
|
private
|
347
361
|
|
348
|
-
def
|
362
|
+
def find_pacts_for_which_the_latest_version_is_required(provider_name, consumer_version_selectors)
|
363
|
+
if consumer_version_selectors.empty?
|
364
|
+
LatestPactPublications
|
365
|
+
.provider(provider_name)
|
366
|
+
.order_ignore_case(:consumer_name)
|
367
|
+
.collect do | latest_pact_publication |
|
368
|
+
pact_publication = PactPublication.find(id: latest_pact_publication.id)
|
369
|
+
SelectedPact.new(pact_publication.to_domain, [Selector.overall_latest])
|
370
|
+
end
|
371
|
+
else
|
372
|
+
[]
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
def find_pacts_for_which_the_latest_version_for_the_tag_is_required(provider_name, consumer_version_selectors)
|
349
377
|
# The tags for which only the latest version is specified
|
350
|
-
latest_tags = consumer_version_selectors.
|
351
|
-
consumer_version_selectors.select(&:latest).collect(&:tag) :
|
352
|
-
nil
|
378
|
+
latest_tags = consumer_version_selectors.select(&:latest).collect(&:tag).compact.uniq
|
353
379
|
|
354
|
-
|
380
|
+
# TODO make this an efficient query!
|
381
|
+
# These are not yet de-duped. Should make the behaviour consistent between this and find_pacts_for_which_all_versions_for_the_tag_are_required ?
|
382
|
+
if latest_tags.any?
|
383
|
+
LatestTaggedPactPublications
|
384
|
+
.provider(provider_name)
|
385
|
+
.order_ignore_case(:consumer_name)
|
386
|
+
.where(tag_name: latest_tags)
|
387
|
+
.all
|
388
|
+
.group_by(&:pact_version_id)
|
389
|
+
.values
|
390
|
+
.collect do | pact_publications |
|
391
|
+
selector_tag_names = pact_publications.collect(&:tag_name)
|
392
|
+
last_pact_publication = pact_publications.sort_by(&:consumer_version_order).last
|
393
|
+
pact_publication = PactPublication.find(id: last_pact_publication.id)
|
394
|
+
SelectedPact.new(
|
395
|
+
pact_publication.to_domain,
|
396
|
+
selector_tag_names.collect{ | tag_name| Selector.latest_for_tag(tag_name) }
|
397
|
+
)
|
398
|
+
end
|
399
|
+
else
|
400
|
+
[]
|
401
|
+
end
|
355
402
|
end
|
356
403
|
|
357
404
|
def find_pacts_for_which_all_versions_for_the_tag_are_required(provider_name, consumer_version_selectors)
|
358
405
|
# The tags for which all versions are specified
|
359
|
-
all_tags = consumer_version_selectors.
|
360
|
-
consumer_version_selectors.reject(&:latest).collect(&:tag) :
|
361
|
-
nil
|
406
|
+
all_tags = consumer_version_selectors.reject(&:latest).collect(&:tag)
|
362
407
|
|
363
|
-
if all_tags
|
364
|
-
|
408
|
+
if all_tags.any?
|
409
|
+
find_all_pact_versions_for_provider_with_consumer_version_tags(provider_name, all_tags)
|
365
410
|
else
|
366
411
|
[]
|
367
412
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
|
3
|
+
module PactBroker
|
4
|
+
module Pacts
|
5
|
+
class SelectedPact < SimpleDelegator
|
6
|
+
attr_reader :pact, :selectors
|
7
|
+
|
8
|
+
def initialize(pact, selectors)
|
9
|
+
super(pact)
|
10
|
+
@pact = pact
|
11
|
+
@selectors = selectors
|
12
|
+
end
|
13
|
+
|
14
|
+
# might actually be, but this code doesn't know it.
|
15
|
+
def overall_latest?
|
16
|
+
selectors.any?(&:overall_latest?)
|
17
|
+
end
|
18
|
+
|
19
|
+
def latest_for_tag?
|
20
|
+
selectors.any?(&:latest_for_tag?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.merge(selected_pacts)
|
24
|
+
latest_selected_pact = selected_pacts.sort_by(&:consumer_version_order).last
|
25
|
+
selectors = selected_pacts.collect(&:selectors).flatten.uniq
|
26
|
+
SelectedPact.new(latest_selected_pact.pact, selectors)
|
27
|
+
end
|
28
|
+
|
29
|
+
def merge(other)
|
30
|
+
if pact_version_sha != other.pact_version_sha
|
31
|
+
raise "These two pacts do not have the same pact_version_sha. They cannot be merged. #{pact_version_sha} and #{other.pact_version_sha}"
|
32
|
+
else
|
33
|
+
SelectedPact.new()
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def tag_names_for_selectors_for_latest_pacts
|
38
|
+
selectors.select(&:latest_for_tag?).collect(&:tag)
|
39
|
+
end
|
40
|
+
|
41
|
+
def consumer_version_order
|
42
|
+
pact.consumer_version.order
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module PactBroker
|
2
|
+
module Pacts
|
3
|
+
class Selector < Hash
|
4
|
+
def initialize(options)
|
5
|
+
merge!(options)
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.overall_latest
|
9
|
+
Selector.new(latest: true)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.latest_for_tag(tag)
|
13
|
+
Selector.new(latest: true, tag: tag)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.one_of_tag(tag)
|
17
|
+
Selector.new(tag: tag)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tag
|
21
|
+
self[:tag]
|
22
|
+
end
|
23
|
+
|
24
|
+
def overall_latest?
|
25
|
+
!!(latest && !tag)
|
26
|
+
end
|
27
|
+
|
28
|
+
def latest_for_tag?
|
29
|
+
!!(latest && tag)
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def latest?
|
35
|
+
self[:latest]
|
36
|
+
end
|
37
|
+
|
38
|
+
def latest
|
39
|
+
self[:latest]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -118,10 +118,8 @@ module PactBroker
|
|
118
118
|
def find_for_verification(provider_name, provider_version_tags, consumer_version_selectors, options)
|
119
119
|
verifiable_pacts_specified_in_request = pact_repository
|
120
120
|
.find_for_verification(provider_name, consumer_version_selectors)
|
121
|
-
.
|
122
|
-
|
123
|
-
.collect do | head_pacts |
|
124
|
-
squash_pacts_for_verification(provider_version_tags, head_pacts)
|
121
|
+
.collect do | selected_pact |
|
122
|
+
squash_pacts_for_verification(provider_version_tags, selected_pact, options[:include_pending_status])
|
125
123
|
end
|
126
124
|
|
127
125
|
verifiable_wip_pacts = if options[:include_wip_pacts_since]
|
@@ -5,28 +5,38 @@
|
|
5
5
|
module PactBroker
|
6
6
|
module Pacts
|
7
7
|
module SquashPactsForVerification
|
8
|
-
def self.call(provider_version_tags,
|
9
|
-
domain_pact =
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
pending =
|
8
|
+
def self.call(provider_version_tags, selected_pact, include_pending_status = false)
|
9
|
+
domain_pact = selected_pact.pact
|
10
|
+
|
11
|
+
if include_pending_status
|
12
|
+
pending_provider_tags = []
|
13
|
+
non_pending_provider_tags = []
|
14
|
+
pending = nil
|
15
|
+
if provider_version_tags.any?
|
16
|
+
pending_provider_tags = domain_pact.select_pending_provider_version_tags(provider_version_tags)
|
17
|
+
pending = pending_provider_tags.any?
|
18
|
+
else
|
19
|
+
pending = domain_pact.pending?
|
20
|
+
end
|
21
|
+
non_pending_provider_tags = provider_version_tags - pending_provider_tags
|
22
|
+
VerifiablePact.new(
|
23
|
+
domain_pact,
|
24
|
+
pending,
|
25
|
+
pending_provider_tags,
|
26
|
+
non_pending_provider_tags,
|
27
|
+
selected_pact.tag_names_for_selectors_for_latest_pacts,
|
28
|
+
selected_pact.overall_latest?
|
29
|
+
)
|
15
30
|
else
|
16
|
-
|
31
|
+
VerifiablePact.new(
|
32
|
+
domain_pact,
|
33
|
+
nil,
|
34
|
+
[],
|
35
|
+
[],
|
36
|
+
selected_pact.tag_names_for_selectors_for_latest_pacts,
|
37
|
+
selected_pact.overall_latest?
|
38
|
+
)
|
17
39
|
end
|
18
|
-
|
19
|
-
non_pending_provider_tags = provider_version_tags - pending_provider_tags
|
20
|
-
|
21
|
-
head_consumer_tags = head_pacts.collect(&:tag)
|
22
|
-
overall_latest = head_consumer_tags.include?(nil)
|
23
|
-
VerifiablePact.new(domain_pact,
|
24
|
-
pending,
|
25
|
-
pending_provider_tags,
|
26
|
-
non_pending_provider_tags,
|
27
|
-
head_consumer_tags.compact,
|
28
|
-
overall_latest
|
29
|
-
)
|
30
40
|
end
|
31
41
|
|
32
42
|
def squash_pacts_for_verification(*args)
|
@@ -6,13 +6,23 @@ module PactBroker
|
|
6
6
|
READ_MORE_PENDING = "Read more at https://pact.io/pending"
|
7
7
|
READ_MORE_WIP = "Read more at https://pact.io/wip"
|
8
8
|
|
9
|
-
delegate [:consumer_name, :provider_name, :head_consumer_tags, :pending_provider_tags, :non_pending_provider_tags, :pending?, :wip?] => :verifiable_pact
|
9
|
+
delegate [:consumer_name, :provider_name, :consumer_version_number, :head_consumer_tags, :pending_provider_tags, :non_pending_provider_tags, :pending?, :wip?] => :verifiable_pact
|
10
10
|
|
11
11
|
def initialize(verifiable_pact, pact_version_url)
|
12
12
|
@verifiable_pact = verifiable_pact
|
13
13
|
@pact_version_url = pact_version_url
|
14
14
|
end
|
15
15
|
|
16
|
+
def pact_description
|
17
|
+
position_descs = if head_consumer_tags.empty?
|
18
|
+
["latest"]
|
19
|
+
else
|
20
|
+
head_consumer_tags.collect { | tag | "latest #{tag}"}
|
21
|
+
end
|
22
|
+
|
23
|
+
"Pact between #{consumer_name} and #{provider_name}, consumer version #{consumer_version_number}, #{position_descs.join(",")}"
|
24
|
+
end
|
25
|
+
|
16
26
|
def inclusion_reason
|
17
27
|
version_text = head_consumer_tags.size == 1 ? "version" : "versions"
|
18
28
|
if wip?
|
data/lib/pact_broker/version.rb
CHANGED
@@ -110,7 +110,8 @@ module PactBroker
|
|
110
110
|
webhooks = webhook_repository.find_by_consumer_and_or_provider_and_event_name pact.consumer, pact.provider, event_name
|
111
111
|
|
112
112
|
if webhooks.any?
|
113
|
-
|
113
|
+
webhook_execution_configuration = options.fetch(:webhook_execution_configuration).with_webhook_context(event_name: event_name)
|
114
|
+
run_later(webhooks, pact, verification, event_name, options.merge(webhook_execution_configuration: webhook_execution_configuration))
|
114
115
|
else
|
115
116
|
logger.info "No enabled webhooks found for consumer \"#{pact.consumer.name}\" and provider \"#{pact.provider.name}\" and event #{event_name}"
|
116
117
|
end
|
@@ -122,6 +123,7 @@ module PactBroker
|
|
122
123
|
begin
|
123
124
|
triggered_webhook = webhook_repository.create_triggered_webhook(trigger_uuid, webhook, pact, verification, RESOURCE_CREATION)
|
124
125
|
logger.info "Scheduling job for webhook with uuid #{webhook.uuid}"
|
126
|
+
logger.debug "Schedule webhook with options #{options}"
|
125
127
|
job_data = { triggered_webhook: triggered_webhook }.deep_merge(options)
|
126
128
|
# Delay slightly to make sure the request transaction has finished before we execute the webhook
|
127
129
|
Job.perform_in(5, job_data)
|
@@ -29,8 +29,9 @@ module PactBroker
|
|
29
29
|
@options = options
|
30
30
|
end
|
31
31
|
|
32
|
-
def log(uuid, webhook_request, http_response, error)
|
32
|
+
def log(uuid, webhook_request, http_response, error, webhook_context)
|
33
33
|
safe_response = http_response ? HttpResponseWithUtf8SafeBody.new(http_response) : nil
|
34
|
+
log_webhook_context(webhook_context)
|
34
35
|
log_request(webhook_request)
|
35
36
|
log_response(uuid, safe_response) if safe_response
|
36
37
|
log_error(uuid, error) if error
|
@@ -42,6 +43,11 @@ module PactBroker
|
|
42
43
|
|
43
44
|
attr_reader :log_stream
|
44
45
|
|
46
|
+
def log_webhook_context(webhook_context)
|
47
|
+
execution_logger.debug "Webhook context #{webhook_context.to_json}"
|
48
|
+
logger.debug("Webhook context #{webhook_context.to_json}")
|
49
|
+
end
|
50
|
+
|
45
51
|
def log_request(webhook_request)
|
46
52
|
http_request = HttpRequestWithRedactedHeaders.new(webhook_request.http_request)
|
47
53
|
logger.info "Making webhook #{webhook_request.uuid} request #{http_request.method.upcase} URI=#{webhook_request.url} (headers and body in debug logs)"
|
data/pact_broker.gemspec
CHANGED
@@ -74,7 +74,7 @@ Gem::Specification.new do |gem|
|
|
74
74
|
gem.add_development_dependency 'webmock', '~>2.3'
|
75
75
|
gem.add_development_dependency 'rspec', '~>3.0'
|
76
76
|
gem.add_development_dependency 'rspec-its', '~>1.2'
|
77
|
-
gem.add_development_dependency 'database_cleaner', '~>1.
|
77
|
+
gem.add_development_dependency 'database_cleaner', '~>1.8', '>= 1.8.1'
|
78
78
|
gem.add_development_dependency 'pg', '~>0.21'
|
79
79
|
gem.add_development_dependency 'conventional-changelog', '~>1.3'
|
80
80
|
gem.add_development_dependency 'bump', '~> 0.5'
|
@@ -18,7 +18,7 @@ module PactBroker
|
|
18
18
|
{
|
19
19
|
provider_version_tags: ['master'],
|
20
20
|
consumer_version_selectors: [ { tag: 'dev', latest: 'true' }],
|
21
|
-
include_pending_status:
|
21
|
+
include_pending_status: 'true',
|
22
22
|
include_wip_pacts_since: '2018-01-01'
|
23
23
|
}
|
24
24
|
end
|
@@ -32,7 +32,11 @@ module PactBroker
|
|
32
32
|
"Bar",
|
33
33
|
["master"],
|
34
34
|
[OpenStruct.new(tag: "dev", latest: true)],
|
35
|
-
{
|
35
|
+
{
|
36
|
+
include_wip_pacts_since: DateTime.parse('2018-01-01'),
|
37
|
+
include_pending_status: true
|
38
|
+
}
|
39
|
+
)
|
36
40
|
subject
|
37
41
|
end
|
38
42
|
|
@@ -54,8 +58,8 @@ module PactBroker
|
|
54
58
|
{
|
55
59
|
providerVersionTags: ['master'],
|
56
60
|
consumerVersionSelectors: [ { tag: 'dev', latest: true }],
|
57
|
-
includePendingStatus:
|
58
|
-
includeWipPactsSince: '2018-01-01'
|
61
|
+
includePendingStatus: true,
|
62
|
+
includeWipPactsSince: '2018-01-01',
|
59
63
|
}
|
60
64
|
end
|
61
65
|
|
@@ -74,7 +78,11 @@ module PactBroker
|
|
74
78
|
"Bar",
|
75
79
|
["master"],
|
76
80
|
[OpenStruct.new(tag: "dev", latest: true)],
|
77
|
-
{
|
81
|
+
{
|
82
|
+
include_wip_pacts_since: DateTime.parse('2018-01-01'),
|
83
|
+
include_pending_status: true
|
84
|
+
}
|
85
|
+
)
|
78
86
|
subject
|
79
87
|
end
|
80
88
|
|
@@ -94,7 +102,7 @@ module PactBroker
|
|
94
102
|
it "uses the correct options for the decorator" do
|
95
103
|
expect(decorator).to receive(:to_json) do | options |
|
96
104
|
expect(options[:user_options][:title]).to eq "Pacts to be verified by provider Bar"
|
97
|
-
expect(options[:user_options][:include_pending_status]).to eq
|
105
|
+
expect(options[:user_options][:include_pending_status]).to eq true
|
98
106
|
end
|
99
107
|
subject
|
100
108
|
end
|
@@ -81,7 +81,7 @@ module PactBroker
|
|
81
81
|
end
|
82
82
|
|
83
83
|
it "generates the execution logs" do
|
84
|
-
expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, http_response, nil)
|
84
|
+
expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, http_response, nil, webhook_context)
|
85
85
|
execute
|
86
86
|
end
|
87
87
|
|
@@ -106,7 +106,7 @@ module PactBroker
|
|
106
106
|
end
|
107
107
|
|
108
108
|
it "generates the execution logs" do
|
109
|
-
expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, nil, instance_of(error_class))
|
109
|
+
expect(webhook_request_logger).to receive(:log).with(uuid, webhook_request, nil, instance_of(error_class), webhook_context)
|
110
110
|
execute
|
111
111
|
end
|
112
112
|
|
@@ -15,32 +15,48 @@ module PactBroker
|
|
15
15
|
subject.find{ |pact| pact.consumer_name == consumer_name && pact.consumer_version_number == consumer_version_number }
|
16
16
|
end
|
17
17
|
|
18
|
-
before do
|
19
|
-
td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
|
20
|
-
.create_consumer_version_tag("prod")
|
21
|
-
.create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
|
22
|
-
.comment("next pact not selected")
|
23
|
-
.create_pact
|
24
|
-
.create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
|
25
|
-
.create_pact
|
26
|
-
.create_consumer("Baz")
|
27
|
-
.create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
|
28
|
-
.create_pact
|
29
|
-
end
|
30
18
|
|
31
19
|
subject { Repository.new.find_for_verification("Bar", consumer_version_selectors) }
|
32
20
|
|
33
21
|
context "when there are no selectors" do
|
22
|
+
before do
|
23
|
+
td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
|
24
|
+
.create_consumer_version_tag("prod")
|
25
|
+
.create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
|
26
|
+
.comment("next pact not selected")
|
27
|
+
.create_pact
|
28
|
+
.create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
|
29
|
+
.create_pact
|
30
|
+
.create_consumer("Baz")
|
31
|
+
.create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
|
32
|
+
.create_pact
|
33
|
+
end
|
34
|
+
|
34
35
|
let(:consumer_version_selectors) { [] }
|
35
36
|
|
36
37
|
it "returns the latest pact for each consumer" do
|
37
38
|
expect(subject.size).to eq 2
|
38
39
|
expect(find_by_consumer_name_and_consumer_version_number("Foo", "foo-latest-dev-version")).to_not be nil
|
39
40
|
expect(find_by_consumer_name_and_consumer_version_number("Baz", "baz-latest-dev-version")).to_not be nil
|
41
|
+
expect(subject.all?(&:overall_latest?)).to be true
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
|
-
context "when consumer tag names are specified" do
|
45
|
+
context "when the latest consumer tag names are specified" do
|
46
|
+
before do
|
47
|
+
td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
|
48
|
+
.create_consumer_version_tag("prod")
|
49
|
+
.create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
|
50
|
+
.comment("next pact not selected")
|
51
|
+
.create_pact
|
52
|
+
.create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
|
53
|
+
.create_pact
|
54
|
+
.create_consumer("Baz")
|
55
|
+
.create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
|
56
|
+
.create_consumer_version_tag("prod")
|
57
|
+
.create_pact
|
58
|
+
end
|
59
|
+
|
44
60
|
let(:pact_selector_1) { double('selector', tag: 'dev', latest: true) }
|
45
61
|
let(:pact_selector_2) { double('selector', tag: 'prod', latest: true) }
|
46
62
|
let(:consumer_version_selectors) do
|
@@ -48,47 +64,78 @@ module PactBroker
|
|
48
64
|
end
|
49
65
|
|
50
66
|
it "returns the latest pact with the specified tags for each consumer" do
|
51
|
-
expect(find_by_consumer_version_number("foo-latest-prod-version")).
|
52
|
-
expect(find_by_consumer_version_number("foo-latest-dev-version")).
|
53
|
-
expect(find_by_consumer_version_number("baz-latest-dev-version")).
|
67
|
+
expect(find_by_consumer_version_number("foo-latest-prod-version").selectors).to eq [Selector.latest_for_tag('prod')]
|
68
|
+
expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [Selector.latest_for_tag('dev')]
|
69
|
+
expect(find_by_consumer_version_number("baz-latest-dev-version").selectors.sort_by{ |s| s[:tag] }).to eq [{ tag: 'dev', latest: true }, { tag: 'prod', latest: true }]
|
54
70
|
expect(subject.size).to eq 3
|
55
71
|
end
|
56
72
|
|
57
73
|
it "sets the latest_consumer_version_tag_names" do
|
58
|
-
expect(find_by_consumer_version_number("foo-latest-prod-version").tag).to eq 'prod'
|
74
|
+
expect(find_by_consumer_version_number("foo-latest-prod-version").selectors.collect(&:tag)).to eq ['prod']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "when all versions with a given tag are requested" do
|
79
|
+
before do
|
80
|
+
td.create_pact_with_hierarchy("Foo2", "prod-version-1", "Bar2")
|
81
|
+
.create_consumer_version_tag("prod")
|
82
|
+
.create_consumer_version("not-prod-version", tag_names: %w[master])
|
83
|
+
.create_pact
|
84
|
+
.create_consumer_version("prod-version-2", tag_names: %w[prod])
|
85
|
+
.create_pact
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:consumer_version_selectors) { [pact_selector_1] }
|
89
|
+
let(:pact_selector_1) { double('selector', tag: 'prod', latest: nil) }
|
90
|
+
|
91
|
+
subject { Repository.new.find_for_verification("Bar2", consumer_version_selectors) }
|
92
|
+
|
93
|
+
it "returns all the versions with the specified tag" do
|
94
|
+
expect(subject.size).to be 2
|
95
|
+
expect(find_by_consumer_version_number("prod-version-1").selectors.collect(&:tag)).to eq %w[prod]
|
96
|
+
expect(find_by_consumer_version_number("prod-version-2").selectors.collect(&:tag)).to eq %w[prod]
|
59
97
|
end
|
60
98
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
.republish_same_pact
|
85
|
-
expect(subject.size).to be 2
|
86
|
-
expect(subject.collect(&:consumer_version_number)).to eq %w[prod-version-1 prod-version-3]
|
87
|
-
end
|
99
|
+
it "dedupes them to ensure that each pact version is only verified once" do
|
100
|
+
td.create_consumer_version("prod-version-3", tag_names: %w[prod])
|
101
|
+
.republish_same_pact
|
102
|
+
expect(subject.size).to be 2
|
103
|
+
expect(subject.collect(&:consumer_version_number)).to eq %w[prod-version-1 prod-version-3]
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "when a pact version has been selected by two different selectors" do
|
108
|
+
before do
|
109
|
+
td.create_pact_with_hierarchy("Foo", "1", "Bar")
|
110
|
+
.create_consumer_version_tag("dev")
|
111
|
+
.create_consumer_version_tag("prod")
|
112
|
+
end
|
113
|
+
|
114
|
+
let(:consumer_version_selectors) { [pact_selector_1, pact_selector_2] }
|
115
|
+
let(:pact_selector_1) { double('selector1', tag: 'prod', latest: nil) }
|
116
|
+
let(:pact_selector_2) { double('selector2', tag: 'dev', latest: true) }
|
117
|
+
let(:consumer_version_selectors) { [pact_selector_1, pact_selector_2] }
|
118
|
+
|
119
|
+
it "returns a single selected pact with multiple selectors" do
|
120
|
+
expect(subject.size).to eq 1
|
121
|
+
expect(subject.first.selectors.size).to eq 2
|
88
122
|
end
|
89
123
|
end
|
90
124
|
|
91
125
|
context "when no selectors are specified" do
|
126
|
+
before do
|
127
|
+
td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
|
128
|
+
.create_consumer_version_tag("prod")
|
129
|
+
.create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
|
130
|
+
.comment("next pact not selected")
|
131
|
+
.create_pact
|
132
|
+
.create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
|
133
|
+
.create_pact
|
134
|
+
.create_consumer("Baz")
|
135
|
+
.create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
|
136
|
+
.create_pact
|
137
|
+
end
|
138
|
+
|
92
139
|
let(:consumer_version_selectors) { [] }
|
93
140
|
|
94
141
|
it "returns the latest pact for each provider" do
|
@@ -98,7 +145,7 @@ module PactBroker
|
|
98
145
|
end
|
99
146
|
|
100
147
|
it "does not set the tag name" do
|
101
|
-
expect(find_by_consumer_version_number("foo-latest-dev-version").
|
148
|
+
expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [{ latest: true }]
|
102
149
|
expect(find_by_consumer_version_number("foo-latest-dev-version").overall_latest?).to be true
|
103
150
|
end
|
104
151
|
end
|
@@ -4,7 +4,7 @@ module PactBroker
|
|
4
4
|
module Pacts
|
5
5
|
module SquashPactsForVerification
|
6
6
|
describe ".call" do
|
7
|
-
let(:
|
7
|
+
let(:selected_pact) { pact_1 }
|
8
8
|
let(:head_tag_1) { "dev" }
|
9
9
|
let(:head_tag_2) { "feat-x" }
|
10
10
|
let(:pact_version_sha_1) { "1" }
|
@@ -15,42 +15,23 @@ module PactBroker
|
|
15
15
|
select_pending_provider_version_tags: pending_provider_version_tags
|
16
16
|
)
|
17
17
|
end
|
18
|
-
let(:domain_pact_2) { double('pact2', pending?: pending_2) }
|
19
18
|
let(:pending_1) { false }
|
20
|
-
let(:pending_2) { false }
|
21
19
|
let(:pending_provider_version_tags) { [] }
|
22
20
|
|
23
21
|
let(:pact_1) do
|
24
|
-
double("
|
25
|
-
|
26
|
-
|
27
|
-
|
22
|
+
double("SelectedPact",
|
23
|
+
tag_names_for_selectors_for_latest_pacts: %w[dev feat-x],
|
24
|
+
pact: domain_pact_1,
|
25
|
+
overall_latest?: false
|
28
26
|
)
|
29
27
|
end
|
30
28
|
|
31
|
-
let(:pact_2) do
|
32
|
-
double("HeadPact",
|
33
|
-
tag: head_tag_2,
|
34
|
-
pact_version_sha: pact_version_sha_2,
|
35
|
-
pact: domain_pact_2
|
36
|
-
)
|
37
|
-
end
|
38
|
-
|
39
|
-
let(:provider_name) { "Bar" }
|
40
29
|
let(:provider_version_tags) { [] }
|
41
30
|
|
42
|
-
subject { SquashPactsForVerification.call(provider_version_tags,
|
31
|
+
subject { SquashPactsForVerification.call(provider_version_tags, selected_pact, true) }
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
its(:overall_latest?) { is_expected.to be false }
|
47
|
-
end
|
48
|
-
|
49
|
-
context "when one of the consumer tags is nil" do
|
50
|
-
let(:head_tag_2) { nil }
|
51
|
-
its(:head_consumer_tags) { is_expected.to eq %w[dev] }
|
52
|
-
its(:overall_latest?) { is_expected.to be true }
|
53
|
-
end
|
33
|
+
its(:head_consumer_tags) { is_expected.to eq %w[dev feat-x] }
|
34
|
+
its(:overall_latest?) { is_expected.to be false }
|
54
35
|
|
55
36
|
context "when there are no provider tags" do
|
56
37
|
context "when the pact version is not pending" do
|
@@ -13,6 +13,7 @@ module PactBroker
|
|
13
13
|
double(VerifiablePact,
|
14
14
|
head_consumer_tags: head_consumer_tags,
|
15
15
|
consumer_name: "Foo",
|
16
|
+
consumer_version_number: "123",
|
16
17
|
provider_name: "Bar",
|
17
18
|
pending_provider_tags: pending_provider_tags,
|
18
19
|
non_pending_provider_tags: non_pending_provider_tags,
|
@@ -32,6 +33,7 @@ module PactBroker
|
|
32
33
|
context "when there is 1 head consumer tags" do
|
33
34
|
let(:head_consumer_tags) { %w[dev] }
|
34
35
|
its(:inclusion_reason) { is_expected.to include "The pact at http://pact is being verified because it is the pact for the latest version of Foo tagged with 'dev'" }
|
36
|
+
its(:pact_description) { is_expected.to eq "Pact between Foo and Bar, consumer version 123, latest dev"}
|
35
37
|
end
|
36
38
|
|
37
39
|
context "when there are 2 head consumer tags" do
|
@@ -152,14 +152,16 @@ module PactBroker
|
|
152
152
|
let(:provider) { PactBroker::Domain::Pacticipant.new(name: 'Provider') }
|
153
153
|
let(:webhooks) { [instance_double(PactBroker::Domain::Webhook, description: 'description', uuid: '1244')]}
|
154
154
|
let(:triggered_webhook) { instance_double(PactBroker::Webhooks::TriggeredWebhook) }
|
155
|
+
let(:webhook_execution_configuration) { double('webhook_execution_configuration') }
|
155
156
|
let(:options) do
|
156
157
|
{ database_connector: double('database_connector'),
|
157
|
-
|
158
|
+
webhook_execution_configuration: webhook_execution_configuration,
|
158
159
|
logging_options: {}
|
159
160
|
}
|
160
161
|
end
|
161
162
|
|
162
163
|
before do
|
164
|
+
allow(webhook_execution_configuration).to receive(:with_webhook_context).and_return(webhook_execution_configuration)
|
163
165
|
allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:find_by_consumer_and_or_provider_and_event_name).and_return(webhooks)
|
164
166
|
allow_any_instance_of(PactBroker::Webhooks::Repository).to receive(:create_triggered_webhook).and_return(triggered_webhook)
|
165
167
|
allow(Job).to receive(:perform_in)
|
@@ -177,6 +179,11 @@ module PactBroker
|
|
177
179
|
expect(Service).to receive(:run_later).with(webhooks, pact, verification, PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED, options)
|
178
180
|
subject
|
179
181
|
end
|
182
|
+
|
183
|
+
it "merges the event name in the options" do
|
184
|
+
expect(webhook_execution_configuration).to receive(:with_webhook_context).with(event_name: PactBroker::Webhooks::WebhookEvent::CONTRACT_CONTENT_CHANGED)
|
185
|
+
subject
|
186
|
+
end
|
180
187
|
end
|
181
188
|
|
182
189
|
context "when no webhooks are found" do
|
@@ -50,9 +50,11 @@ module PactBroker
|
|
50
50
|
}
|
51
51
|
end
|
52
52
|
|
53
|
+
let(:webhook_context) { { consumer_version_number: "123" } }
|
54
|
+
|
53
55
|
let(:webhook_request_logger) { WebhookRequestLogger.new(options) }
|
54
56
|
|
55
|
-
subject(:logs) { webhook_request_logger.log(uuid, webhook_request, response, error) }
|
57
|
+
subject(:logs) { webhook_request_logger.log(uuid, webhook_request, response, error, webhook_context) }
|
56
58
|
|
57
59
|
describe "application logs" do
|
58
60
|
it "logs the request" do
|
@@ -69,6 +71,9 @@ module PactBroker
|
|
69
71
|
end
|
70
72
|
|
71
73
|
describe "execution logs" do
|
74
|
+
it "logs the application context" do
|
75
|
+
expect(logs).to include webhook_context.to_json
|
76
|
+
end
|
72
77
|
|
73
78
|
it "logs the request method and path" do
|
74
79
|
expect(logs).to include "POST http://example.org/hook"
|
@@ -11,10 +11,12 @@ if __FILE__ == $0
|
|
11
11
|
def webrick_opts port
|
12
12
|
certificate = OpenSSL::X509::Certificate.new(File.read(SSL_CERT))
|
13
13
|
cert_name = certificate.subject.to_a.collect{|a| a[0..1] }
|
14
|
+
logger_stream = ENV['DEBUG'] ? $stderr : StringIO.new
|
14
15
|
{
|
15
16
|
Port: port,
|
16
17
|
Host: "0.0.0.0",
|
17
18
|
AccessLog: [],
|
19
|
+
Logger: WEBrick::Log.new(logger_stream,WEBrick::Log::INFO),
|
18
20
|
SSLCertificate: certificate,
|
19
21
|
SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read(SSL_KEY)),
|
20
22
|
SSLEnable: true,
|
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.
|
4
|
+
version: 2.47.0
|
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: 2020-01
|
13
|
+
date: 2020-02-01 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: httparty
|
@@ -480,14 +480,20 @@ dependencies:
|
|
480
480
|
requirements:
|
481
481
|
- - "~>"
|
482
482
|
- !ruby/object:Gem::Version
|
483
|
-
version: '1.
|
483
|
+
version: '1.8'
|
484
|
+
- - ">="
|
485
|
+
- !ruby/object:Gem::Version
|
486
|
+
version: 1.8.1
|
484
487
|
type: :development
|
485
488
|
prerelease: false
|
486
489
|
version_requirements: !ruby/object:Gem::Requirement
|
487
490
|
requirements:
|
488
491
|
- - "~>"
|
489
492
|
- !ruby/object:Gem::Version
|
490
|
-
version: '1.
|
493
|
+
version: '1.8'
|
494
|
+
- - ">="
|
495
|
+
- !ruby/object:Gem::Version
|
496
|
+
version: 1.8.1
|
491
497
|
- !ruby/object:Gem::Dependency
|
492
498
|
name: pg
|
493
499
|
requirement: !ruby/object:Gem::Requirement
|
@@ -1051,6 +1057,8 @@ files:
|
|
1051
1057
|
- lib/pact_broker/pacts/parse.rb
|
1052
1058
|
- lib/pact_broker/pacts/placeholder_pact.rb
|
1053
1059
|
- lib/pact_broker/pacts/repository.rb
|
1060
|
+
- lib/pact_broker/pacts/selected_pact.rb
|
1061
|
+
- lib/pact_broker/pacts/selector.rb
|
1054
1062
|
- lib/pact_broker/pacts/service.rb
|
1055
1063
|
- lib/pact_broker/pacts/sort_content.rb
|
1056
1064
|
- lib/pact_broker/pacts/squash_pacts_for_verification.rb
|
@@ -1604,7 +1612,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
1604
1612
|
- !ruby/object:Gem::Version
|
1605
1613
|
version: '0'
|
1606
1614
|
requirements: []
|
1607
|
-
|
1615
|
+
rubyforge_project:
|
1616
|
+
rubygems_version: 2.7.6
|
1608
1617
|
signing_key:
|
1609
1618
|
specification_version: 4
|
1610
1619
|
summary: See description
|