summaryrefslogtreecommitdiff
path: root/app/services/groups/import_export/import_service.rb
blob: ac181245986460eb224778cc57d090ed257471bc (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
# frozen_string_literal: true

module Groups
  module ImportExport
    class ImportService
      attr_reader :current_user, :group, :shared

      def initialize(group:, user:)
        @group = group
        @current_user = user
        @user_role = user_role
        @shared = Gitlab::ImportExport::Shared.new(@group)
        @logger = Gitlab::Import::Logger.build
      end

      def async_execute
        group_import_state = GroupImportState.safe_find_or_create_by!(group: group, user: current_user)
        jid = GroupImportWorker.with_status.perform_async(current_user.id, group.id)

        if jid.present?
          group_import_state.update!(jid: jid)
        else
          group_import_state.fail_op('Failed to schedule import job')

          false
        end
      end

      def execute
        Gitlab::Tracking.event(self.class.name, 'create', label: 'import_group_from_file')

        if valid_user_permissions? && import_file && restorers.all?(&:restore)
          notify_success

          Gitlab::Tracking.event(
            self.class.name,
            'create',
            label: 'import_access_level',
            user: current_user,
            extra: { user_role: user_role, import_type: 'import_group_from_file' }
          )

          group
        else
          notify_error!
        end

      ensure
        remove_base_tmp_dir
        remove_import_file
      end

      private

      def user_role
        # rubocop:disable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
        access_level = group.parent ?
          current_user&.group_members&.find_by(source_id: group.parent&.id)&.access_level :
          Gitlab::Access::OWNER
        Gitlab::Access.human_access(access_level)
        # rubocop:enable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
      end

      def import_file
        @import_file ||= Gitlab::ImportExport::FileImporter.import(
          importable: group,
          archive_file: nil,
          shared: shared
        )
      end

      def restorers
        [tree_restorer]
      end

      def tree_restorer
        @tree_restorer ||=
          if ndjson?
            Gitlab::ImportExport::Group::TreeRestorer.new(
              user: current_user,
              shared: shared,
              group: group
            )
          else
            Gitlab::ImportExport::Group::LegacyTreeRestorer.new(
              user: current_user,
              shared: shared,
              group: group,
              group_hash: nil
            )
          end
      end

      def ndjson?
        ::Feature.enabled?(:group_import_ndjson, group&.parent) &&
          File.exist?(File.join(shared.export_path, 'tree/groups/_all.ndjson'))
      end

      def remove_import_file
        upload = group.import_export_upload

        return unless upload&.import_file&.file

        upload.remove_import_file!
        upload.save!
      end

      def valid_user_permissions?
        if current_user.can?(:admin_group, group)
          true
        else
          shared.error(::Gitlab::ImportExport::Error.permission_error(current_user, group))

          false
        end
      end

      def notify_success
        @logger.info(
          group_id: group.id,
          group_name: group.name,
          message: 'Group Import/Export: Import succeeded'
        )
      end

      def notify_error
        @logger.error(
          group_id: group.id,
          group_name: group.name,
          message: "Group Import/Export: Errors occurred, see '#{Gitlab::ErrorTracking::Logger.file_name}' for details"
        )
      end

      def notify_error!
        notify_error

        raise Gitlab::ImportExport::Error, shared.errors.to_sentence
      end

      def remove_base_tmp_dir
        FileUtils.rm_rf(shared.base_path)
      end
    end
  end
end

Groups::ImportExport::ImportService.prepend_mod_with('Groups::ImportExport::ImportService')