class PactBroker::Matrix::Repository

Constants

GROUP_BY_PACT
GROUP_BY_PROVIDER
GROUP_BY_PROVIDER_VERSION_NUMBER
TP_COLS

TODO move latest verification logic in to database

Public Instance Methods

find(specified_selectors, options = {}) click to toggle source

Return the latest matrix row (pact/verification) for each consumer_version_number/provider_version_number

# File lib/pact_broker/matrix/repository.rb, line 60
def find specified_selectors, options = {}
  resolved_ignore_selectors = resolve_ignore_selectors(options)
  resolved_specified_selectors = resolve_versions_and_add_ids(specified_selectors, :specified, resolved_ignore_selectors)
  integrations = find_integrations_for_specified_selectors(resolved_specified_selectors, options)
  resolved_selectors = add_any_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
  considered_rows, ignored_rows = find_considered_and_ignored_rows(resolved_selectors, resolved_ignore_selectors, options)
  QueryResults.new(considered_rows.sort, ignored_rows.sort, specified_selectors, options, resolved_selectors, resolved_ignore_selectors, integrations)
end
find_for_consumer_and_provider(pacticipant_1_name, pacticipant_2_name) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 69
def find_for_consumer_and_provider pacticipant_1_name, pacticipant_2_name
  selectors = [ UnresolvedSelector.new(pacticipant_name: pacticipant_1_name), UnresolvedSelector.new(pacticipant_name: pacticipant_2_name)]
  options = { latestby: "cvpv" }
  find(selectors, options)
end
find_ids_for_pacticipant_names(params) click to toggle source

rubocop: disable Metrics/CyclomaticComplexity

# File lib/pact_broker/matrix/repository.rb, line 34
def find_ids_for_pacticipant_names params
  criteria  = {}

  if params[:consumer_name] || params[:provider_name]
    if params[:consumer_name]
      pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:consumer_name])).single_record
      criteria[:consumer_id] = pacticipant.id if pacticipant
    end

    if params[:provider_name]
      pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:provider_name])).single_record
      criteria[:provider_id] = pacticipant.id if pacticipant
    end
  end

  if params[:pacticipant_name]
    pacticipant = PactBroker::Domain::Pacticipant.where(name_like(:name, params[:pacticipant_name])).single_record
    criteria[:pacticipant_id] = pacticipant.id if pacticipant
  end

  criteria[:tag_name] = params[:tag_name] if params[:tag_name].is_a?(String) # Could be a sym from resource parameters in api.rb
  criteria
end

Private Instance Methods

add_any_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 155
def add_any_inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
  if infer_selectors_for_integrations?(options)
    resolved_specified_selectors + inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
  else
    resolved_specified_selectors
  end
end
apply_latestby(options, lines) click to toggle source

rubocop: disable Metrics/CyclomaticComplexity It would be nicer to do this in the SQL, but it requires time that I don't have at the moment

# File lib/pact_broker/matrix/repository.rb, line 183
def apply_latestby options, lines
  return lines unless options[:latestby]
  group_by_columns = case options[:latestby]
                     when "cvpv" then GROUP_BY_PROVIDER_VERSION_NUMBER
                     when "cvp" then GROUP_BY_PROVIDER
                     when "cp" then GROUP_BY_PACT
                     end

  # The group with the nil provider_version_numbers will be the results of the left outer join
  # that don't have verifications, so we need to include them all.
  lines.group_by{|line| group_by_columns.collect{|key| line.send(key) }}
    .values
    .collect{ | line | line.first.provider_version_number.nil? ? line : line.sort_by(&:provider_version_order).last }
    .flatten
end
apply_success_filter(rows_with_latest_by_applied, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 163
def apply_success_filter(rows_with_latest_by_applied, options)
  # This needs to be done after the latestby, so can't be done in the db unless
  # the latestby logic is moved to the db
  if options.key?(:success)
    rows_with_latest_by_applied.select{ |l| options[:success].include?(l.success) }
   else
     rows_with_latest_by_applied
  end
end
base_model(options = {}) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 209
def base_model(options = {})
  options[:latestby] ? QuickRow : EveryRow
end
base_model_for_integrations() click to toggle source

To make it easy for pf to override

# File lib/pact_broker/matrix/repository.rb, line 218
def base_model_for_integrations
  QuickRow
end
build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selectors, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 278
def build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selectors, options)
  selectors = inferred_pacticipant_names.collect do | pacticipant_name |
    selector = UnresolvedSelector.new(pacticipant_name: pacticipant_name)
    selector.tag = options[:tag] if options[:tag]
    selector.branch = options[:branch] if options[:branch]
    selector.latest = options[:latest] if options[:latest]
    selector.environment_name = options[:environment_name] if options[:environment_name]
    selector
  end
  resolve_versions_and_add_ids(selectors, :inferred, resolved_ignore_selectors)
end
build_resolved_selectors(pacticipant, versions, unresolved_selector, selector_type, resolved_ignore_selectors) click to toggle source

When a single selector specifies multiple versions (eg. “all prod pacts”), this expands the single selector into one selector for each version.

