diff --git a/lib/pact_broker/db/clean_incremental.rb b/lib/pact_broker/db/clean_incremental.rb index eb50920f6..dde02ba1d 100644 --- a/lib/pact_broker/db/clean_incremental.rb +++ b/lib/pact_broker/db/clean_incremental.rb @@ -43,35 +43,20 @@ def version_ids_to_delete end def version_ids_to_keep - @version_ids_to_keep ||= keep.collect do | selector | + @version_ids_to_keep ||= selected_versions_to_keep.reduce(&:union) + end + + def selected_versions_to_keep + keep.collect do | selector | PactBroker::Domain::Version.select(:id).for_selector(selector) - end.reduce(&:union) + end end def call require 'pact_broker/db/models' if dry_run? - to_delete = PactBroker::Domain::Version - .where(id: version_ids_to_delete.select(:id)) - .all - .group_by{ | v | v.pacticipant_id } - .each_with_object({}) do | (pacticipant_id, versions), thing | - thing[versions.first.pacticipant.name] = { - count: versions.count, - from_version: { - number: versions.first.number, - created: DateHelper.distance_of_time_in_words(versions.first.created_at, DateTime.now) + " ago", - tags: versions.first.tags.collect(&:name) - }, - to_version: { - number: versions.last.number, - created: DateHelper.distance_of_time_in_words(versions.last.created_at, DateTime.now) + " ago", - tags: versions.last.tags.collect(&:name) - } - } - end - { "to_delete" => to_delete } + dry_run_results else before_counts = current_counts result = PactBroker::Domain::Version.where(id: resolve_ids(version_ids_to_delete)).delete @@ -102,6 +87,131 @@ def delete_orphan_pact_versions referenced_pact_version_ids = db[:pact_publications].select(:pact_version_id).union(db[:verifications].select(:pact_version_id)) db[:pact_versions].where(id: referenced_pact_version_ids).invert.delete end + + def version_info(version) + { + "number" => version.number, + "created" => DateHelper.distance_of_time_in_words(version.created_at, DateTime.now) + " ago", + "tags" => version.tags.collect(&:name) + } + end + + def dry_run_results + to_delete = dry_run_to_delete + to_keep = dry_run_to_keep + + kept_per_selector = keep.collect do | selector | + { + selector: selector.to_hash, + count: PactBroker::Domain::Version.for_selector(selector).count + } + end + + pacticipant_results = pacticipants.each_with_object({}) do | pacticipant, results | + results[pacticipant.name] = { + "toDelete" => to_delete[pacticipant.name] || { "count" => 0 }, + "toKeep" => to_keep[pacticipant.id] + } + end + + total_versions_count = PactBroker::Domain::Version.count + versions_to_keep_count = version_ids_to_keep.count + versions_to_delete_count = version_ids_to_delete.count + + { + "counts" => { + "totalVersions" => total_versions_count, + "versionsToDelete" => versions_to_delete_count, + "versionsNotToKeep" => total_versions_count - versions_to_keep_count, + "versionsToKeep" => versions_to_keep_count, + "versionsToKeepBySelector" => kept_per_selector, + }, + "versionSummary" => pacticipant_results + } + end + + def dry_run_latest_versions_to_keep + latest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id)) + .invert + .select_group(:pacticipant_id) + .select_append{ max(order).as(latest_order) } + + lv_versions_join = { + Sequel[:lv][:latest_order] => Sequel[:versions][:order], + Sequel[:lv][:pacticipant_id] => Sequel[:versions][:pacticipant_id] + } + + PactBroker::Domain::Version + .select_all_qualified + .join(latest_undeleted_versions_by_order, lv_versions_join, { table_alias: :lv }) + end + + def dry_run_earliest_versions_to_keep + earliest_undeleted_versions_by_order = PactBroker::Domain::Version.where(id: version_ids_to_delete.select(:id)) + .invert + .select_group(:pacticipant_id) + .select_append{ min(order).as(first_order) } + + ev_versions_join = { + Sequel[:lv][:first_order] => Sequel[:versions][:order], + Sequel[:lv][:pacticipant_id] => Sequel[:versions][:pacticipant_id] + } + + PactBroker::Domain::Version + .select_all_qualified + .join(earliest_undeleted_versions_by_order, ev_versions_join, { table_alias: :lv }) + end + + def dry_run_to_delete + PactBroker::Domain::Version + .where(id: version_ids_to_delete.select(:id)) + .all + .group_by{ | v | v.pacticipant_id } + .each_with_object({}) do | (pacticipant_id, versions), thing | + thing[versions.first.pacticipant.name] = { + "count" => versions.count, + "fromVersion" => version_info(versions.first), + "toVersion" => version_info(versions.last) + } + end + end + + def dry_run_to_keep + latest_to_keep = dry_run_latest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r | + r[version.pacticipant_id] = { + "firstVersion" => version_info(version) + } + end + + earliest_to_keep = dry_run_earliest_versions_to_keep.eager(:tags).each_with_object({}) do | version, r | + r[version.pacticipant_id] = { + "latestVersion" => version_info(version) + } + end + + counts = counts_to_keep + + pacticipants.collect(&:id).each_with_object({}) do | pacticipant_id, results | + results[pacticipant_id] = { "count" => counts[pacticipant_id] || 0 } + .merge(earliest_to_keep[pacticipant_id] || {}) + .merge(latest_to_keep[pacticipant_id] || {}) + end + end + + def counts_to_keep + db[:versions].where(id: version_ids_to_delete.select(:id)) + .invert + .select_group(:pacticipant_id) + .select_append{ count(1).as(count) } + .all + .each_with_object({}) do | row, counts | + counts[row[:pacticipant_id]] = row[:count] + end + end + + def pacticipants + @pacticipants ||= PactBroker::Domain::Pacticipant.order_ignore_case(:name).all + end end end end diff --git a/lib/pact_broker/domain/tag.rb b/lib/pact_broker/domain/tag.rb index f1283c7db..2c9a59047 100644 --- a/lib/pact_broker/domain/tag.rb +++ b/lib/pact_broker/domain/tag.rb @@ -13,6 +13,30 @@ class Tag < Sequel::Model dataset_module do include PactBroker::Repositories::Helpers + def latest_tags + tags_versions_join = { + Sequel[:tags][:version_id] => Sequel[:versions][:id], + } + + latest_tags_versions_join = { + Sequel[:latest_tags][:name] => Sequel[:tags][:name], + Sequel[:latest_tags][:latest_order] => Sequel[:versions][:order], + Sequel[:latest_tags][:pacticipant_id] => Sequel[:versions][:pacticipant_id], + } + + latest_tags = PactBroker::Domain::Tag + .select_group(Sequel[:tags][:name], Sequel[:versions][:pacticipant_id]) + .select_append{ max(order).as(latest_order) } + .join(:versions, tags_versions_join) + + PactBroker::Domain::Tag + .select_all_qualified + .join(:versions, + { Sequel[:tags][:version_id] => Sequel[:versions][:id] } + ) + .join(latest_tags, latest_tags_versions_join, { table_alias: :latest_tags }) + end + # Does NOT care about whether or not there is a pact publication # for the version def latest_tags_for_pacticipant_ids(pacticipant_ids) diff --git a/spec/fixtures/approvals/clean_incremental_dry_run.approved.json b/spec/fixtures/approvals/clean_incremental_dry_run.approved.json new file mode 100644 index 000000000..6a2bb43ac --- /dev/null +++ b/spec/fixtures/approvals/clean_incremental_dry_run.approved.json @@ -0,0 +1,100 @@ +{ + "counts": { + "totalVersions": 9, + "versionsToDelete": 3, + "versionsNotToKeep": 6, + "versionsToKeep": 3, + "versionsToKeepBySelector": [ + { + "selector": { + "tag": "prod" + }, + "count": 2 + }, + { + "selector": { + "tag": "dev", + "latest": true + }, + "count": 1 + } + ] + }, + "versionSummary": { + "Bar": { + "toDelete": { + "count": 0 + }, + "toKeep": { + "count": 0 + } + }, + "Foo": { + "toDelete": { + "count": 3, + "fromVersion": { + "number": "2", + "created": "1 day ago", + "tags": [ + + ] + }, + "toVersion": { + "number": "6", + "created": "5 days ago", + "tags": [ + "foo" + ] + } + }, + "toKeep": { + "count": 4, + "latestVersion": { + "number": "1", + "created": "less than a minute ago", + "tags": [ + "dev", + "prod" + ] + }, + "firstVersion": { + "number": "7", + "created": "6 days ago", + "tags": [ + + ] + } + } + }, + "Meep": { + "toDelete": { + "count": 0 + }, + "toKeep": { + "count": 2, + "latestVersion": { + "number": "2", + "created": "6 days ago", + "tags": [ + "foop" + ] + }, + "firstVersion": { + "number": "3", + "created": "6 days ago", + "tags": [ + "blah" + ] + } + } + }, + "Moop": { + "toDelete": { + "count": 0 + }, + "toKeep": { + "count": 0 + } + } + } +} diff --git a/spec/lib/pact_broker/db/clean_incremental_spec.rb b/spec/lib/pact_broker/db/clean_incremental_spec.rb index 67fb04222..4d387c3cc 100644 --- a/spec/lib/pact_broker/db/clean_incremental_spec.rb +++ b/spec/lib/pact_broker/db/clean_incremental_spec.rb @@ -74,12 +74,20 @@ def pact_publication_count_for(consumer_name, version_number) context "when dry_run: true" do before do td.create_pact_with_hierarchy("Meep", "2", "Moop") + .create_consumer_version_tag("foop") + .create_consumer_version("3") + .create_consumer_version_tag("blah") end + let(:dry_run) { true } it "doesn't delete anything" do expect { subject }.to_not change { PactBroker::Domain::Version.count } end + + it "returns info on what will be deleted" do + Approvals.verify(subject, :name => 'clean_incremental_dry_run', format: :json) + end end end