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

Commit d7d0b62

Browse files
committed
Merge pull request #367 from Ghost141/user_activation_email_api
user_activation_email_api
2 parents 283c14e + a1b29d8 commit d7d0b62

File tree

9 files changed

+365
-7
lines changed

9 files changed

+365
-7
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: 57 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

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_user_email_and_handle

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
SELECT handle, email FROM email_user WHERE user_id = @userId@
1+
SELECT
2+
u.handle
3+
, e.address
4+
FROM user u
5+
, email e
6+
WHERE u.user_id = @userId@
7+
AND u.user_id = e.user_id

routes.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
* Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved.
33
*
4-
* @version 1.58
4+
* @version 1.59
55
* @author vangavroche, Sky_, muzehyun, kurtrips, Ghost_141, ecnu_haozi, hesibo, LazyChild, isv, flytoj2ee,
66
* @author panoptimum, bugbuka, Easyhard
77
*
@@ -136,6 +136,8 @@
136136
* - Added route for Active / Upcoming Data Science Challenges API.
137137
* Changes in 1.58:
138138
* - Add routes for SRM practice problems API.
139+
* Changes in 1.59:
140+
* - Add route for user activation email api.
139141
*/
140142
/*jslint node:true, nomen: true */
141143
"use strict";
@@ -251,6 +253,7 @@ exports.routes = {
251253
{ path: "/:apiVersion/design/download/:submissionId", action: "downloadDesignSubmission" },
252254

253255
{ path: "/:apiVersion/user/challenges", action: "getMyChallenges" },
256+
{ path: "/:apiVersion/user/activation-email", action: "userActivationEmail" },
254257

255258
{ path: "/:apiVersion/users/tops/:trackType", action: "getTopTrackMembers" },
256259
{ path: "/:apiVersion/users/resetToken", action: "generateResetToken" },
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
DELETE FROM email WHERE email_id IN (100326, 100325);
2+
DELETE FROM user WHERE user_id IN (100326, 100325);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
INSERT INTO user (user_id, handle, status) VALUES(100326, 'promise', 'U');
2+
INSERT INTO user (user_id, handle, status) VALUES(100325, 'callback', 'U');
3+
4+
INSERT INTO email(user_id, email_id, email_type_id, address, create_date, modify_date, primary_ind, status_id)
5+
VALUES(100326, 100326, 1, 'promise@fakeserver.com', CURRENT, CURRENT, 1, 1);
6+
7+
INSERT INTO email(user_id, email_id, email_type_id, address, create_date, modify_date, primary_ind, status_id)
8+
VALUES(100325, 100325, 1, 'callback@fakeserver.com', CURRENT, CURRENT, 1, 1);

0 commit comments

Comments
 (0)