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

June 16 deploy #69

Merged
merged 10 commits into from
Jun 16, 2021
Prev Previous commit
Next Next commit
May 2021 release challenge
  • Loading branch information
jmgasper committed Jun 3, 2021
commit d236550758f6b46ad64c270168fc7835eb1f46e4
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ The following config parameters are supported, they are defined in `config/defau
|AUTH0_CLIENT_ID| The Auth0 ClientID for generating Machine-to-machine token ||
|AUTH0_CLIENT_SECRET| The Auth0 Client Secret for generating Machine-to-machine token ||
|ROLE_ID_COPILOT| The registered role id of copilot ||
|ROLE_ID_ITERATIVE_REVIEWER| The registered role id of iterative reviewer ||
|ROLE_ID_SUBMITTER| The registered role id of submitter ||
|TYPE_ID_TASK| The registered type id of a task ||
|DEFAULT_TIMELINE_TEMPLATE_ID| The default timeline template id ||
Expand Down
1 change: 1 addition & 0 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ module.exports = {
WEBSITE_SECURE: process.env.WEBSITE_SECURE || 'https://topcoderx.topcoder-dev.com',

ROLE_ID_COPILOT: process.env.ROLE_ID_COPILOT || 'cfe12b3f-2a24-4639-9d8b-ec86726f76bd',
ROLE_ID_ITERATIVE_REVIEWER: process.env.ROLE_ID_ITERATIVE_REVIEWER || 'f6df7212-b9d6-4193-bfb1-b383586fce63',
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',
Expand Down
1 change: 1 addition & 0 deletions configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ The following config parameters are supported, they are defined in `config/defau
|AUTH0_CLIENT_ID| The Auth0 ClientID for generating Machine-to-machine token ||
|AUTH0_CLIENT_SECRET| The Auth0 Client Secret for generating Machine-to-machine token ||
|ROLE_ID_COPILOT| The registered role id of copilot ||
|ROLE_ID_ITERATIVE_REVIEWER| The registered role id of iterative reviewer ||
|ROLE_ID_SUBMITTER| The registered role id of submitter ||
|TYPE_ID_TASK| The registered type id of a task ||
|DEFAULT_TIMELINE_TEMPLATE_ID| The default timeline template id ||
Expand Down
2 changes: 1 addition & 1 deletion models/Issue.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ const schema = new Schema({
required: true,
index: {
global: true,
rangeKey: 'id',
rangeKey: 'number',
project: true,
name: 'RepositoryIdIndex'
}
Expand Down
9 changes: 6 additions & 3 deletions models/Project.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,18 @@ const schema = new Schema({
title: {type: String, required: true},
tcDirectId: {
type: Number,
required: true
},
repoUrl: {
type: String,
required: true,
index: {
global: true,
rangeKey: 'id',
rangeKey: 'archived',
project: true,
name: 'TcDirectIdIndex'
name: 'RepoUrlIndex'
}
},
repoUrl: {type: String, required: true},
repoId: {type: String, required: false},
rocketChatWebhook: {type: String, required: false},
rocketChatChannelName: {type: String, required: false},
Expand Down
2 changes: 1 addition & 1 deletion models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const schema = new Schema({
required: true,
index: {
global: true,
rangeKey: 'id',
rangeKey: 'type',
project: true,
name: 'UsernameIndex'
}
Expand Down
40 changes: 36 additions & 4 deletions models/UserMapping.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,42 @@ const schema = new Schema({
name: 'TopcoderUsernameIndex'
}
},
githubUsername: String,
gitlabUsername: String,
githubUserId: Number,
gitlabUserId: Number
githubUsername: {
type: String,
index: {
global: true,
project: true,
rangKey: 'id',
name: 'GithubUsernameIndex'
}
},
gitlabUsername: {
type: String,
index: {
global: true,
project: true,
rangKey: 'id',
name: 'GitlabUsernameIndex'
}
},
githubUserId: {
type: Number,
index: {
global: true,
project: true,
rangKey: 'id',
name: 'GithubUserIdIndex'
}
},
gitlabUserId: {
type: Number,
index: {
global: true,
project: true,
rangKey: 'id',
name: 'GitlabUserIdIndex'
}
}
});

module.exports = schema;
8 changes: 3 additions & 5 deletions services/EventService.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ async function handleEventGracefully(event, data, err) {
// reschedule event
if (event.retryCount < config.RETRY_COUNT) {
logger.debug('Scheduling event for next retry');
const newEvent = { ...event };
const newEvent = {...event};
newEvent.retryCount += 1;
delete newEvent.copilot;
const timeoutKey = setTimeout(async () => {
Expand Down Expand Up @@ -79,17 +79,15 @@ async function handleEventGracefully(event, data, err) {
} else if (event.event === 'issue.created') {
if (err.name === 'ProcessorError' && err.statusCode && err.message) {
// comment for challenge creation failed
comment = `[${err.statusCode}]: ${err.message}`
comment = `[${err.statusCode}]: ${err.message}`;
} else {
// comment for challenge creation failed
comment = 'The challenge creation on the Topcoder platform failed. Please contact support to try again';
}
} else if (event.event === 'copilotPayment.add') {
// comment for copilot payment challenge create failed
comment = 'The copilot payment challenge creation on the Topcoder platform failed. Please contact support to try again';
await dbHelper.remove(models.CopilotPayment, {
id: { eq: data.id }
});
await dbHelper.removeCopilotPayment(models.CopilotPayment, data.id);
// we dont need to put comment for copilot payment
return;
}
Expand Down
2 changes: 1 addition & 1 deletion services/GithubService.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function _parseRepoUrl(fullName) {
const results = fullName.split('/');
const repo = results[results.length - 1];
const owner = _(results).slice(0, results.length - 1).join('/');
return { owner, repo };
return {owner, repo};
}

/**
Expand Down
59 changes: 30 additions & 29 deletions services/IssueService.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,7 @@ async function ensureChallengeExists(event, issue, create = true) {
*/
async function getProjectDetail(event) {
const fullRepoUrl = gitHelper.getFullRepoUrl(event);
const project = await dbHelper.scanOne(models.Project, {
repoUrl: fullRepoUrl,
archived: 'false'
});
const project = await dbHelper.queryOneActiveProject(models.Project, fullRepoUrl);

return project;
}
Expand Down Expand Up @@ -202,7 +199,11 @@ async function handleIssueAssignment(event, issue, force = false) {
}
return;
}

// if the issue has payment success we'll ignore this process.
if (dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_PAYMENT_SUCCESSFUL) {
logger.debugWithContext('Ignoring this issue processing. The issue has challenge_payment_successful.', event, issue);
return;
}
// Handle multiple assignees. TC-X allows only one assignee.
if (event.data.issue.assignees && event.data.issue.assignees.length > 1) {
const comment = 'Topcoder-X only supports a single assignee on a ticket to avoid issues with payment';
Expand Down Expand Up @@ -332,7 +333,11 @@ async function handleIssueUpdate(event, issue) {
}
return;
}

// if the issue has payment success we'll ignore this process.
if (dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_PAYMENT_SUCCESSFUL) {
logger.debugWithContext('Ignoring this issue processing. The issue has challenge_payment_successful.', event, issue);
return;
}
if (dbIssue.title === issue.title &&
dbIssue.body === issue.body &&
dbIssue.prizes.length === issue.prizes.length &&
Expand Down Expand Up @@ -410,7 +415,7 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
let comment = 'This ticket was not processed for payment. If you would like to process it for payment,';
comment += ' please reopen it, add the ```' + config.FIX_ACCEPTED_ISSUE_LABEL + '``` label, and then close it again';// eslint-disable-line
await gitHelper.createComment(event, issue.number, comment);
closeChallenge = true;
return;
}

// if issue is close with cancelled label
Expand Down Expand Up @@ -493,8 +498,8 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
event.createCopilotPayments = createCopilotPayments;

if (createCopilotPayments) {
logger.debugWithContext(`Setting copilot payment`);
logger.debugWithContext('Setting copilot payment');

const updateBody = {
prizeSets: [{
type: 'placement',
Expand All @@ -504,13 +509,11 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
type: 'copilot',
prizes: [{type: 'USD', value: 40}]
}
]
]
};
await topcoderApiHelper.updateChallenge(dbIssue.challengeUUID, updateBody);

}
else {
logger.debugWithContext('Create copilot payments is unchecked on the Topcoder-X project setup, so skipping', event, issue);
} else {
logger.debugWithContext('Create copilot payments is unchecked on the Topcoder-X project setup, so skipping', event, issue);
}

logger.debugWithContext(`Getting the topcoder member ID for member name: ${assigneeMember.topcoderUsername}`, event, issue);
Expand All @@ -525,15 +528,6 @@ async function handleIssueClose(event, issue) { // eslint-disable-line
logger.debugWithContext('Assignee is already set, so skipping', event, issue);
}

// activate challenge

if (challenge.status === 'Draft') {
await topcoderApiHelper.activateChallenge(dbIssue.challengeUUID);
//HACK - sleep 30 seconds so the legacy processor has time to "catch up"
// logger.debugWithContext('Sleeping for 1 seconds after activation so everything propagates...', event, issue);
// await new Promise(resolve => setTimeout(resolve, 1000));
}

logger.debugWithContext(`Closing challenge with winner ${assigneeMember.topcoderUsername}(${winnerId})`, event, issue);
await topcoderApiHelper.closeChallenge(dbIssue.challengeUUID, winnerId, assigneeMember.topcoderUsername);
event.paymentSuccessful = true;
Expand Down Expand Up @@ -644,11 +638,11 @@ async function handleIssueCreate(event, issue, forceAssign = false) {
status: constants.ISSUE_STATUS.CHALLENGE_CREATION_SUCCESSFUL,
updatedAt: new Date()
});

logger.debugWithContext(`Adding copilot to issue: ${event.copilot.topcoderUsername}`, event, issue);
// get copilot tc user id
await topcoderApiHelper.addResourceToChallenge(issue.challengeUUID, event.copilot.topcoderUsername, config.ROLE_ID_COPILOT);

await topcoderApiHelper.addResourceToChallenge(issue.challengeUUID, event.copilot.topcoderUsername, config.ROLE_ID_ITERATIVE_REVIEWER);
} catch (e) {
logger.error(`Challenge creation failure: ${e}`);
delete issueCreationLock[creationLockKey];
Expand Down Expand Up @@ -698,6 +692,11 @@ async function handleIssueLabelUpdated(event, issue) {
logger.debugWithContext('DB record not found. Issue label update ignored.', event, issue);
return;
}
// if the issue has payment success we'll ignore this process.
if (dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_PAYMENT_SUCCESSFUL) {
logger.debugWithContext('Ignoring this issue processing. The issue has challenge_payment_successful.', event, issue);
return;
}
await dbHelper.update(models.Issue, dbIssue.id, {
labels: issue.labels,
updatedAt: new Date()
Expand All @@ -720,6 +719,11 @@ async function handleIssueUnAssignment(event, issue) {
// Ignore it.
return;
}
// if the issue has payment success we'll ignore this process.
if (dbIssue.status === constants.ISSUE_STATUS.CHALLENGE_PAYMENT_SUCCESSFUL) {
logger.debugWithContext('Ignoring this issue processing. The issue has challenge_payment_successful.', event, issue);
return;
}
if (dbIssue.assignee) {
const assigneeUserId = await gitHelper.getUserIdByLogin(event, dbIssue.assignee);
if (!assigneeUserId) {
Expand Down Expand Up @@ -861,10 +865,7 @@ async function process(event) {
const fullRepoUrl = gitHelper.getFullRepoUrl(event);
event.data.repository.repoUrl = fullRepoUrl;

const project = await dbHelper.scanOne(models.Project, {
repoUrl: fullRepoUrl,
archived: 'false'
});
const project = await dbHelper.queryOneActiveProject(models.Project, fullRepoUrl);

issue.projectId = project.id;

Expand Down
25 changes: 9 additions & 16 deletions services/UserService.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,20 @@ async function getTCUserName(provider, gitUser) {
const criteria = {};
if (_.isNumber(gitUser) || v.isUUID(gitUser)) {
if (provider === 'github') {
criteria.githubUserId = gitUser;
return await dbHelper.queryOneUserMappingByGithubUserId(models.UserMapping, gitUser);
} else if (provider === 'gitlab') {
criteria.gitlabUserId = gitUser;
return await dbHelper.queryOneUserMappingByGitlabUserId(models.UserMapping, gitUser);
}
} else if (_.isString(gitUser) || v.isEmail(gitUser)) {
if (provider === 'github') {
criteria.githubUsername = gitUser;
return await dbHelper.queryOneUserMappingByGithubUsername(models.UserMapping, gitUser);
} else if (provider === 'gitlab') {
criteria.gitlabUsername = gitUser;
return await dbHelper.queryOneUserMappingByGitlabUsername(models.UserMapping, gitUser);
}
}
if (_.isEmpty(criteria)) {
throw new Error('Can\'t find the TCUserName. Invalid gitUser.');
}
return await dbHelper.scanOne(models.UserMapping, criteria);
}

getTCUserName.schema = {
Expand All @@ -65,19 +64,16 @@ async function getRepositoryCopilotOrOwner(provider, repoFullName) {
} else if (provider === 'gitlab') {
fullRepoUrl = `${config.GITLAB_API_BASE_URL}/${repoFullName}`;
}
const project = await dbHelper.scanOne(models.Project, {
repoUrl: fullRepoUrl
});
const project = await dbHelper.queryOneActiveProject(models.Project, fullRepoUrl);

const hasCopilot = project.copilot !== undefined; // eslint-disable-line no-undefined
if (!project || !project.owner) {
// throw this repo is not managed by Topcoder x tool
throw new Error(`This repository '${repoFullName}' is not managed by Topcoder X tool.`);
}

const userMapping = await dbHelper.scanOne(models.UserMapping, {
topcoderUsername: {eq: hasCopilot ? project.copilot.toLowerCase() : project.owner.toLowerCase()}
});
const userMapping = await dbHelper.queryOneUserMappingByTCUsername(
models.UserMapping, hasCopilot ? project.copilot.toLowerCase() : project.owner.toLowerCase());

logger.debug('userMapping');
logger.debug(userMapping);
Expand All @@ -87,11 +83,8 @@ async function getRepositoryCopilotOrOwner(provider, repoFullName) {
(provider === 'gitlab' && !userMapping.gitlabUserId)) {
throw new Error(`Couldn't find githost username for '${provider}' for this repository '${repoFullName}'.`);
}
const user = await dbHelper.scanOne(models.User, {
username: provider === 'github' ? userMapping.githubUsername : // eslint-disable-line no-nested-ternary
userMapping.gitlabUsername,
type: provider
});
const user = await dbHelper.queryOneUserByType(models.User,
provider === 'github' ? userMapping.githubUsername : userMapping.gitlabUsername, provider); // eslint-disable-line no-nested-ternary

if (!user && !hasCopilot) {
// throw no copilot is configured
Expand Down
Loading