BigW Consortium Gitlab

relation_factory.rb 4.99 KB
Newer Older
1
module Gitlab
2
  module ImportExport
3
    class RelationFactory
4
      OVERRIDES = { snippets: :project_snippets,
5
                    pipelines: 'Ci::Pipeline',
6 7 8 9 10 11
                    statuses: 'commit_status',
                    variables: 'Ci::Variable',
                    triggers: 'Ci::Trigger',
                    builds: 'Ci::Build',
                    hooks: 'ProjectHook' }.freeze

12
      USER_REFERENCES = %w[author_id assignee_id updated_by_id user_id].freeze
13

James Lopez committed
14 15
      BUILD_MODELS = %w[Ci::Build commit_status].freeze

16 17 18
      def self.create(*args)
        new(*args).create
      end
19

20
      def initialize(relation_sym:, relation_hash:, members_mapper:, user:)
21
        @relation_name = OVERRIDES[relation_sym] || relation_sym
22
        @relation_hash = relation_hash.except('id', 'noteable_id')
23
        @members_mapper = members_mapper
24
        @user = user
25
      end
James Lopez committed
26

27 28 29 30 31 32 33
      # Creates an object from an actual model with name "relation_sym" with params from
      # the relation_hash, updating references with new object IDs, mapping users using
      # the "members_mapper" object, also updating notes if required.
      def create
        set_note_author if @relation_name == :notes
        update_user_references
        update_project_references
34
        reset_ci_tokens if @relation_name == 'Ci::Trigger'
35
        @relation_hash['data'].deep_symbolize_keys! if @relation_name == :events && @relation_hash['data']
36
        set_st_diffs if @relation_name == :merge_request_diff
James Lopez committed
37

38
        generate_imported_object
39 40
      end

41
      private
42

43
      def update_user_references
44
        USER_REFERENCES.each do |reference|
45 46
          if @relation_hash[reference]
            @relation_hash[reference] = @members_mapper.map[@relation_hash[reference]]
47 48 49 50
          end
        end
      end

51 52 53 54 55 56
      # Sets the author for a note. If the user importing the project
      # has admin access, an actual mapping with new project members
      # will be used. Otherwise, a note stating the original author name
      # is left.
      def set_note_author
        old_author_id = @relation_hash['author_id']
57

58
        # Users with admin access can map users
59
        @relation_hash['author_id'] = admin_user? ? @members_mapper.map[old_author_id] : @members_mapper.default_user_id
60

61
        author = @relation_hash.delete('author')
62

63
        update_note_for_missing_author(author['name']) if missing_author?(old_author_id)
James Lopez committed
64 65
      end

66
      def missing_author?(old_author_id)
67
        !admin_user? || @members_mapper.missing_author_ids.include?(old_author_id)
68 69 70
      end

      def missing_author_note(updated_at, author_name)
71 72
        timestamp = updated_at.split('.').first
        "\n\n *By #{author_name} on #{timestamp} (imported from GitLab project)*"
73 74
      end

75
      def generate_imported_object
James Lopez committed
76
        if BUILD_MODELS.include?(@relation_name) # call #trace= method after assigning the other attributes
77 78 79 80
          trace = @relation_hash.delete('trace')
          imported_object do |object|
            object.trace = trace
            object.commit_id = nil
81 82
          end
        else
83
          imported_object
84 85 86
        end
      end

87 88
      def update_project_references
        project_id = @relation_hash.delete('project_id')
89 90

        # project_id may not be part of the export, but we always need to populate it if required.
91
        @relation_hash['project_id'] = project_id
92 93 94 95 96 97 98 99 100
        @relation_hash['gl_project_id'] = project_id if @relation_hash['gl_project_id']
        @relation_hash['target_project_id'] = project_id if @relation_hash['target_project_id']
        @relation_hash['source_project_id'] = -1 if @relation_hash['source_project_id']

        # If source and target are the same, populate them with the new project ID.
        if @relation_hash['source_project_id'] && @relation_hash['target_project_id'] &&
          @relation_hash['target_project_id'] == @relation_hash['source_project_id']
          @relation_hash['source_project_id'] = project_id
        end
101 102
      end

103
      def reset_ci_tokens
104 105
        return unless Gitlab::ImportExport.reset_tokens?

106
        # If we import/export a project to the same instance, tokens will have to be reset.
107
        @relation_hash['token'] = nil
108
      end
109

110 111
      def relation_class
        @relation_class ||= @relation_name.to_s.classify.constantize
112
      end
James Lopez committed
113

114
      def imported_object
115
        imported_object = relation_class.new(parsed_relation_hash)
116
        yield(imported_object) if block_given?
James Lopez committed
117 118 119
        imported_object.importing = true if imported_object.respond_to?(:importing)
        imported_object
      end
120 121 122 123 124 125 126

      def update_note_for_missing_author(author_name)
        @relation_hash['note'] = '*Blank note*' if @relation_hash['note'].blank?
        @relation_hash['note'] += missing_author_note(@relation_hash['updated_at'], author_name)
      end

      def admin_user?
127
        @user.is_admin?
128
      end
129 130 131 132

      def parsed_relation_hash
        @relation_hash.reject { |k, _v| !relation_class.attribute_method?(k) }
      end
133 134 135 136

      def set_st_diffs
        @relation_hash['st_diffs'] = @relation_hash.delete('utf8_st_diffs')
      end
137 138 139
    end
  end
end