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

Commit b680134

Browse files
committed
Merge branch 'dev' of https://github.com/cloudspokes/tc-api into thabo-I-129863-new-registration-emails
2 parents 4ab97fa + d7d0b62 commit b680134

25 files changed

+900
-168
lines changed

actions/user.js

Lines changed: 127 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
/*
22
* Copyright (C) 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.0
5-
* @author muzehyun
4+
* @version 1.1
5+
* @author muzehyun, Ghost_141
6+
* Changes in 1.1:
7+
* - Implement user activation email api.
68
*/
79
'use strict';
810
var async = require('async');
@@ -12,6 +14,18 @@ var ForbiddenError = require('../errors/ForbiddenError');
1214
var UnauthorizedError = require('../errors/UnauthorizedError');
1315
var IllegalArgumentError = require('../errors/IllegalArgumentError');
1416

17+
/**
18+
* The activation email subject.
19+
* @since 1.1
20+
*/
21+
var activationEmailSubject = "Topcoder User Registration Activation";
22+
23+
/**
24+
* The activation email sender name.
25+
* @since 1.1
26+
*/
27+
var activationEmailSenderName = "Topcoder API";
28+
1529
/**
1630
* It validates activation code and retrieves user id from activation code
1731
* @param {String} activationCode - activation code string
@@ -27,6 +41,16 @@ function getCoderId(activationCode, helper) {
2741
return 0;
2842
}
2943

44+
/**
45+
* Get cache key for user resend times in cache.
46+
* @param {String} handle - The handle of user.
47+
* @returns {string} The cache key.
48+
* @since 1.1
49+
*/
50+
function getCacheKeyForResendTimes(handle) {
51+
return 'user-activation-' + handle;
52+
}
53+
3054
/**
3155
* The API for activate user
3256
*/
@@ -121,8 +145,12 @@ exports.activateUser = {
121145
};
122146
// send email
123147
api.tasks.enqueue("sendEmail", emailParams, 'default');
148+
124149
result = { success: true };
125-
cb();
150+
// Remove cache from resend times from server.
151+
api.cache.destroy(getCacheKeyForResendTimes(handle), function () {
152+
cb();
153+
});
126154
}
127155
], function (err) {
128156
if (err) {
@@ -134,3 +162,99 @@ exports.activateUser = {
134162
});
135163
}
136164
};
165+
166+
/**
167+
* Handle user activation email here.
168+
* @param {Object} api - The api object.
169+
* @param {Object} connection - The database connection object map.
170+
* @param {Function} next - The callback function.
171+
* @since 1.1
172+
*/
173+
function userActivationEmail(api, connection, next) {
174+
var helper = api.helper, caller = connection.caller, currentResendTimes, activationCode,
175+
dbConnectionMap = connection.dbConnectionMap,
176+
cacheKey = 'user-activation-' + caller.handle;
177+
178+
async.waterfall([
179+
function (cb) {
180+
cb(helper.checkMember(connection, 'You must login for this endpoint.'));
181+
},
182+
function (cb) {
183+
api.dataAccess.executeQuery('check_user_activated', { handle: caller.handle }, dbConnectionMap, cb);
184+
},
185+
function (rs, cb) {
186+
if (rs[0].status === 'A') {
187+
cb(new BadRequestError("You're already activated."));
188+
return;
189+
}
190+
helper.getCachedValue(cacheKey, cb);
191+
},
192+
function (resendTimes, cb) {
193+
if (_.isUndefined(resendTimes)) {
194+
// We need to send the activation email and store the resend times.
195+
currentResendTimes = 0;
196+
} else {
197+
if (resendTimes >= api.config.tcConfig.userActivationResendLimit) {
198+
cb(new BadRequestError('Sorry, you already reached the limit of resend times. Please contact for support.'));
199+
return;
200+
}
201+
currentResendTimes = resendTimes;
202+
}
203+
api.dataAccess.executeQuery('get_user_email_and_handle', { userId: caller.userId }, dbConnectionMap, cb);
204+
},
205+
function (rs, cb) {
206+
activationCode = helper.generateActivationCode(caller.userId);
207+
api.tasks.enqueue("sendEmail",
208+
{
209+
subject : activationEmailSubject,
210+
activationCode : activationCode,
211+
template : 'activation_email',
212+
toAddress : rs[0].email,
213+
fromAddress : process.env.TC_EMAIL_ACCOUNT,
214+
senderName : activationEmailSenderName,
215+
url : process.env.TC_ACTIVATION_SERVER_NAME + '/reg2/activate.action?code=' + activationCode,
216+
userHandle : rs[0].handle
217+
}, 'default');
218+
api.cache.save(cacheKey, currentResendTimes + 1, api.config.tcConfig.userActivationCacheLifeTime,
219+
function (err) {
220+
cb(err);
221+
});
222+
}
223+
], function (err) {
224+
if (err) {
225+
helper.handleError(api, connection, err);
226+
} else {
227+
connection.response = {
228+
success: true
229+
};
230+
}
231+
next(connection, true);
232+
});
233+
}
234+
235+
/**
236+
* The API for activate user email.
237+
* @since 1.1
238+
*/
239+
exports.userActivationEmail = {
240+
name: 'userActivationEmail',
241+
description: 'Trigger sending user activation email.',
242+
inputs: {
243+
required: [],
244+
optional: []
245+
},
246+
blockedConnectionTypes: [],
247+
outputExample: {},
248+
version: 'v2',
249+
transaction: 'read',
250+
databases: ['common_oltp'],
251+
cacheEnabled: false,
252+
run: function (api, connection, next) {
253+
if (connection.dbConnectionMap) {
254+
api.log('Execute userActivationEmail#run', 'debug');
255+
userActivationEmail(api, connection, next);
256+
} else {
257+
api.helper.handleNoConnection(api, connection, next);
258+
}
259+
}
260+
};

