Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Version 1.2.2 #98

Merged
merged 16 commits into from
Jun 22, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,10 @@ module.exports = {
ROLE_ID_SUBMITTER: process.env.ROLE_ID_SUBMITTER || '732339e7-8e30-49d7-9198-cccf9451e221',
TYPE_ID_TASK: process.env.TYPE_ID_TASK || 'ecd58c69-238f-43a4-a4bb-d172719b9f31',
DEFAULT_TIMELINE_TEMPLATE_ID: process.env.DEFAULT_TIMELINE_TEMPLATE_ID || '53a307ce-b4b3-4d6f-b9a1-3741a58f77e6',
DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825'
DEFAULT_TRACK_ID: process.env.DEFAULT_TRACK_ID || '9b6fc876-f4d9-4ccb-9dfd-419247628825',
GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION: 3600 * 2,
GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION: 300,
GITLAB_CLIENT_ID: process.env.GITLAB_CLIENT_ID,
GITLAB_CLIENT_SECRET: process.env.GITLAB_CLIENT_SECRET,
GITLAB_OWNER_USER_CALLBACK_URL: process.env.GITLAB_OWNER_USER_CALLBACK_URL
};
3 changes: 3 additions & 0 deletions configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ The following config parameters are supported, they are defined in `config/defau
| NEW_CHALLENGE_DURATION_IN_DAYS | the duration of new challenge | 5 |
|TC_URL| the base URL of topcoder to get the challenge URL| defaults to `https://www.topcoder-dev.com`|
|GITLAB_API_BASE_URL| the URL for gitlab host| defaults to `https://gitlab.com`|
| GITLAB_CLIENT_ID | the GitLab client id | No default - needs to be set up with same value found in topcoder-x-ui |
| GITLAB_CLIENT_SECRET | the GitLab client secret | No default - needs to be set up with same value found in topcoder-x-ui |
| GITLAB_OWNER_USER_CALLBACK_URL | the GitLab callback redirect uri for refreshing copilot token | No default - needs to be set up with same owner user callback value in topcoder-x-ui |
|PAID_ISSUE_LABEL|the label name for paid, should be one of the label configured in topcoder x ui|'tcx_Paid'|
|FIX_ACCEPTED_ISSUE_LABEL|the label name for fix accepted, should be one of the label configured in topcoder x ui|'tcx_FixAccepted'|
|ASSIGNED_ISSUE_LABEL| the label name for assigned, should be one of the label configured in topcoder x ui| 'tcx_Assigned'|
Expand Down
4 changes: 3 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ process.on('unhandledRejection', (err) => {
});

// dump the configuration to logger
const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET'];
const ignoreConfigLog = ['cert', 'key', 'AWS_ACCESS_KEY_ID', 'AWS_SECRET_ACCESS_KEY', 'AUTH0_CLIENT_ID', 'AUTH0_CLIENT_SECRET',
'GITLAB_CLIENT_ID', 'GITLAB_CLIENT_SECRET'];

