Welcome to mirror list, hosted at ThFree Co, Russian Federation.

object_builder.rb « project « import_export « gitlab « lib - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: b03dceba303118bdd070c439a0677d288b2f275c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# frozen_string_literal: true

module Gitlab
  module ImportExport
    module Project
      # Given a class, it finds or creates a new object
      # (initializes in the case of Label) at group or project level.
      # If it does not exist in the group, it creates it at project level.
      #
      # Example:
      #   `ObjectBuilder.build(Label, label_attributes)`
      #    finds or initializes a label with the given attributes.
      #
      # It also adds some logic around Group Labels/Milestones for edge cases.
      class ObjectBuilder < Base::ObjectBuilder
        def self.build(*args)
          ::Project.transaction do
            super
          end
        end

        def initialize(klass, attributes)
          super

          @group = @attributes['group']
          @project = @attributes['project']
        end

        def find
          return if epic? && group.nil?
          return find_diff_commit_user if diff_commit_user?

          super
        end

        private

        attr_reader :group, :project

        def where_clauses
          [
            where_clause_base,
            where_clause_for_title,
            where_clause_for_klass
          ].compact
        end

        # Returns Arel clause `"{table_name}"."project_id" = {project.id}` if project is present
        # For example: merge_request has :target_project_id, and we are searching by :iid
        # or, if group is present:
        # `"{table_name}"."project_id" = {project.id} OR "{table_name}"."group_id" = {group.id}`
        def where_clause_base
          [].tap do |clauses|
            clauses << table[:project_id].eq(project.id) if project
            clauses << table[:group_id].in(group.self_and_ancestors_ids) if group
          end.reduce(:or)
        end

        # Returns Arel clause for a particular model or `nil`.
        def where_clause_for_klass
          return attrs_to_arel(attributes.slice('filename')).and(table[:issue_id].eq(nil)) if design?

          attrs_to_arel(attributes.slice('iid')) if merge_request?
        end

        def prepare_attributes
          attributes.dup.tap do |atts|
            atts.delete('group') unless epic?

            if label?
              atts['type'] = 'ProjectLabel' # Always create project labels
            elsif milestone?
              if atts['group_id'] # Transform new group milestones into project ones
                atts['iid'] = nil
                atts.delete('group_id')
              else
                claim_iid
              end
            end

            atts['importing'] = true if klass.ancestors.include?(Importable)
          end
        end

        def find_diff_commit_user
          find_with_cache do
            MergeRequest::DiffCommitUser
              .find_or_create(@attributes['name'], @attributes['email'])
          end
        end

        def label?
          klass == Label
        end

        def milestone?
          klass == Milestone
        end

        def merge_request?
          klass == MergeRequest
        end

        def epic?
          klass == Epic
        end

        def design?
          klass == ::DesignManagement::Design
        end

        def diff_commit_user?
          klass == MergeRequest::DiffCommitUser
        end

        # If an existing group milestone used the IID
        # claim the IID back and set the group milestone to use one available
        # This is necessary to fix situations like the following:
        #  - Importing into a user namespace project with exported group milestones
        #    where the IID of the Group milestone could conflict with a project one.
        def claim_iid
          # The milestone has to be a group milestone, as it's the only case where
          # we set the IID as the maximum. The rest of them are fixed.
          milestone = project.milestones.find_by(iid: attributes['iid'])

          return unless milestone

          milestone.iid = nil
          milestone.ensure_project_iid!
          milestone.save!
        end
      end
    end
  end
end