pact_broker 2.88.0 → 2.89.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/release_gem.yml +1 -1
- data/.github/workflows/trigger_pact_docs_update.yml +1 -0
- data/CHANGELOG.md +14 -0
- data/docs/CONFIGURATION.md +1 -1
- data/lib/pact_broker/api/contracts/pacts_for_verification_json_query_schema.rb +14 -4
- data/lib/pact_broker/api/decorators/deployed_version_decorator.rb +2 -1
- data/lib/pact_broker/api/decorators/pacts_for_verification_query_decorator.rb +18 -0
- data/lib/pact_broker/api/resources/currently_deployed_versions_for_environment.rb +3 -1
- data/lib/pact_broker/api/resources/deployed_versions_for_version_and_environment.rb +4 -3
- data/lib/pact_broker/api/resources/pact.rb +1 -1
- data/lib/pact_broker/api/resources/publish_contracts.rb +27 -4
- data/lib/pact_broker/config/runtime_configuration_database_methods.rb +1 -1
- data/lib/pact_broker/contracts/contracts_publication_results.rb +2 -7
- data/lib/pact_broker/contracts/notice.rb +8 -0
- data/lib/pact_broker/contracts/service.rb +25 -0
- data/lib/pact_broker/doc/views/index/publish-contracts.markdown +32 -2
- data/lib/pact_broker/doc/views/provider-pacts-for-verification.markdown +2 -0
- data/lib/pact_broker/locale/en.yml +1 -1
- data/lib/pact_broker/pacts/content.rb +18 -0
- data/lib/pact_broker/pacts/create_formatted_diff.rb +9 -2
- data/lib/pact_broker/pacts/selector.rb +35 -21
- data/lib/pact_broker/pacts/sort_content.rb +1 -0
- data/lib/pact_broker/verifications/required_verification.rb +5 -2
- data/lib/pact_broker/verifications/service.rb +14 -7
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/selector.rb +32 -0
- data/lib/pact_broker/versions/selectors.rb +20 -0
- data/lib/pact_broker/webhooks/pact_and_verification_parameters.rb +2 -2
- data/lib/pact_broker/webhooks/trigger_service.rb +9 -1
- data/script/docs/generate-configuration-docs.rb +9 -9
- data/script/github-issues/add-branch-support/issue-text.txt +7 -0
- data/script/github-issues/add-branch-support/issues.txt +0 -0
- data/script/github-issues/add-branch-support/raise-issue-in-client-repos.sh +10 -0
- data/script/github-issues/add-branch-support-for-provider-versions/issue-text.txt +9 -0
- data/script/github-issues/add-branch-support-for-provider-versions/issues.txt +6 -0
- data/script/github-issues/add-branch-support-for-provider-versions/raise-issue-in-client-repos.sh +10 -0
- data/script/github-issues/branch-consumer-version-selector/issue-text.txt +52 -0
- data/script/github-issues/branch-consumer-version-selector/issues.txt +9 -0
- data/script/github-issues/branch-consumer-version-selector/raise-issue-in-client-repos.sh +10 -0
- data/script/{issues → github-issues}/consumer-version-selectors-docs/issue-text.txt +0 -0
- data/script/{issues → github-issues}/consumer-version-selectors-docs/issues.txt +0 -0
- data/script/{issues → github-issues}/consumer-version-selectors-docs/raise-issue-in-client-repos.sh +0 -0
- data/script/github-issues/deployed-and-released-selectors-docs/issue-text.txt +26 -0
- data/script/github-issues/deployed-and-released-selectors-docs/issues.txt +9 -0
- data/script/github-issues/deployed-and-released-selectors-docs/raise-issue-in-client-repos.sh +10 -0
- data/script/github-issues/include-pending-by-default/issue-text.txt +5 -0
- data/script/github-issues/include-pending-by-default/issues.txt +10 -0
- data/script/github-issues/include-pending-by-default/raise-issue-in-client-repos.sh +10 -0
- data/spec/features/get_currently_deployed_versions_for_environment_spec.rb +23 -6
- data/spec/features/publish_pact_all_in_one_spec.rb +19 -0
- data/spec/features/record_deployment_spec.rb +17 -4
- data/spec/lib/pact_broker/api/contracts/pacts_for_verification_json_query_schema_combinations_spec.rb +5 -3
- data/spec/lib/pact_broker/api/contracts/pacts_for_verification_json_query_schema_spec.rb +10 -0
- data/spec/lib/pact_broker/api/decorators/pacts_for_verification_query_decorator_spec.rb +10 -0
- data/spec/lib/pact_broker/contracts/service_spec.rb +64 -0
- data/spec/lib/pact_broker/verifications/service_spec.rb +18 -12
- data/spec/lib/pact_broker/webhooks/trigger_service_spec.rb +67 -3
- metadata +22 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb2a319d08e394deac2b0ddab3c97cb09e3ef17025daebe82e54a30577a049b6
|
4
|
+
data.tar.gz: b3767941da7f343cff6b14ddeca1398bb86e492b84d2447d03e59658c7d1ae31
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4373701434b40c1529c20469d860e352ffb32a100b22a2ea6774c7579ddc4a30956fc7879b1fb3f58d0e6676116cbb7ac504648178a28513b684056918e3818a
|
7
|
+
data.tar.gz: a2f2b2d4b5ffb90e4ed31394d47a8a05aa75e90d2329f22cad4092930097f5958f6ccbd53ebb499c05d375bc980de62ddaf31252bd58d22ed9ff09911da106c7
|
@@ -35,7 +35,7 @@ jobs:
|
|
35
35
|
needs: release
|
36
36
|
strategy:
|
37
37
|
matrix:
|
38
|
-
repository: [pact-foundation/pact-broker-docker, DiUS/pact_broker-docker]
|
38
|
+
repository: [pact-foundation/pact-broker-docker, DiUS/pact_broker-docker, pact-foundation/pact_broker]
|
39
39
|
runs-on: ubuntu-latest
|
40
40
|
steps:
|
41
41
|
- name: Notify ${{ matrix.repository }} of gem release
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
<a name="v2.89.0"></a>
|
2
|
+
### v2.89.0 (2021-10-15)
|
3
|
+
|
4
|
+
#### Features
|
5
|
+
|
6
|
+
* add applicationInstance to deployed version resource as a replacement for target ([9bfafc8a](/../../commit/9bfafc8a))
|
7
|
+
* apply allow_dangerous_contract_modification setting when publishing using the /contracts/publish endpoint ([956227fe](/../../commit/956227fe))
|
8
|
+
* add support for matchingBranch property in consumer version selectors ([48068d29](/../../commit/48068d29))
|
9
|
+
|
10
|
+
#### Bug Fixes
|
11
|
+
|
12
|
+
* set missing provider branch name parameter for contract_requiring_verification_published webhooks ([777ccdd2](/../../commit/777ccdd2))
|
13
|
+
* correct misnamed database port configuration property causing the PACT_BROKER_DATABASE_PORT not to be respected ([3d14013a](/../../commit/3d14013a))
|
14
|
+
|
1
15
|
<a name="v2.88.0"></a>
|
2
16
|
### v2.88.0 (2021-10-11)
|
3
17
|
|
data/docs/CONFIGURATION.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# Pact Broker Configuration
|
2
2
|
|
3
3
|
|
4
|
-
<!-- This is a generated file. Please do not edit it directly. -->
|
4
|
+
<!-- This is a generated file. Please do not edit it directly. The source is https://github.com/pact-foundation/pact_broker/blob/master/docs/configuration.yml -->
|
5
5
|
|
6
6
|
The Pact Broker supports configuration via environment variables or a YAML file from version 2.87.0.1 of the Docker images.
|
7
7
|
|
@@ -15,7 +15,7 @@ module PactBroker
|
|
15
15
|
using PactBroker::HashRefinements
|
16
16
|
using PactBroker::StringRefinements
|
17
17
|
|
18
|
-
BRANCH_KEYS = [:latest, :tag, :fallbackTag, :branch, :fallbackBranch]
|
18
|
+
BRANCH_KEYS = [:latest, :tag, :fallbackTag, :branch, :fallbackBranch, :matchingBranch]
|
19
19
|
ENVIRONMENT_KEYS = [:environment, :deployed, :released, :deployedOrReleased]
|
20
20
|
ALL_KEYS = BRANCH_KEYS + ENVIRONMENT_KEYS
|
21
21
|
|
@@ -36,6 +36,7 @@ module PactBroker
|
|
36
36
|
optional(:mainBranch).filled(included_in?: [true])
|
37
37
|
optional(:tag).filled(:str?)
|
38
38
|
optional(:branch).filled(:str?)
|
39
|
+
optional(:matchingBranch).filled(included_in?: [true])
|
39
40
|
optional(:latest).filled(included_in?: [true, false])
|
40
41
|
optional(:fallbackTag).filled(:str?)
|
41
42
|
optional(:fallbackBranch).filled(:str?)
|
@@ -65,7 +66,7 @@ module PactBroker
|
|
65
66
|
# This is a ducking joke. Need to get rid of dry-validation
|
66
67
|
if params[:consumerVersionSelectors].is_a?(Array)
|
67
68
|
errors = params[:consumerVersionSelectors].each_with_index.flat_map do | selector, index |
|
68
|
-
validate_consumer_version_selector(selector, index)
|
69
|
+
validate_consumer_version_selector(selector, index, params)
|
69
70
|
end
|
70
71
|
if errors.any?
|
71
72
|
results[:consumerVersionSelectors] ||= []
|
@@ -82,7 +83,7 @@ module PactBroker
|
|
82
83
|
# when setting the branch, it doesn't make sense to verify all pacts for a branch,
|
83
84
|
# so latest is not required, but cannot be set to false
|
84
85
|
# rubocop: disable Metrics/CyclomaticComplexity, Metrics/MethodLength
|
85
|
-
def self.validate_consumer_version_selector(selector, index)
|
86
|
+
def self.validate_consumer_version_selector(selector, index, params)
|
86
87
|
errors = []
|
87
88
|
|
88
89
|
if selector[:fallbackTag] && !selector[:latest]
|
@@ -97,17 +98,22 @@ module PactBroker
|
|
97
98
|
not_provided?(selector[:tag]) &&
|
98
99
|
not_provided?(selector[:branch]) &&
|
99
100
|
not_provided?(selector[:environment]) &&
|
101
|
+
selector[:matchingBranch] != true &&
|
100
102
|
selector[:deployed] != true &&
|
101
103
|
selector[:released] != true &&
|
102
104
|
selector[:deployedOrReleased] != true &&
|
103
105
|
selector[:latest] != true
|
104
|
-
errors << "must specify a value for environment or tag or branch, or specify mainBranch=true, latest=true, deployed=true, released=true or deployedOrReleased=true (at index #{index})"
|
106
|
+
errors << "must specify a value for environment or tag or branch, or specify mainBranch=true, matchingBranch=true, latest=true, deployed=true, released=true or deployedOrReleased=true (at index #{index})"
|
105
107
|
end
|
106
108
|
|
107
109
|
if selector[:mainBranch] && selector.slice(*ALL_KEYS - [:consumer, :mainBranch]).any?
|
108
110
|
errors << "cannot specify mainBranch=true with any other criteria apart from consumer (at index #{index})"
|
109
111
|
end
|
110
112
|
|
113
|
+
if selector[:matchingBranch] && selector.slice(*ALL_KEYS - [:consumer, :matchingBranch]).any?
|
114
|
+
errors << "cannot specify matchingBranch=true with any other criteria apart from consumer (at index #{index})"
|
115
|
+
end
|
116
|
+
|
111
117
|
if selector[:tag] && selector[:branch]
|
112
118
|
errors << "cannot specify both a tag and a branch (at index #{index})"
|
113
119
|
end
|
@@ -136,6 +142,10 @@ module PactBroker
|
|
136
142
|
errors << "cannot specify both released=true and deployedOrReleased=true (at index #{index})"
|
137
143
|
end
|
138
144
|
|
145
|
+
if selector[:matchingBranch] && not_provided?(params[:providerVersionBranch])
|
146
|
+
errors << "the providerVersionBranch must be specified when the selector matchingBranch=true is used (at index #{index})"
|
147
|
+
end
|
148
|
+
|
139
149
|
non_environment_fields = selector.slice(*BRANCH_KEYS).keys.sort
|
140
150
|
environment_related_fields = selector.slice(*ENVIRONMENT_KEYS).keys.sort
|
141
151
|
|
@@ -9,7 +9,8 @@ module PactBroker
|
|
9
9
|
class DeployedVersionDecorator < BaseDecorator
|
10
10
|
property :uuid
|
11
11
|
property :currently_deployed, camelize: true
|
12
|
-
property :target, camelize: true
|
12
|
+
property :target, camelize: true # deprecated
|
13
|
+
property :applicationInstance, getter: lambda { |_| target }
|
13
14
|
include Timestamps
|
14
15
|
property :undeployedAt, getter: lambda { |_| undeployed_at ? FormatDateTime.call(undeployed_at) : nil }, writeable: false
|
15
16
|
|
@@ -21,6 +21,10 @@ module PactBroker
|
|
21
21
|
represented.branch = fragment
|
22
22
|
represented.latest = true
|
23
23
|
}
|
24
|
+
property :matching_branch, setter: -> (fragment:, represented:, **) {
|
25
|
+
represented.matching_branch = fragment
|
26
|
+
represented.latest = true
|
27
|
+
}
|
24
28
|
property :latest,
|
25
29
|
setter: ->(fragment:, represented:, **) {
|
26
30
|
represented.latest = (fragment == "true" || fragment == true)
|
@@ -53,6 +57,9 @@ module PactBroker
|
|
53
57
|
result = super(hash&.snakecase_keys)
|
54
58
|
|
55
59
|
result.consumer_version_selectors = split_out_deployed_or_released_selectors(result.consumer_version_selectors)
|
60
|
+
if result.provider_version_branch
|
61
|
+
result.consumer_version_selectors = set_branch_for_matching_branch_selectors(result.consumer_version_selectors, result.provider_version_branch)
|
62
|
+
end
|
56
63
|
|
57
64
|
if result.consumer_version_selectors && !result.consumer_version_selectors.is_a?(PactBroker::Pacts::Selectors)
|
58
65
|
result.consumer_version_selectors = PactBroker::Pacts::Selectors.new(result.consumer_version_selectors)
|
@@ -62,6 +69,17 @@ module PactBroker
|
|
62
69
|
|
63
70
|
private
|
64
71
|
|
72
|
+
def set_branch_for_matching_branch_selectors(consumer_version_selectors, provider_version_branch)
|
73
|
+
consumer_version_selectors.collect do | consumer_version_selector |
|
74
|
+
if consumer_version_selector[:matching_branch]
|
75
|
+
consumer_version_selector[:branch] = provider_version_branch
|
76
|
+
consumer_version_selector
|
77
|
+
else
|
78
|
+
consumer_version_selector
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
65
83
|
def split_out_deployed_or_released_selectors(consumer_version_selectors)
|
66
84
|
consumer_version_selectors.flat_map do | selector |
|
67
85
|
if selector.currently_deployed && selector.currently_supported
|
@@ -60,7 +60,9 @@ module PactBroker
|
|
60
60
|
query = Rack::Utils.parse_query(request.env["QUERY_STRING"])
|
61
61
|
q = {}
|
62
62
|
q[:pacticipant_name] = request.query["pacticipant"] if query["pacticipant"]
|
63
|
-
if query["
|
63
|
+
if query["applicationInstance"]
|
64
|
+
q[:target] = query["applicationInstance"].blank? ? nil : query["applicationInstance"]
|
65
|
+
elsif query["target"]
|
64
66
|
q[:target] = query["target"].blank? ? nil : query["target"]
|
65
67
|
end
|
66
68
|
q
|
@@ -30,7 +30,7 @@ module PactBroker
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def from_json
|
33
|
-
@deployed_version = deployed_version_service.find_or_create(deployed_version_uuid, version, environment,
|
33
|
+
@deployed_version = deployed_version_service.find_or_create(deployed_version_uuid, version, environment, application_instance)
|
34
34
|
response.headers["Location"] = deployed_version_url(deployed_version, base_url)
|
35
35
|
response.body = decorator_class(:deployed_version_decorator).new(deployed_version).to_json(decorator_options)
|
36
36
|
end
|
@@ -72,8 +72,9 @@ module PactBroker
|
|
72
72
|
end
|
73
73
|
|
74
74
|
# TODO disallow an empty string because that is used as a NULL indicator in the database
|
75
|
-
def
|
76
|
-
params(default: {})
|
75
|
+
def application_instance
|
76
|
+
parameters = params(default: {})
|
77
|
+
(parameters[:applicationInstance] || parameters[:target])&.to_s
|
77
78
|
end
|
78
79
|
|
79
80
|
def title
|
@@ -110,7 +110,7 @@ module PactBroker
|
|
110
110
|
|
111
111
|
def disallowed_modification?
|
112
112
|
if request.really_put? && pact_service.disallowed_modification?(pact, pact_params.json_content)
|
113
|
-
message_params = { consumer_name: pact_params.consumer_name, consumer_version_number: pact_params.consumer_version_number }
|
113
|
+
message_params = { consumer_name: pact_params.consumer_name, consumer_version_number: pact_params.consumer_version_number, provider_name: pact_params.provider_name }
|
114
114
|
set_json_error_message(message("errors.validation.pact_content_modification_not_allowed", message_params))
|
115
115
|
true
|
116
116
|
else
|
@@ -31,11 +31,13 @@ module PactBroker
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def process_post
|
34
|
-
|
35
|
-
|
36
|
-
|
34
|
+
if conflict_notices.any?
|
35
|
+
set_conflict_response
|
36
|
+
409
|
37
|
+
else
|
38
|
+
publish_contracts
|
39
|
+
true
|
37
40
|
end
|
38
|
-
true
|
39
41
|
end
|
40
42
|
|
41
43
|
def policy_name
|
@@ -75,6 +77,27 @@ module PactBroker
|
|
75
77
|
contract["decodedParsedContent"] = PactBroker::Pacts::Parse.call(contract["decodedContent"]) rescue nil
|
76
78
|
end
|
77
79
|
end
|
80
|
+
|
81
|
+
def publish_contracts
|
82
|
+
handle_webhook_events(consumer_version_branch: parsed_contracts.branch, build_url: parsed_contracts.build_url) do
|
83
|
+
results = contract_service.publish(parsed_contracts, base_url: base_url)
|
84
|
+
response.body = decorator_class(:publish_contracts_results_decorator).new(results).to_json(decorator_options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def set_conflict_response
|
89
|
+
response.body = {
|
90
|
+
notices: conflict_notices.collect(&:to_h),
|
91
|
+
errors: {
|
92
|
+
contracts: conflict_notices.select(&:error?).collect(&:text)
|
93
|
+
}
|
94
|
+
}.to_json
|
95
|
+
response.headers["Content-Type"] = "application/json;charset=utf-8"
|
96
|
+
end
|
97
|
+
|
98
|
+
def conflict_notices
|
99
|
+
@conflict_notices ||= contract_service.conflict_notices(parsed_contracts)
|
100
|
+
end
|
78
101
|
end
|
79
102
|
end
|
80
103
|
end
|
@@ -1,13 +1,8 @@
|
|
1
1
|
module PactBroker
|
2
2
|
module Contracts
|
3
|
-
ContractsPublicationResults = Struct.new(:pacticipant, :version, :tags, :contracts, :notices) do
|
3
|
+
ContractsPublicationResults = Struct.new(:pacticipant, :version, :tags, :contracts, :notices, keyword_init: true) do
|
4
4
|
def self.from_hash(params)
|
5
|
-
new(params
|
6
|
-
params[:version],
|
7
|
-
params[:tags],
|
8
|
-
params[:contracts],
|
9
|
-
params[:notices]
|
10
|
-
)
|
5
|
+
new(params)
|
11
6
|
end
|
12
7
|
end
|
13
8
|
end
|
@@ -6,6 +6,7 @@ require "pact_broker/contracts/contracts_publication_results"
|
|
6
6
|
require "pact_broker/contracts/notice"
|
7
7
|
require "pact_broker/events/subscriber"
|
8
8
|
require "pact_broker/api/pact_broker_urls"
|
9
|
+
require "pact_broker/pacts/create_formatted_diff"
|
9
10
|
|
10
11
|
module PactBroker
|
11
12
|
module Contracts
|
@@ -42,6 +43,30 @@ module PactBroker
|
|
42
43
|
)
|
43
44
|
end
|
44
45
|
|
46
|
+
def conflict_notices(parsed_contracts)
|
47
|
+
notices = []
|
48
|
+
parsed_contracts.contracts.collect do | contract_to_publish |
|
49
|
+
pact_params = create_pact_params(parsed_contracts, contract_to_publish)
|
50
|
+
existing_pact = pact_service.find_pact(pact_params)
|
51
|
+
if existing_pact && pact_service.disallowed_modification?(existing_pact, contract_to_publish.decoded_content)
|
52
|
+
add_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_pact.json_content, contract_to_publish.decoded_content)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
notices
|
56
|
+
end
|
57
|
+
|
58
|
+
def add_conflict_notice(notices, parsed_contracts, contract_to_publish, existing_json_content, new_json_content)
|
59
|
+
message_params = {
|
60
|
+
consumer_name: contract_to_publish.provider_name,
|
61
|
+
consumer_version_number: parsed_contracts.pacticipant_version_number,
|
62
|
+
provider_name: contract_to_publish.provider_name
|
63
|
+
}
|
64
|
+
notices << Notice.error(message("errors.validation.pact_content_modification_not_allowed", message_params))
|
65
|
+
notices << Notice.info(PactBroker::Pacts::CreateFormattedDiff.call(new_json_content, existing_json_content))
|
66
|
+
end
|
67
|
+
|
68
|
+
private :add_conflict_notice
|
69
|
+
|
45
70
|
def create_version(parsed_contracts)
|
46
71
|
version_params = {
|
47
72
|
build_url: parsed_contracts.build_url,
|
@@ -12,6 +12,8 @@ This is the preferred endpoint with which to publish contracts (previously, cont
|
|
12
12
|
|
13
13
|
The previous tag and pact endpoints are still supported, however, future features that build on this endpoint may not be able to be backported into those endpoints.
|
14
14
|
|
15
|
+
This endpoint is designed to be used by a command line tool, and hence, the response notices are designed for output to the user in a terminal.
|
16
|
+
|
15
17
|
## Parameters
|
16
18
|
|
17
19
|
* `pacticipantName`: the name of the application. Required.
|
@@ -31,7 +33,7 @@ The previous tag and pact endpoints are still supported, however, future feature
|
|
31
33
|
### Success
|
32
34
|
|
33
35
|
* `notices`
|
34
|
-
* `level`: one of `debug`, `info`, `warning`,`prompt`,`success`
|
36
|
+
* `level`: one of `debug`, `info`, `warning`,`prompt`,`success`, `error`, `danger`
|
35
37
|
* `text`: the text of the notice. This is designed to be displayed in the output of a CLI.
|
36
38
|
|
37
39
|
The `_links` section will contain links to all the resources created by the publication. The relations are:
|
@@ -43,7 +45,9 @@ The `_links` section will contain links to all the resources created by the publ
|
|
43
45
|
|
44
46
|
### Errors
|
45
47
|
|
46
|
-
|
48
|
+
### Schema validation errors
|
49
|
+
|
50
|
+
Any validation errors will be returned in the standard Pact Broker format with a 400 status:
|
47
51
|
|
48
52
|
{
|
49
53
|
"errors": {
|
@@ -51,6 +55,32 @@ Any validation errors will be returned in the standard Pact Broker format:
|
|
51
55
|
}
|
52
56
|
}
|
53
57
|
|
58
|
+
### Contract conflict errors
|
59
|
+
|
60
|
+
If there is a conflict with an existing published pact and `allow_dangerous_contract_modification` is set to false, a 409 will be returned with an array of notices, which will contain a diff between the existing pact content and the content that was attempted to be published. For consistency with the existing error responses, the errors hash will also contain the error messages, but there will be no diff included. For CLI usage, when there are notices and errors, just the notices should be displayed to the user.
|
61
|
+
|
62
|
+
{
|
63
|
+
"notices":
|
64
|
+
[
|
65
|
+
{
|
66
|
+
"text": "Cannot change the content of the pact for Foo version 183a77b0 and provider Bar, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. For more information see https://docs.pact.io/go/versioning",
|
67
|
+
"type": "error"
|
68
|
+
},
|
69
|
+
{
|
70
|
+
"text": "<the diff, will include new lines>",
|
71
|
+
"type": "info"
|
72
|
+
}
|
73
|
+
],
|
74
|
+
"errors":
|
75
|
+
{
|
76
|
+
"content":
|
77
|
+
[
|
78
|
+
"Cannot change the content of the pact for Foo version 183a77b0 and provider Bar, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. For more information see https://docs.pact.io/go/versioning"
|
79
|
+
]
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
|
54
84
|
## Example
|
55
85
|
|
56
86
|
POST http://broker/contracts/publish
|
@@ -30,6 +30,8 @@ Example: This data structure represents the way a user might specify "I want to
|
|
30
30
|
|
31
31
|
`consumerVersionSelectors.branch`: the branch name of the consumer versions to get the pacts for. Use of this selector requires that the consumer has configured a branch name when publishing the pacts.
|
32
32
|
|
33
|
+
`consumerVersionSelectors.matchingBranch`: if the key is specified, can only be set to `true`. When true, returns the latest pact for any branch with the same name as the specified `providerVersionBranch`.
|
34
|
+
|
33
35
|
`consumerVersionSelectors.fallbackBranch`: the name of the branch to fallback to if the specified `branch` does not exist. Use of this property is discouraged as it may allow a pact to pass on a feature branch while breaking backwards compatibility with the main branch, which is generally not desired. It is better to use two separate consumer version selectors, one with the main branch name, and one with the feature branch name, rather than use this property.
|
34
36
|
|
35
37
|
`consumerVersionSelectors.deployed`: if the key is specified, can only be set to `true`. Returns the pacts for all versions of the consumer that are currently deployed to any environment. Use of this selector requires that the deployment of the consumer application is recorded in the Pact Broker using the `pact-broker record-deployment` CLI.
|
@@ -74,7 +74,7 @@ en:
|
|
74
74
|
invalid_http_method: "Invalid HTTP method '%{method}'"
|
75
75
|
invalid_url: "Invalid URL '%{url}'. Expected format: http://example.org"
|
76
76
|
pact_missing_pacticipant_name: "was not found at expected path $.%{pacticipant}.name in the submitted pact file."
|
77
|
-
pact_content_modification_not_allowed: "Cannot change the content of the pact for %{consumer_name} version %{consumer_version_number}, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. For more information see https://docs.pact.io/go/versioning"
|
77
|
+
pact_content_modification_not_allowed: "Cannot change the content of the pact for %{consumer_name} version %{consumer_version_number} and provider %{provider_name}, as race conditions will cause unreliable results for can-i-deploy. Each pact must be published with a unique consumer version number. For more information see https://docs.pact.io/go/versioning"
|
78
78
|
consumer_version_number_missing: "Please specify the consumer version number by setting the X-Pact-Consumer-Version header."
|
79
79
|
consumer_version_number_header_invalid: "X-Pact-Consumer-Version '%{consumer_version_number}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
|
80
80
|
consumer_version_number_invalid: "Consumer version number '%{consumer_version_number}' cannot be parsed to a version number. The expected format (unless this configuration has been overridden) is a semantic version. eg. 1.3.0 or 2.0.4.rc1"
|
@@ -1,11 +1,13 @@
|
|
1
1
|
require "pact_broker/pacts/parse"
|
2
2
|
require "pact_broker/pacts/sort_content"
|
3
3
|
require "pact_broker/pacts/generate_interaction_sha"
|
4
|
+
require "pact_broker/hash_refinements"
|
4
5
|
|
5
6
|
module PactBroker
|
6
7
|
module Pacts
|
7
8
|
class Content
|
8
9
|
include GenerateInteractionSha
|
10
|
+
using PactBroker::HashRefinements
|
9
11
|
|
10
12
|
def initialize pact_hash
|
11
13
|
@pact_hash = pact_hash
|
@@ -75,6 +77,18 @@ module PactBroker
|
|
75
77
|
Content.from_hash(new_pact_hash)
|
76
78
|
end
|
77
79
|
|
80
|
+
def without_ids
|
81
|
+
new_pact_hash = pact_hash.dup
|
82
|
+
if interactions && interactions.is_a?(Array)
|
83
|
+
new_pact_hash["interactions"] = remove_ids(interactions)
|
84
|
+
end
|
85
|
+
|
86
|
+
if messages && messages.is_a?(Array)
|
87
|
+
new_pact_hash["messages"] = remove_ids(messages)
|
88
|
+
end
|
89
|
+
Content.from_hash(new_pact_hash)
|
90
|
+
end
|
91
|
+
|
78
92
|
def interaction_ids
|
79
93
|
messages_or_interaction_or_empty_array.collect do | interaction |
|
80
94
|
interaction["_id"]
|
@@ -138,6 +152,10 @@ module PactBroker
|
|
138
152
|
end
|
139
153
|
end
|
140
154
|
|
155
|
+
def remove_ids(interactions_or_messages)
|
156
|
+
interactions_or_messages.collect{ | h | h.without("_id") }
|
157
|
+
end
|
158
|
+
|
141
159
|
def merge_verification_results(interactions, tests)
|
142
160
|
interactions.collect(&:dup).collect do | interaction |
|
143
161
|
interaction["tests"] = tests.select do | test |
|
@@ -1,16 +1,23 @@
|
|
1
1
|
require "pact/matchers"
|
2
2
|
require "pact_broker/json"
|
3
3
|
require "pact/matchers/unix_diff_formatter"
|
4
|
+
require "pact_broker/pacts/sort_content"
|
5
|
+
require "pact_broker/pacts/content"
|
4
6
|
|
5
7
|
module PactBroker
|
6
8
|
module Pacts
|
7
9
|
class CreateFormattedDiff
|
8
|
-
|
9
10
|
extend Pact::Matchers
|
10
11
|
|
11
|
-
def self.call pact_json_content, previous_pact_json_content
|
12
|
+
def self.call pact_json_content, previous_pact_json_content, raw: false
|
12
13
|
pact_hash = JSON.load(pact_json_content, nil, PactBroker::PACT_PARSING_OPTIONS)
|
13
14
|
previous_pact_hash = JSON.load(previous_pact_json_content, nil, PactBroker::PACT_PARSING_OPTIONS)
|
15
|
+
|
16
|
+
if !raw
|
17
|
+
pact_hash = SortContent.call(PactBroker::Pacts::Content.from_hash(pact_hash).without_ids.to_hash)
|
18
|
+
previous_pact_hash = SortContent.call(PactBroker::Pacts::Content.from_hash(previous_pact_hash).without_ids.to_hash)
|
19
|
+
end
|
20
|
+
|
14
21
|
difference = diff(previous_pact_hash, pact_hash)
|
15
22
|
Pact::Matchers::UnixDiffFormatter.call(difference, colour: false, include_explanation: false)
|
16
23
|
end
|
@@ -6,7 +6,7 @@ module PactBroker
|
|
6
6
|
class Selector < Hash
|
7
7
|
using PactBroker::HashRefinements
|
8
8
|
|
9
|
-
PROPERTY_NAMES = [:latest, :tag, :branch, :consumer, :consumer_version, :environment_name, :fallback_tag, :fallback_branch, :main_branch, :currently_supported, :currently_deployed]
|
9
|
+
PROPERTY_NAMES = [:latest, :tag, :branch, :consumer, :consumer_version, :environment_name, :fallback_tag, :fallback_branch, :main_branch, :matching_branch, :currently_supported, :currently_deployed]
|
10
10
|
|
11
11
|
def initialize(properties = {})
|
12
12
|
properties.without(*PROPERTY_NAMES).tap { |it| warn("WARN: Unsupported property for #{self.class.name}: #{it.keys.join(", ")} at #{caller[0..3]}") if it.any? }
|
@@ -31,6 +31,8 @@ module PactBroker
|
|
31
31
|
def type
|
32
32
|
if latest_for_branch?
|
33
33
|
:latest_for_branch
|
34
|
+
elsif matching_branch?
|
35
|
+
:matching_branch
|
34
36
|
elsif currently_deployed?
|
35
37
|
:currently_deployed
|
36
38
|
elsif currently_supported?
|
@@ -53,6 +55,10 @@ module PactBroker
|
|
53
55
|
self[:main_branch] = main_branch
|
54
56
|
end
|
55
57
|
|
58
|
+
def matching_branch= matching_branch
|
59
|
+
self[:matching_branch] = matching_branch
|
60
|
+
end
|
61
|
+
|
56
62
|
def tag= tag
|
57
63
|
self[:tag] = tag
|
58
64
|
end
|
@@ -130,83 +136,83 @@ module PactBroker
|
|
130
136
|
end
|
131
137
|
|
132
138
|
def self.overall_latest
|
133
|
-
|
139
|
+
new(latest: true)
|
134
140
|
end
|
135
141
|
|
136
142
|
def self.for_main_branch
|
137
|
-
|
143
|
+
new(main_branch: true)
|
138
144
|
end
|
139
145
|
|
140
146
|
def self.latest_for_tag(tag)
|
141
|
-
|
147
|
+
new(latest: true, tag: tag)
|
142
148
|
end
|
143
149
|
|
144
150
|
def self.latest_for_branch(branch)
|
145
|
-
|
151
|
+
new(latest: true, branch: branch)
|
146
152
|
end
|
147
153
|
|
148
154
|
def self.latest_for_tag_with_fallback(tag, fallback_tag)
|
149
|
-
|
155
|
+
new(latest: true, tag: tag, fallback_tag: fallback_tag)
|
150
156
|
end
|
151
157
|
|
152
158
|
def self.latest_for_branch_with_fallback(branch, fallback_branch)
|
153
|
-
|
159
|
+
new(latest: true, branch: branch, fallback_branch: fallback_branch)
|
154
160
|
end
|
155
161
|
|
156
162
|
def self.all_for_tag(tag)
|
157
|
-
|
163
|
+
new(tag: tag)
|
158
164
|
end
|
159
165
|
|
160
166
|
def self.all_for_tag_and_consumer(tag, consumer)
|
161
|
-
|
167
|
+
new(tag: tag, consumer: consumer)
|
162
168
|
end
|
163
169
|
|
164
170
|
def self.latest_for_tag_and_consumer(tag, consumer)
|
165
|
-
|
171
|
+
new(latest: true, tag: tag, consumer: consumer)
|
166
172
|
end
|
167
173
|
|
168
174
|
def self.latest_for_branch_and_consumer(branch, consumer)
|
169
|
-
|
175
|
+
new(latest: true, branch: branch, consumer: consumer)
|
170
176
|
end
|
171
177
|
|
172
178
|
def self.latest_for_consumer(consumer)
|
173
|
-
|
179
|
+
new(latest: true, consumer: consumer)
|
174
180
|
end
|
175
181
|
|
176
182
|
def self.for_currently_deployed(environment_name = nil)
|
177
|
-
|
183
|
+
new( { currently_deployed: true, environment_name: environment_name }.compact )
|
178
184
|
end
|
179
185
|
|
180
186
|
def self.for_currently_supported(environment_name = nil)
|
181
|
-
|
187
|
+
new( { currently_supported: true, environment_name: environment_name }.compact )
|
182
188
|
end
|
183
189
|
|
184
190
|
def self.for_currently_deployed_and_consumer(consumer)
|
185
|
-
|
191
|
+
new(currently_deployed: true, consumer: consumer)
|
186
192
|
end
|
187
193
|
|
188
194
|
def self.for_currently_deployed_and_environment_and_consumer(environment_name, consumer)
|
189
|
-
|
195
|
+
new(currently_deployed: true, environment_name: environment_name, consumer: consumer)
|
190
196
|
end
|
191
197
|
|
192
198
|
def self.for_currently_supported_and_environment_and_consumer(environment_name, consumer)
|
193
|
-
|
199
|
+
new(currently_supported: true, environment_name: environment_name, consumer: consumer)
|
194
200
|
end
|
195
201
|
|
196
202
|
def self.for_environment(environment_name)
|
197
|
-
|
203
|
+
new(environment_name: environment_name)
|
198
204
|
end
|
199
205
|
|
200
206
|
def self.for_environment_and_consumer(environment_name, consumer)
|
201
|
-
|
207
|
+
new(environment_name: environment_name, consumer: consumer)
|
202
208
|
end
|
203
209
|
|
204
210
|
def self.from_hash hash
|
205
|
-
|
211
|
+
new(hash)
|
206
212
|
end
|
207
213
|
|
208
214
|
def for_consumer(consumer)
|
209
|
-
|
215
|
+
self.class.new(to_h.merge(consumer: consumer))
|
210
216
|
end
|
211
217
|
|
212
218
|
def latest_for_main_branch?
|
@@ -233,6 +239,14 @@ module PactBroker
|
|
233
239
|
self[:branch]
|
234
240
|
end
|
235
241
|
|
242
|
+
def matching_branch
|
243
|
+
self[:matching_branch]
|
244
|
+
end
|
245
|
+
|
246
|
+
def matching_branch?
|
247
|
+
!!matching_branch
|
248
|
+
end
|
249
|
+
|
236
250
|
def overall_latest?
|
237
251
|
!!(latest? && !tag && !branch && !main_branch && !currently_deployed && !currently_supported && !environment_name)
|
238
252
|
end
|