apiary.apib

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2083,6 +2083,63 @@ Register a new user.
20832083
"description":"Servers are up but overloaded. Try again later."
20842084
}
20852085

2086+
## My Profile [/user/activation-email]
2087+
### My Profile [GET]
2088+
+ Request
2089+
2090+
+ Headers
2091+
2092+
Authorization : Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZHwxMzI0NTYiLCJleHAiOjEzOTI4MTc4ODQsImF1ZCI6InRvcGNvZGVyIiwiaWF0IjoxMzkyNzU3ODg0fQ.7X2IKkiyyI1ExSM5GNpdhJ8fGGK5-oAjzccX6YL_BKY
2093+
2094+
+ Response 200 (application/json)
2095+
2096+
{
2097+
success: true
2098+
}
2099+
2100+
+ Response 400 (application/json)
2101+
2102+
{
2103+
"name":"Bad Request",
2104+
"value":"400",
2105+
"description":"This message will explain why the request is invalid or cannot be served."
2106+
"details:":"You're already activated."
2107+
}
2108+
2109+
+ Response 400 (application/json)
2110+
2111+
{
2112+
"name":"Bad Request",
2113+
"value":"400",
2114+
"description":"This message will explain why the request is invalid or cannot be served."
2115+
"details:":"Sorry, you already reached the limit of resend times. Please contact for support."
2116+
}
2117+
2118+
+ Response 401 (application/json)
2119+
2120+
{
2121+
"name":"Unauthorized",
2122+
"value":"401",
2123+
"description":"This message will explain why the request is invalid or cannot be served."
2124+
"details:":"You must login for this endpoint."
2125+
}
2126+
2127+
+ Response 500 (application/json)
2128+
2129+
{
2130+
"name":"Internal Server Error",
2131+
"value":"500",
2132+
"description":"Unknown server error. Please contact support."
2133+
}
2134+
2135+
+ Response 503 (application/json)
2136+
2137+
{
2138+
"name":"Service Unavailable",
2139+
"value":"503",
2140+
"description":"Servers are up but overloaded. Try again later."
2141+
}
2142+
20862143
## Search My Challenges [/user/challenges?type={type}&pageIndex={pageIndex}&pageSize={pageSize}&sortColumn={sortColumn}&sortOrder={sortOrder}&communityId={communityId}&submissionEndFrom={submissionEndFrom}&submissionEndTo={submissionEndTo}&challengeType={challengeType}&platforms={platforms}&technologies={technologies}&prizeLowerBound={prizeLowerBound}&prizeUpperBound={prizeUpperBound}]
20872144
### Search My Challenges [GET]
20882145

