From a27b9a892e336f772312794d6ffccec13784f8e5 Mon Sep 17 00:00:00 2001 From: dingjian2 Date: Mon, 22 Dec 2014 12:01:26 +0800 Subject: [PATCH] User Develop and Design api --- actions/userDesignChallenges.js | 218 ++++++++++++ actions/userDevelopChallenges.js | 198 +++++++++++ apiary.apib | 163 +++++++++ initializers/helper.js | 6 +- queries/check_user | 1 + queries/check_user.json | 5 + queries/get_user_challenges | 22 ++ queries/get_user_challenges.json | 5 + queries/get_user_challenges_count | 10 + queries/get_user_challenges_count.json | 5 + queries/get_user_design_challenges | 47 +++ queries/get_user_design_challenges.json | 5 + queries/get_user_design_challenges_count | 23 ++ queries/get_user_design_challenges_count.json | 5 + routes.js | 12 +- test/sqls/userChallenges/tcs_catalog__clean | 14 + .../tcs_catalog__insert_test_data | 57 ++++ test/sqls/userChallenges/tcs_dw__clean | 6 + .../userChallenges/tcs_dw__insert_test_data | 35 ++ test/test.userDesignChallenges.js | 319 ++++++++++++++++++ test/test.userDevelopChallenges.js | 319 ++++++++++++++++++ .../expected_get_user_challenges_1.json | 35 ++ .../expected_get_user_challenges_2.json | 46 +++ ...ted_get_user_challenges_error_message.json | 26 ++ 24 files changed, 1578 insertions(+), 4 deletions(-) create mode 100644 actions/userDesignChallenges.js create mode 100644 actions/userDevelopChallenges.js create mode 100644 queries/check_user create mode 100644 queries/check_user.json create mode 100644 queries/get_user_challenges create mode 100644 queries/get_user_challenges.json create mode 100644 queries/get_user_challenges_count create mode 100644 queries/get_user_challenges_count.json create mode 100644 queries/get_user_design_challenges create mode 100644 queries/get_user_design_challenges.json create mode 100644 queries/get_user_design_challenges_count create mode 100644 queries/get_user_design_challenges_count.json create mode 100644 test/sqls/userChallenges/tcs_catalog__clean create mode 100644 test/sqls/userChallenges/tcs_catalog__insert_test_data create mode 100644 test/sqls/userChallenges/tcs_dw__clean create mode 100644 test/sqls/userChallenges/tcs_dw__insert_test_data create mode 100644 test/test.userDesignChallenges.js create mode 100644 test/test.userDevelopChallenges.js create mode 100644 test/test_files/expected_get_user_challenges_1.json create mode 100644 test/test_files/expected_get_user_challenges_2.json create mode 100644 test/test_files/expected_get_user_challenges_error_message.json diff --git a/actions/userDesignChallenges.js b/actions/userDesignChallenges.js new file mode 100644 index 000000000..db9b986a0 --- /dev/null +++ b/actions/userDesignChallenges.js @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2014 TopCoder Inc., All Rights Reserved. + * + * Get user design challenges api + * + * @version 1.0 + * @author TCSASSEMBLER + * + */ +"use strict"; +/*jslint stupid: true, unparam: true, continue: true */ + + +var async = require('async'); +var _ = require('underscore'); +var BadRequestError = require('../errors/BadRequestError'); +var NotFoundError = require('../errors/NotFoundError'); + +/** + * The default pzge size. + */ +var DEFAULT_PAGE_SIZE = 50; + +/** + * The constants of project type. + */ +var DESIGN_PROJECT_TYPE = "17,20,16,32,30,31,21,18,22,34,40,1"; + +/** + * The valid sort column for user challenges api. + */ +var USER_CHALLENGE_ALLOWABLE_SORT_COLUMN = ["id", "type", "placement", "prize", "num_contestants", "num_submitters", "coding_duration"]; + + +/** + * check whether given user is activated and existed. + * @param {String} handle - the handle to check. + * @param {Object} api - the action hero api object + * @param {Object} dbConnectionMap - the database connection map + * @param {Function} callback - the callback function + */ +function checkUserExistAndActivated(handle, api, dbConnectionMap, callback) { + api.dataAccess.executeQuery('check_user', { handle: handle }, dbConnectionMap, function (err, result) { + if (err) { + callback(err); + return; + } + + if (!result || result.length === 0) { + callback(new NotFoundError("User does not exist.")); + return; + } + + if (result && result[0] && result[0].status === 'A') { + callback(null, result); + } else { + callback(new BadRequestError('User is not activated.')); + } + }); +} + +/** + * Build str to array. + * + * @param {String} str - the str like str1,str2,str3 + * @return the array by split str. + */ +function buildArray(str) { + if (str.trim().length === 0) { + return []; + } + + return _.map(str.split(','), function (item) { + return item.trim(); + }); + +} + +/** + * Handle get user challenges api. + * + * @param {Object} api - the api object. + * @param {Object} connection - the connection object. + * @param {Integer} challengeType - the challenge type + * @param {Function} next - the callback function. + */ +var getUserChallenges = function (api, connection, challengeType, next) { + var helper = api.helper, params = connection.params, sqlParams, + pageIndex, pageSize, sortColumn, sortOrder, error, result, handle = connection.params.handle, + dbConnectionMap = connection.dbConnectionMap; + + sortOrder = (params.sortOrder || "asc").toLowerCase(); + sortColumn = (params.sortColumn || "id").toLowerCase(); + pageIndex = Number(params.pageIndex || 1); + pageSize = Number(params.pageSize || DEFAULT_PAGE_SIZE); + + if (!_.isDefined(params.sortOrder) && sortColumn === "id") { + sortOrder = "desc"; + } + + async.waterfall([ + function (cb) { + checkUserExistAndActivated(handle, api, dbConnectionMap, cb); + }, + function (result, cb) { + var allowedSort = helper.getLowerCaseList(USER_CHALLENGE_ALLOWABLE_SORT_COLUMN), scriptName = "get_user_design_challenges"; + if (_.isDefined(params.pageIndex) && pageIndex !== -1) { + error = helper.checkDefined(params.pageSize, "pageSize"); + } + error = error || + helper.checkMaxNumber(pageIndex, helper.MAX_INT, "pageIndex") || + helper.checkMaxNumber(pageSize, helper.MAX_INT, "pageSize") || + helper.checkPageIndex(pageIndex, "pageIndex") || + helper.checkPositiveInteger(pageSize, "pageSize") || + helper.checkContains(["asc", "desc"], sortOrder, "sortOrder") || + helper.checkContains(allowedSort, sortColumn, "sortColumn"); + + if (error) { + cb(error); + return; + } + + if (pageIndex === -1) { + pageIndex = 1; + pageSize = helper.MAX_INT; + } + sqlParams = { + firstRowIndex: (pageIndex - 1) * pageSize, + pageSize: pageSize, + sortColumn: helper.getSortColumnDBName(sortColumn), + sortOrder: sortOrder, + userId: result[0].user_id, + challengeType: challengeType + }; + async.parallel({ + count: function (cbx) { + api.dataAccess.executeQuery(scriptName + "_count", + sqlParams, + dbConnectionMap, + cbx); + }, + data: function (cbx) { + api.dataAccess.executeQuery(scriptName, + sqlParams, + dbConnectionMap, + cbx); + } + }, cb); + }, function (results, cb) { + + var total = results.count[0].total; + if (total === 0 || results.data.length === 0) { + result = { + data: [], + total: total, + pageIndex: pageIndex, + pageSize: Number(params.pageIndex) === -1 ? total : pageSize + }; + cb(); + return; + } + result = { + data: _.map(results.data, function (item) { + + var challenge = { + id: item.id, + type: item.type, + placement: item.placement, + prize: item.payment > 0 ? true : false, + numContestants: item.num_contestants, + numSubmitters: item.num_submitters, + codingDuration: item.coding_duration, + platforms: buildArray(item.platforms), + technologies: buildArray(item.technologies) + }; + return challenge; + }), + total: total, + pageIndex: pageIndex, + pageSize: Number(params.pageIndex) === -1 ? total : pageSize + }; + cb(); + } + ], function (err) { + if (err) { + helper.handleError(api, connection, err); + } else { + connection.response = result; + } + next(connection, true); + }); +}; + + +/** + * The API for get user design challenges + */ +exports.getUserDesignChallenges = { + name: "getUserDesignChallenges", + description: "get user design challenges api", + inputs: { + required: ['handle'], + optional: ['pageSize', 'pageIndex', 'sortColumn', 'sortOrder'] + }, + blockedConnectionTypes: [], + outputExample: {}, + version: 'v2', + transaction: 'read', + databases: ['tcs_catalog'], + run: function (api, connection, next) { + if (connection.dbConnectionMap) { + api.log("Execute getUserDesignChallenges#run", 'debug'); + getUserChallenges(api, connection, DESIGN_PROJECT_TYPE, next); + } else { + api.helper.handleNoConnection(api, connection, next); + } + } +}; \ No newline at end of file diff --git a/actions/userDevelopChallenges.js b/actions/userDevelopChallenges.js new file mode 100644 index 000000000..16895aa33 --- /dev/null +++ b/actions/userDevelopChallenges.js @@ -0,0 +1,198 @@ +/* + * Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved. + * + * Get user develop challenges api + * + * @version 1.0 + * @author TCSASSEMBLER + * + */ +"use strict"; +/*jslint stupid: true, unparam: true, continue: true */ + + +var async = require('async'); +var _ = require('underscore'); +var BadRequestError = require('../errors/BadRequestError'); +var NotFoundError = require('../errors/NotFoundError'); +/** + * The default pzge size. + */ +var DEFAULT_PAGE_SIZE = 50; +/** + * The constants of project type. + */ +var DEVELOPMENT_PROJECT_TYPE = "23,6,7,19,14,38,39,8,13,35,26,9,2"; +/** + * The valid sort column for user challenges api. + */ +var USER_CHALLENGE_ALLOWABLE_SORT_COLUMN = ["id", "type", "placement", "prize", "numContestants", "numSubmissions", "codingDuration"]; + + +/** + * check whether given user is activated and existed. + * @param {String} handle - the handle to check. + * @param {Object} api - the action hero api object + * @param {Object} dbConnectionMap - the database connection map + * @param {Function} callback - the callback function + */ +function checkUserExistAndActivated(handle, api, dbConnectionMap, callback) { + api.dataAccess.executeQuery('check_coder_activated', { handle: handle }, dbConnectionMap, function (err, result) { + if (err) { + callback(err); + return; + } + + if (!result || result.length === 0) { + callback(new NotFoundError("User does not exist.")); + return; + } + + if (result && result[0] && result[0].status === 'A') { + callback(); + } else { + callback(new BadRequestError('User is not activated.')); + } + }); +} + +/** + * Handle get user challenges api. + * + * @param {Object} api - the api object. + * @param {Object} connection - the connection object. + * @param {Integer} challengeType - the challenge type + * @param {Function} next - the callback function. + */ +var getUserChallenges = function (api, connection, challengeType, next) { + var helper = api.helper, params = connection.params, sqlParams, + pageIndex, pageSize, sortColumn, sortOrder, error, result, handle = connection.params.handle, + dbConnectionMap = connection.dbConnectionMap; + + sortOrder = (params.sortOrder || "asc").toLowerCase(); + sortColumn = (params.sortColumn || "id").toLowerCase(); + pageIndex = Number(params.pageIndex || 1); + pageSize = Number(params.pageSize || DEFAULT_PAGE_SIZE); + + if (!_.isDefined(params.sortOrder) && sortColumn === "id") { + sortOrder = "desc"; + } + + async.waterfall([ + function (cb) { + checkUserExistAndActivated(handle, api, dbConnectionMap, cb); + }, + function (cb) { + var allowedSort = helper.getLowerCaseList(USER_CHALLENGE_ALLOWABLE_SORT_COLUMN), scriptName = "get_user_challenges"; + if (_.isDefined(params.pageIndex) && pageIndex !== -1) { + error = helper.checkDefined(params.pageSize, "pageSize"); + } + error = error || + helper.checkMaxNumber(pageIndex, helper.MAX_INT, "pageIndex") || + helper.checkMaxNumber(pageSize, helper.MAX_INT, "pageSize") || + helper.checkPageIndex(pageIndex, "pageIndex") || + helper.checkPositiveInteger(pageSize, "pageSize") || + helper.checkContains(["asc", "desc"], sortOrder, "sortOrder") || + helper.checkContains(allowedSort, sortColumn, "sortColumn"); + + if (error) { + cb(error); + return; + } + + if (pageIndex === -1) { + pageIndex = 1; + pageSize = helper.MAX_INT; + } + sqlParams = { + firstRowIndex: (pageIndex - 1) * pageSize, + pageSize: pageSize, + sortColumn: helper.getSortColumnDBName(sortColumn), + sortOrder: sortOrder, + handle: handle, + challengeType: challengeType + }; + async.parallel({ + count: function (cbx) { + api.dataAccess.executeQuery(scriptName + "_count", + sqlParams, + dbConnectionMap, + cbx); + }, + data: function (cbx) { + api.dataAccess.executeQuery(scriptName, + sqlParams, + dbConnectionMap, + cbx); + } + }, cb); + }, function (results, cb) { + + var total = results.count[0].total; + if (total === 0 || results.data.length === 0) { + result = { + data: [], + total: total, + pageIndex: pageIndex, + pageSize: Number(params.pageIndex) === -1 ? total : pageSize + }; + cb(); + return; + } + result = { + data: _.map(results.data, function (item) { + + var challenge = { + id: item.id, + type: item.type, + placement: item.placement, + prize: item.payment > 0 ? true : false, + numContestants: item.num_contestants, + numSubmissions: item.num_submissions, + codingDuration: item.coding_duration, + platforms: item.platforms.length > 0 ? item.platforms.split(',') : [], + technologies: item.technologies.length > 0 ? item.technologies.split(',') : [] + }; + return challenge; + }), + total: total, + pageIndex: pageIndex, + pageSize: Number(params.pageIndex) === -1 ? total : pageSize + }; + cb(); + } + ], function (err) { + if (err) { + helper.handleError(api, connection, err); + } else { + connection.response = result; + } + next(connection, true); + }); +}; + + +/** + * The API for get user develop challenges + */ +exports.getUserDevelopChallenges = { + name: "getUserDevelopChallenges", + description: "get user develop challenges api", + inputs: { + required: ['handle'], + optional: ['pageSize', 'pageIndex', 'sortColumn', 'sortOrder'] + }, + blockedConnectionTypes: [], + outputExample: {}, + version: 'v2', + transaction: 'read', + databases: ['topcoder_dw', 'tcs_dw'], + run: function (api, connection, next) { + if (connection.dbConnectionMap) { + api.log("Execute getUserDevelopChallenges#run", 'debug'); + getUserChallenges(api, connection, DEVELOPMENT_PROJECT_TYPE, next); + } else { + api.helper.handleNoConnection(api, connection, next); + } + } +}; \ No newline at end of file diff --git a/apiary.apib b/apiary.apib index 235aef0c5..0b645e720 100644 --- a/apiary.apib +++ b/apiary.apib @@ -2140,6 +2140,169 @@ Register a new user. "description":"Servers are up but overloaded. Try again later." } + +## Get User Develop Challenges [/user/{handle}/challenges/develop?pageIndex={pageIndex}&pageSize={pageSize}&sortColumn={sortColumn}&sortOrder={sortOrder}] +### Get User Challenges [GET] + ++ Parameters + + handle (required, string, `super`) ... Member Handle + + pageIndex (optional, number, `1`) ... The paging number, 1-based, -1 if no paging. It can be null. + + pageSize (optional, number, `50`) ... The max number of the results, should be set if pageIndex is set. + + sortColumn (optional, string, `type`) ... The column name to sort, can be null.It should be one of id, type, placement, prize, numContestants, numSubmissions, codingDuration. + + sortOrder (optional, string, `asc`) ... The sorting order, can be null. If it's set, it can only be 'asc' or 'desc'. + ++ Response 200 (application/json) + + { + "data": [ + { + "id": 1100000, + "type": "Code", + "placement": 1, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1440, + "platforms": [ + "AWS", + "wordpress" + ], + "technologies": [ + "AJAX", + "Applet" + ] + }, + { + "id": 1100001, + "type": "Assembly", + "placement": 2, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1380, + "platforms": [], + "technologies": [] + } + ], + "total": 5, + "pageIndex": 1, + "pageSize": 2 + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + } + ++ Response 404 (application/json) + + { + "name":"Not Found", + "value":"404", + "description":"This message will explain why the URI requested is invalid or the resource does not exist." + } + ++ Response 500 (application/json) + + { + "name":"Internal Server Error", + "value":"500", + "description":"Unknown server error. Please contact support." + } + ++ Response 503 (application/json) + + { + "name":"Service Unavailable", + "value":"503", + "description":"Servers are up but overloaded. Try again later." + } + +## Get User Design Challenges [/user/{handle}/challenges/design?pageIndex={pageIndex}&pageSize={pageSize}&sortColumn={sortColumn}&sortOrder={sortOrder}] +### Get User Challenges [GET] + ++ Parameters + + handle (required, string, `super`) ... Member Handle + + pageIndex (optional, number, `1`) ... The paging number, 1-based, -1 if no paging. It can be null. + + pageSize (optional, number, `50`) ... The max number of the results, should be set if pageIndex is set. + + sortColumn (optional, string, `id`) ... The column name to sort, can be null,it's value should be one of id, type, placement, prize, num_contestants, num_submitters, coding_duration. + + sortOrder (optional, string, `asc`) ... The sorting order, can be null. If it's set, it can only be 'asc' or 'desc'. + ++ Response 200 (application/json) + + { + "data": [ + { + "id": 1100000, + "type": "Web Design", + "placement": 1, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1440, + "platforms": [ + "AWS", + "wordpress" + ], + "technologies": [ + "AJAX", + "Applet" + ] + }, + { + "id": 1100001, + "type": "Logo Design", + "placement": 2, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1380, + "platforms": [], + "technologies": [] + } + ], + "total": 5, + "pageIndex": 1, + "pageSize": 2 + } + ++ Response 400 (application/json) + + { + "name":"Bad Request", + "value":"400", + "description":"This message will explain why the request is invalid or cannot be served." + } + ++ Response 404 (application/json) + + { + "name":"Not Found", + "value":"404", + "description":"This message will explain why the URI requested is invalid or the resource does not exist." + } + ++ Response 500 (application/json) + + { + "name":"Internal Server Error", + "value":"500", + "description":"Unknown server error. Please contact support." + } + ++ Response 503 (application/json) + + { + "name":"Service Unavailable", + "value":"503", + "description":"Servers are up but overloaded. Try again later." + } + + + ## 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}] ### Search My Challenges [GET] diff --git a/initializers/helper.js b/initializers/helper.js index b75eb804f..effd866f0 100644 --- a/initializers/helper.js +++ b/initializers/helper.js @@ -6,7 +6,7 @@ /** * This module contains helper functions. * @author Sky_, Ghost_141, muzehyun, kurtrips, isv, LazyChild, hesibo, panoptimum, flytoj2ee - * @version 1.38 + * @version 1.39 * changes in 1.1: * - add mapProperties * changes in 1.2: @@ -101,6 +101,8 @@ * - Updated checkDates function to accept optional 'errorMessage' parameter. * Changes in 1.38: * - Add method editSql, readQuery and constant QUERY_PATH. + * Changes in 1.39 + * - Updated method checkDefined(). */ "use strict"; @@ -469,7 +471,7 @@ helper.SEGMENTS_ID_MAP = { */ helper.checkDefined = function (obj, objName) { if (_.isNull(obj) || _.isUndefined(obj)) { - return new IllegalArgumentError(objName + " should not be null or undefined"); + return new IllegalArgumentError(objName + " should be provided"); } return null; }; diff --git a/queries/check_user b/queries/check_user new file mode 100644 index 000000000..619f72748 --- /dev/null +++ b/queries/check_user @@ -0,0 +1 @@ +SELECT status, user_id FROM user WHERE handle_lower = LOWER('@handle@') diff --git a/queries/check_user.json b/queries/check_user.json new file mode 100644 index 000000000..1326da2c7 --- /dev/null +++ b/queries/check_user.json @@ -0,0 +1,5 @@ +{ + "name" : "check_user", + "db" : "tcs_catalog", + "sqlfile" : "check_user" +} diff --git a/queries/get_user_challenges b/queries/get_user_challenges new file mode 100644 index 000000000..9e02a2744 --- /dev/null +++ b/queries/get_user_challenges @@ -0,0 +1,22 @@ +SELECT +SKIP @firstRowIndex@ FIRST @pageSize@ + p.project_id as id, + p.project_category_name as type, + p.num_submissions as num_submissions, + p.num_registrations as num_contestants, + NVL(pr.payment, 0) as payment, + CASE WHEN pr.payment>0 THEN 'true' else 'false' END as prize, + ((pr.submit_timestamp - pr.inquire_timestamp) ::INTERVAL SECOND(9) TO SECOND + ::CHAR(20) ::INT /60 ) as coding_duration, + platform_list(pr.project_id) as platforms , + technology_list(pr.project_id) as technologies , + pr.placed as placement + FROM project_result pr, project p, coder c + WHERE p.project_id = pr.project_id + AND pr.placed > 0 + AND p.status_id in (4, 5, 7) + AND p.project_category_id in (@challengeType@) + AND c.handle = '@handle@' + AND user_id = c.coder_id + AND c.status = 'A' + ORDER BY @sortColumn@ @sortOrder@ \ No newline at end of file diff --git a/queries/get_user_challenges.json b/queries/get_user_challenges.json new file mode 100644 index 000000000..c29a3ba21 --- /dev/null +++ b/queries/get_user_challenges.json @@ -0,0 +1,5 @@ +{ + "name" : "get_user_challenges", + "db" : "tcs_dw", + "sqlfile" : "get_user_challenges" +} diff --git a/queries/get_user_challenges_count b/queries/get_user_challenges_count new file mode 100644 index 000000000..91d8eac00 --- /dev/null +++ b/queries/get_user_challenges_count @@ -0,0 +1,10 @@ +SELECT +COUNT(*) AS total + FROM project_result pr, project p, coder c + WHERE p.project_id = pr.project_id + AND pr.placed > 0 + AND p.status_id in (4, 5, 7) + AND p.project_category_id in (@challengeType@) + AND c.handle = '@handle@' + AND user_id = c.coder_id + AND c.status = 'A' diff --git a/queries/get_user_challenges_count.json b/queries/get_user_challenges_count.json new file mode 100644 index 000000000..d014734e8 --- /dev/null +++ b/queries/get_user_challenges_count.json @@ -0,0 +1,5 @@ +{ + "name" : "get_user_challenges_count", + "db" : "tcs_dw", + "sqlfile" : "get_user_challenges_count" +} diff --git a/queries/get_user_design_challenges b/queries/get_user_design_challenges new file mode 100644 index 000000000..5ac781757 --- /dev/null +++ b/queries/get_user_design_challenges @@ -0,0 +1,47 @@ +SELECT +SKIP @firstRowIndex@ FIRST @pageSize@ + pj.project_id as id, + pcl.name as type, + s.placement as placement, + NVL(p.prize_amount, 0) as payment, + CASE + WHEN p.prize_amount > 0 THEN + 'true' else 'false' END as prize, + (SELECT COUNT(*) + FROM resource r1 + WHERE r1.project_id = pj.project_id + AND r1.resource_role_id = 1) AS num_contestants, + (SELECT COUNT(DISTINCT(ri.value)) + from upload u1, submission s1, resource_info ri + where ri.resource_id = u1.resource_id + and u1.project_id = pj.project_id + and s1.upload_id = u1.upload_id + and s1.submission_type_id = 1 + and resource_info_type_id = '1' + ) as num_submitters, + ((u.create_date - NVL(pp1.actual_start_time, pp1.scheduled_start_time)) + ::INTERVAL SECOND(9) TO SECOND ::CHAR(20) ::INT / 60) as coding_duration, + platform_list(u.project_id) as platforms, + technology_list(u.project_id) as technologies + FROM project pj, + project_category_lu pcl, + project_result pr, + upload u, + resource r, + project_phase pp1, + submission s + LEFT OUTER JOIN prize p ON s.prize_id = p.prize_id + WHERE pj.project_id = pr.project_id + AND pp1.project_id = pj.project_id + AND pj.project_id = u.project_id + AND pp1.phase_type_id = 1 + AND s.upload_id = u.upload_id + AND u.resource_id = r.resource_id + AND pcl.project_category_id = pj.project_category_id + AND upload_status_id = 1 + AND upload_type_id = 1 + AND submission_status_id <> 5 + AND submission_type_id IN (1, 3) + AND r.user_id=@userId@ + AND pj.project_category_id in (@challengeType@) + ORDER BY @sortColumn@ @sortOrder@ \ No newline at end of file diff --git a/queries/get_user_design_challenges.json b/queries/get_user_design_challenges.json new file mode 100644 index 000000000..f3fa8a9e3 --- /dev/null +++ b/queries/get_user_design_challenges.json @@ -0,0 +1,5 @@ +{ + "name" : "get_user_design_challenges", + "db" : "tcs_catalog", + "sqlfile" : "get_user_design_challenges" +} diff --git a/queries/get_user_design_challenges_count b/queries/get_user_design_challenges_count new file mode 100644 index 000000000..11d95761c --- /dev/null +++ b/queries/get_user_design_challenges_count @@ -0,0 +1,23 @@ + SELECT +COUNT(*) AS total + FROM project pj, + project_category_lu pcl, + project_result pr, + upload u, + resource r, + project_phase pp1, + submission s + LEFT OUTER JOIN prize p ON s.prize_id = p.prize_id + WHERE pj.project_id = pr.project_id + AND pp1.project_id = pj.project_id + AND pj.project_id = u.project_id + AND pp1.phase_type_id = 1 + AND s.upload_id = u.upload_id + AND u.resource_id = r.resource_id + AND pcl.project_category_id = pj.project_category_id + AND upload_status_id = 1 + AND upload_type_id = 1 + AND submission_status_id <> 5 + AND submission_type_id IN (1, 3) + AND r.user_id=@userId@ + AND pj.project_category_id in (@challengeType@) \ No newline at end of file diff --git a/queries/get_user_design_challenges_count.json b/queries/get_user_design_challenges_count.json new file mode 100644 index 000000000..eab2e7a66 --- /dev/null +++ b/queries/get_user_design_challenges_count.json @@ -0,0 +1,5 @@ +{ + "name" : "get_user_design_challenges_count", + "db" : "tcs_catalog", + "sqlfile" : "get_user_design_challenges_count" +} diff --git a/routes.js b/routes.js index 24fd7090f..6a9a26088 100755 --- a/routes.js +++ b/routes.js @@ -1,7 +1,7 @@ /* * Copyright (C) 2013 - 2014 TopCoder Inc., All Rights Reserved. * - * @version 1.62 + * @version 1.64 * @author vangavroche, Sky_, muzehyun, kurtrips, Ghost_141, ecnu_haozi, hesibo, LazyChild, isv, flytoj2ee, * @author panoptimum, bugbuka, Easyhard, TCASSEMBLER * @@ -144,6 +144,10 @@ * - Added routes for modifying/deleting round question answers. * Changes in 1.62: * - Added route for src2image api. + * Changes in 1.63: + * - Added get user design challenges api. + * Changed in 1.64: + * - Added get user develop challenges api. */ /*jslint node:true, nomen: true */ "use strict"; @@ -249,7 +253,7 @@ exports.routes = { { path: "/:apiVersion/develop/reviewOpportunities/:challengeId", action: "getSoftwareReviewOpportunity" }, { path: "/:apiVersion/develop/reviewOpportunities", action: "searchReviewOpportunities" }, { path: "/:apiVersion/develop/download/:submissionId", action: "downloadDevSubmission" }, - + { path: "/:apiVersion/design/challengetypes", action: "studioTypes" }, { path: "/:apiVersion/design/challenges/result/:challengeId", action: "getStudioChallengeResults" }, { path: "/:apiVersion/design/reviewOpportunities/:id", action: "getStudioReviewOpportunity" }, @@ -258,6 +262,10 @@ exports.routes = { { path: "/:apiVersion/design/reviewOpportunities", action: "getStudioReviewOpportunities" }, { path: "/:apiVersion/design/download/:submissionId", action: "downloadDesignSubmission" }, + { path: "/:apiVersion/user/:handle/challenges/design", action: "getUserDesignChallenges" }, + { path: "/:apiVersion/user/:handle/challenges/develop", action: "getUserDevelopChallenges" }, + + { path: "/:apiVersion/user/challenges", action: "getMyChallenges" }, { path: "/:apiVersion/user/activation-email", action: "userActivationEmail" }, { path: "/:apiVersion/user/tcid/:id", action: "getUserIdentityByAuth0Id" }, diff --git a/test/sqls/userChallenges/tcs_catalog__clean b/test/sqls/userChallenges/tcs_catalog__clean new file mode 100644 index 000000000..66b7130ab --- /dev/null +++ b/test/sqls/userChallenges/tcs_catalog__clean @@ -0,0 +1,14 @@ +delete from submission where submission_id>=654024; +delete from upload where project_id>=1100000; +delete from resource_info where resource_id>=654030; +delete from resource where project_id>=1100000; +delete from project_platform where project_id>=1100000; +delete from comp_technology where comp_vers_id>=1100000; +delete from project_result where project_id>=1100000; +delete from project_phase where project_id>=1100000; +delete from comp_technology where comp_vers_id>=1100000; +delete from comp_versions where comp_vers_id>=1100000; +delete from comp_catalog where component_id=654001; +delete from prize where project_id>=1100000; +delete from project where project_id>=1100000; +update user set status='A' where handle='reassembler'; \ No newline at end of file diff --git a/test/sqls/userChallenges/tcs_catalog__insert_test_data b/test/sqls/userChallenges/tcs_catalog__insert_test_data new file mode 100644 index 000000000..dd5a2994b --- /dev/null +++ b/test/sqls/userChallenges/tcs_catalog__insert_test_data @@ -0,0 +1,57 @@ +INSERT INTO comp_catalog (component_id, current_version, short_desc, component_name, description, function_desc, create_time, status_id, root_category_id, modify_date, public_ind) +VALUES ('654001', '1', 'Sample contest for download des submission API', 'NodeJS Download Des Submission API Dev Project', 'Sample contest for Download Des submission API', 'Component', '2010-05-21 07:20:14.48', '102', '5801778', '2010-05-21 07:23:44.0', '0'); + +INSERT INTO comp_versions (comp_vers_id, component_id, version, version_text, create_time, phase_id, phase_time, price, comments, modify_date, suspended_ind) +VALUES ('1100000', '654001', '1', '1.0', '2010-05-21 07:20:14.48', '112', '1976-05-04 00:00:00.0', '1000.00', 'Cool', '2010-05-21 07:27:01.0', '0'); +INSERT INTO comp_technology (comp_tech_id,comp_vers_id,technology_type_id) values(1000,1100000,4202325); +INSERT INTO comp_technology (comp_tech_id,comp_vers_id,technology_type_id) values(1001,1100000,4202326); + +INSERT INTO project (project_id, project_status_id, create_date,modify_date,project_category_id,modify_user,create_user) + VALUES (1100000, 7, '2014-01-01 08:00:00', '2014-01-01 09:00:00',20,132457,132457); +INSERT INTO project (project_id, project_status_id, create_date,modify_date,project_category_id,modify_user,create_user) + VALUES (1100001, 7, '2014-01-01 08:00:00', '2014-01-01 09:00:00',32,132457,132457); +INSERT INTO project (project_id, project_status_id, create_date,modify_date,project_category_id,modify_user,create_user) + VALUES (1100002, 7, '2014-01-01 08:00:00', '2014-01-01 09:00:00',32,132457,132457); +INSERT INTO project (project_id, project_status_id, create_date,modify_date,project_category_id,modify_user,create_user) + VALUES (1100003, 7, '2014-01-01 08:00:00', '2014-01-01 09:00:00',32,132457,132457); +INSERT INTO project (project_id, project_status_id, create_date,modify_date,project_category_id,modify_user,create_user) + VALUES (1100004, 7, '2014-01-01 08:00:00', '2014-01-01 09:00:00',32,132457,132457); + +INSERT INTO project_result(project_id,user_id,placed,final_score,create_date,payment) +VALUES(1100000,132457,1,450,'2014-01-05 08:00:00',800); +INSERT INTO project_result(project_id,user_id,placed,final_score,create_date,payment) +VALUES(1100001,132457,2,410,'2014-01-05 08:00:00',400); + + +INSERT INTO project_result(project_id,user_id,placed,final_score,create_date,payment) +VALUES(1100002,132457,3,410,'2014-01-05 08:00:00',800); +INSERT INTO project_result(project_id,user_id,placed,final_score,create_date,payment) +VALUES(1100003,132457,1,453,'2014-01-05 08:00:00',800); +INSERT INTO project_result(project_id,user_id,placed,final_score,create_date,payment) +VALUES(1100004,132457,1,454,'2014-01-05 08:00:00',800); + +INSERT INTO project_platform (project_id, project_platform_id,create_user,create_date,modify_user,modify_date) VALUES (1100000, 20,132457,'2013-12-12 00:00:00',132457,'2013-12-12 00:00:00'); +INSERT INTO project_platform (project_id, project_platform_id,create_user,create_date,modify_user,modify_date) VALUES (1100000, 4,132457,'2013-12-12 00:00:00',132457,'2013-12-12 00:00:00'); + +INSERT INTO prize (prize_id,project_id,prize_amount,prize_type_id ,create_user, create_date, modify_user, modify_date,place,number_of_submissions) +values(1000,1100000,800,1, 132457, '2013-12-12 00:00:00', 132457, '2013-12-12 00:00:00',1,1 ); +INSERT INTO prize (prize_id,project_id,prize_amount,prize_type_id ,create_user, create_date, modify_user, modify_date,place,number_of_submissions) +values(1001,1100001,800,1, 132457, '2013-12-12 00:00:00', 132457, '2013-12-12 00:00:00',1,2 ); +INSERT INTO prize (prize_id,project_id,prize_amount,prize_type_id ,create_user, create_date, modify_user, modify_date,place,number_of_submissions) +values(1002,1100001,400,2, 132457, '2013-12-12 00:00:00', 132457, '2013-12-12 00:00:00',2,2 ); + +INSERT INTO resource (resource_id, resource_role_id, project_id, create_user, create_date, modify_user, modify_date,user_id) VALUES (654030, 1, 1100000, 132457, '2013-12-12 00:00:00', 132457, '2013-12-12 00:00:00',132457); +INSERT INTO resource_info (resource_id, resource_info_type_id, value, create_user, create_date, modify_user, modify_date) VALUES (654030, 1, 132457, 132456, '2013-12-12 00:00:00', 132456, '2013-12-12 00:00:00'); +INSERT INTO upload (upload_id, project_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) VALUES (654024, 1100000, 654030, 1, 1, 'test.zip', 132456, '2013-12-12 01:00:00', 132456, '2013-12-12 00:00:00'); +INSERT INTO submission (submission_id, upload_id, submission_status_id, screening_score, initial_score, final_score, placement, submission_type_id, create_user, create_date, modify_user, modify_date, prize_id) VALUES (654024, 654024, 1, 100.0, 88.6, 97.47, 1, 1, 132458, '2013-12-12 01:00:00', 132458, '2013-12-12 01:00:00', 1000); +INSERT INTO project_phase (project_phase_id, project_id, phase_type_id, phase_status_id, fixed_start_time, scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, duration, create_user, create_date, modify_user, modify_date) VALUES ('654001', 1100000, '1', '3', '2010-05-27 09:00:00.0', '2010-05-27 09:00:00.0', '2010-06-01 09:00:00.0', NULL, NULL, '259200000', '132457', '2010-05-21 07:26:30.0', '132457', '2010-05-21 07:26:30.0'); + +INSERT INTO resource (resource_id, resource_role_id, project_id, create_user, create_date, modify_user, modify_date,user_id) VALUES (654031, 1, 1100001, 132457, '2013-12-12 00:00:00', 132457, '2013-12-12 00:00:00',132457); +INSERT INTO resource_info (resource_id, resource_info_type_id, value, create_user, create_date, modify_user, modify_date) VALUES (654031, 1, 132457, 132456, '2013-12-12 00:00:00', 132456, '2013-12-12 00:00:00'); +INSERT INTO upload (upload_id, project_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) VALUES (654025, 1100001, 654031, 1, 1, 'test.zip', 132456, '2013-12-19 01:00:00', 132456, '2013-12-12 00:00:00'); +INSERT INTO submission (submission_id, upload_id, submission_status_id, screening_score, initial_score, final_score, placement, submission_type_id, create_user, create_date, modify_user, modify_date, prize_id) VALUES (654025, 654025, 1, 100.0, 88.6, 97.47, 1, 1, 132458, '2013-12-12 01:00:00', 132458, '2013-12-12 01:00:00', 1001); +INSERT INTO project_phase (project_phase_id, project_id, phase_type_id, phase_status_id, fixed_start_time, scheduled_start_time, scheduled_end_time, actual_start_time, actual_end_time, duration, create_user, create_date, modify_user, modify_date) VALUES ('654002', 1100001, '1', '3', '2010-05-27 09:00:00.0', '2010-05-27 09:00:00.0', '2010-06-01 09:00:00.0', NULL, NULL, '259200000', '132457', '2010-05-21 07:26:30.0', '132457', '2010-05-21 07:26:30.0'); + +INSERT INTO upload (upload_id, project_id, resource_id, upload_type_id, upload_status_id, parameter, create_user, create_date, modify_user, modify_date) VALUES (654026, 1100001, 654031, 1, 1, 'test.zip', 132456, '2013-12-19 01:00:00', 132456, '2013-12-12 00:00:00'); +INSERT INTO submission (submission_id, upload_id, submission_status_id, screening_score, initial_score, final_score, placement, submission_type_id, create_user, create_date, modify_user, modify_date, prize_id) VALUES (654026, 654026, 1, 100.0, 88.6, 97.47, 2, 1, 132458, '2013-12-12 01:00:00', 132458, '2013-12-12 01:00:00', 1002); +UPDATE user set status='F' where handle='reassembler'; \ No newline at end of file diff --git a/test/sqls/userChallenges/tcs_dw__clean b/test/sqls/userChallenges/tcs_dw__clean new file mode 100644 index 000000000..37f3c9221 --- /dev/null +++ b/test/sqls/userChallenges/tcs_dw__clean @@ -0,0 +1,6 @@ +delete from coder where coder_id =132457; +delete from coder where coder_id =932457; +delete from project where project_id>=1100000; +delete from project_result where project_id>=1100000; +delete from project_platform where project_id>=1100000; +delete from project_technology where project_id>=1100000; diff --git a/test/sqls/userChallenges/tcs_dw__insert_test_data b/test/sqls/userChallenges/tcs_dw__insert_test_data new file mode 100644 index 000000000..cc7de43df --- /dev/null +++ b/test/sqls/userChallenges/tcs_dw__insert_test_data @@ -0,0 +1,35 @@ +INSERT INTO project (project_id, status_id, component_name, num_submissions, num_registrations, posting_date, submitby_date, project_category_name,project_category_id) + VALUES (1100000, 7, 'Challenge N1', 2, 5, '2014-01-01 08:00:00', '2014-01-05 09:00:00', 'Code',2); +INSERT INTO project (project_id, status_id, component_name, num_submissions, num_registrations, posting_date, submitby_date, project_category_name,project_category_id) + VALUES (1100001, 7, 'Challenge N2', 2, 5, '2014-01-01 08:00:00', '2014-01-05 08:00:00', 'Assembly',2); + +INSERT INTO project (project_id, status_id, component_name, num_submissions, num_registrations, posting_date, submitby_date, project_category_name,project_category_id) + VALUES (1100002, 7, 'Challenge N3', 3, 5, '2014-01-01 08:00:00', '2014-01-05 07:00:00', 'Code',2); + +INSERT INTO project (project_id, status_id, component_name, num_submissions, num_registrations, posting_date, submitby_date, project_category_name,project_category_id) + VALUES (1100003, 7, 'Challenge N4', 3, 5, '2014-01-01 08:00:00', '2014-01-05 09:11:00', 'Assembly',2); + +INSERT INTO project (project_id, status_id, component_name, num_submissions, num_registrations, posting_date, submitby_date, project_category_name,project_category_id) + VALUES (1100004, 7, 'Challenge N5', 3, 5, '2014-01-01 08:00:00', '2014-01-05 09:12:00', 'Assembly',2); + + +INSERT INTO coder(coder_id,handle,status,handle_lower) VALUES(132457,'super','A','super'); +INSERT INTO coder(coder_id,handle,status,handle_lower) VALUES(932457,'super2','S','super2'); + + +INSERT INTO project_result(project_id,user_id,placed,final_points,submit_timestamp,submit_ind,payment,inquire_timestamp) +VALUES(1100000,132457,1,450.0,'2014-01-05 08:00',1,800,'2014-01-04 08:00'); +INSERT INTO project_result(project_id,user_id,placed,final_points,submit_timestamp,submit_ind,payment,inquire_timestamp) +VALUES(1100001,132457,2,451.0,'2014-01-05 07:00',1,800,'2014-01-04 08:00'); +INSERT INTO project_result(project_id,user_id,placed,final_points,submit_timestamp,submit_ind,payment,inquire_timestamp) +VALUES(1100002,132457,3,452.0,'2014-01-05 06:00',1,0,'2014-01-04 08:00'); +INSERT INTO project_result(project_id,user_id,placed,final_points,submit_timestamp,submit_ind,payment,inquire_timestamp) +VALUES(1100003,132457,1,453.0,'2014-01-05 05:00',1,800,'2014-01-04 08:00'); +INSERT INTO project_result(project_id,user_id,placed,final_points,submit_timestamp,submit_ind,payment,inquire_timestamp) +VALUES(1100004,132457,1,454.0,'2014-01-05 04:00',1,800,'2014-01-04 08:00'); + +INSERT INTO project_platform (project_id, project_platform_id, name) VALUES (1100000, 1, 'wordpress'); +INSERT INTO project_platform (project_id, project_platform_id, name) VALUES (1100000, 2, 'AWS'); + +INSERT INTO project_technology (project_id, project_technology_id, name) VALUES (1100000, 1, 'AJAX'); +INSERT INTO project_technology (project_id, project_technology_id, name) VALUES (1100000, 2, 'Applet'); diff --git a/test/test.userDesignChallenges.js b/test/test.userDesignChallenges.js new file mode 100644 index 000000000..a7ef166b6 --- /dev/null +++ b/test/test.userDesignChallenges.js @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2014 TopCoder Inc., All Rights Reserved. + * + * @version 1.0 + * @author TCSASSEMBLER + */ +'use strict'; +/*global describe, it, before, beforeEach, after, afterEach */ +/*jslint node: true, stupid: true, unparam: true */ + +/** + * Module dependencies. + */ +var fs = require('fs'); +var _ = require('underscore'); +var request = require('supertest'); +var assert = require('chai').assert; +var expect = require('chai').expect; +var async = require('async'); + +var testHelper = require('./helpers/testHelper'); +var SQL_DIR = __dirname + '/sqls/userChallenges/'; +var API_ENDPOINT = process.env.API_ENDPOINT || 'http://localhost:8080'; + +var userHandle = 'super'; +describe('Get user challenges API', function () { + this.timeout(150000); // The api with testing remote db could be quit slow + + var errorObject = require('../test/test_files/expected_get_user_challenges_error_message'); + + /** + * create a http request and test it. + * @param {String} url - the request url. + * @param {Number} expectStatus - the expected response status code. + * @param {Function} cb - the call back function. + */ + function createGetRequest(url, expectStatus, cb) { + request(API_ENDPOINT) + .get(url) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(expectStatus) + .end(cb); + } + + /** + * Assert the bad request + * @param {String} url - the request url. + * @param {Number} expectStatus - the expected response status code. + * @param {String} expectMessage - the expected error message. + * @param {Function} cb - the call back function. + */ + function assertBadCall(url, expectStatus, expectMessage, cb) { + createGetRequest(url, expectStatus, function (err, result) { + if (err) { + cb(err); + return; + } + assert.equal(result.body.error.details, expectMessage, 'invalid error message'); + cb(); + }); + } + + describe('--Get User Design Challenges API--', function () { + var URL = '/v2/user/' + userHandle + '/challenges/design'; + + /** + * Clear database + * @param {Function} done the callback + */ + function clearDb(done) { + async.waterfall([ + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'tcs_catalog__clean', 'tcs_catalog', cb); + } + ], done); + } + + /** + * This function is run before all tests. + * Generate tests data. + * @param {Function} done the callback + */ + before(function (done) { + async.waterfall([ + clearDb, + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'tcs_catalog__insert_test_data', 'tcs_catalog', cb); + } + ], done); + }); + + /** + * This function is run after all tests. + * Clean up all data. + * @param {Function} done the callback + */ + after(function (done) { + clearDb(done); + }); + + /** + * Assert the response. + * @param err the error. + * @param result the actual result. + * @param filePath the expected response. + * @param cb the callback. + */ + function assertResponse(err, result, filePath, cb) { + if (err) { + cb(err); + return; + } + var expected = require('../test/test_files/' + filePath), + actual = testHelper.getTrimmedData(result.res.text); + assert.deepEqual(actual, expected, 'invalid response'); + cb(); + } + + + /** + * Test /v2/user/{handle}/challenges/design. + */ + it('should return 404 not found. User does not exist.', function (done) { + assertBadCall('/v2/user/notFoundUserHandle/challenges/design', 404, errorObject.user.notFound, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design. + */ + it('should return 400 bad request. User is not activated.', function (done) { + assertBadCall('/v2/user/reassembler/challenges/design', 400, errorObject.user.notActivated, done); + }); + + + /** + * Test for success results + */ + it('should return success results.', function (done) { + createGetRequest(URL + '?pageIndex=1&pageSize=3&sortOrder=asc&sortColumn=id', 200, function (err, result) { + assertResponse(err, result, 'expected_get_user_challenges_2', done); + }); + }); + + + /** + * Test when only pageSize is set. + */ + it('should return success results. The pageSize is set but the pageIndex is not set.', function (done) { + createGetRequest(URL + '?pageSize=10', 200, done); + }); + + /** + * Test when only pageIndex is set. + */ + it('should return bad results. The pageIndex is set but the pageSize is not set.', function (done) { + createGetRequest(URL + '?pageIndex=1', 400, done); + }); + + /** + * Test when only sortOrder is set. + */ + it('should return success results. The sortOrder is set but the sortColumn is not set.', function (done) { + createGetRequest(URL + '?sortOrder=asc', 200, done); + }); + + /** + * Test when only sortColumn is set. + */ + it('should return success results. The sortColumn is set but the sortOrder is not set.', function (done) { + createGetRequest(URL + '?sortColumn=id', 200, done); + }); + + /** + * Test when sortOrder is in upper case. + */ + it('should return success results. The sortOrder is in upper case.', function (done) { + createGetRequest(URL + '?sortOrder=ASC', 200, done); + }); + + /** + * Test when sortColumn is in upper case. + */ + it('should return success results. The sortColumn is in upper case', function (done) { + createGetRequest(URL + '?sortColumn=ID', 200, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageSize=-1. + */ + it('should return bad request. The pageSize is negative', function (done) { + assertBadCall(URL + '?pageSize=-1', 400, errorObject.pageSize.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageSize=0. + */ + it('should return bad request. The pageSize is zero.', function (done) { + assertBadCall(URL + '?pageSize=0', 400, errorObject.pageSize.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageSize=abc. + */ + it('should return bad request. The pageSize is not a number.', function (done) { + assertBadCall(URL + '?pageSize=abc', 400, errorObject.pageSize.notNumber, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageSize=1.234. + */ + it('should return bad request. The pageSize is not integer.', function (done) { + assertBadCall(URL + '?pageSize=1.234', 400, errorObject.pageSize.notInteger, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageSize=2147483648. + */ + it('should return bad request. The pageSize is larger than 2147483647.', function (done) { + assertBadCall(URL + '?pageSize=2147483648', 400, errorObject.pageSize.tooBig, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageIndex=-2&pageSize=10. + */ + it('should return bad request. The pageIndex is negative', function (done) { + assertBadCall(URL + '?pageIndex=-2&pageSize=10', 400, errorObject.pageIndex.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageIndex=0&pageSize=10. + */ + it('should return bad request. The pageIndex is zero.', function (done) { + assertBadCall(URL + '?pageIndex=0&pageSize=10', 400, errorObject.pageIndex.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageIndex=abc&pageSize=10. + */ + it('should return bad request. The pageIndex is not a number.', function (done) { + assertBadCall(URL + '?pageIndex=abc&pageSize=10', 400, errorObject.pageIndex.notNumber, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?pageIndex=1.234&pageSize=10. + */ + it('should return bad request. The pageIndex is not integer.', function (done) { + assertBadCall(URL + '?pageIndex=1.234&pageSize=10', 400, errorObject.pageIndex.notInteger, done); + }); + + /** + * Test /v2/user/{handle}/challenges/design?sortOrder=abc + */ + it('should return bad request. The sortOrder is invalid.', function (done) { + assertBadCall(URL + '?sortOrder=abc', 400, errorObject.sortOrder.invalid, done); + }); + + /** + * Test when sort column is invalid. + */ + it('should return bad request. The sortColumn is invalid.', function (done) { + assertBadCall(URL + '?sortColumn=abc', 400, errorObject.sortColumn.invalidColumn2, done); + }); + + + /** + * Test id column + */ + it('should return success results. Test sortColumn id.', function (done) { + createGetRequest(URL + '?sortColumn=id', 200, done); + }); + + /** + * Test type column + */ + it('should return success results. Test sortColumn type.', function (done) { + createGetRequest(URL + '?sortColumn=type', 200, done); + }); + + /** + * Test placement column + */ + it('should return success results. Test sortColumn placement.', function (done) { + createGetRequest(URL + '?sortColumn=placement', 200, done); + }); + + /** + * Test prize column + */ + it('should return success results. Test sortColumn prize.', function (done) { + createGetRequest(URL + '?sortColumn=prize', 200, done); + }); + + /** + * Test numContestants column + */ + it('should return success results. Test sortColumn num_contestants.', function (done) { + createGetRequest(URL + '?sortColumn=num_contestants', 200, done); + }); + + /** + * Test numSubmitters column + */ + it('should return success results. Test sortColumn num_submitters.', function (done) { + createGetRequest(URL + '?sortColumn=num_submitters', 200, done); + }); + + /** + * Test codingDuration column + */ + it('should return success results. Test sortColumn coding_duration.', function (done) { + createGetRequest(URL + '?sortColumn=coding_duration', 200, done); + }); + + + }); + + +}); diff --git a/test/test.userDevelopChallenges.js b/test/test.userDevelopChallenges.js new file mode 100644 index 000000000..78130720f --- /dev/null +++ b/test/test.userDevelopChallenges.js @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2014 TopCoder Inc., All Rights Reserved. + * + * @version 1.0 + * @author TCSASSEMBLER + */ +'use strict'; +/*global describe, it, before, beforeEach, after, afterEach */ +/*jslint node: true, stupid: true, unparam: true */ + +/** + * Module dependencies. + */ +var fs = require('fs'); +var _ = require('underscore'); +var request = require('supertest'); +var assert = require('chai').assert; +var expect = require('chai').expect; +var async = require('async'); + +var testHelper = require('./helpers/testHelper'); +var SQL_DIR = __dirname + '/sqls/userChallenges/'; +var API_ENDPOINT = process.env.API_ENDPOINT || 'http://localhost:8080'; + +var userHandle = 'super'; +describe('Get user challenges API', function () { + this.timeout(120000); // The api with testing remote db could be quit slow + + var errorObject = require('../test/test_files/expected_get_user_challenges_error_message'); + + /** + * create a http request and test it. + * @param {String} url - the request url. + * @param {Number} expectStatus - the expected response status code. + * @param {Function} cb - the call back function. + */ + function createGetRequest(url, expectStatus, cb) { + request(API_ENDPOINT) + .get(url) + .set('Accept', 'application/json') + .expect('Content-Type', /json/) + .expect(expectStatus) + .end(cb); + } + + /** + * Assert the bad request + * @param {String} url - the request url. + * @param {Number} expectStatus - the expected response status code. + * @param {String} expectMessage - the expected error message. + * @param {Function} cb - the call back function. + */ + function assertBadCall(url, expectStatus, expectMessage, cb) { + createGetRequest(url, expectStatus, function (err, result) { + if (err) { + cb(err); + return; + } + assert.equal(result.body.error.details, expectMessage, 'invalid error message'); + cb(); + }); + } + + describe('--Get User Develop Challenges API--', function () { + var URL = '/v2/user/' + userHandle + '/challenges/develop'; + + /** + * Clear database + * @param {Function} done the callback + */ + function clearDb(done) { + async.waterfall([ + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'tcs_dw__clean', 'tcs_dw', cb); + } + ], done); + } + + /** + * This function is run before all tests. + * Generate tests data. + * @param {Function} done the callback + */ + before(function (done) { + async.waterfall([ + clearDb, + function (cb) { + testHelper.runSqlFile(SQL_DIR + 'tcs_dw__insert_test_data', 'tcs_dw', cb); + } + ], done); + }); + + /** + * This function is run after all tests. + * Clean up all data. + * @param {Function} done the callback + */ + after(function (done) { + clearDb(done); + }); + + /** + * Assert the response. + * @param err the error. + * @param result the actual result. + * @param filePath the expected response. + * @param cb the callback. + */ + function assertResponse(err, result, filePath, cb) { + if (err) { + cb(err); + return; + } + var expected = require('../test/test_files/' + filePath), + actual = testHelper.getTrimmedData(result.res.text); + assert.deepEqual(actual, expected, 'invalid response'); + cb(); + } + + + /** + * Test /v2/user/{handle}/challenges/develop. + */ + it('should return 404 not found. User does not exist.', function (done) { + assertBadCall('/v2/user/notFoundUserHandle/challenges/develop', 404, errorObject.user.notFound, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop. + */ + it('should return 400 bad request. User is not activated.', function (done) { + assertBadCall('/v2/user/super2/challenges/develop', 400, errorObject.user.notActived, done); + }); + + + /** + * Test for success results + */ + it('should return success results.', function (done) { + createGetRequest(URL + '?pageIndex=1&pageSize=2&sortOrder=asc&sortColumn=id', 200, function (err, result) { + assertResponse(err, result, 'expected_get_user_challenges_1', done); + }); + }); + + + /** + * Test when only pageSize is set. + */ + it('should return success results. The pageSize is set but the pageIndex is not set.', function (done) { + createGetRequest(URL + '?pageSize=10', 200, done); + }); + + /** + * Test when only pageIndex is set. + */ + it('should return bad results. The pageIndex is set but the pageSize is not set.', function (done) { + createGetRequest(URL + '?pageIndex=1', 400, done); + }); + + /** + * Test when only sortOrder is set. + */ + it('should return success results. The sortOrder is set but the sortColumn is not set.', function (done) { + createGetRequest(URL + '?sortOrder=asc', 200, done); + }); + + /** + * Test when only sortColumn is set. + */ + it('should return success results. The sortColumn is set but the sortOrder is not set.', function (done) { + createGetRequest(URL + '?sortColumn=id', 200, done); + }); + + /** + * Test when sortOrder is in upper case. + */ + it('should return success results. The sortOrder is in upper case.', function (done) { + createGetRequest(URL + '?sortOrder=ASC', 200, done); + }); + + /** + * Test when sortColumn is in upper case. + */ + it('should return success results. The sortColumn is in upper case', function (done) { + createGetRequest(URL + '?sortColumn=ID', 200, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageSize=-1. + */ + it('should return bad request. The pageSize is negative', function (done) { + assertBadCall(URL + '?pageSize=-1', 400, errorObject.pageSize.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageSize=0. + */ + it('should return bad request. The pageSize is zero.', function (done) { + assertBadCall(URL + '?pageSize=0', 400, errorObject.pageSize.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageSize=abc. + */ + it('should return bad request. The pageSize is not a number.', function (done) { + assertBadCall(URL + '?pageSize=abc', 400, errorObject.pageSize.notNumber, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageSize=1.234. + */ + it('should return bad request. The pageSize is not integer.', function (done) { + assertBadCall(URL + '?pageSize=1.234', 400, errorObject.pageSize.notInteger, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageSize=2147483648. + */ + it('should return bad request. The pageSize is larger than 2147483647.', function (done) { + assertBadCall(URL + '?pageSize=2147483648', 400, errorObject.pageSize.tooBig, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageIndex=-2&pageSize=10. + */ + it('should return bad request. The pageIndex is negative', function (done) { + assertBadCall(URL + '?pageIndex=-2&pageSize=10', 400, errorObject.pageIndex.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageIndex=0&pageSize=10. + */ + it('should return bad request. The pageIndex is zero.', function (done) { + assertBadCall(URL + '?pageIndex=0&pageSize=10', 400, errorObject.pageIndex.negative, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageIndex=abc&pageSize=10. + */ + it('should return bad request. The pageIndex is not a number.', function (done) { + assertBadCall(URL + '?pageIndex=abc&pageSize=10', 400, errorObject.pageIndex.notNumber, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?pageIndex=1.234&pageSize=10. + */ + it('should return bad request. The pageIndex is not integer.', function (done) { + assertBadCall(URL + '?pageIndex=1.234&pageSize=10', 400, errorObject.pageIndex.notInteger, done); + }); + + /** + * Test /v2/user/{handle}/challenges/develop?sortOrder=abc + */ + it('should return bad request. The sortOrder is invalid.', function (done) { + assertBadCall(URL + '?sortOrder=abc', 400, errorObject.sortOrder.invalid, done); + }); + + /** + * Test when sort column is invalid. + */ + it('should return bad request. The sortColumn is invalid.', function (done) { + assertBadCall(URL + '?sortColumn=abc', 400, errorObject.sortColumn.invalidColumn, done); + }); + + + /** + * Test id column + */ + it('should return success results. Test sortColumn id.', function (done) { + createGetRequest(URL + '?sortColumn=id', 200, done); + }); + + /** + * Test type column + */ + it('should return success results. Test sortColumn type.', function (done) { + createGetRequest(URL + '?sortColumn=type', 200, done); + }); + + /** + * Test placement column + */ + it('should return success results. Test sortColumn placement.', function (done) { + createGetRequest(URL + '?sortColumn=placement', 200, done); + }); + + /** + * Test prize column + */ + it('should return success results. Test sortColumn prize.', function (done) { + createGetRequest(URL + '?sortColumn=prize', 200, done); + }); + + /** + * Test numContestants column + */ + it('should return success results. Test sortColumn numContestants.', function (done) { + createGetRequest(URL + '?sortColumn=numContestants', 200, done); + }); + + /** + * Test numSubmissions column + */ + it('should return success results. Test sortColumn numSubmissions.', function (done) { + createGetRequest(URL + '?sortColumn=numSubmissions', 200, done); + }); + + /** + * Test codingDuration column + */ + it('should return success results. Test sortColumn codingDuration.', function (done) { + createGetRequest(URL + '?sortColumn=codingDuration', 200, done); + }); + + + }); + + +}); diff --git a/test/test_files/expected_get_user_challenges_1.json b/test/test_files/expected_get_user_challenges_1.json new file mode 100644 index 000000000..fbfd5d1db --- /dev/null +++ b/test/test_files/expected_get_user_challenges_1.json @@ -0,0 +1,35 @@ +{ + "data": [ + { + "id": 1100000, + "type": "Code", + "placement": 1, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1440, + "platforms": [ + "AWS", + "wordpress" + ], + "technologies": [ + "AJAX", + "Applet" + ] + }, + { + "id": 1100001, + "type": "Assembly", + "placement": 2, + "prize": true, + "numContestants": 5, + "numSubmissions": 2, + "codingDuration": 1380, + "platforms": [], + "technologies": [] + } + ], + "total": 5, + "pageIndex": 1, + "pageSize": 2 +} \ No newline at end of file diff --git a/test/test_files/expected_get_user_challenges_2.json b/test/test_files/expected_get_user_challenges_2.json new file mode 100644 index 000000000..fb5dfe985 --- /dev/null +++ b/test/test_files/expected_get_user_challenges_2.json @@ -0,0 +1,46 @@ +{ + "data": [ + { + "id": 1100000, + "type": "Logo Design", + "placement": 1, + "prize": true, + "numContestants": 1, + "numSubmitters": 1, + "codingDuration": 1864320, + "platforms": [ + "AWS", + "Wordpress" + ], + "technologies": [ + "HTML", + "HTTP" + ] + }, + { + "id": 1100001, + "type": "Application Front-End Design", + "placement": 1, + "prize": true, + "numContestants": 1, + "numSubmitters": 1, + "codingDuration": 1874400, + "platforms": [], + "technologies": [] + }, + { + "id": 1100001, + "type": "Application Front-End Design", + "placement": 2, + "prize": true, + "numContestants": 1, + "numSubmitters": 1, + "codingDuration": 1874400, + "platforms": [], + "technologies": [] + } + ], + "total": 3, + "pageIndex": 1, + "pageSize": 3 +} \ No newline at end of file diff --git a/test/test_files/expected_get_user_challenges_error_message.json b/test/test_files/expected_get_user_challenges_error_message.json new file mode 100644 index 000000000..b38ffbab7 --- /dev/null +++ b/test/test_files/expected_get_user_challenges_error_message.json @@ -0,0 +1,26 @@ +{ + "pageSize": { + "notNumber": "pageSize should be number.", + "negative": "pageSize should be positive.", + "notInteger": "pageSize should be Integer.", + "tooBig": "pageSize should be less or equal to 2147483647." + }, + "pageIndex": { + "notNumber": "pageIndex should be number.", + "negative": "pageIndex should be equal to -1 or greater than 0", + "notInteger": "pageIndex should be Integer.", + "tooBig": "pageIndex should be less or equal to 2147483647." + }, + "sortOrder": { + "invalid": "sortOrder should be an element of asc,desc." + }, + "sortColumn": { + "invalidColumn": "sortColumn should be an element of id,type,placement,prize,numcontestants,numsubmissions,codingduration.", + "invalidColumn2": "sortColumn should be an element of id,type,placement,prize,num_contestants,num_submitters,coding_duration." + }, + "user": { + "notFound": "User does not exist.", + "notActived": "User is not activated.", + "notActivated": "User is not activated." + } +}