Skip to content

Commit

Permalink
feat(pacts for verification): support released:true selector (#451)
Browse files Browse the repository at this point in the history
* feat: add support for selecting currently released and supported pacts for verification

* feat: support specifying an environment in a consumer version selector without a deployed/released property

* chore: add warning when pact selector invalid keys used

* chore: rename currentlyDeployed:true selector to deployed:true, and currentlySuportedReleases to released:true

* refactor: rename pact selector environment to environment name, and add separate environment property when resolving

* chore: update verifiable pact description for pacts for currently supported versions

* feat: show released versions in matrix

* feat: show environments on index page with tags

* feat: update colours and add text to badges on index and matrix

* chore: drop unused columns from deployed_versions

* feat: add environment query box to matrix UI

* chore: update paths and relations for currently deployed version and currently supported released versions

* chore: sort selected pacts deterministically
  • Loading branch information
bethesque authored Jul 2, 2021
1 parent 5a65421 commit 8a90cc5
Show file tree
Hide file tree
Showing 41 changed files with 815 additions and 176 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
Sequel.migration do
up do
alter_table(:deployed_versions) do
drop_column(:replaced_previous_deployed_version)
drop_column(:currently_deployed)
end
end

down do
alter_table(:deployed_versions) do
add_column(:replaced_previous_deployed_version, TrueClass)
add_column(:currently_deployed, TrueClass)
end
end
end
4 changes: 2 additions & 2 deletions lib/pact_broker/api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ def self.build_api(application_context = PactBroker::ApplicationContext.default_
if PactBroker.feature_enabled?(:environments)
add ["environments"], Api::Resources::Environments, { resource_name: "environments" }
add ["environments", :environment_uuid], Api::Resources::Environment, { resource_name: "environment" }
add ["environments", :environment_uuid, "currently-deployed-versions"], Api::Resources::CurrentlyDeployedVersionsForEnvironment, { resource_name: "environment_deployed_versions" }
add ["environments", :environment_uuid, "currently-supported-versions"], Api::Resources::CurrentlySupportedVersionsForEnvironment, { resource_name: "environment_supported_versions" }
add ["environments", :environment_uuid, "deployed-versions", "currently-deployed"], Api::Resources::CurrentlyDeployedVersionsForEnvironment, { resource_name: "environment_currently_deployed_deployed_versions" }
add ["environments", :environment_uuid, "released-versions", "currently-supported"], Api::Resources::CurrentlySupportedVersionsForEnvironment, { resource_name: "environment_currently_supported_released_versions" }
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "deployed-versions", "environment", :environment_uuid], Api::Resources::DeployedVersionsForVersionAndEnvironment, { resource_name: "deployed_versions_for_version_and_environment" }
add ["pacticipants", :pacticipant_name, "versions", :pacticipant_version_number, "released-versions", "environment", :environment_uuid], Api::Resources::ReleasedVersionsForVersionAndEnvironment, { resource_name: "released_versions_for_version_and_environment" }
add ["released-versions", :uuid], Api::Resources::ReleasedVersion, { resource_name: "released_version" }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ class VerifiablePactsJSONQuerySchema
optional(:fallbackTag).filled(:str?)
optional(:fallbackBranch).filled(:str?)
optional(:consumer).filled(:str?, :not_blank?)
optional(:currentlyDeployed).filled(included_in?: [true])
optional(:deployed).filled(included_in?: [true])
optional(:released).filled(included_in?: [true])
optional(:environment).filled(:str?)

# rule(fallbackTagMustBeForLatest: [:fallbackTag, :latest]) do | fallback_tag, latest |
Expand Down Expand Up @@ -89,9 +90,10 @@ def self.validate_consumer_version_selector(selector, index)
if not_provided?(selector[:tag]) &&
not_provided?(selector[:branch]) &&
not_provided?(selector[:environment]) &&
selector[:currentlyDeployed] != true &&
selector[:deployed] != true &&
selector[:released] != true &&
selector[:latest] != true
errors << "must specify a value for environment or tag, or specify latest=true, or specify currentlyDeployed=true (at index #{index})"
errors << "must specify a value for environment or tag, or specify latest=true, or specify deployed=true or released=true (at index #{index})"
end

if selector[:tag] && selector[:branch]
Expand All @@ -110,8 +112,12 @@ def self.validate_consumer_version_selector(selector, index)
errors << "cannot specify a branch with latest=false (at index #{index})"
end

if selector[:deployed] && selector[:released]
errors << "cannot specify both deployed=true and released=true (at index #{index})"
end

non_environment_fields = selector.slice(:latest, :tag, :fallbackTag, :branch, :fallbackBranch).keys.sort
environment_related_fields = selector.slice(:environment, :currentlyDeployed).keys.sort
environment_related_fields = selector.slice(:environment, :deployed, :released).keys.sort

if (non_environment_fields.any? && environment_related_fields.any?)
errors << "cannot specify the #{pluralize("field", non_environment_fields.count)} #{non_environment_fields.join("/")} with the #{pluralize("field", environment_related_fields.count)} #{environment_related_fields.join("/")} (at index #{index})"
Expand Down
6 changes: 3 additions & 3 deletions lib/pact_broker/api/decorators/environment_decorator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,16 @@ class EnvironmentDecorator < BaseDecorator
}
end

