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: ac28ae6bfe0d89fa96b8362aca15781ec5c9c382 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# 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 initialize(klass, attributes)
          super

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

        def find
          return if group_relation_without_group?
          return find_diff_commit_user if diff_commit_user?
          return find_diff_commit if diff_commit?

          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', 'target_project_id')) if merge_request?
        end

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

            if label?
              atts['type'] = 'ProjectLabel' # Always create project labels
              atts.delete('group_id')
            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_or_create_diff_commit_user(@attributes['name'], @attributes['email'])
        end

        def find_diff_commit
          row = @attributes.dup

          # Diff commits come in two formats:
          #
          # 1. The old format where author/committer details are separate fields
          # 2. The new format where author/committer details are nested objects,
          #    and pre-processed by `find_diff_commit_user`.
          #
          # The code here ensures we support both the old and new format.
          aname = row.delete('author_name')
          amail = row.delete('author_email')
          cname = row.delete('committer_name')
          cmail = row.delete('committer_email')
          author = row.delete('commit_author')
          committer = row.delete('committer')

          row['commit_author'] = author ||
            find_or_create_diff_commit_user(aname, amail)

          row['committer'] = committer ||
            find_or_create_diff_commit_user(cname, cmail)

          MergeRequestDiffCommit.new(row)
        end

        def find_or_create_diff_commit_user(name, email)
          find_with_cache([MergeRequest::DiffCommitUser, name, email]) do
            MergeRequest::DiffCommitUser.find_or_create(name, 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

        def diff_commit?
          klass == MergeRequestDiffCommit
        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

        def group_relation_without_group?
          group_level_object? && group.nil?
        end

        def group_level_object?
          epic?
        end
      end
    end
  end
end

Gitlab::ImportExport::Project::ObjectBuilder.prepend_mod