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

Commit 3e535dd

Browse files
committed
Merge pull request #181 from bugbuka/master
Module Assembly - TopCoder NodeJS Unregister Challenge API
2 parents e53b3b7 + e60992f commit 3e535dd

File tree

54 files changed

+1211
-49
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+1211
-49
lines changed

actions/challengeRegistration.js

Lines changed: 7 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,17 @@
33
*
44
* The APIs to register a challenge (studio category or software category) for the current logged-in user.
55
*
6-
* @version 1.2
7-
* @author ecnu_haozi, xjtufreeman, TCSASSEMBLER
6+
* @version 1.3
7+
* @author ecnu_haozi, xjtufreeman, bugbuka
88
*
99
* changes in 1.1:
1010
* Combine Challenge Registration API(BUGR-11058)
1111
*
1212
* changes in 1.2:
1313
* Integrate the forums operation(Module Assembly - Integrating Forums Wrapper with Challenge Registration API)
14+
*
15+
* changes in 1.3:
16+
* move common function getForumWrapper, aduitResourceAddition to challengeHelper.js
1417
*/
1518
"use strict";
1619

@@ -26,25 +29,6 @@ var ForbiddenError = require('../errors/ForbiddenError');
2629
*/
2730
var forumWrapper = null;
2831

29-
/**
30-
* Get forum wrapper. It is initialized only once.
31-
* @param {Object} api The api object that is used to access the infrastructure.
32-
* @param {Function<err, forumWrapper>} callback the callback function
33-
*/
34-
var getForumWrapper = function (api, callback) {
35-
if (forumWrapper) {
36-
callback(null, forumWrapper);
37-
} else {
38-
try {
39-
forumWrapper = new ForumWrapper(api.config.general.devForumJNDI);
40-
callback(null, forumWrapper);
41-
} catch (ex) {
42-
api.log('Failed to connect to forum: ' + ex + " " + (ex.stack || ''), 'error');
43-
callback(new Error('Failed to connect to forum'));
44-
}
45-
}
46-
};
47-
4832
//constants
4933
var DESIGN_PROJECT_TYPE = 1,
5034
DEVELOPMENT_PROJECT_TYPE = 2,
@@ -162,27 +146,6 @@ var persistResource = function (api, resourceId, userId, challengeId, dbConnecti
162146
});
163147
};
164148

