pact_broker 2.77.0 → 2.78.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/db/migrations/20210210_create_environments_table.rb +16 -0
- data/lib/pact_broker/api.rb +5 -0
- data/lib/pact_broker/api/contracts/dry_validation_predicates.rb +8 -0
- data/lib/pact_broker/api/contracts/environment_schema.rb +49 -0
- data/lib/pact_broker/api/decorators/base_decorator.rb +11 -0
- data/lib/pact_broker/api/decorators/environment_decorator.rb +30 -0
- data/lib/pact_broker/api/decorators/environments_decorator.rb +21 -0
- data/lib/pact_broker/api/decorators/version_decorator.rb +12 -1
- data/lib/pact_broker/api/pact_broker_urls.rb +8 -0
- data/lib/pact_broker/api/resources/default_base_resource.rb +9 -0
- data/lib/pact_broker/api/resources/environment.rb +76 -0
- data/lib/pact_broker/api/resources/environments.rb +75 -0
- data/lib/pact_broker/api/resources/index.rb +14 -0
- data/lib/pact_broker/api/resources/version.rb +2 -2
- data/lib/pact_broker/configuration.rb +1 -0
- data/lib/pact_broker/deployments/environment.rb +15 -0
- data/lib/pact_broker/deployments/environment_service.rb +39 -0
- data/lib/pact_broker/doc/views/index/environment.markdown +37 -0
- data/lib/pact_broker/doc/views/index/environments.markdown +53 -0
- data/lib/pact_broker/doc/views/index/latest-pact-versions.markdown +1 -1
- data/lib/pact_broker/doc/views/index/pacticipant-version-tag.markdown +1 -0
- data/lib/pact_broker/locale/en.yml +3 -1
- data/lib/pact_broker/services.rb +9 -0
- data/lib/pact_broker/test/test_data_builder.rb +14 -0
- data/lib/pact_broker/version.rb +1 -1
- data/lib/pact_broker/versions/repository.rb +15 -4
- data/lib/pact_broker/versions/service.rb +2 -2
- data/lib/pact_broker/webhooks/webhook_execution_result.rb +4 -1
- data/spec/features/create_environment_spec.rb +47 -0
- data/spec/features/create_tag_spec.rb +32 -0
- data/spec/features/create_version_spec.rb +30 -4
- data/spec/features/delete_environment_spec.rb +16 -0
- data/spec/features/end_deployment_spec.rb +29 -0
- data/spec/features/get_environment_spec.rb +19 -0
- data/spec/features/get_environments_spec.rb +20 -0
- data/spec/features/record_deployment_spec.rb +28 -0
- data/spec/features/update_environment_spec.rb +44 -0
- data/spec/fixtures/approvals/modifiable_resources.approved.json +6 -0
- data/spec/lib/pact_broker/api/contracts/environment_schema_spec.rb +83 -0
- data/spec/lib/pact_broker/api/decorators/version_decorator_spec.rb +18 -0
- data/spec/lib/pact_broker/api/resources/default_base_resource_approval_spec.rb +1 -1
- data/spec/lib/pact_broker/api/resources/webhook_execution_result_spec.rb +56 -0
- data/spec/lib/pact_broker/versions/repository_spec.rb +14 -4
- data/spec/service_consumers/hal_relation_proxy_app.rb +3 -1
- data/spec/service_consumers/provider_states_for_pact_broker_client.rb +16 -0
- data/spec/support/shared_examples_for_responses.rb +11 -0
- metadata +33 -3
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/plugins/serialization'
|
3
|
+
|
4
|
+
|
5
|
+
module PactBroker
|
6
|
+
module Deployments
|
7
|
+
class Environment < Sequel::Model
|
8
|
+
OPEN_STRUCT_TO_JSON = lambda { |thing| Sequel.object_to_json(thing.collect(&:to_h)) }
|
9
|
+
JSON_TO_OPEN_STRUCT = lambda { | json | Sequel.parse_json(json).collect{ | hash| OpenStruct.new(hash) } }
|
10
|
+
plugin :upsert, identifying_columns: [:uuid]
|
11
|
+
plugin :serialization
|
12
|
+
serialize_attributes [OPEN_STRUCT_TO_JSON, JSON_TO_OPEN_STRUCT], :contacts
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'pact_broker/deployments/environment'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module PactBroker
|
5
|
+
module Deployments
|
6
|
+
module EnvironmentService
|
7
|
+
|
8
|
+
def self.next_uuid
|
9
|
+
SecureRandom.uuid
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.create(uuid, environment)
|
13
|
+
environment.uuid = uuid
|
14
|
+
environment.save
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.update(uuid, environment)
|
18
|
+
environment.uuid = uuid
|
19
|
+
environment.upsert
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find_all
|
23
|
+
PactBroker::Deployments::Environment.order(Sequel.function(:lower, :display_name)).all
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.find(uuid)
|
27
|
+
PactBroker::Deployments::Environment.where(uuid: uuid).single_record
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.find_by_name(name)
|
31
|
+
PactBroker::Deployments::Environment.where(name: name).single_record
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.delete(uuid)
|
35
|
+
PactBroker::Deployments::Environment.where(uuid: uuid).delete
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# Environment
|
2
|
+
|
3
|
+
Allowed methods: `GET`, `PUT`, `DELETE`
|
4
|
+
|
5
|
+
Path: `/environment/{uuid}`
|
6
|
+
|
7
|
+
## Viewing an environment
|
8
|
+
|
9
|
+
Example:
|
10
|
+
|
11
|
+
curl http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729 \
|
12
|
+
-H "Accept: application/hal+json"
|
13
|
+
{
|
14
|
+
"uuid": "79060381-269c-4769-9894-9ec3cab44729",
|
15
|
+
"name": "test",
|
16
|
+
"displayName": "Test",
|
17
|
+
"production": false
|
18
|
+
}
|
19
|
+
|
20
|
+
## Updating an environment
|
21
|
+
|
22
|
+
Example:
|
23
|
+
|
24
|
+
curl -X PUT http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729 \
|
25
|
+
-H "Content-Type: application/json" \
|
26
|
+
-H "Accept: application/hal+json" \
|
27
|
+
-d '{
|
28
|
+
"name": "test",
|
29
|
+
"displayName": "Test",
|
30
|
+
"production": false
|
31
|
+
}'
|
32
|
+
|
33
|
+
## Deleting an environment
|
34
|
+
|
35
|
+
Example:
|
36
|
+
|
37
|
+
curl -v -X DELETE http://${PACT_BROKER_HOST}/environments/79060381-269c-4769-9894-9ec3cab44729
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Environments
|
2
|
+
|
3
|
+
Allowed methods: `GET`, `POST`
|
4
|
+
|
5
|
+
Path: `/environments`
|
6
|
+
|
7
|
+
## Creating an environment
|
8
|
+
|
9
|
+
Send a POST to `/environments` with the environment payload.
|
10
|
+
|
11
|
+
Example:
|
12
|
+
|
13
|
+
curl http://${PACT_BROKER_HOST}/environments \
|
14
|
+
-H "Content-Type: application/json" \
|
15
|
+
-H "Accept: application/hal+json" \
|
16
|
+
-d '{
|
17
|
+
"name": "test",
|
18
|
+
"displayName": "Test",
|
19
|
+
"production": false
|
20
|
+
}'
|
21
|
+
|
22
|
+
Alternatively, you can use the HAL Browser.
|
23
|
+
|
24
|
+
* Click on the `API Browser` link at the top of the Pact Broker index page.
|
25
|
+
* In the `Links` section on the left, locate the `pb:environments` relation, and click on the yellow `!` "Perform non-GET request" button.
|
26
|
+
* In the `Body:` text box, fill in the required JSON properties.
|
27
|
+
* Click `Make Request`.
|
28
|
+
|
29
|
+
Properties:
|
30
|
+
|
31
|
+
* `uuid`: System generated unique identifier.
|
32
|
+
* `name`: Must be unique. No spaces allowed. This will be the name used in the `can-i-deploy` and `record-deployment` CLI commands. eg. "payments-sit-1"
|
33
|
+
* `displayName`: A more verbose name for the environment. "Payments Team SIT 1"
|
34
|
+
* `production`: Whether or not this environment is a production environment.
|
35
|
+
|
36
|
+
If all the services in the Broker are deployed to the same "public" internet, then there only needs to be one Production environment. If there are multiple segregated production environments (eg. when maintaining on-premises software for multiple customers ) then you should create a separate production Environment for each logical deployment environment.
|
37
|
+
|
38
|
+
## Listing environments
|
39
|
+
|
40
|
+
`GET /environments`
|
41
|
+
|
42
|
+
{
|
43
|
+
"_embedded": {
|
44
|
+
"environments": [
|
45
|
+
{
|
46
|
+
"uuid": "79060381-269c-4769-9894-9ec3cab44729",
|
47
|
+
"name": "production",
|
48
|
+
"displayName": "Production",
|
49
|
+
"production": true
|
50
|
+
}
|
51
|
+
]
|
52
|
+
}
|
53
|
+
}
|
@@ -9,7 +9,8 @@ en:
|
|
9
9
|
valid_consumer_version_number?: "Consumer version number '%{value}' 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"
|
10
10
|
non_templated_host?: "cannot have a template parameter in the host"
|
11
11
|
pacticipant_exists?: "does not match an existing pacticipant"
|
12
|
-
|
12
|
+
single_line?: "cannot contain multiple lines"
|
13
|
+
no_spaces?: "cannot contain spaces"
|
13
14
|
|
14
15
|
pact_broker:
|
15
16
|
messages:
|
@@ -46,6 +47,7 @@ en:
|
|
46
47
|
connection_encoding_not_utf8: "The Sequel connection encoding (%{encoding}) is strongly recommended to be \"utf8\". If you need to set it to %{encoding} for some particular reason, then disable this check by setting config.validate_database_connection_config = false"
|
47
48
|
invalid_webhook_uuid: The UUID can only contain the characters A-Z, a-z, 0-9, _ and -, and must be 16 or more characters.
|
48
49
|
pacticipant_not_found: No pacticipant with name '%{name}' found
|
50
|
+
environment_name_must_be_unique: Another environment with name '%{name}' already exists.
|
49
51
|
duplicate_pacticipant: |
|
50
52
|
This is the first time a pact has been published for "%{new_name}".
|
51
53
|
The name "%{new_name}" is very similar to the following existing consumers/providers:
|
data/lib/pact_broker/services.rb
CHANGED
@@ -73,6 +73,10 @@ module PactBroker
|
|
73
73
|
get(:metrics_service)
|
74
74
|
end
|
75
75
|
|
76
|
+
def environment_service
|
77
|
+
get(:environment_service)
|
78
|
+
end
|
79
|
+
|
76
80
|
def register_default_services
|
77
81
|
register_service(:index_service) do
|
78
82
|
require 'pact_broker/index/service'
|
@@ -148,6 +152,11 @@ module PactBroker
|
|
148
152
|
require 'pact_broker/webhooks/trigger_service'
|
149
153
|
Webhooks::TriggerService
|
150
154
|
end
|
155
|
+
|
156
|
+
register_service(:environment_service) do
|
157
|
+
require 'pact_broker/deployments/environment_service'
|
158
|
+
Deployments::EnvironmentService
|
159
|
+
end
|
151
160
|
end
|
152
161
|
end
|
153
162
|
end
|
@@ -24,6 +24,7 @@ require 'pact_broker/tags/repository'
|
|
24
24
|
require 'pact_broker/webhooks/repository'
|
25
25
|
require 'pact_broker/certificates/certificate'
|
26
26
|
require 'pact_broker/matrix/row'
|
27
|
+
require 'pact_broker/deployments/environment_service'
|
27
28
|
require 'ostruct'
|
28
29
|
|
29
30
|
module PactBroker
|
@@ -45,6 +46,7 @@ module PactBroker
|
|
45
46
|
attr_reader :webhook
|
46
47
|
attr_reader :webhook_execution
|
47
48
|
attr_reader :triggered_webhook
|
49
|
+
attr_reader :environment
|
48
50
|
|
49
51
|
def initialize(params = {})
|
50
52
|
@now = DateTime.now
|
@@ -381,6 +383,18 @@ module PactBroker
|
|
381
383
|
self
|
382
384
|
end
|
383
385
|
|
386
|
+
def create_environment(name, params = {})
|
387
|
+
uuid = params[:uuid] || PactBroker::Deployments::EnvironmentService.next_uuid
|
388
|
+
production = params[:production] || false
|
389
|
+
@environment = PactBroker::Deployments::EnvironmentService.create(uuid, PactBroker::Deployments::Environment.new(params.merge(name: name, production: production)))
|
390
|
+
set_created_at_if_set(params[:created_at], :environments, id: environment.id)
|
391
|
+
self
|
392
|
+
end
|
393
|
+
|
394
|
+
def create_deployment(_)
|
395
|
+
self
|
396
|
+
end
|
397
|
+
|
384
398
|
def create_everything_for_an_integration
|
385
399
|
create_pact_with_verification("Foo", "1", "Bar", "2")
|
386
400
|
.create_label("label")
|
data/lib/pact_broker/version.rb
CHANGED
@@ -9,6 +9,7 @@ module PactBroker
|
|
9
9
|
|
10
10
|
include PactBroker::Logging
|
11
11
|
include PactBroker::Repositories::Helpers
|
12
|
+
include PactBroker::Repositories
|
12
13
|
|
13
14
|
def find_by_pacticipant_id_and_number pacticipant_id, number
|
14
15
|
PactBroker::Domain::Version.where(number: number, pacticipant_id: pacticipant_id).single_record
|
@@ -60,13 +61,23 @@ module PactBroker
|
|
60
61
|
PactBroker::Domain::Version.new(version_params).upsert
|
61
62
|
end
|
62
63
|
|
63
|
-
def
|
64
|
-
PactBroker::Domain::Version.new(
|
64
|
+
def create_or_overwrite(pacticipant, version_number, open_struct_version)
|
65
|
+
saved_version = PactBroker::Domain::Version.new(
|
65
66
|
number: version_number,
|
66
67
|
pacticipant: pacticipant,
|
67
|
-
build_url:
|
68
|
-
branch:
|
68
|
+
build_url: open_struct_version.build_url,
|
69
|
+
branch: open_struct_version.branch
|
69
70
|
).upsert
|
71
|
+
|
72
|
+
if open_struct_version.tags
|
73
|
+
tag_repository.delete_by_version_id(saved_version.id)
|
74
|
+
open_struct_version.tags.collect do | open_struct_tag |
|
75
|
+
tag_repository.create(version: saved_version, name: open_struct_tag.name)
|
76
|
+
end
|
77
|
+
saved_version.refresh
|
78
|
+
end
|
79
|
+
|
80
|
+
saved_version
|
70
81
|
end
|
71
82
|
|
72
83
|
def find_by_pacticipant_id_and_number_or_create pacticipant_id, number
|
@@ -18,9 +18,9 @@ module PactBroker
|
|
18
18
|
version_repository.find_by_pacticipant_name_and_latest_tag(pacticipant_name, tag)
|
19
19
|
end
|
20
20
|
|
21
|
-
def self.
|
21
|
+
def self.create_or_overwrite(pacticipant_name, version_number, version)
|
22
22
|
pacticipant = pacticipant_repository.find_by_name_or_create(pacticipant_name)
|
23
|
-
version_repository.
|
23
|
+
version_repository.create_or_overwrite(pacticipant, version_number, version)
|
24
24
|
end
|
25
25
|
|
26
26
|
def self.delete version
|
@@ -14,7 +14,10 @@ module PactBroker
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def success?
|
17
|
-
|
17
|
+
unless response.nil?
|
18
|
+
# Response HTTP Code must be in success list otherwise it is false
|
19
|
+
PactBroker.configuration.webhook_http_code_success.include? response.code.to_i
|
20
|
+
end
|
18
21
|
end
|
19
22
|
end
|
20
23
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'pact_broker/api/pact_broker_urls'
|
2
|
+
|
3
|
+
describe "Creating an environment" do
|
4
|
+
let(:path) { PactBroker::Api::PactBrokerUrls.environments_url }
|
5
|
+
let(:headers) { { "CONTENT_TYPE" => "application/json" } }
|
6
|
+
let(:response_body) { JSON.parse(subject.body, symbolize_names: true)}
|
7
|
+
let(:environment_hash) do
|
8
|
+
{
|
9
|
+
name: "test",
|
10
|
+
displayName: "Test",
|
11
|
+
production: false,
|
12
|
+
contacts: [
|
13
|
+
{ name: "Team Awesome", details: { email: "foo@bar.com", arbitraryThing: "thing" } }
|
14
|
+
]
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
subject { post(path, environment_hash.to_json, headers) }
|
19
|
+
|
20
|
+
it "returns a 201 response" do
|
21
|
+
expect(subject.status).to be 201
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns the Location header" do
|
25
|
+
expect(subject.headers["Location"]).to eq PactBroker::Api::PactBrokerUrls.environment_url(PactBroker::Deployments::Environment.order(:id).last, "http://example.org")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns a JSON Content Type" do
|
29
|
+
expect(subject.headers["Content-Type"]).to eq "application/hal+json;charset=utf-8"
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns the newly created environment" do
|
33
|
+
expect(response_body).to include environment_hash.merge(name: "test")
|
34
|
+
expect(response_body[:uuid]).to_not be nil
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with invalid params" do
|
38
|
+
before do
|
39
|
+
td.create_environment("test")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns a 400 response" do
|
43
|
+
expect(subject.status).to be 400
|
44
|
+
expect(response_body[:errors]).to_not be nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
describe "Creating a tag" do
|
2
|
+
let(:path) { "/pacticipants/Foo/versions/1234/tags/foo" }
|
3
|
+
let(:headers) { { 'CONTENT_TYPE' => 'application/json' } }
|
4
|
+
let(:response_body) { JSON.parse(subject.body, symbolize_names: true)}
|
5
|
+
|
6
|
+
subject { put(path, {}, headers) }
|
7
|
+
|
8
|
+
it "returns a 201 response" do
|
9
|
+
expect(subject.status).to be 201
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns a HAL JSON Content Type" do
|
13
|
+
expect(subject.headers['Content-Type']).to eq 'application/hal+json;charset=utf-8'
|
14
|
+
end
|
15
|
+
|
16
|
+
it "returns the newly created tag" do
|
17
|
+
expect(response_body).to include name: "foo"
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when the tag already exists" do
|
21
|
+
before do
|
22
|
+
td.subtract_day
|
23
|
+
.create_consumer("Foo")
|
24
|
+
.create_consumer_version("1234")
|
25
|
+
.create_consumer_version_tag("foo")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "returns a 200 response" do
|
29
|
+
expect(subject.status).to be 200
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -2,7 +2,13 @@ describe "Creating a pacticipant version" do
|
|
2
2
|
let(:path) { "/pacticipants/Foo/versions/1234" }
|
3
3
|
let(:headers) { { 'CONTENT_TYPE' => 'application/json' } }
|
4
4
|
let(:response_body) { JSON.parse(subject.body, symbolize_names: true)}
|
5
|
-
let(:version_hash)
|
5
|
+
let(:version_hash) do
|
6
|
+
{
|
7
|
+
branch: "main",
|
8
|
+
buildUrl: "http://build",
|
9
|
+
tags: [{ name: "foo" }, { name: "bar" }]
|
10
|
+
}
|
11
|
+
end
|
6
12
|
|
7
13
|
subject { put(path, version_hash.to_json, headers) }
|
8
14
|
|
@@ -15,7 +21,12 @@ describe "Creating a pacticipant version" do
|
|
15
21
|
end
|
16
22
|
|
17
23
|
it "returns the newly created version" do
|
18
|
-
expect(response_body).to include
|
24
|
+
expect(response_body).to include branch: "main", buildUrl: "http://build"
|
25
|
+
expect(response_body[:_embedded][:tags].size).to eq 2
|
26
|
+
end
|
27
|
+
|
28
|
+
it "creates the specified tags" do
|
29
|
+
expect { subject }.to change { PactBroker::Domain::Tag.count }.by(2)
|
19
30
|
end
|
20
31
|
|
21
32
|
context "when the version already exists" do
|
@@ -28,13 +39,28 @@ describe "Creating a pacticipant version" do
|
|
28
39
|
|
29
40
|
let(:version_hash) { { branch: "new-branch" } }
|
30
41
|
|
42
|
+
it "returns a 200" do
|
43
|
+
expect(subject.status).to be 200
|
44
|
+
end
|
45
|
+
|
31
46
|
it "overwrites the direct properties" do
|
32
47
|
expect(response_body[:branch]).to eq "new-branch"
|
33
48
|
expect(response_body).to_not have_key(:buildUrl)
|
34
49
|
end
|
35
50
|
|
36
|
-
|
37
|
-
|
51
|
+
context "when not tags are specified" do
|
52
|
+
it "does not change the tags" do
|
53
|
+
expect { subject }.to_not change { PactBroker::Domain::Version.for("Foo", "1234").tags }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when tags are specified" do
|
58
|
+
let(:version_hash) { { branch: "new-branch", tags: [ { name: "main" }] } }
|
59
|
+
|
60
|
+
it "overwrites the tags" do
|
61
|
+
expect(response_body[:_embedded][:tags].size).to eq 1
|
62
|
+
expect(response_body[:_embedded][:tags].first[:name]).to eq "main"
|
63
|
+
end
|
38
64
|
end
|
39
65
|
|
40
66
|
it "does not change the created date" do
|