@@ -82,7 +82,7 @@ async function _validateProjectData(project, repoUrl) {
82
82
}
83
83
if ( existsInDatabase ) {
84
84
throw new errors . ValidationError ( `This repo already has a Topcoder-X project associated with it.
85
- Copilot: ${ existsInDatabase . copilot } , Owner: ${ existsInDatabase . owner } ` )
85
+ Repo: ${ repoUrl } , Copilot: ${ existsInDatabase . copilot } , Owner: ${ existsInDatabase . owner } ` )
86
86
}
87
87
const provider = await helper . getProviderType ( repoUrl ) ;
88
88
const userRole = project . copilot ? project . copilot : project . owner ;
@@ -112,9 +112,80 @@ async function _ensureEditPermissionAndGetInfo(projectId, currentUser) {
112
112
) {
113
113
throw new errors . ForbiddenError ( 'You don\'t have access on this project' ) ;
114
114
}
115
+ if ( dbProject . archived ) {
116
+ throw new errors . ForbiddenError ( 'You can\'t access on this archived project' ) ;
117
+ }
115
118
return dbProject ;
116
119
}
117
120
121
+ /**
122
+ * create Repository as well as adding git label, hook, wiki
123
+ * or
124
+ * migrate Repository as well as related Issue and CopilotPayment
125
+ * @param {String } repoUrl the repository url
126
+ * @param {Object } project the new project
127
+ * @param {String } currentUser the topcoder current user
128
+ * @returns {void }
129
+ * @private
130
+ */
131
+ async function _createOrMigrateRepository ( repoUrl , project , currentUser ) {
132
+ let oldRepo = await dbHelper . queryOneRepository ( repoUrl ) ;
133
+ if ( oldRepo ) {
134
+ if ( oldRepo . projectId === project . id ) {
135
+ throw new Error ( `This error should never occur: the projectId of the repository to be migrate
136
+ will never equal to the new project id` ) ;
137
+ }
138
+ if ( oldRepo . archived === false ) {
139
+ throw new Error ( `Duplicate active repository should be blocked by _validateProjectData,
140
+ or a time-sequence cornercase encountered here` ) ;
141
+ }
142
+ try {
143
+ let oldIssues = await models . Issue . query ( { repoUrl : oldRepo . url } ) ;
144
+ let oldCopilotPaymentPromise = oldIssues . filter ( issue => issue . challengeUUID )
145
+ . map ( issue => models . CopilotPayment . query ( { challengeUUID : issue . challengeUUID } )
146
+ . then ( payments => {
147
+ if ( ! payments || payments . length === 0 ) {
148
+ /* eslint-disable-next-line no-console */
149
+ console . log ( `No CopilotPayment correspond to Issue with challengeUUID ${ issue . challengeUUID } .
150
+ The corresponding CopilotPayment may have been removed.
151
+ Or, there is bug in old version.` ) ;
152
+ return null ;
153
+ }
154
+ if ( payments . length > 1 ) {
155
+ throw new Error ( `Duplicate CopilotPayment correspond to one Issue with challengeUUID ${ issue . challengeUUID } .
156
+ There must be bug in old version` ) ;
157
+ }
158
+ return payments [ 0 ] ;
159
+ } ) ) ;
160
+ let oldCopilotPayment = await Promise . all ( oldCopilotPaymentPromise ) . filter ( payment => payment ) ;
161
+
162
+ await models . Repository . update ( { id : oldRepo . id } , { projectId : project . id , archived : false } ) ;
163
+ await oldIssues . forEach ( issue => models . Issue . update ( { id : issue . id } , { projectId : project . id } ) ) ;
164
+ await oldCopilotPayment . forEach (
165
+ payment => models . CopilotPayment . update ( { id : payment . id } , { project : project . id } )
166
+ ) ;
167
+ }
168
+ catch ( err ) {
169
+ throw new Error ( `Update ProjectId for Repository, Issue, CopilotPayment failed. Repo ${ repoUrl } . Internal Error: ${ err . message } ` ) ;
170
+ }
171
+ } else {
172
+ try {
173
+ await dbHelper . create ( models . Repository , {
174
+ id : helper . generateIdentifier ( ) ,
175
+ projectId : project . id ,
176
+ url : repoUrl ,
177
+ archived : project . archived
178
+ } )
179
+ await createLabel ( { projectId : project . id } , currentUser , repoUrl ) ;
180
+ await createHook ( { projectId : project . id } , currentUser , repoUrl ) ;
181
+ await addWikiRules ( { projectId : project . id } , currentUser , repoUrl ) ;
182
+ }
183
+ catch ( err ) {
184
+ throw new Error ( `Project created. Adding the webhook, issue labels, and wiki rules failed. Repo ${ repoUrl } . Internal Error: ${ err . message } ` ) ;
185
+ }
186
+ }
187
+ }
188
+
118
189
/**
119
190
* creates project
120
191
* @param {Object } project the project detail
@@ -142,24 +213,16 @@ async function create(project, currentUser) {
142
213
project . copilot = project . copilot ? project . copilot . toLowerCase ( ) : null ;
143
214
project . id = helper . generateIdentifier ( ) ;
144
215
145
- const createdProject = await dbHelper . create ( models . Project , project ) ;
146
-
216
+ // TODO: The following db operation should/could be moved into one transaction
147
217
for ( const repoUrl of repoUrls ) { // eslint-disable-line no-restricted-syntax
148
- await dbHelper . create ( models . Repository , {
149
- id : helper . generateIdentifier ( ) ,
150
- projectId : project . id ,
151
- url : repoUrl ,
152
- archived : project . archived
153
- } )
154
218
try {
155
- await createLabel ( { projectId : project . id } , currentUser , repoUrl ) ;
156
- await createHook ( { projectId : project . id } , currentUser , repoUrl ) ;
157
- await addWikiRules ( { projectId : project . id } , currentUser , repoUrl ) ;
219
+ await _createOrMigrateRepository ( repoUrl , project , currentUser ) ;
158
220
}
159
221
catch ( err ) {
160
- throw new Error ( `Project created. Adding the webhook, issue labels, and wiki rules failed. Repo ${ repoUrl } ` ) ;
222
+ throw new Error ( `Create or migrate repository failed. Repo ${ repoUrl } . Internal Error: ${ err . message } ` ) ;
161
223
}
162
224
}
225
+ const createdProject = await dbHelper . create ( models . Project , project ) ;
163
226
164
227
return createdProject ;
165
228
}
@@ -178,6 +241,7 @@ async function update(project, currentUser) {
178
241
for ( const repoUrl of repoUrls ) { // eslint-disable-line no-restricted-syntax
179
242
await _validateProjectData ( project , repoUrl ) ;
180
243
}
244
+ // TODO: remove the useless code-block
181
245
if ( dbProject . archived === 'false' && project . archived === true ) {
182
246
// project archived detected.
183
247
const result = {
@@ -201,22 +265,22 @@ async function update(project, currentUser) {
201
265
dbProject [ item [ 0 ] ] = item [ 1 ] ;
202
266
return item ;
203
267
} ) ;
204
- const oldRepositories = await dbHelper . queryRepositoriesByProjectId ( dbProject . id ) ;
205
- const weebhookIds = { } ;
206
- for ( const repo of oldRepositories ) { // eslint-disable-line
207
- if ( repo . registeredWebhookId ) {
208
- weebhookIds [ repo . url ] = repo . registeredWebhookId ;
209
- }
210
- await dbHelper . removeById ( models . Repository , repo . id ) ;
211
- }
268
+
269
+ // TODO: move the following logic into one dynamoose transaction
270
+ const repoUrl2Repo = await dbHelper . queryRepositoriesByProjectId ( dbProject . id )
271
+ . map ( repo => { return { [ repo . url ] : repo } ; } ) ;
272
+
212
273
for ( const repoUrl of repoUrls ) { // eslint-disable-line no-restricted-syntax
213
- await dbHelper . create ( models . Repository , {
214
- id : helper . generateIdentifier ( ) ,
215
- projectId : dbProject . id ,
216
- url : repoUrl ,
217
- archived : project . archived ,
218
- registeredWebhookId : weebhookIds [ repoUrl ]
219
- } )
274
+ if ( repoUrl in repoUrl2Repo ) {
275
+ await models . Repository . update ( { id : repoUrl2Repo [ repoUrl ] . id } , { archived : project . archived } ) ;
276
+ } else {
277
+ try {
278
+ await _createOrMigrateRepository ( repoUrl , project , currentUser ) ;
279
+ }
280
+ catch ( err ) {
281
+ throw new Error ( `Create or migrate repository failed. Repo ${ repoUrl } . Internal Error: ${ err . message } ` ) ;
282
+ }
283
+ }
220
284
}
221
285
dbProject . updatedAt = new Date ( ) ;
222
286
return await dbHelper . update ( models . Project , dbProject . id , dbProject ) ;
0 commit comments