@@ -16,9 +16,18 @@ const GitlabAPI = require('node-gitlab-api');
16
16
const logger = require ( '../utils/logger' ) ;
17
17
const errors = require ( '../utils/errors' ) ;
18
18
const helper = require ( '../utils/helper' ) ;
19
+ const dbHelper = require ( '../utils/db-helper' ) ;
20
+ const superagent = require ( 'superagent' ) ;
21
+ const superagentPromise = require ( 'superagent-promise' ) ;
22
+
23
+ const request = superagentPromise ( superagent , Promise ) ;
24
+ // milliseconds per second
25
+ const MS_PER_SECOND = 1000 ;
19
26
20
27
const copilotUserSchema = Joi . object ( ) . keys ( {
21
28
accessToken : Joi . string ( ) . required ( ) ,
29
+ accessTokenExpiration : Joi . date ( ) . required ( ) ,
30
+ refreshToken : Joi . string ( ) . required ( ) ,
22
31
userProviderId : Joi . number ( ) . required ( ) ,
23
32
topcoderUsername : Joi . string ( )
24
33
} ) . required ( ) ;
@@ -80,7 +89,8 @@ function _getIssueUrl(repoPath, issueId) {
80
89
async function createComment ( copilot , project , issueId , body ) {
81
90
const projectId = project . id ;
82
91
Joi . attempt ( { copilot, projectId, issueId, body} , createComment . schema ) ;
83
- const gitlab = await _authenticate ( copilot . accessToken ) ;
92
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
93
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
84
94
try {
85
95
body = helper . prepareAutomatedComment ( body , copilot ) ;
86
96
await gitlab . projects . issues . notes . create ( projectId , issueId , { body} ) ;
@@ -107,7 +117,8 @@ createComment.schema = {
107
117
async function updateIssue ( copilot , project , issueId , title ) {
108
118
const projectId = project . id ;
109
119
Joi . attempt ( { copilot, projectId, issueId, title} , updateIssue . schema ) ;
110
- const gitlab = await _authenticate ( copilot . accessToken ) ;
120
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
121
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
111
122
try {
112
123
await gitlab . projects . issues . edit ( projectId , issueId , { title} ) ;
113
124
} catch ( err ) {
@@ -133,7 +144,8 @@ updateIssue.schema = {
133
144
async function assignUser ( copilot , project , issueId , userId ) {
134
145
const projectId = project . id ;
135
146
Joi . attempt ( { copilot, projectId, issueId, userId} , assignUser . schema ) ;
136
- const gitlab = await _authenticate ( copilot . accessToken ) ;
147
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
148
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
137
149
try {
138
150
const issue = await gitlab . projects . issues . show ( projectId , issueId ) ;
139
151
const oldAssignees = _ . without ( issue . assignee_ids , userId ) ;
@@ -164,7 +176,8 @@ assignUser.schema = {
164
176
async function removeAssign ( copilot , project , issueId , userId ) {
165
177
const projectId = project . id ;
166
178
Joi . attempt ( { copilot, projectId, issueId, userId} , removeAssign . schema ) ;
167
- const gitlab = await _authenticate ( copilot . accessToken ) ;
179
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
180
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
168
181
await _removeAssignees ( gitlab , projectId , issueId , [ userId ] ) ;
169
182
logger . debug ( `Gitlab user ${ userId } is unassigned from issue number ${ issueId } ` ) ;
170
183
}
@@ -179,7 +192,8 @@ removeAssign.schema = assignUser.schema;
179
192
*/
180
193
async function getUsernameById ( copilot , userId ) {
181
194
Joi . attempt ( { copilot, userId} , getUsernameById . schema ) ;
182
- const gitlab = await _authenticate ( copilot . accessToken ) ;
195
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
196
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
183
197
const user = await gitlab . users . show ( userId ) ;
184
198
return user ? user . username : null ;
185
199
}
@@ -197,7 +211,8 @@ getUsernameById.schema = {
197
211
*/
198
212
async function getUserIdByLogin ( copilot , login ) {
199
213
Joi . attempt ( { copilot, login} , getUserIdByLogin . schema ) ;
200
- const gitlab = await _authenticate ( copilot . accessToken ) ;
214
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
215
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
201
216
const user = await gitlab . users . all ( { username : login } ) ;
202
217
return user . length ? user [ 0 ] . id : null ;
203
218
}
@@ -220,7 +235,8 @@ getUserIdByLogin.schema = {
220
235
async function markIssueAsPaid ( copilot , project , issueId , challengeUUID , existLabels , winner , createCopilotPayments ) { // eslint-disable-line max-params
221
236
const projectId = project . id ;
222
237
Joi . attempt ( { copilot, projectId, issueId, challengeUUID, existLabels, winner, createCopilotPayments} , markIssueAsPaid . schema ) ;
223
- const gitlab = await _authenticate ( copilot . accessToken ) ;
238
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
239
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
224
240
const labels = _ ( existLabels ) . filter ( ( i ) => i !== config . FIX_ACCEPTED_ISSUE_LABEL )
225
241
. push ( config . FIX_ACCEPTED_ISSUE_LABEL , config . PAID_ISSUE_LABEL ) . value ( ) ;
226
242
try {
@@ -263,7 +279,8 @@ markIssueAsPaid.schema = {
263
279
async function changeState ( copilot , project , issueId , state ) {
264
280
const projectId = project . id ;
265
281
Joi . attempt ( { copilot, projectId, issueId, state} , changeState . schema ) ;
266
- const gitlab = await _authenticate ( copilot . accessToken ) ;
282
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
283
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
267
284
try {
268
285
await gitlab . projects . issues . edit ( projectId , issueId , { state_event : state } ) ;
269
286
} catch ( err ) {
@@ -289,7 +306,8 @@ changeState.schema = {
289
306
async function addLabels ( copilot , project , issueId , labels ) {
290
307
const projectId = project . id ;
291
308
Joi . attempt ( { copilot, projectId, issueId, labels} , addLabels . schema ) ;
292
- const gitlab = await _authenticate ( copilot . accessToken ) ;
309
+ const refreshedCopilot = await _refreshGitlabUserAccessToken ( copilot ) ;
310
+ const gitlab = await _authenticate ( refreshedCopilot . accessToken ) ;
293
311
try {
294
312
await gitlab . projects . issues . edit ( projectId , issueId , { labels : _ . join ( labels , ',' ) } ) ;
295
313
} catch ( err ) {
@@ -305,6 +323,35 @@ addLabels.schema = {
305
323
labels : Joi . array ( ) . items ( Joi . string ( ) ) . required ( )
306
324
} ;
307
325
326
+ /**
327
+ * Refresh the copilot access token if token is needed
328
+ * @param {Object } copilot the copilot
329
+ * @returns {Promise } the promise result of copilot with refreshed token
330
+ */
331
+ async function _refreshGitlabUserAccessToken ( copilot ) {
332
+ if ( copilot . accessTokenExpiration && new Date ( ) . getTime ( ) > copilot . accessTokenExpiration . getTime ( ) -
333
+ ( config . GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION * MS_PER_SECOND ) ) {
334
+ const refreshTokenResult = await request
335
+ . post ( `${ config . GITLAB_API_BASE_URL } /oauth/token` )
336
+ . query ( {
337
+ client_id : config . GITLAB_CLIENT_ID ,
338
+ client_secret : config . GITLAB_CLIENT_SECRET ,
339
+ refresh_token : copilot . refreshToken ,
340
+ grant_type : 'refresh_token' ,
341
+ redirect_uri : config . GITLAB_OWNER_USER_CALLBACK_URL ,
342
+ } )
343
+ . end ( ) ;
344
+ // save user token data
345
+ const expiresIn = refreshTokenResult . body . expires_in || config . GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION ;
346
+ return await dbHelper . update ( User , copilot . id , {
347
+ accessToken : refreshTokenResult . body . access_token ,
348
+ accessTokenExpiration : new Date ( new Date ( ) . getTime ( ) + expiresIn * MS_PER_SECOND ) ,
349
+ refreshToken : refreshTokenResult . body . refresh_token ,
350
+ } ) ;
351
+ }
352
+ return copilot ;
353
+ }
354
+
308
355
309
356
module . exports = {
310
357
createComment,
0 commit comments