@@ -6461,6 +6518,10 @@ Request
64616518
{
64626519
"problemId": 10194,
64636520
"problemName": "DoubleLetter",
6521+
"component_id": 30305,
6522+
"room_id": 28,
6523+
"round_id": 9873,
6524+
"division_id": 2,
64646525
"problemType": "Single",
64656526
"points": 500,
64666527
"difficulty": "Medium",
@@ -6470,6 +6531,10 @@ Request
64706531
{
64716532
"problemId": 10195,
64726533
"problemName": "BlackAndWhite",
6534+
"component_id": 30309,
6535+
"room_id": 30,
6536+
"round_id": 9877,
6537+
"division_id": 2,
64736538
"problemType": "Team",
64746539
"points": 800,
64756540
"difficulty": "Hard",

config/tc-config.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
/*
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv, bugbuka, flytoj2ee
5-
* @version 1.26
4+
* @author vangavroche, Ghost_141, kurtrips, Sky_, isv, bugbuka, flytoj2ee, TCSASSEMBLER
5+
* @version 1.27
66
* changes in 1.1:
77
* - add defaultCacheLifetime parameter
88
* changes in 1.2:
@@ -62,12 +62,16 @@
6262
* - Move configuration contents in tc-config.js
6363
* Changes in 1.26:
6464
* - Add studioReview object for get studio review opportunities api.
65+
* Changes in 1.27:
66+
* Add userActivationResendLimit and userActivationCacheLifeTime for user activation email api.
6567
*/
6668

6769
"use strict";
6870

6971
var config = {
7072
defaultUserCacheLifetime: process.env.USER_CACHE_EXPIRY || 1000 * 60 * 60 * 24, //24 hours default
73+
userActivationResendLimit: 5,
74+
userActivationCacheLifeTime: 1000 * 60 * 60 * 23 * 30,
7175
pastChallengesCacheLifetime: 24 * 60 * 60 * 1000,
7276
resetTokenPrefix: 'tokens-',
7377
resetTokenSuffix: '-reset-token',

queries/get_practice_problems

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,77 +5,72 @@ FIRST @pageSize@
55
FROM table(MULTISET(
66
SELECT
77
p.problem_id
8+
, c.component_id
9+
, ro.room_id
10+
, rc.round_id
11+
, rc.division_id
812
, p.name AS problem_name
913
, ptl.problem_type_desc AS problem_type
1014
, CASE WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 1) THEN 'Easy'::nvarchar(50)
1115
WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 2) THEN 'Medium'::nvarchar(50)
1216
WHEN (p.problem_type_id = 1 AND p.proposed_difficulty_id = 3) THEN 'Hard'::nvarchar(50)
1317
END AS difficulty
14-
, (
15-
SELECT DISTINCT rc.points
16-
FROM round_component rc
17-
INNER JOIN component c ON c.component_id = rc.component_id AND c.problem_id = p.problem_id
18-
INNER JOIN round_segment rs ON rs.round_id = rc.round_id AND rs.segment_id = 1
19-
WHERE rs.start_time = (SELECT MAX(rss.start_time) FROM round_component rc
20-
INNER JOIN component c ON c.component_id = rc.component_id AND c.problem_id = p.problem_id
21-
INNER JOIN round_segment rss ON rss.round_id = rc.round_id AND rss.segment_id = 1)
22-
)AS points
18+
, rc.points
2319
, CASE WHEN EXISTS (
2420
SELECT 1
2521
FROM component_state cs
26-
INNER JOIN component c ON c.component_id = cs.component_id AND c.problem_id = p.problem_id
2722
WHERE cs.status_id < 120
23+
AND cs.component_id = c.component_id
2824
AND cs.coder_id = @userId@
2925
UNION ALL
3026
SELECT 1
3127
FROM practice_component_state pcs
32-
INNER JOIN component c ON c.component_id = pcs.component_id AND c.problem_id = p.problem_id
3328
WHERE pcs.status_id < 120
29+
AND pcs.component_id = c.component_id
3430
AND pcs.coder_id = @userId@
3531
) THEN 'New'::nvarchar(50)
3632
WHEN EXISTS (
3733
SELECT 1
3834
FROM component_state cs
39-
INNER JOIN component c ON c.component_id = cs.component_id AND c.problem_id = p.problem_id
4035
WHERE cs.status_id = 150
36+
AND cs.component_id = c.component_id
4137
AND cs.coder_id = @userId@
4238
UNION ALL
4339
SELECT 1
4440
FROM practice_component_state pcs
45-
INNER JOIN component c ON c.component_id = pcs.component_id AND c.problem_id = p.problem_id
4641
WHERE pcs.status_id = 150
42+
AND pcs.component_id = c.component_id
4743
AND pcs.coder_id = @userId@
4844
) THEN 'Solved'::nvarchar(50)
4945
WHEN EXISTS (
5046
SELECT 1
5147
FROM component_state cs
52-
INNER JOIN component c ON c.component_id = cs.component_id AND c.problem_id = p.problem_id
5348
WHERE cs.status_id >= 120
49+
AND cs.component_id = c.component_id
5450
AND cs.status_id != 150
5551
AND cs.coder_id = @userId@
5652
UNION ALL
5753
SELECT 1
5854
FROM practice_component_state pcs
59-
INNER JOIN component c ON c.component_id = pcs.component_id AND c.problem_id = p.problem_id
6055
WHERE pcs.status_id >= 120
56+
AND pcs.component_id = c.component_id
6157
AND pcs.status_id != 150
6258
AND pcs.coder_id = @userId@
6359
) THEN 'Viewed'::nvarchar(50)
6460
END AS status
6561
, NVL((
6662
SELECT
67-
points
68-
FROM component_state cs
69-
INNER JOIN component c ON c.component_id = cs.component_id AND c.problem_id = p.problem_id
70-
INNER JOIN submission s ON s.submission_number = cs.submission_number AND s.component_state_id = cs.component_state_id
71-
WHERE cs.coder_id = @userId@
72-
AND s.submit_time = (
73-
SELECT MAX(submit_time)
74-
FROM component_state cs
75-
INNER JOIN submission s ON s.submission_number = cs.submission_number AND s.component_state_id = cs.component_state_id
76-
INNER JOIN component c ON c.component_id = cs.component_id AND c.problem_id = p.problem_id)
63+
points
64+
FROM practice_component_state pcs
65+
WHERE pcs.round_id = rc.round_id
66+
AND pcs.component_id = c.component_id
67+
AND pcs.coder_id = @userId@
7768
), 0) AS my_points
7869
FROM problem p
70+
INNER JOIN component c ON c.problem_id = p.problem_id
71+
INNER JOIN round_component rc ON rc.component_id = c.component_id
72+
INNER JOIN round r ON r.round_id = rc.round_id AND r.status = 'A'
73+
INNER JOIN room ro ON ro.round_id = rc.round_id AND ro.room_type_id = 3 -- practice room
7974
INNER JOIN problem_type_lu ptl ON ptl.problem_type_id = p.problem_type_id
8075
WHERE p.status_id = 90
8176
)) srp

0 commit comments

Comments
 (0)