1
+ # frozen_string_literal: true
2
+
1
3
#-- copyright
2
4
# OpenProject is an open source project management software.
3
5
# Copyright (C) 2012-2024 the OpenProject GmbH
27
29
#++
28
30
29
31
class CopyProjectJob < ApplicationJob
30
- queue_with_priority :above_normal
31
32
include OpenProject ::LocaleHelper
33
+ include GoodJob ::ActiveJobExtensions ::Batches
32
34
33
- attr_reader :user_id ,
34
- :source_project_id ,
35
- :target_project_params ,
36
- :target_project_name ,
37
- :target_project ,
38
- :errors ,
39
- :associations_to_copy ,
40
- :send_mails
41
-
42
- def perform ( user_id :,
43
- source_project_id :,
44
- target_project_params :,
45
- associations_to_copy :,
46
- send_mails : false )
47
- # Needs refactoring after moving to activejob
48
-
49
- @user_id = user_id
50
- @source_project_id = source_project_id
51
- @target_project_params = target_project_params . with_indifferent_access
52
- @associations_to_copy = associations_to_copy
53
- @send_mails = send_mails
35
+ queue_with_priority :above_normal
54
36
37
+ # Again error handling pushing the branch costs up
38
+ def perform ( target_project_params :, associations_to_copy :, send_mails : false )
55
39
User . current = user
56
- @target_project_name = target_project_params [ :name ]
40
+ target_project_params = target_project_params . with_indifferent_access
57
41
58
- @ target_project, @ errors = with_locale_for ( user ) do
59
- create_project_copy
42
+ target_project , errors = with_locale_for ( user ) do
43
+ create_project_copy ( target_project_params , associations_to_copy , send_mails )
60
44
end
61
45
46
+ update_batch ( target_project :, errors :, target_project_name : target_project_params [ :name ] )
47
+
62
48
if target_project
63
- successful_status_update
64
- ProjectMailer . copy_project_succeeded ( user , source_project , target_project , errors ) . deliver_later
49
+ successful_status_update ( target_project , errors )
65
50
else
66
- failure_status_update
67
- ProjectMailer . copy_project_failed ( user , source_project , target_project_name , errors ) . deliver_later
51
+ failure_status_update ( errors )
68
52
end
69
53
rescue StandardError => e
70
54
logger . error { "Failed to finish copy project job: #{ e } #{ e . message } " }
71
55
errors = [ I18n . t ( "copy_project.failed_internal" ) ]
72
- failure_status_update
73
- ProjectMailer . copy_project_failed ( user , source_project , target_project_name , errors ) . deliver_later
56
+ update_batch ( errors : )
57
+ failure_status_update ( errors )
74
58
end
75
59
76
- def store_status?
77
- true
78
- end
60
+ def store_status? = true
79
61
80
- def updates_own_status?
81
- true
82
- end
62
+ def updates_own_status? = true
83
63
84
64
protected
85
65
86
- def title
87
- I18n . t ( :label_copy_project )
88
- end
66
+ def title = I18n . t ( :label_copy_project )
89
67
90
68
private
91
69
92
- def successful_status_update
93
- payload = redirect_payload ( url_helpers . project_url ( target_project ) )
94
- . merge ( hal_links ( target_project ) )
70
+ def update_batch ( hash )
71
+ batch . properties . merge! ( hash )
72
+ batch . save
73
+ end
74
+
75
+ def successful_status_update ( target_project , errors )
76
+ payload = redirect_payload ( url_helpers . project_url ( target_project ) ) . merge ( hal_links ( target_project ) )
95
77
96
78
if errors . any?
97
79
payload [ :errors ] = errors
@@ -102,7 +84,7 @@ def successful_status_update
102
84
payload :
103
85
end
104
86
105
- def failure_status_update
87
+ def failure_status_update ( errors )
106
88
message = I18n . t ( "copy_project.failed" , source_project_name : source_project . name )
107
89
108
90
if errors
@@ -123,60 +105,66 @@ def hal_links(project)
123
105
}
124
106
end
125
107
126
- def user
127
- @user ||= User . find user_id
128
- end
108
+ def user = batch . properties [ :user ]
129
109
130
- def source_project
131
- @source_project ||= Project . find source_project_id
132
- end
110
+ def source_project = batch . properties [ :source_project ]
133
111
134
- def create_project_copy
112
+ # rubocop:disable Metrics/AbcSize
113
+ # Most of the cost is from handling errors, we need to check what can be moved around / removed
114
+ def create_project_copy ( target_project_params , associations_to_copy , send_mails )
135
115
errors = [ ]
136
116
137
117
ProjectMailer . with_deliveries ( send_mails ) do
138
- service_call = copy_project
139
- target_project = service_call . result
140
- errors = service_call . errors . full_messages
118
+ service_result = copy_project ( target_project_params , associations_to_copy , send_mails )
119
+ target_project = service_result . result
120
+ errors = service_result . errors . full_messages
141
121
142
122
# We assume the copying worked "successfully" if the project was saved
143
- unless target_project &.persisted?
144
- target_project = nil
123
+ if target_project &.persisted?
124
+ return target_project , errors
125
+ else
145
126
logger . error ( "Copying project fails with validation errors: #{ errors . join ( "\n " ) } " )
127
+ return nil , errors
146
128
end
147
-
148
- return target_project , errors
149
129
end
150
130
rescue ActiveRecord ::RecordNotFound => e
151
131
logger . error ( "Entity missing: #{ e . message } #{ e . backtrace . join ( "\n " ) } " )
132
+ raise e
152
133
rescue StandardError => e
153
134
logger . error ( "Encountered an error when trying to copy project " \
154
- "'#{ source_project_id } ' : #{ e . message } #{ e . backtrace . join ( "\n " ) } " )
135
+ "'#{ source_project . id } ' : #{ e . message } #{ e . backtrace . join ( "\n " ) } " )
136
+ raise e
155
137
ensure
156
- unless errors . empty ?
138
+ if errors . any ?
157
139
logger . error ( "Encountered an errors while trying to copy related objects for " \
158
- "project '#{ source_project_id } ': #{ errors . inspect } " )
140
+ "project '#{ source_project . id } ': #{ errors . inspect } " )
159
141
end
160
142
end
143
+ # rubocop:enable Metrics/AbcSize
161
144
162
- def copy_project
163
- ::Projects ::CopyService
164
- . new ( source : source_project , user :)
165
- . call ( copy_project_params )
166
- end
145
+ def copy_project ( target_project_params , associations_to_copy , send_notifications )
146
+ copy_service = ::Projects ::CopyService . new ( source : source_project , user :)
147
+ result = copy_service . call ( { target_project_params :, send_notifications :, only : Array ( associations_to_copy ) } )
167
148
168
- def copy_project_params
169
- params = { target_project_params : , send_notifications : send_mails }
170
- params [ :only ] = associations_to_copy if associations_to_copy . present?
149
+ enqueue_copy_project_folder_jobs ( copy_service . state . copied_project_storages ,
150
+ copy_service . state . work_package_id_lookup ,
151
+ associations_to_copy )
171
152
172
- params
153
+ result
173
154
end
174
155
175
- def logger
176
- Rails . logger
177
- end
156
+ def enqueue_copy_project_folder_jobs ( copied_storages , work_packages_map , only )
157
+ return unless only . intersect? ( %w[ file_links storage_project_folders ] )
178
158
179
- def url_helpers
180
- @url_helpers ||= OpenProject ::StaticRouting ::StaticUrlHelpers . new
159
+ Array ( copied_storages ) . each do |storage_pair |
160
+ batch . enqueue do
161
+ Storages ::CopyProjectFoldersJob
162
+ . perform_later ( source : storage_pair [ :source ] , target : storage_pair [ :target ] , work_packages_map :)
163
+ end
164
+ end
181
165
end
166
+
167
+ def logger = OpenProject . logger
168
+
169
+ def url_helpers = OpenProject ::StaticRouting ::StaticUrlHelpers . new
182
170
end
0 commit comments