165-
/**
166-
* Audit the challenge registration on table 'tcs_catalog.project_user_audit'.
167-
*
168-
* @param {Object} api The api object that is used to access the infrastructure.
169-
* @param {Number} userId The current logged-in user's id.
170-
* @param {Number} challengeId The id of the challenge to register.
171-
* @param {Object} dbConnectionMap The database connection map for the current request.
172-
* @param {Function<err, data>} next The callback to be called after this function is done.
173-
*/
174-
var aduitResourceAddition = function (api, userId, challengeId, dbConnectionMap, next) {
175-
api.dataAccess.executeQuery("audit_challenge_registration", {
176-
projectId: challengeId,
177-
resourceUserId: userId,
178-
resourceRoleId: SUBMITTER_RESOURCE_ROLE_ID,
179-
auditActionTypeId: PROJECT_USER_AUDIT_CREATE_TYPE,
180-
actionUserId: userId
181-
},
182-
dbConnectionMap,
183-
next);
184-
};
185-
186149
/**
187150
* Check if the rating suit for software category contests.
188151
* The code logic is duplicated from server-side java code.
@@ -282,7 +245,7 @@ var projectTrack = function (api, userId, challengeId, componentInfo, dbConnecti
282245
function (resourceId, callback) {
283246
async.parallel([
284247
function (cb) {
285-
aduitResourceAddition(api, userId, challengeId, dbConnectionMap, cb);
248+
api.challengeHelper.aduitResourceAddition(api, userId, challengeId, SUBMITTER_RESOURCE_ROLE_ID, PROJECT_USER_AUDIT_CREATE_TYPE, dbConnectionMap, cb);
286249
},
287250
function (cb) {
288251
prepareProjectResult(
@@ -487,7 +450,7 @@ var grantForumAccess = function (api, userId, activeForumCategoryId, next) {
487450
api.log('start to grant user ' + userId + ' forum category ' + activeForumCategoryId + ' access.');
488451
async.waterfall([
489452
function (cb) {
490-
getForumWrapper(api, cb);
453+
api.challengeHelper.getForumWrapper(api, cb);
491454
}, function (forumWrapper, cb) {
492455
forumWrapper.assignRole(userId, "Software_Users_" + activeForumCategoryId, function (err) {
493456
if (err) {

actions/challengeUnregistration.js

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/*
2+
* Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
3+
*
4+
* The APIs to un-register a challenge (studio category or software category) for the current logged-in user.
5+
*
6+
* @version 1.0
7+
* @author bugbuka
8+
*/
9+
"use strict";
10+
11+
var async = require('async');
12+
var _ = require('underscore');
13+
var moment = require('moment');
14+
var ForumWrapper = require("forum-connector").ForumWrapper;
15+
var UnauthorizedError = require('../errors/UnauthorizedError');
16+
var NotFoundError = require('../errors/NotFoundError');
17+
var ForbiddenError = require('../errors/ForbiddenError');
18+
19+
//constants
20+
var SUBMITTER_RESOURCE_ROLE_ID = 1,
21+
PROJECT_USER_AUDIT_DELETE_TYPE = 2;
22+
23+
/**
24+
* Checks if specified challenge category ID implies the presence of records in project_result and component_inquiry
25+
* tables for challenge registrants.
26+
*
27+
* @param {Number} categoryId - ID for challenge category.
28+
* @returns {boolean} true if above records are required; false otherwise.
29+
* @since 1.3
30+
*/
31+
function isProjectResultCategory(categoryId) {
32+
return (categoryId === 1 // Component Design
33+
|| categoryId === 2 // Component Development
34+
|| categoryId === 5 // Component Testing
35+
|| categoryId === 6 // Application Specification
36+
|| categoryId === 7 // Application Architecture
37+
|| categoryId === 9 // Bug Hunt
38+
|| categoryId === 13 // Test Scenarios
39+
|| categoryId === 26 // Test Suites
40+
|| categoryId === 14 // Application Assembly
41+
|| categoryId === 23 // Application Conceptualization
42+
|| categoryId === 19 // UI Prototype
43+
|| categoryId === 24 // RIA Build
44+
|| categoryId === 25 // RIA Component
45+
|| categoryId === 29 // Copilot Posting
46+
|| categoryId === 35 // Content Creation
47+
|| categoryId === 36 // Reporting
48+
|| categoryId === 38 // First2Finish
49+
|| categoryId === 39 // Code
50+
);
51+
}
52+
53+
/**
54+
* Remove forum permissions. It is initialized only once.
55+
*
56+
* @param {Object} api The api object that is used to access the infrastructure.
57+
* @param {Number} userId The current logged-in user's id.
58+
* @param {Number} forumCategoryId The sql params.
59+
* @param {Function<err, data>} next The callback to be called after this function is done.
60+
*/
61+
var removeForumPermissions = function (api, userId, forumCategoryId, next) {
62+
63+
if (api.config.general.grantForumAccess !== true || forumCategoryId === 0) {
64+
next();
65+
return;
66+
}
67+
68+
if (forumCategoryId === null) {
69+
api.log('Could not find forum category ' + forumCategoryId, 'error');
70+
next(new Error('Could not find forum category ' + forumCategoryId));
71+
return;
72+
}
73+
74+
api.log('start to remove user ' + userId + ' from forum category ' + forumCategoryId + '.');
75+
async.waterfall([
76+
function (cb) {
77+
api.challengeHelper.getForumWrapper(api, cb);
78+
}, function (forumWrapper, cb) {
79+
forumWrapper.removeRole(userId, "Software_Users_" + forumCategoryId, cb);
80+
}, function (forumWrapper, cb) {
81+
forumWrapper.removeRole(userId, "Software_Moderators_" + forumCategoryId, cb);
82+
}, function (forumWrapper, cb) {
83+
forumWrapper.removeUserPermission(userId, forumCategoryId, cb);
84+
}, function (forumWrapper, cb) {
85+
forumWrapper.deleteCategoryWatch(userId, forumCategoryId, cb);
86+
}
87+
], function (err) {
88+
if (err) {
89+
next(err);
90+
return;
91+
}
92+
next();
93+
});
94+
};
95+
96+
/**
97+
* Unregister a development (software) challenge for the current logged-in user.
98+
*
99+
* @param {Object} api The api object that is used to access the infrastructure.
100+
* @param {Number} userId The current logged-in user's id.
101+
* @param {Object} sqlParams The sql params.
102+
* @param {Object} unregisterInfo The data used to do unregistration.
103+
* @param {Object} dbConnectionMap The database connection map for the current request.
104+
* @param {Function<err, data>} next The callback to be called after this function is done.
105+
*/
106+
var unregisterChallenge = function (api, userId, sqlParams, unregisterInfo, dbConnectionMap, next) {
107+
async.series([
108+
function (cb) {
109+
if (sqlParams.isStudio || isProjectResultCategory(sqlParams.categoryId)) {
110+
api.dataAccess.executeQuery("delete_challenge_result", sqlParams, dbConnectionMap, cb);
111+
} else {
112+
cb();
113+
}
114+
115+
}, function (cb) {
116+
117+
if (_.size(unregisterInfo.userChallengeResources) < 1) {
118+
api.log("Could not find user challenge resource", 'error');
119+
cb(new Error('Could not find user challenge resource'));
120+
return;
121+
}
122+
var submitterRoleResourceId = _.filter(unregisterInfo.userChallengeResources, function (resource) {
123+
return resource.resource_role_id === SUBMITTER_RESOURCE_ROLE_ID;
124+
})[0].resource_id;
125+
126+
api.dataAccess.executeQuery("delete_challenge_resources", {resourceId : submitterRoleResourceId}, dbConnectionMap, cb);
127+
}, function (cb) {
128+
129+
api.challengeHelper.aduitResourceAddition(api, userId, sqlParams.challengeId, SUBMITTER_RESOURCE_ROLE_ID, PROJECT_USER_AUDIT_DELETE_TYPE, dbConnectionMap, cb);
130+
}, function (cb) {
131+
132+
if (_.size(unregisterInfo.userChallengeResources) === 1 && unregisterInfo.userChallengeResources[0].resource_role_id === SUBMITTER_RESOURCE_ROLE_ID) { // Only remove forum permissions if the user has no other roles left.
133+
if (unregisterInfo.challengeForum.length === 0) {
134+
api.log("Could not find user challenge forum", 'error');
135+
cb(new Error('Could not find user challenge forum'));
136+
return;
137+
}
138+
var forumCategoryId = parseInt(unregisterInfo.challengeForum[0].forum_category_id, 10);
139+
removeForumPermissions(api, userId, forumCategoryId, cb);
140+
}
141+
cb();
142+
}
143+
], next);
144+
};
145+
146+
/**
147+
* The action to unregister a challenge for the current logged-in user.
148+
*
149+
* @param {Object} api The api object that is used to access the infrastructure.
150+
* @param {Object} connection The connection for the current request.
151+
* @param {Function<err, data>} next The callback to be called after this function is done.
152+
*/
153+
var unregisterChallengeAction = function (api, connection, next) {
154+
155+
var helper = api.helper,
156+
sqlParams = {},
157+
userId = connection.caller.userId,
158+
challengeId = Number(connection.params.challengeId),
159+
160+
execQuery = function (name) {
161+
return function (cbx) {
162+
api.dataAccess.executeQuery(name, sqlParams, connection.dbConnectionMap, cbx);
163+
};
164+
};
165+
166+
async.waterfall([
167+
function (cb) {
168+
169+
//Simple validations of the incoming parameters
170+
var error = helper.checkPositiveInteger(challengeId, 'challengeId') ||
171+
helper.checkMaxInt(challengeId, 'challengeId') ||
172+
helper.checkMember(connection, 'You don\'t have the authority to access this. Please login.');
173+
174+
if (error) {
175+
cb(error);
176+
return;
177+
}
178+
179+
//Check if the user passes validations for joining the challenge
180+
sqlParams.userId = userId;
181+
sqlParams.challengeId = challengeId;
182+
183+
api.dataAccess.executeQuery("challenge_unregistration_validations", sqlParams, connection.dbConnectionMap, cb);
184+
}, function (rows, cb) {
185+
if (rows.length === 0) {
186+
187+
cb(new NotFoundError('No such challenge exists.'));
188+
} else if (!rows[0].reg_open) {
189+
190+
cb(new ForbiddenError('You cannot unregister since registration phase is closed.'));
191+
} else if (!rows[0].user_has_submitter_resource_role) {
192+
193+
cb(new ForbiddenError('You are not registered for this challenge.'));
194+
}
195+
196+
sqlParams.categoryId = rows[0].category_id;
197+
sqlParams.isStudio = rows[0].is_studio;
198+
199+
async.series({
200+
userChallengeResources: execQuery('get_user_challenge_resource'),
201+
challengeForum: execQuery('get_challenge_forum')
202+
}, cb);
203+
204+
},
205+
function (result, cb) {
206+
unregisterChallenge(api, userId, sqlParams, result, connection.dbConnectionMap, cb);
207+
}
208+
], function (err) {
209+
if (err) {
210+
api.helper.handleError(api, connection, err);
211+
} else {
212+
api.log("unregister the challenge succeeded.", 'debug');
213+
connection.response = {message : "ok"};
214+
}
215+
next(connection, true);
216+
});
217+
218+
};
219+
220+
/**
221+
* The API to unregister a challenge for the current logged-in user.
222+
*/
223+
exports.unregisterChallenge = {
224+
name: "unregisterChallenge",
225+
description: "unregisterChallenge",
226+
inputs: {
227+
required: ["challengeId"],
228+
optional: []
229+
},
230+
blockedConnectionTypes: [],
231+
outputExample: {},
232+
version: 'v2',
233+
cacheEnabled : false,
234+
transaction: 'write',
235+
databases: ["tcs_catalog"],
236+
run: function (api, connection, next) {
237+
if (connection.dbConnectionMap) {
238+
api.log("Execute unregisterChallenge#run", 'debug');
239+
unregisterChallengeAction(api, connection, next);
240+
} else {
241+
api.helper.handleNoConnection(api, connection, next);
242+
}
243+
}
244+
};
245+
246+

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy