Handle crm objects when moving groups
What does this MR do and why?
Related to #350327 (closed)
Issue contacts can only come from an issues root group. This MR ensures that moving a group to a new root group will delete all projects issue contacts.
It will also delete all contacts and organizations if the group was a root group (as it will no longer be after moving).
At a later stage we will look to see if we can copy the contacts to the new root group, but we need to tackle things like duplication.
To ensure performance, we have tweaked an existing index and added a new one. In a follow up we will look at batching more of the SQL, but while this remains new and behind a feature flag, we can be confident performance will not be impacted.
Migrations
lee@cc-gdk-2:~/gitlab-development-kit/gitlab$ bundle exec rake db:migrate:up VERSION=20220316112118 RAILS_ENV=development
== 20220316112118 UpdateOrganizationsNameIndexAddId: migrating ================
-- transaction_open?()
-> 0.0000s
-- indexes(:customer_relations_organizations)
-> 0.0019s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- remove_index(:customer_relations_organizations, {:algorithm=>:concurrently, :name=>"index_customer_relations_organizations_on_unique_name_per_group"})
-> 0.0018s
-- execute("RESET statement_timeout")
-> 0.0006s
-- transaction_open?()
-> 0.0000s
-- index_exists?(:customer_relations_organizations, "group_id, lower(name), id", {:name=>"index_customer_relations_organizations_on_unique_name_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0010s
-- add_index(:customer_relations_organizations, "group_id, lower(name), id", {:name=>"index_customer_relations_organizations_on_unique_name_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0025s
== 20220316112118 UpdateOrganizationsNameIndexAddId: migrated (0.0160s) =======
lee@cc-gdk-2:~/gitlab-development-kit/gitlab$ bundle exec rake db:migrate:down VERSION=20220316112118 RAILS_ENV=development
== 20220316112118 UpdateOrganizationsNameIndexAddId: reverting ================
-- transaction_open?()
-> 0.0000s
-- indexes(:customer_relations_organizations)
-> 0.0024s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- remove_index(:customer_relations_organizations, {:algorithm=>:concurrently, :name=>"index_customer_relations_organizations_on_unique_name_per_group"})
-> 0.0020s
-- execute("RESET statement_timeout")
-> 0.0006s
-- transaction_open?()
-> 0.0000s
-- index_exists?(:customer_relations_organizations, "group_id, lower(name)", {:name=>"index_customer_relations_organizations_on_unique_name_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0010s
-- add_index(:customer_relations_organizations, "group_id, lower(name)", {:name=>"index_customer_relations_organizations_on_unique_name_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0037s
== 20220316112118 UpdateOrganizationsNameIndexAddId: reverted (0.0193s) =======
lee@cc-gdk-2:~/gitlab-development-kit/gitlab$ bundle exec rake db:migrate:up VERSION=20220316112206 RAILS_ENV=development
== 20220316112206 AddContactsIndexOnGroupEmailAndId: migrating ================
-- transaction_open?()
-> 0.0000s
-- index_exists?(:customer_relations_contacts, "group_id, lower(email), id", {:name=>"index_customer_relations_contacts_on_unique_email_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0032s
-- execute("SET statement_timeout TO 0")
-> 0.0007s
-- add_index(:customer_relations_contacts, "group_id, lower(email), id", {:name=>"index_customer_relations_contacts_on_unique_email_per_group", :unique=>true, :algorithm=>:concurrently})
-> 0.0039s
-- execute("RESET statement_timeout")
-> 0.0006s
== 20220316112206 AddContactsIndexOnGroupEmailAndId: migrated (0.0137s) =======
lee@cc-gdk-2:~/gitlab-development-kit/gitlab$ bundle exec rake db:migrate:down VERSION=20220316112206 RAILS_ENV=development
== 20220316112206 AddContactsIndexOnGroupEmailAndId: reverting ================
-- transaction_open?()
-> 0.0000s
-- indexes(:customer_relations_contacts)
-> 0.0024s
-- execute("SET statement_timeout TO 0")
-> 0.0006s
-- remove_index(:customer_relations_contacts, {:algorithm=>:concurrently, :name=>"index_customer_relations_contacts_on_unique_email_per_group"})
-> 0.0024s
-- execute("RESET statement_timeout")
-> 0.0006s
== 20220316112206 AddContactsIndexOnGroupEmailAndId: reverted (0.0114s) =======
How to set up and validate locally
0. Enable :customer_relations feature flag via the rails console
1. Visit a group settings page (e.g. http://gdk.test:3000/groups/flightjs/-/edit
2. Expand "Permissions and group features"
3. At the very bottom, select "Enable customer relations"
4. Select "Save changes"
5. Repeat for another group
7. Create some contacts in both groups (customer relations -> contacts) *if you've recently setup GDK or reseeded your database, there should already be some setup! Alternatively you can run rake db:seed_fu FILTER=crm
8. Add some contacts to a few issues in a few projects using the "/add_contacts email@example.org email2@example" quick action
9. Move the group
10. If the group you moved was a root group (containing organizations and contacts): ensure they have been moved to the new root group (or deduplicated where appropriate, and the relevant IssueContacts updated)
11. If a subgroup was moved to a new hierarchy: ensure the issues no longer have contacts attached
12. If a subgroup was moved within the same hierarch: ensure the issues still have contacts attached
MR acceptance checklist
This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.
-
I have evaluated the MR acceptance checklist for this MR.