require "forwardable" require "pact_broker/dataset" require "pact_broker/domain/pact" require "pact_broker/pacts/pact_version" require "pact_broker/integrations/integration" require "pact_broker/tags/head_pact_tags" require "pact_broker/pacts/pact_publication_dataset_module" require "pact_broker/pacts/pact_publication_wip_dataset_module" require "pact_broker/pacts/eager_loaders" require "pact_broker/pacts/lazy_loaders" require "pact_broker/pacts/pact_publication_clean_selector_dataset_module" require "pact_broker/pacts/head_pact" module PactBroker module Pacts class PactPublication < Sequel::Model(:pact_publications) extend Forwardable delegate [:consumer_version_number, :name, :provider_name, :consumer_name] => :cached_domain_for_delegation set_primary_key :id associate(:many_to_one, :provider, :class => "PactBroker::Domain::Pacticipant", :key => :provider_id, :primary_key => :id) associate(:many_to_one, :consumer, :class => "PactBroker::Domain::Pacticipant", :key => :consumer_id, :primary_key => :id) associate(:many_to_one, :consumer_version, :class => "PactBroker::Domain::Version", :key => :consumer_version_id, :primary_key => :id) associate(:many_to_one, :pact_version, class: "PactBroker::Pacts::PactVersion", :key => :pact_version_id, :primary_key => :id) associate(:many_to_one, :integration, class: "PactBroker::Integrations::Integration", key: [:consumer_id, :provider_id], primary_key: [:consumer_id, :provider_id], read_only: true, forbid_lazy_load: false) # TODO rename to consumer_version_tags associate(:one_to_many, :tags, :class => "PactBroker::Domain::Tag", :key => :version_id, :primary_key => :consumer_version_id) many_to_one(:latest_verification_for_consumer_branches, class: "PactBroker::Domain::Verification", read_only: true, key: :id, primary_key: :id, forbid_lazy_load: false, dataset: PactBroker::Pacts::LazyLoaders::LATEST_VERIFICATION_FOR_CONSUMER_BRANCHES, eager_loader: proc do | _ | raise NotImplementedError end ) one_to_many(:head_pact_publications_for_tags, class: PactPublication, read_only: true, dataset: PactBroker::Pacts::LazyLoaders::HEAD_PACT_PUBLICATIONS_FOR_TAGS, eager_loader: PactBroker::Pacts::EagerLoaders::HeadPactPublicationsForTags, forbid_lazy_load: false ) plugin :upsert, identifying_columns: [:consumer_version_id, :provider_id, :revision_number] plugin :timestamps, update_on_create: true dataset_module do include PactBroker::Dataset include PactPublicationDatasetModule include PactPublicationCleanSelectorDatasetModule include PactPublicationWipDatasetModule def eager_for_domain_with_content eager(:tags, :consumer, :provider, :pact_version, { consumer_version: :branch_versions }) end end def with_version_branches_and_tags consumer_version.tags consumer_version.branch_versions self end def self.subtract(a, *b) b_ids = b.flat_map{ |pact_publications| pact_publications.collect(&:id) } a.reject{ |pact_publication| b_ids.include?(pact_publication.id) } end def before_create super self.revision_number ||= 1 end def head_pact_tags consumer_version.tags.select{ |tag| head_tag_names.include?(tag.name) } end # The names of the tags for which this pact is the latest pact with that tag # (ie. it is not necessarily the pact for the latest consumer version with the given tag) def head_tag_names @head_tag_names ||= head_pact_publications_for_tags .select { |head_pact_publication| head_pact_publication.id == id } .collect { | head_pact_publication| head_pact_publication.values.fetch(:tag_name) } end # If the consumer_version is already loaded, and it already has tags, use those # otherwise, load them directly. def consumer_version_tags if associations[:consumer_version] && associations[:consumer_version].associations[:tags] consumer_version.tags else tags end end def latest_verification pact_version.latest_verification end def latest_main_branch_verification pact_version.latest_main_branch_verification end def latest_for_branch? if !defined?(@latest_for_branch) if consumer_version.branch_versions.empty? @latest_for_branch = nil else self_order = self.consumer_version.order @latest_for_branch = consumer_version.branch_versions.any? do | branch_version | branch_versions_join = { Sequel[:cv][:id] => Sequel[:branch_versions][:version_id], Sequel[:branch_versions][:branch_name] => branch_version.branch_name } PactPublication.where(consumer_id: consumer_id, provider_id: provider_id) .join_consumer_versions(:cv) do Sequel[:cv][:order] > self_order end .join(:branch_versions, branch_versions_join) .empty? end end end @latest_for_branch end # rubocop:disable Metrics/CyclomaticComplexity def to_domain attributes = { id: id, provider: provider, consumer: consumer, consumer_version_number: consumer_version.number, consumer_version: to_version_domain, revision_number: revision_number, json_content: pact_version.content, pact_version_sha: pact_version.sha, latest_verification: pact_version.latest_verification, created_at: created_at, head_tag_names: [], db_model: self } if associations[:tags] || consumer_version.associations[:tags] attributes[:consumer_version_tag_names] = associations[:tags]&.collect(&:name) || consumer_version.associations[:tags]&.collect(&:name) end if consumer_version.associations[:branch_versions] attributes[:consumer_version_branch_names] = consumer_version.branch_versions.collect(&:branch_name) end PactBroker::Domain::Pact.new(attributes) end # rubocop:enable Metrics/CyclomaticComplexity def to_domain_lightweight PactBroker::Domain::Pact.new( id: id, provider: provider, consumer: consumer, consumer_version_number: consumer_version.number, consumer_version: to_version_domain_lightweight, revision_number: revision_number, pact_version_sha: pact_version.sha, created_at: created_at, db_model: self ) end # Think we really could just use the version here. def to_version_domain consumer_version end def to_version_domain_lightweight consumer_version end def to_domain_with_content to_domain end def to_head_pact HeadPact.new(to_domain, consumer_version.number, values[:tag_name]) end def pact_version_sha pact_version.sha 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 def cached_domain_for_delegation @domain_object ||= to_domain end end end end # Table: pact_publications # Columns: # id | integer | PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY # consumer_version_id | integer | NOT NULL # provider_id | integer | NOT NULL # revision_number | integer | NOT NULL # pact_version_id | integer | NOT NULL # created_at | timestamp without time zone | NOT NULL # consumer_id | integer | # consumer_version_order | integer | # Indexes: # pact_publications_pkey | PRIMARY KEY btree (id) # cv_prov_revision_unq | UNIQUE btree (consumer_version_id, provider_id, revision_number) # cv_prov_id_ndx | btree (consumer_version_id, provider_id, id) # pact_publications_cid_pid_cvo_index | btree (consumer_id, provider_id, consumer_version_order) # pact_publications_consumer_id_index | btree (consumer_id) # Foreign key constraints: # pact_publications_consumer_id_fkey | (consumer_id) REFERENCES pacticipants(id) # pact_publications_consumer_version_id_fkey | (consumer_version_id) REFERENCES versions(id) # pact_publications_pact_version_id_fkey | (pact_version_id) REFERENCES pact_versions(id) # pact_publications_provider_id_fkey | (provider_id) REFERENCES pacticipants(id) # Referenced By: # latest_pact_publication_ids_for_consumer_versions | latest_pact_publication_ids_for_consum_pact_publication_id_fkey | (pact_publication_id) REFERENCES pact_publications(id) ON DELETE CASCADE # triggered_webhooks | triggered_webhooks_pact_publication_id_fkey | (pact_publication_id) REFERENCES pact_publications(id) # webhook_executions | webhook_executions_pact_publication_id_fkey | (pact_publication_id) REFERENCES pact_publications(id)