link :'pb:currently-deployed-versions' do | user_options |
link :'pb:currently-deployed-deployed-versions' do | user_options |
{
title: "Versions currently deployed to #{represented.display_name} environment",
href: currently_deployed_versions_for_environment_url(represented, user_options.fetch(:base_url))
}
end

link :'pb:currently-supported-versions' do | user_options |
link :'pb:currently-supported-released-versions' do | user_options |
{
title: "Versions currently supported in #{represented.display_name} environment",
title: "Versions released and supported in #{represented.display_name} environment",
href: currently_supported_versions_for_environment_url(represented, user_options.fetch(:base_url))
}
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ class VerifiablePactsQueryDecorator < BaseDecorator
property :fallback_tag
property :fallback_branch
property :consumer
property :environment, setter: -> (fragment:, represented:, **) {
represented.environment = fragment
represented.currently_deployed = true
}
property :currently_deployed
property :environment_name, as: :environment
property :currently_deployed, as: :deployed
property :currently_supported, as: :released
end

property :include_pending_status, default: false,
Expand Down
4 changes: 2 additions & 2 deletions lib/pact_broker/api/pact_broker_urls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,11 @@ def deployed_versions_for_version_and_environment_url(version, environment, base
end

def currently_deployed_versions_for_environment_url(environment, base_url = "")
"#{base_url}/environments/#{environment.uuid}/currently-deployed-versions"
"#{base_url}/environments/#{environment.uuid}/deployed-versions/currently-deployed"
end

def currently_supported_versions_for_environment_url(environment, base_url = "")
"#{base_url}/environments/#{environment.uuid}/currently-supported-versions"
"#{base_url}/environments/#{environment.uuid}/released-versions/currently-supported"
end

def record_undeployment_url(deployed_version, base_url = "")
Expand Down
4 changes: 1 addition & 3 deletions lib/pact_broker/deployments/deployed_version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@

module PactBroker
module Deployments
DEPLOYED_VERSION_COLUMNS = [:id, :uuid, :version_id, :pacticipant_id, :environment_id, :target, :target_for_index, :created_at, :updated_at, :undeployed_at]
DEPLOYED_VERSION_DATASET = Sequel::Model.db[:deployed_versions].select(*DEPLOYED_VERSION_COLUMNS)
class DeployedVersion < Sequel::Model(DEPLOYED_VERSION_DATASET)
class DeployedVersion < Sequel::Model
many_to_one :version, :class => "PactBroker::Domain::Version", :key => :version_id, :primary_key => :id
many_to_one :environment, :class => "PactBroker::Deployments::Environment", :key => :environment_id, :primary_key => :id
one_to_one :currently_deployed_version_id, :class => "PactBroker::Deployments::CurrentlyDeployedVersionId", key: :deployed_version_id, primary_key: :id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ Returns a deduplicated list of pacts to be verified by the specified provider.

### Body

Example: This data structure represents the way a user might specify "I want to verify the latest 'main' pact, all the pacts for the consumer versionst that are currently deployed, and when I publish the verification results, the provider version will be be on the "main" branch.
Example: This data structure represents the way a user might specify "I want to verify the latest 'main' pact, all the pacts for the consumer versions that are currently deployed, and when I publish the verification results, the provider version will be be on the "main" branch.

{
"consumerVersionSelectors": [
{
"branch": "main"
},
{
"currentlyDeployed": true
"deployed": true
}
],
"providerVersionBranch": "main",
Expand All @@ -30,9 +30,11 @@ Example: This data structure represents the way a user might specify "I want to

`consumerVersionSelectors.fallbackBranch`: the name of the branch to fallback to if the specified `branch` does not exist. This is useful when the consumer and provider use matching branch names to coordinate the development of new features.

`consumerVersionSelectors.currentlyDeployed`: 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.
`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.

`consumerVersionSelectors.environment`: the name of the environment containing the consumer versions for which to return the pacts. Used to further qualify `{ "currentlyDeployed": true }`. Normally, this would not be needed, as it is recommended to verify the pacts for all currently deployed versions. If the `environment` is set, `currentlyDeployed` must be set to `true`, or the key ommitted (in which case it will be inferred to be `true`).
`consumerVersionSelectors.released`: if the key is specified, can only be set to `true`. Returns the pacts for all versions of the consumer that are released and currently supported in 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-release` CLI.

`consumerVersionSelectors.environment`: the name of the environment containing the consumer versions for which to return the pacts. Used to further qualify `{ "deployed": true }` or `{ "released": true }`. Normally, this would not be needed, as it is recommended to verify the pacts for all currently deployed/currently supported released versions.

`consumerVersionSelectors.latest`: true. Used in conjuction with the `tag` and `branch` properties. When used with a `branch`, it may be `true` or the key ommitted (in which case it will be inferred to be `true`). This is because it only makes sense to verify the latest pact for a branch. If a `tag` is specified, and `latest` is `true`, then the latest pact for each of the consumers with that tag will be returned. If a `tag` is specified and the latest flag is *not* set to `true`, *all* the pacts with the specified tag will be returned. (This might seem a bit weird, but it's done this way to match the syntax used for the matrix query params. See https://docs.pact.io/selectors).

Expand Down
19 changes: 17 additions & 2 deletions lib/pact_broker/domain/index_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def consumer_version_branch
end

def consumer_version_environment_names
consumer_version.current_deployed_versions.collect(&:environment).collect(&:name)
(consumer_version.current_deployed_versions.collect(&:environment).collect(&:name) + consumer_version.current_supported_released_versions.collect(&:environment).collect(&:name)).uniq
end

def latest_for_branch?
Expand All @@ -97,7 +97,12 @@ def provider_version_branch
end

def provider_version_environment_names
provider_version&.current_deployed_versions&.collect(&:environment)&.collect(&:name) || []
if provider_version
(provider_deployed_environment_names + provider_released_environment_names).uniq
else
[]
end

end

# these are the consumer tag names for which this pact publication
Expand Down Expand Up @@ -187,6 +192,16 @@ def to_a
def last_activity_date
@last_activity_date ||= [latest_pact.created_at, latest_verification ? latest_verification.execution_date : nil].compact.max
end

private

def provider_deployed_environment_names
provider_version.current_deployed_versions.collect(&:environment)&.collect(&:name)
end

def provider_released_environment_names
provider_version.current_supported_released_versions.collect(&:environment)&.collect(&:name)
end
end
end
end
6 changes: 6 additions & 0 deletions lib/pact_broker/domain/pact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,12 @@ def pending?
!pact_version.verified_successfully_by_any_provider_version?
end

def <=> other
self_fields = [consumer_name.downcase, provider_name.downcase, consumer_version.order || 0]
other_fields = [other.consumer_name.downcase, other.provider_name.downcase, other.consumer_version.order || 0]
self_fields <=> other_fields
end

private

attr_accessor :db_model
Expand Down
15 changes: 14 additions & 1 deletion lib/pact_broker/domain/version.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class Version < Sequel::Model
one_to_many :current_deployed_versions, class: "PactBroker::Deployments::DeployedVersion", key: :version_id, primary_key: :id, order: [:created_at, :id] do | ds |
ds.currently_deployed
end
one_to_many :current_supported_released_versions, class: "PactBroker::Deployments::ReleasedVersion", key: :version_id, primary_key: :id, order: [:created_at, :id] do | ds |
ds.currently_supported
end

one_to_many :deployed_versions, class: "PactBroker::Deployments::DeployedVersion", key: :version_id, primary_key: :id, order: [:created_at, :id]

Expand Down Expand Up @@ -88,12 +91,22 @@ def where_pacticipant_name(pacticipant_name)
# end
end

def currently_in_environment(environment_name, pacticipant_name)
currently_deployed_to_environment(environment_name, pacticipant_name).union(currently_supported_in_environment(environment_name, pacticipant_name))
end

def currently_deployed_to_environment(environment_name, pacticipant_name)
deployed_version_query = PactBroker::Deployments::DeployedVersion.currently_deployed.for_environment_name(environment_name)
deployed_version_query = deployed_version_query.for_pacticipant_name(pacticipant_name) if pacticipant_name
where(id: deployed_version_query.select(:version_id))
end

def currently_supported_in_environment(environment_name, pacticipant_name)
supported_version_query = PactBroker::Deployments::ReleasedVersion.currently_supported.for_environment_name(environment_name)
supported_version_query = supported_version_query.for_pacticipant_name(pacticipant_name) if pacticipant_name
where(id: supported_version_query.select(:version_id))
end

def where_tag(tag)
if tag == true
join(:tags, Sequel[:tags][:version_id] => Sequel[first_source_alias][:id])
Expand Down Expand Up @@ -139,7 +152,7 @@ def delete
def for_selector(selector)
query = self
query = query.where_pacticipant_name(selector.pacticipant_name) if selector.pacticipant_name
query = query.currently_deployed_to_environment(selector.environment_name, selector.pacticipant_name) if selector.environment_name
query = query.currently_in_environment(selector.environment_name, selector.pacticipant_name) if selector.environment_name
query = query.where_tag(selector.tag) if selector.tag
query = query.where_branch(selector.branch) if selector.branch
query = query.where_number(selector.pacticipant_version_number) if selector.pacticipant_version_number
Expand Down
10 changes: 5 additions & 5 deletions lib/pact_broker/index/service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ def self.find_index_items options = {}
.eager(:provider)
.eager(:pact_version)
.eager(integration: [{latest_verification: :provider_version}, :latest_triggered_webhooks])
.eager(consumer_version: [{ current_deployed_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }])
.eager(latest_verification: { provider_version: [{ current_deployed_versions: :environment }, :latest_version_for_branch, { tags: :head_tag } ] })
.eager(consumer_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }])
.eager(latest_verification: { provider_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :latest_version_for_branch, { tags: :head_tag } ] })
.eager(:head_pact_publications_for_tags)