# File lib/pact_broker/matrix/repository.rb, line 250
def build_resolved_selectors(pacticipant, versions, unresolved_selector, selector_type, resolved_ignore_selectors)
  if versions
    one_of_many = versions.compact.size > 1
    versions.collect do | version |
      if version
        selector_for_found_version(pacticipant, version, unresolved_selector, selector_type, one_of_many, resolved_ignore_selectors)
      else
        selector_for_non_existing_version(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
      end
    end
  else
    selector_for_all_versions_of_a_pacticipant(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
  end
end
deduplicate_integrations(integrations) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 148
def deduplicate_integrations(integrations)
  integrations
    .group_by{ | integration| [integration.consumer_id, integration.provider_id] }
    .values
    .collect { | duplicate_integrations | duplicate_integrations.find(&:required?) || duplicate_integrations.first }
end
find_considered_and_ignored_rows(resolved_selectors, resolved_ignore_selectors, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 77
def find_considered_and_ignored_rows(resolved_selectors, resolved_ignore_selectors, options)
  rows = query_matrix(resolved_selectors, options)
  rows = apply_latestby(options, rows)
  rows = apply_success_filter(rows, options)
  considered_rows, ignored_rows = split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
  return considered_rows, ignored_rows
end
find_integrations_for_specified_selectors(resolved_specified_selectors, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 85
def find_integrations_for_specified_selectors(resolved_specified_selectors, options)
  if infer_selectors_for_integrations?(options)
    find_integrations_for_specified_selectors_with_inferred_integrations(resolved_specified_selectors, options)
  else
    find_integrations_for_specified_selectors_only(resolved_specified_selectors)
  end
end
find_integrations_for_specified_selectors_only(resolved_specified_selectors) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 93
def find_integrations_for_specified_selectors_only(resolved_specified_selectors)
  specified_pacticipant_names = resolved_specified_selectors.collect(&:pacticipant_name)
  base_model_for_integrations
    .distinct_integrations(resolved_specified_selectors, false)
    .collect(&:to_hash)
    .collect do | integration_hash |
      required = is_a_row_for_this_integration_required?(specified_pacticipant_names, integration_hash[:consumer_name])
      Integration.from_hash(integration_hash.merge(required: required))
    end
end
find_integrations_for_specified_selectors_with_inferred_integrations(resolved_specified_selectors, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 104
def find_integrations_for_specified_selectors_with_inferred_integrations(resolved_specified_selectors, options)
  integrations = integrations_where_specified_selector_is_consumer(resolved_specified_selectors) +
                  integrations_where_specified_selector_is_provider(resolved_specified_selectors, options)
  deduplicate_integrations(integrations)
end
find_versions_for_selector(selector) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 235
def find_versions_for_selector(selector)
  # For selectors that just set the pacticipant name, there's no need to resolve the version -
  # only the pacticipant ID will be used in the query
  return nil if selector.all_for_pacticipant?
  versions = version_repository.find_versions_for_selector(selector)

  if selector.latest
    [versions.first]
  else
    versions.empty? ? [nil] : versions
  end
end
ignore_row?(resolved_ignore_selectors, row) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 337
def ignore_row?(resolved_ignore_selectors, row)
  resolved_ignore_selectors.any? do | s |
    s.pacticipant_id == row.consumer_id  && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.consumer_version_id) ||
      s.pacticipant_id == row.provider_id  && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == row.provider_version_id)
  end
end
infer_selectors_for_integrations?(options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 265
def infer_selectors_for_integrations?(options)
  options[:latest] || options[:tag] || options[:branch] || options[:environment_name]
end
inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options) click to toggle source

When only one selector is specified, (eg. checking to see if Foo version 2 can be deployed to prod), need to look up all integrated pacticipants, and determine their relevant (eg. latest prod) versions

# File lib/pact_broker/matrix/repository.rb, line 271
def inferred_selectors(resolved_specified_selectors, resolved_ignore_selectors, integrations, options)
  all_pacticipant_names = integrations.collect(&:pacticipant_names).flatten.uniq
  specified_names = resolved_specified_selectors.collect{ |s| s[:pacticipant_name] }
  inferred_pacticipant_names = all_pacticipant_names - specified_names
  build_inferred_selectors(inferred_pacticipant_names, resolved_ignore_selectors, options)
end
integrations_where_specified_selector_is_consumer(resolved_specified_selectors) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 110
def integrations_where_specified_selector_is_consumer(resolved_specified_selectors)
  resolved_specified_selectors.flat_map do | selector |
    base_model_for_integrations
      .distinct_integrations_for_selector_as_consumer(selector)
      .all
      .collect do | integration |
        Integration.from_hash(
          consumer_id: integration[:consumer_id],
          consumer_name: integration[:consumer_name],
          provider_id: integration[:provider_id],
          provider_name: integration[:provider_name],
          required: true
        )
      end
  end
end
integrations_where_specified_selector_is_provider(resolved_specified_selectors, options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 127
def integrations_where_specified_selector_is_provider(resolved_specified_selectors, options)
  integrations_involving_specified_providers = PactBroker::Integrations::Integration
                                                .where(provider_id: resolved_specified_selectors.collect(&:pacticipant_id))
                                                .eager(:consumer)
                                                .all

  destination_selector = PactBroker::Matrix::UnresolvedSelector.new(options.slice(:latest, :tag, :branch, :environment_name).compact)

  integrations_involving_specified_providers.collect do | integration |
    required = PactBroker::Domain::Version.where(pacticipant: integration.consumer).for_selector(destination_selector).any?

    Integration.from_hash(
      consumer_id: integration.consumer.id,
      consumer_name: integration.consumer.name,
      provider_id: integration.provider.id,
      provider_name: integration.provider.name,
      required: required
    )
  end
end
is_a_row_for_this_integration_required?(specified_pacticipant_names, consumer_name) click to toggle source

If a specified pacticipant is a consumer, then its provider is required to be deployed to the same environment before the consumer can be deployed. If a specified pacticipant is a provider only, then it may be deployed without the consumer being present.

# File lib/pact_broker/matrix/repository.rb, line 177
def is_a_row_for_this_integration_required?(specified_pacticipant_names, consumer_name)
  specified_pacticipant_names.include?(consumer_name)
end
query_matrix(selectors, options) click to toggle source

rubocop: enable Metrics/CyclomaticComplexity

# File lib/pact_broker/matrix/repository.rb, line 200
def query_matrix selectors, options
  query = base_model(options).select_all_columns
            .matching_selectors(selectors)
            .order_by_last_action_date

  query = query.limit(options[:limit]) if options[:limit]
  query.eager_all_the_things.all
end
resolve_ignore_selectors(options) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 213
def resolve_ignore_selectors(options)
  resolve_versions_and_add_ids(options.fetch(:ignore_selectors, []), :ignored)
end
resolve_versions_and_add_ids(unresolved_selectors, selector_type, resolved_ignore_selectors = []) click to toggle source

Find the version number for selectors with the latest and/or tag specified

# File lib/pact_broker/matrix/repository.rb, line 223
def resolve_versions_and_add_ids(unresolved_selectors, selector_type, resolved_ignore_selectors = [])
  unresolved_selectors.collect do | unresolved_selector |
    pacticipant = PactBroker::Domain::Pacticipant.find(name: unresolved_selector.pacticipant_name)
    if pacticipant
      versions = find_versions_for_selector(unresolved_selector)
      build_resolved_selectors(pacticipant, versions, unresolved_selector, selector_type, resolved_ignore_selectors)
    else
      selector_for_non_existing_pacticipant(unresolved_selector, selector_type)
    end
  end.flatten
end
selector_for_all_versions_of_a_pacticipant(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors) click to toggle source

rubocop: enable Metrics/ParameterLists

# File lib/pact_broker/matrix/repository.rb, line 306
def selector_for_all_versions_of_a_pacticipant(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
  # Doesn't make sense to ignore this, as you can't have a can-i-deploy query
  # for "all versions of a pacticipant". But whatever.
  ignore = resolved_ignore_selectors.any? do | s |
    s.pacticipant_id == pacticipant.id && s.only_pacticipant_name_specified?
  end
  ResolvedSelector.for_pacticipant(pacticipant, unresolved_selector, selector_type, ignore)
end
selector_for_found_version(pacticipant, version, unresolved_selector, selector_type, one_of_many, resolved_ignore_selectors) click to toggle source

rubocop: disable Metrics/ParameterLists

# File lib/pact_broker/matrix/repository.rb, line 298
def selector_for_found_version(pacticipant, version, unresolved_selector, selector_type, one_of_many, resolved_ignore_selectors)
  ignore = resolved_ignore_selectors.any? do | s |
    s.pacticipant_id == pacticipant.id && (s.only_pacticipant_name_specified? || s.pacticipant_version_id == version.id)
  end
  ResolvedSelector.for_pacticipant_and_version(pacticipant, version, unresolved_selector, selector_type, ignore, one_of_many)
end
selector_for_non_existing_pacticipant(unresolved_selector, selector_type) click to toggle source

only relevant for ignore selectors, validation stops this happening for the normal selectors

# File lib/pact_broker/matrix/repository.rb, line 317
def selector_for_non_existing_pacticipant(unresolved_selector, selector_type)
  ResolvedSelector.for_non_existing_pacticipant(unresolved_selector, selector_type, false)
end
selector_for_non_existing_version(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 290
def selector_for_non_existing_version(pacticipant, unresolved_selector, selector_type, resolved_ignore_selectors)
  ignore = resolved_ignore_selectors.any? do | s |
    s.pacticipant_id == pacticipant.id && s.only_pacticipant_name_specified?
  end
  ResolvedSelector.for_pacticipant_and_non_existing_version(pacticipant, unresolved_selector, selector_type, ignore)
end
split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors) click to toggle source
# File lib/pact_broker/matrix/repository.rb, line 321
def split_rows_into_considered_and_ignored(rows, resolved_ignore_selectors)
  if resolved_ignore_selectors.any?
    considered, ignored = [], []
    rows.each do | row |
      if ignore_row?(resolved_ignore_selectors, row)
        ignored << row
      else
        considered << row
      end
    end
    return considered, ignored
  else
    return rows, []
  end
end