/**
* Print configs to logger
* @param {Object} params the config params
Expand Down
65 changes: 56 additions & 9 deletions services/GitlabService.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,18 @@ const GitlabAPI = require('node-gitlab-api');
const logger = require('../utils/logger');
const errors = require('../utils/errors');
const helper = require('../utils/helper');
const dbHelper = require('../utils/db-helper');
const superagent = require('superagent');
const superagentPromise = require('superagent-promise');

const request = superagentPromise(superagent, Promise);
// milliseconds per second
const MS_PER_SECOND = 1000;

const copilotUserSchema = Joi.object().keys({
accessToken: Joi.string().required(),
accessTokenExpiration: Joi.date().required(),
refreshToken: Joi.string().required(),
userProviderId: Joi.number().required(),
topcoderUsername: Joi.string()
}).required();
Expand Down Expand Up @@ -80,7 +89,8 @@ function _getIssueUrl(repoPath, issueId) {
async function createComment(copilot, project, issueId, body) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, body}, createComment.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
try {
body = helper.prepareAutomatedComment(body, copilot);
await gitlab.projects.issues.notes.create(projectId, issueId, {body});
Expand All @@ -107,7 +117,8 @@ createComment.schema = {
async function updateIssue(copilot, project, issueId, title) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, title}, updateIssue.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
try {
await gitlab.projects.issues.edit(projectId, issueId, {title});
} catch (err) {
Expand All @@ -133,7 +144,8 @@ updateIssue.schema = {
async function assignUser(copilot, project, issueId, userId) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, userId}, assignUser.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
try {
const issue = await gitlab.projects.issues.show(projectId, issueId);
const oldAssignees = _.without(issue.assignee_ids, userId);
Expand Down Expand Up @@ -164,7 +176,8 @@ assignUser.schema = {
async function removeAssign(copilot, project, issueId, userId) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, userId}, removeAssign.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
await _removeAssignees(gitlab, projectId, issueId, [userId]);
logger.debug(`Gitlab user ${userId} is unassigned from issue number ${issueId}`);
}
Expand All @@ -179,7 +192,8 @@ removeAssign.schema = assignUser.schema;
*/
async function getUsernameById(copilot, userId) {
Joi.attempt({copilot, userId}, getUsernameById.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
const user = await gitlab.users.show(userId);
return user ? user.username : null;
}
Expand All @@ -197,7 +211,8 @@ getUsernameById.schema = {
*/
async function getUserIdByLogin(copilot, login) {
Joi.attempt({copilot, login}, getUserIdByLogin.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
const user = await gitlab.users.all({username: login});
return user.length ? user[0].id : null;
}
Expand All @@ -220,7 +235,8 @@ getUserIdByLogin.schema = {
async function markIssueAsPaid(copilot, project, issueId, challengeUUID, existLabels, winner, createCopilotPayments) { // eslint-disable-line max-params
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, challengeUUID, existLabels, winner, createCopilotPayments}, markIssueAsPaid.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
const labels = _(existLabels).filter((i) => i !== config.FIX_ACCEPTED_ISSUE_LABEL)
.push(config.FIX_ACCEPTED_ISSUE_LABEL, config.PAID_ISSUE_LABEL).value();
try {
Expand Down Expand Up @@ -263,7 +279,8 @@ markIssueAsPaid.schema = {
async function changeState(copilot, project, issueId, state) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, state}, changeState.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
try {
await gitlab.projects.issues.edit(projectId, issueId, {state_event: state});
} catch (err) {
Expand All @@ -289,7 +306,8 @@ changeState.schema = {
async function addLabels(copilot, project, issueId, labels) {
const projectId = project.id;
Joi.attempt({copilot, projectId, issueId, labels}, addLabels.schema);
const gitlab = await _authenticate(copilot.accessToken);
const refreshedCopilot = await _refreshGitlabUserAccessToken(copilot);
const gitlab = await _authenticate(refreshedCopilot.accessToken);
try {
await gitlab.projects.issues.edit(projectId, issueId, {labels: _.join(labels, ',')});
} catch (err) {
Expand All @@ -305,6 +323,35 @@ addLabels.schema = {
labels: Joi.array().items(Joi.string()).required()
};

/**
* Refresh the copilot access token if token is needed
* @param {Object} copilot the copilot
* @returns {Promise} the promise result of copilot with refreshed token
*/
async function _refreshGitlabUserAccessToken(copilot) {
if (copilot.accessTokenExpiration && new Date().getTime() > copilot.accessTokenExpiration.getTime() -
(config.GITLAB_REFRESH_TOKEN_BEFORE_EXPIRATION * MS_PER_SECOND)) {
const refreshTokenResult = await request
.post(`${config.GITLAB_API_BASE_URL}/oauth/token`)
.query({
client_id: config.GITLAB_CLIENT_ID,
client_secret: config.GITLAB_CLIENT_SECRET,
refresh_token: copilot.refreshToken,
grant_type: 'refresh_token',
redirect_uri: config.GITLAB_OWNER_USER_CALLBACK_URL,
})
.end();
// save user token data
const expiresIn = refreshTokenResult.body.expires_in || config.GITLAB_ACCESS_TOKEN_DEFAULT_EXPIRATION;
return await dbHelper.update(User, copilot.id, {
accessToken: refreshTokenResult.body.access_token,
accessTokenExpiration: new Date(new Date().getTime() + expiresIn * MS_PER_SECOND),
refreshToken: refreshTokenResult.body.refresh_token,
});
}
return copilot;
}


module.exports = {
createComment,
Expand Down
2 changes: 2 additions & 0 deletions services/UserService.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ async function getRepositoryCopilotOrOwner(provider, repoFullName) {

return {
accessToken: user.accessToken,
accessTokenExpiration: user.accessTokenExpiration,
refreshToken: user.refreshToken,
userProviderId: user.userProviderId,
topcoderUsername: userMapping.topcoderUsername
};
Expand Down