index_items = pact_publications.all.collect do | pact_publication |
Expand Down Expand Up @@ -108,8 +108,8 @@ def self.find_index_items_for_api(consumer_name: nil, provider_name: nil, **_ign
.eager(:consumer)
.eager(:provider)
.eager(:pact_version)
.eager(consumer_version: [{ current_deployed_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }])
.eager(latest_verification: { provider_version: [{ current_deployed_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }]})
.eager(consumer_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }])
.eager(latest_verification: { provider_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }, :latest_version_for_branch, { tags: :head_tag }]})
.eager(:head_pact_publications_for_tags)

pact_publications.all.collect do | pact_publication |
Expand Down Expand Up @@ -188,7 +188,7 @@ def self.latest_verifications_for_consumer_version_tags(options)
.all
elsif options[:tags] # server side rendered index page with tags=true
PactBroker::Verifications::LatestVerificationForConsumerVersionTag
.eager(provider_version: { current_deployed_versions: :environment })
.eager(provider_version: [{ current_deployed_versions: :environment }, { current_supported_released_versions: :environment }])
.all
else
nil # should not be used
Expand Down
1 change: 1 addition & 0 deletions lib/pact_broker/matrix/parse_query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ def self.parse_selector(i)
p.latest = true if i["latest"] == "true"
p.branch = i["branch"] if i["branch"] && i["branch"] != ""
p.tag = i["tag"] if i["tag"] && i["tag"] != ""
p.environment_name = i["environment"] if i["environment"] && i["environment"] != ""
p
end
# rubocop: enable Metrics/CyclomaticComplexity
Expand Down
3 changes: 2 additions & 1 deletion lib/pact_broker/pacts/pact_publication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,8 @@ def to_version_domain_lightweight
pacticipant: consumer,
order: consumer_version.order,
branch: consumer_version.branch,
current_deployed_versions: consumer_version.associations[:current_deployed_versions]
current_deployed_versions: consumer_version.associations[:current_deployed_versions],
current_supported_released_versions: consumer_version.associations[:current_supported_released_versions],
)
end

Expand Down
Loading

0 comments on commit 8a90cc5

Please sign in to comment.