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
|
# frozen_string_literal: true
# Entry point of the BulkImport feature.
# This service receives a Gitlab Instance connection params
# and a list of groups to be imported.
#
# Process topography:
#
# sync | async
# |
# User +--> P1 +----> Pn +---+
# | ^ | Enqueue new job
# | +-----+
#
# P1 (sync)
#
# - Create a BulkImport record
# - Create a BulkImport::Entity for each group to be imported
# - Enqueue a BulkImportWorker job (P2) to import the given groups (entities)
#
# Pn (async)
#
# - For each group to be imported (BulkImport::Entity.with_status(:created))
# - Import the group data
# - Create entities for each subgroup of the imported group
# - Enqueue a BulkImports::CreateService job (Pn) to import the new entities (subgroups)
#
module BulkImports
class CreateService
attr_reader :current_user, :params, :credentials
def initialize(current_user, params, credentials)
@current_user = current_user
@params = params
@credentials = credentials
end
def execute
validate!
bulk_import = create_bulk_import
Gitlab::Tracking.event(self.class.name, 'create', label: 'bulk_import_group')
BulkImportWorker.perform_async(bulk_import.id)
ServiceResponse.success(payload: bulk_import)
rescue ActiveRecord::RecordInvalid, BulkImports::Error, BulkImports::NetworkError => e
ServiceResponse.error(
message: e.message,
http_status: :unprocessable_entity
)
end
private
def validate!
client.validate_instance_version!
client.validate_import_scopes!
end
def create_bulk_import
BulkImport.transaction do
bulk_import = BulkImport.create!(
user: current_user,
source_type: 'gitlab',
source_version: client.instance_version,
source_enterprise: client.instance_enterprise
)
bulk_import.create_configuration!(credentials.slice(:url, :access_token))
Array.wrap(params).each do |entity_params|
track_access_level(entity_params)
validate_destination_full_path(entity_params)
BulkImports::Entity.create!(
bulk_import: bulk_import,
source_type: entity_params[:source_type],
source_full_path: entity_params[:source_full_path],
destination_slug: entity_params[:destination_slug] || entity_params[:destination_name],
destination_namespace: entity_params[:destination_namespace],
migrate_projects: Gitlab::Utils.to_boolean(entity_params[:migrate_projects], default: true)
)
end
bulk_import
end
end
def track_access_level(entity_params)
Gitlab::Tracking.event(
self.class.name,
'create',
label: 'import_access_level',
user: current_user,
extra: { user_role: user_role(entity_params[:destination_namespace]), import_type: 'bulk_import_group' }
)
end
def validate_destination_full_path(entity_params)
source_type = entity_params[:source_type]
full_path = [
entity_params[:destination_namespace],
entity_params[:destination_slug] || entity_params[:destination_name]
].reject(&:blank?).join('/')
case source_type
when 'group_entity'
return if Namespace.find_by_full_path(full_path).nil?
when 'project_entity'
return if Project.find_by_full_path(full_path).nil?
end
raise BulkImports::Error.destination_full_path_validation_failure(full_path)
end
def user_role(destination_namespace)
namespace = Namespace.find_by_full_path(destination_namespace)
# if there is no parent namespace we assume user will be group creator/owner
return owner_role unless destination_namespace
return owner_role unless namespace
return owner_role unless namespace.group_namespace? # user namespace
membership = current_user.group_members.find_by(source_id: namespace.id) # rubocop:disable CodeReuse/ActiveRecord
return 'Not a member' unless membership
Gitlab::Access.human_access(membership.access_level)
end
def owner_role
Gitlab::Access.human_access(Gitlab::Access::OWNER)
end
def client
@client ||= BulkImports::Clients::HTTP.new(
url: @credentials[:url],
token: @credentials[:access_token]
)
end
end
end
|