Project Import: calculate `UNIQUE_RELATIONS` instead of keeping an explicit list in the code

UNIQUE_RELATIONS is an edge case in our Import/RelationFactory, which could be integrated into GroupProjectObjectBuilder or a separate object.

Being an edge case, it's extremely error-prone, as it is completely undiscoverable for the person who adds a relation from the product side.

This implicitness leads to new relations having a unique index on project_id or project_id as a primary key not being imported which is a bug.

The idea is to drop an explicit UNIQUE_RELATIONS list from the codebase in favor of calculating it in place.

Please check the discussion for the additional info:

The following discussion from !20174 (merged) should be addressed:

  • @georgekoltsov started a discussion: (+9 comments)

    Looks like both ci_cd_settings and project_feature relations are not being imported, but rather skipped, since by the time we try to import, these settings already exists (because of after_save/create callbacks on project creation).

    There might be a need for a new object builder specific for these types of cases.

    Just a quick note on purpose of GroupProjectObjectBuilder. This might be known information, but wanted to reiterate anyway. Main purpose of this class is to instead of always building a new object, we attempt to find and reuse already created ones, to avoid situations of duplicate records of relations like Milestone, Label. For example, if 2 issues have 1 label applied, this class will reuse and return this label, instead of creating a duplicate. Similar to milestones, we want to link to existing milestones instead of creating new ones.

    In this case it looks like what we want to do is to either completely recreate a record (i.e. destroy & create) or update attributes of an existing record. Current functionality does not provide this afaik (might be wrong). So there might be a need in new logic, given that a relation exists in UNIQUE_RELATIONS - we destroy that record & create new one, or update attributes of existing one - I am not sure if there are any validations that would prevent us from deleting that record.

    I know that ultimately we want to get rid of UNIQUE_RELATIONS. In this case we could reflect on association, and if it's a has_one relation, destroy & re-create / update it's attributes.

    e.g.

    if @relation_class.reflect_on_association(:project) && Project.reflect_on_association(relation_sym).macro == :has_one
       @relation_class.find_by_project_id(@project.id).destroy
       object = @relation_class.new(inheritance_attributes)
       object.assign_attributes(parsed_relation_hash)
       object 
    end

    I did not test this but perhaps something like this is worth considering. Hope this helps

Edited by Aleksei Lipniagov