From 93546f65465311fc3e33703ca972c8463736820e Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Sun, 15 May 2016 22:55:06 +0200 Subject: [PATCH 01/10] v3: add TODO for client side spells --- website/client/js/controllers/rootCtrl.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/client/js/controllers/rootCtrl.js b/website/client/js/controllers/rootCtrl.js index f7eb1f4041..01b55b8ea7 100644 --- a/website/client/js/controllers/rootCtrl.js +++ b/website/client/js/controllers/rootCtrl.js @@ -316,7 +316,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ if (targetId) spellUrl += '?targetId=' + targetId; $http.post(spellUrl) - .success(function(){ + .success(function(){ // TODO response will always include the modified data, no need to sync! var msg = window.env.t('youCast', {spell: spell.text()}); switch (type) { case 'task': msg = window.env.t('youCastTarget', {spell: spell.text(), target: target.text});break; From ba1628427ebd7f9f56bdd8bd239a51e01a3dc0f0 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 16 May 2016 11:38:22 +0200 Subject: [PATCH 02/10] v3: fix stealth casting --- common/script/content/spells.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/script/content/spells.js b/common/script/content/spells.js index e7a198ec97..9381ba89d5 100644 --- a/common/script/content/spells.js +++ b/common/script/content/spells.js @@ -194,7 +194,7 @@ spells.rogue = { notes: t('spellRogueStealthNotes'), cast (user) { if (!user.stats.buffs.stealth) user.stats.buffs.stealth = 0; - user.stats.buffs.stealth += Math.ceil(diminishingReturns(user._statsComputed.per, user.dailys.length * 0.64, 55)); + user.stats.buffs.stealth += Math.ceil(diminishingReturns(user._statsComputed.per, user.tasksOrder.dailys.length * 0.64, 55)); }, }, }; From 793ca3b172a83a700ef8a74a1d5713c70f684414 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 16 May 2016 12:04:09 +0200 Subject: [PATCH 03/10] v3: fix tasks saving and selection for rebirth reroll and reset (server-only) --- common/script/ops/rebirth.js | 25 +++++++--------- common/script/ops/reroll.js | 6 ++-- common/script/ops/reset.js | 1 - website/server/controllers/api-v3/user.js | 35 +++++++++++++++++------ 4 files changed, 42 insertions(+), 25 deletions(-) diff --git a/common/script/ops/rebirth.js b/common/script/ops/rebirth.js index 40920fb0c7..54f9533fc5 100644 --- a/common/script/ops/rebirth.js +++ b/common/script/ops/rebirth.js @@ -11,16 +11,11 @@ import equip from './equip'; const USERSTATSLIST = ['per', 'int', 'con', 'str', 'points', 'gp', 'exp', 'mp']; module.exports = function rebirth (user, tasks = [], req = {}, analytics) { - let analyticsData; - let flags; - let lvl; - let stats; - if (user.balance < 2 && user.stats.lvl < MAX_LEVEL) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } - analyticsData = { + let analyticsData = { uuid: user._id, category: 'behavior', }; @@ -38,18 +33,20 @@ module.exports = function rebirth (user, tasks = [], req = {}, analytics) { analytics.track('Rebirth', analyticsData); } - lvl = capByLevel(user.stats.lvl); + let lvl = capByLevel(user.stats.lvl); _.each(tasks, function resetTasks (task) { - if (task.type !== 'reward') { - task.value = 0; - } - if (task.type === 'daily') { - task.streak = 0; + if (!task.challenge || !task.challenge.id || task.challenge.broken) { + if (task.type !== 'reward') { + task.value = 0; + } + if (task.type === 'daily') { + task.streak = 0; + } } }); - stats = user.stats; + let stats = user.stats; stats.buffs = {}; stats.hp = 50; stats.lvl = 1; @@ -79,7 +76,7 @@ module.exports = function rebirth (user, tasks = [], req = {}, analytics) { }); } - flags = user.flags; + let flags = user.flags; if (!user.achievements.beastMaster) { flags.rebirthEnabled = false; } diff --git a/common/script/ops/reroll.js b/common/script/ops/reroll.js index fee26fb88b..72414173f7 100644 --- a/common/script/ops/reroll.js +++ b/common/script/ops/reroll.js @@ -13,8 +13,10 @@ module.exports = function reroll (user, tasks = [], req = {}, analytics) { user.stats.hp = 50; _.each(tasks, function resetTaskValues (task) { - if (task.type !== 'reward') { - task.value = 0; + if (!task.challenge || !task.challenge.id || task.challenge.broken) { + if (task.type !== 'reward') { + task.value = 0; + } } }); diff --git a/common/script/ops/reset.js b/common/script/ops/reset.js index eb8d25033e..3e48fa4f2d 100644 --- a/common/script/ops/reset.js +++ b/common/script/ops/reset.js @@ -13,7 +13,6 @@ module.exports = function reset (user, tasks = [], req = {}) { tasksToRemove.push(task._id); let i = user.tasksOrder[`${task.type}s`].indexOf(task._id); if (i !== -1) user.tasksOrder[`${task.type}s`].splice(i, 1); - tasksToRemove.push(task._id); } }); diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 6307b57293..de06b90352 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -1106,16 +1106,22 @@ api.userRebirth = { url: '/user/rebirth', async handler (req, res) { let user = res.locals.user; - let query = { + let tasks = await Tasks.Task.find({ userId: user._id, type: {$in: ['daily', 'habit', 'todo']}, - }; - let tasks = await Tasks.Task.find(query).exec(); + $or: [ // exclude challenge tasks + {'challenge.id': {$exists: false}}, + {'challenge.broken': {$exists: true}}, + ], + }).exec(); + let rebirthRes = common.ops.rebirth(user, tasks, req, res.analytics); - await user.save(); + let toSave = tasks.map(task => task.save()); - await Bluebird.all(tasks.map(task => task.save())); + toSave.push(user.save()); + + await Bluebird.all(toSave); res.respond(200, ...rebirthRes); }, @@ -1224,6 +1230,10 @@ api.userReroll = { let query = { userId: user._id, type: {$in: ['daily', 'habit', 'todo']}, + $or: [ // exclude challenge tasks + {'challenge.id': {$exists: false}}, + {'challenge.broken': {$exists: true}}, + ], }; let tasks = await Tasks.Task.find(query).exec(); let rerollRes = common.ops.reroll(user, tasks, req, res.analytics); @@ -1280,11 +1290,20 @@ api.userReset = { async handler (req, res) { let user = res.locals.user; - let tasks = await Tasks.Task.find({userId: user._id}).select('_id type challenge').exec(); + let tasks = await Tasks.Task.find({ + userId: user._id, + $or: [ // exclude challenge tasks + {'challenge.id': {$exists: false}}, + {'challenge.broken': {$exists: true}}, + ], + }).select('_id type challenge').exec(); - let resetRes = common.ops.reset(user, tasks); + let resetRes = common.ops.reset(user, tasks, req); - await Bluebird.all([Tasks.Task.remove({_id: {$in: resetRes[0].tasksToRemove}, userId: user._id}), user.save()]); + await Bluebird.all([ + Tasks.Task.remove({_id: {$in: resetRes[0].tasksToRemove}, userId: user._id}), + user.save(), + ]); res.respond(200, ...resetRes); }, From 4d08fde068cedccc651e2d350533f5dcbbbb9a32 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 16 May 2016 12:17:26 +0200 Subject: [PATCH 04/10] v3: fix auto allocation --- common/script/fns/autoAllocate.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/common/script/fns/autoAllocate.js b/common/script/fns/autoAllocate.js index 85fe01b78d..71a4898031 100644 --- a/common/script/fns/autoAllocate.js +++ b/common/script/fns/autoAllocate.js @@ -10,17 +10,19 @@ import splitWhitespace from '../libs/splitWhitespace'; function getStatToAllocate (user) { let suggested; + let statsObj = user.stats.toObject ? user.stats.toObject() : user.stats; + switch (user.preferences.allocationMode) { case 'flat': { - let stats = _.pick(user.stats, splitWhitespace('con str per int')); + let stats = _.pick(statsObj, splitWhitespace('con str per int')); return _.invert(stats)[_.min(stats)]; } case 'classbased': { - let lvlDiv7 = user.stats.lvl / 7; + let lvlDiv7 = statsObj.lvl / 7; let ideal = [lvlDiv7 * 3, lvlDiv7 * 2, lvlDiv7, lvlDiv7]; let preference; - switch (user.stats.class) { + switch (statsObj.class) { case 'wizard': { preference = ['int', 'per', 'con', 'str']; break; @@ -39,10 +41,10 @@ function getStatToAllocate (user) { } let diff = [ - user.stats[preference[0]] - ideal[0], - user.stats[preference[1]] - ideal[1], - user.stats[preference[2]] - ideal[2], - user.stats[preference[3]] - ideal[3], + statsObj[preference[0]] - ideal[0], + statsObj[preference[1]] - ideal[1], + statsObj[preference[2]] - ideal[2], + statsObj[preference[3]] - ideal[3], ]; suggested = _.findIndex(diff, (val) => { @@ -52,9 +54,9 @@ function getStatToAllocate (user) { return suggested !== -1 ? preference[suggested] : 'str'; } case 'taskbased': { - suggested = _.invert(user.stats.training)[_.max(user.stats.training)]; + suggested = _.invert(statsObj.training)[_.max(statsObj.training)]; - let training = user.stats.training; + let training = statsObj.training; training.str = 0; training.int = 0; training.con = 0; From ab27ef47feac5a65f9b1f602839d4f736e37e520 Mon Sep 17 00:00:00 2001 From: Matteo Pagliazzi Date: Mon, 16 May 2016 12:54:43 +0200 Subject: [PATCH 05/10] v3 client: misc fixes --- website/client/js/controllers/rootCtrl.js | 2 +- website/client/js/controllers/settingsCtrl.js | 1 + website/client/js/services/chatServices.js | 3 +- website/client/js/services/guideServices.js | 4 +- website/client/js/services/userServices.js | 49 ++++++++++++------- 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/website/client/js/controllers/rootCtrl.js b/website/client/js/controllers/rootCtrl.js index 01b55b8ea7..1cd06e865d 100644 --- a/website/client/js/controllers/rootCtrl.js +++ b/website/client/js/controllers/rootCtrl.js @@ -289,7 +289,7 @@ habitrpg.controller("RootCtrl", ['$scope', '$rootScope', '$location', 'User', '$ } }); } else if (spell.target == 'tasks') { - var tasks = User.user.habits.concat(User.user.dailys).concat(User.user.rewards); + var tasks = User.user.habits.concat(User.user.dailys).concat(User.user.rewards).concat(User.user.todos); // exclude challenge tasks tasks = tasks.filter(function (t) { if (!t.challenge) return true; diff --git a/website/client/js/controllers/settingsCtrl.js b/website/client/js/controllers/settingsCtrl.js index a93dde0fb3..d778fadc54 100644 --- a/website/client/js/controllers/settingsCtrl.js +++ b/website/client/js/controllers/settingsCtrl.js @@ -176,6 +176,7 @@ habitrpg.controller('SettingsCtrl', $scope.reset = function(){ User.reset({}); + User.sync(); $rootScope.$state.go('tasks'); } diff --git a/website/client/js/services/chatServices.js b/website/client/js/services/chatServices.js index ed812e08ad..a79de22b85 100644 --- a/website/client/js/services/chatServices.js +++ b/website/client/js/services/chatServices.js @@ -81,8 +81,7 @@ angular.module('habitrpg') clearCards: clearCards, } - //@TOOD: Port when User service is updated function clearCards() { - User.user.ops.update && User.set({'flags.cardReceived':false}); + User.user._wrapped && User.set({'flags.cardReceived':false}); } }]); diff --git a/website/client/js/services/guideServices.js b/website/client/js/services/guideServices.js index 08b51732c8..7f3ce9e499 100644 --- a/website/client/js/services/guideServices.js +++ b/website/client/js/services/guideServices.js @@ -264,8 +264,8 @@ function($rootScope, User, $timeout, $state, Analytics) { } //Init and show the welcome tour (only after user is pulled from server & wrapped). - var watcher = $rootScope.$watch('User.user.ops.update', function(updateFn){ - if (!updateFn) return; // only run after user has been wrapped + var watcher = $rootScope.$watch('User.user._wrapped', function(wrapped){ + if (!wrapped) return; // only run after user has been wrapped watcher(); // deregister watcher if (window.env.IS_MOBILE) return; // Don't show tour immediately on mobile devices if (User.user.flags.welcomed == false) { diff --git a/website/client/js/services/userServices.js b/website/client/js/services/userServices.js index 206052c8ad..a25584e5fb 100644 --- a/website/client/js/services/userServices.js +++ b/website/client/js/services/userServices.js @@ -61,21 +61,13 @@ angular.module('habitrpg') // replicated. We need to wrap each op to provide a callback to send that operation $window.habitrpgShared.wrap(user); _.each(user.ops, function(op,k){ - user.ops[k] = function(req,cb){ - if (cb) return op(req,cb); - op(req,function(err,response) { - for(var updatedItem in req.body) { - var itemUpdateResponse = userNotifications[updatedItem]; - if(itemUpdateResponse) Notification.text(itemUpdateResponse); - } - if (err) { - var message = err.code ? err.message : err; - Notification.text(message); - // In the case of 200s, they're friendly alert messages like "Your pet has hatched!" - still send the op - if ((err.code && err.code >= 400) || !err.code) return; - } - userServices.log({op:k, params: req.params, query:req.query, body:req.body}); - }); + user.ops[k] = function(req){ + try { + op(req); + } catch (err) { + Notification.text(err.message); + return; + } } }); } @@ -106,13 +98,27 @@ angular.module('habitrpg') function callOpsFunctionAndRequest (opName, endPoint, method, paramString, opData) { if (!opData) opData = {}; + var clientResponse; + try { - $window.habitrpgShared.ops[opName](user, opData); - } catch(err) { + var args = [user]; + if (opName === 'rebirth' || opName === 'reroll' || opName === 'reset') { + args.push(user.habits.concat(user.dailys).concat(user.rewards).concat(user.todos)); + } + + args.push(opData); + clientResponse = $window.habitrpgShared.ops[opName].apply(null, args); + } catch (err) { Notification.text(err.message); return; } + var clientMessage = clientResponse[1]; + + if (clientMessage) { + Notification.text(clientMessage); + } + var url = '/api/v3/user/' + endPoint; if (paramString) { url += '/' + paramString @@ -130,7 +136,7 @@ angular.module('habitrpg') body: body, }) .then(function (response) { - if (response.data.message) Notification.text(response.data.message); + if (response.data.message && response.data.message !== clientMessage) Notification.text(response.data.message); save(); }) } @@ -182,7 +188,12 @@ angular.module('habitrpg') }, score: function (data) { - $window.habitrpgShared.ops.scoreTask({user: user, task: data.params.task, direction: data.params.direction}, data.params); + try { + $window.habitrpgShared.ops.scoreTask({user: user, task: data.params.task, direction: data.params.direction}, data.params); + } catch (err) { + Notification.text(err.message); + return; + } save(); Tasks.scoreTask(data.params.task._id, data.params.direction).then(function (res) { var tmp = res.data.data._tmp || {}; // used to notify drops, critical hits and other bonuses From daa0955ac1e170e0349156c4cfc726f04a83d29e Mon Sep 17 00:00:00 2001 From: Alys Date: Mon, 16 May 2016 23:15:01 +1000 Subject: [PATCH 06/10] rename buyPotion and buy-potion to buyHealthPotion and buy-health-potion; fix apidoc param error --- common/script/index.js | 6 +++--- common/script/ops/buy.js | 4 ++-- .../script/ops/{buyPotion.js => buyHealthPotion.js} | 2 +- common/script/ops/index.js | 4 ++-- .../integration/user/POST-user_buy_armoire.test.js | 4 ++-- ...n.test.js => POST-user_buy_health_potion.test.js} | 6 +++--- test/common/ops/{buyPotion.js => buyHealthPotion.js} | 12 ++++++------ website/server/controllers/api-v3/user.js | 12 +++++------- 8 files changed, 24 insertions(+), 26 deletions(-) rename common/script/ops/{buyPotion.js => buyHealthPotion.js} (92%) rename test/api/v3/integration/user/{POST-user_buy_potion.test.js => POST-user_buy_health_potion.test.js} (84%) rename test/common/ops/{buyPotion.js => buyHealthPotion.js} (84%) diff --git a/common/script/index.js b/common/script/index.js index 3b87ac1a7c..e3a0e0a0de 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -114,7 +114,7 @@ import sleep from './ops/sleep'; import allocate from './ops/allocate'; import buy from './ops/buy'; import buyGear from './ops/buyGear'; -import buyPotion from './ops/buyPotion'; +import buyHealthPotion from './ops/buyHealthPotion'; import buyArmoire from './ops/buyArmoire'; import buyMysterySet from './ops/buyMysterySet'; import buyQuest from './ops/buyQuest'; @@ -155,7 +155,7 @@ api.ops = { allocate, buy, buyGear, - buyPotion, + buyHealthPotion, buyArmoire, buyMysterySet, buySpecialSpell, @@ -274,7 +274,7 @@ api.wrap = function wrapUser (user, main = true) { releaseMounts: _.partial(importedOps.releaseMounts, user), releaseBoth: _.partial(importedOps.releaseBoth, user), buy: _.partial(importedOps.buy, user), - buyPotion: _.partial(importedOps.buyPotion, user), + buyHealthPotion: _.partial(importedOps.buyHealthPotion, user), buyArmoire: _.partial(importedOps.buyArmoire, user), buyGear: _.partial(importedOps.buyGear, user), buyQuest: _.partial(importedOps.buyQuest, user), diff --git a/common/script/ops/buy.js b/common/script/ops/buy.js index d62842acea..ded5b034d2 100644 --- a/common/script/ops/buy.js +++ b/common/script/ops/buy.js @@ -3,7 +3,7 @@ import _ from 'lodash'; import { BadRequest, } from '../libs/errors'; -import buyPotion from './buyPotion'; +import buyHealthPotion from './buyHealthPotion'; import buyArmoire from './buyArmoire'; import buyGear from './buyGear'; @@ -13,7 +13,7 @@ module.exports = function buy (user, req = {}, analytics) { let buyRes; if (key === 'potion') { - buyRes = buyPotion(user, req, analytics); + buyRes = buyHealthPotion(user, req, analytics); } else if (key === 'armoire') { buyRes = buyArmoire(user, req, analytics); } else { diff --git a/common/script/ops/buyPotion.js b/common/script/ops/buyHealthPotion.js similarity index 92% rename from common/script/ops/buyPotion.js rename to common/script/ops/buyHealthPotion.js index 5c64b19609..1a6c8b0e18 100644 --- a/common/script/ops/buyPotion.js +++ b/common/script/ops/buyHealthPotion.js @@ -4,7 +4,7 @@ import { NotAuthorized, } from '../libs/errors'; -module.exports = function buyPotion (user, req = {}, analytics) { +module.exports = function buyHealthPotion (user, req = {}, analytics) { let item = content.potion; if (user.stats.gp < item.value) { diff --git a/common/script/ops/index.js b/common/script/ops/index.js index 42e77d8718..2e8bca246d 100644 --- a/common/script/ops/index.js +++ b/common/script/ops/index.js @@ -31,7 +31,7 @@ import releaseMounts from './releaseMounts'; import releaseBoth from './releaseBoth'; import buy from './buy'; import buyGear from './buyGear'; -import buyPotion from './buyPotion'; +import buyHealthPotion from './buyHealthPotion'; import buyArmoire from './buyArmoire'; import buyQuest from './buyQuest'; import buyMysterySet from './buyMysterySet'; @@ -83,7 +83,7 @@ module.exports = { releaseBoth, buy, buyGear, - buyPotion, + buyHealthPotion, buyArmoire, buyQuest, buyMysterySet, diff --git a/test/api/v3/integration/user/POST-user_buy_armoire.test.js b/test/api/v3/integration/user/POST-user_buy_armoire.test.js index 7538be64b8..3fed828aad 100644 --- a/test/api/v3/integration/user/POST-user_buy_armoire.test.js +++ b/test/api/v3/integration/user/POST-user_buy_armoire.test.js @@ -18,7 +18,7 @@ describe('POST /user/buy-armoire', () => { // More tests in common code unit tests it('returns an error if user does not have enough gold', async () => { - await expect(user.post('/user/buy-potion')) + await expect(user.post('/user/buy-health-potion')) .to.eventually.be.rejected.and.eql({ code: 401, error: 'NotAuthorized', @@ -32,7 +32,7 @@ describe('POST /user/buy-armoire', () => { }); let potion = content.potion; - let res = await user.post('/user/buy-potion'); + let res = await user.post('/user/buy-health-potion'); await user.sync(); expect(user.stats.hp).to.equal(50); diff --git a/test/api/v3/integration/user/POST-user_buy_potion.test.js b/test/api/v3/integration/user/POST-user_buy_health_potion.test.js similarity index 84% rename from test/api/v3/integration/user/POST-user_buy_potion.test.js rename to test/api/v3/integration/user/POST-user_buy_health_potion.test.js index e37f908e3e..835e893bd7 100644 --- a/test/api/v3/integration/user/POST-user_buy_potion.test.js +++ b/test/api/v3/integration/user/POST-user_buy_health_potion.test.js @@ -6,7 +6,7 @@ import shared from '../../../../../common/script'; let content = shared.content; -describe('POST /user/buy-potion', () => { +describe('POST /user/buy-health-potion', () => { let user; beforeEach(async () => { @@ -18,7 +18,7 @@ describe('POST /user/buy-potion', () => { // More tests in common code unit tests it('returns an error if user does not have enough gold', async () => { - await expect(user.post('/user/buy-potion')) + await expect(user.post('/user/buy-health-potion')) .to.eventually.be.rejected.and.eql({ code: 401, error: 'NotAuthorized', @@ -32,7 +32,7 @@ describe('POST /user/buy-potion', () => { }); let potion = content.potion; - let res = await user.post('/user/buy-potion'); + let res = await user.post('/user/buy-health-potion'); await user.sync(); expect(user.stats.hp).to.equal(50); diff --git a/test/common/ops/buyPotion.js b/test/common/ops/buyHealthPotion.js similarity index 84% rename from test/common/ops/buyPotion.js rename to test/common/ops/buyHealthPotion.js index 5230040a35..6a70f71b62 100644 --- a/test/common/ops/buyPotion.js +++ b/test/common/ops/buyHealthPotion.js @@ -2,13 +2,13 @@ import { generateUser, } from '../../helpers/common.helper'; -import buyPotion from '../../../common/script/ops/buyPotion'; +import buyHealthPotion from '../../../common/script/ops/buyHealthPotion'; import { NotAuthorized, } from '../../../common/script/libs/errors'; import i18n from '../../../common/script/i18n'; -describe('shared.ops.buyPotion', () => { +describe('shared.ops.buyHealthPotion', () => { let user; beforeEach(() => { @@ -30,19 +30,19 @@ describe('shared.ops.buyPotion', () => { context('Potion', () => { it('recovers 15 hp', () => { user.stats.hp = 30; - buyPotion(user); + buyHealthPotion(user); expect(user.stats.hp).to.eql(45); }); it('does not increase hp above 50', () => { user.stats.hp = 45; - buyPotion(user); + buyHealthPotion(user); expect(user.stats.hp).to.eql(50); }); it('deducts 25 gp', () => { user.stats.hp = 45; - buyPotion(user); + buyHealthPotion(user); expect(user.stats.gp).to.eql(175); }); @@ -51,7 +51,7 @@ describe('shared.ops.buyPotion', () => { user.stats.hp = 45; user.stats.gp = 5; try { - buyPotion(user); + buyHealthPotion(user); } catch (err) { expect(err).to.be.an.instanceof(NotAuthorized); expect(err.message).to.equal(i18n.t('messageNotEnoughGold')); diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 32cc1fb7bb..0efe484666 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -577,25 +577,23 @@ api.buyArmoire = { }; /** - * @api {post} /user/buy-potion Buy a potion. + * @api {post} /user/buy-health-potion Buy a health potion * @apiVersion 3.0.0 * @apiName UserBuyPotion * @apiGroup User * - * @apiParam {string} key The item to buy. - * * @apiSuccess {Object} data user.stats * @apiSuccess {string} message Success message */ -api.buyPotion = { +api.buyHealthPotion = { method: 'POST', middlewares: [authWithHeaders()], - url: '/user/buy-potion', + url: '/user/buy-health-potion', async handler (req, res) { let user = res.locals.user; - let buyPotionResponse = common.ops.buyPotion(user, req, res.analytics); + let buyHealthPotionResponse = common.ops.buyHealthPotion(user, req, res.analytics); await user.save(); - res.respond(200, ...buyPotionResponse); + res.respond(200, ...buyHealthPotionResponse); }, }; From 4bba10fac290e0110f01895b779d73de85043bf0 Mon Sep 17 00:00:00 2001 From: Alys Date: Tue, 17 May 2016 02:08:54 +1000 Subject: [PATCH 07/10] adjust text in apidocs for errors / clarity / consistency / standard terminology (no code changes) (#7298) --- website/server/controllers/api-v3/content.js | 2 +- website/server/controllers/api-v3/debug.js | 4 +- website/server/controllers/api-v3/hall.js | 16 ++-- .../server/controllers/api-v3/modelsPaths.js | 2 +- website/server/controllers/api-v3/tasks.js | 12 +-- website/server/controllers/api-v3/user.js | 91 ++++++++++--------- .../controllers/top-level/dataexport.js | 6 +- 7 files changed, 68 insertions(+), 65 deletions(-) diff --git a/website/server/controllers/api-v3/content.js b/website/server/controllers/api-v3/content.js index 7522a59096..0235f61645 100644 --- a/website/server/controllers/api-v3/content.js +++ b/website/server/controllers/api-v3/content.js @@ -61,7 +61,7 @@ async function saveContentToDisk (language, content) { } /** - * @api {get} /api/v3/content Get all available content objects. + * @api {get} /api/v3/content Get all available content objects * @apiDescription Does not require authentication. * @apiVersion 3.0.0 * @apiName ContentGet diff --git a/website/server/controllers/api-v3/debug.js b/website/server/controllers/api-v3/debug.js index 9f6d344546..9949421513 100644 --- a/website/server/controllers/api-v3/debug.js +++ b/website/server/controllers/api-v3/debug.js @@ -7,7 +7,7 @@ import _ from 'lodash'; let api = {}; /** - * @api {post} /api/v3/debug/add-ten-gems Add ten gems to the current user. + * @api {post} /api/v3/debug/add-ten-gems Add ten gems to the current user * @apiDescription Only available in development mode. * @apiVersion 3.0.0 * @apiName AddTenGems @@ -31,7 +31,7 @@ api.addTenGems = { }; /** - * @api {post} /api/v3/debug/add-hourglass Add Hourglass to the current user. + * @api {post} /api/v3/debug/add-hourglass Add Hourglass to the current user * @apiDescription Only available in development mode. * @apiVersion 3.0.0 * @apiName AddHourglass diff --git a/website/server/controllers/api-v3/hall.js b/website/server/controllers/api-v3/hall.js index c8f11fdfa8..077b1ef74b 100644 --- a/website/server/controllers/api-v3/hall.js +++ b/website/server/controllers/api-v3/hall.js @@ -9,8 +9,8 @@ import _ from 'lodash'; let api = {}; /** - * @api {get} /api/v3/hall/patrons Get all Patrons. - * @apiDescription Only the first 50 patrons are returned. More can be accessed passing ?page=n. + * @api {get} /api/v3/hall/patrons Get all patrons + * @apiDescription Only the first 50 patrons are returned. More can be accessed passing ?page=n * @apiVersion 3.0.0 * @apiName GetPatrons * @apiGroup Hall @@ -79,13 +79,13 @@ api.getHeroes = { const heroAdminFields = 'contributor balance profile.name purchased items auth'; /** - * @api {get} /api/v3/hall/heroes/:heroId Get an hero given his _id. - * @apiDescription Must be an admin to make this request + * @api {get} /api/v3/hall/heroes/:heroId Get any user ("hero") given the UUID + * @apiDescription Must be an admin to make this request. * @apiVersion 3.0.0 * @apiName GetHero * @apiGroup Hall * - * @apiSuccess {Object} data The hero object + * @apiSuccess {Object} data The user object */ api.getHero = { method: 'GET', @@ -117,13 +117,13 @@ api.getHero = { const gemsPerTier = {1: 3, 2: 3, 3: 3, 4: 4, 5: 4, 6: 4, 7: 4, 8: 0, 9: 0}; /** - * @api {put} /api/v3/hall/heroes/:heroId Update an hero. - * @apiDescription Must be an admin to make this request + * @api {put} /api/v3/hall/heroes/:heroId Update any user ("hero") + * @apiDescription Must be an admin to make this request. * @apiVersion 3.0.0 * @apiName UpdateHero * @apiGroup Hall * - * @apiSuccess {Object} data The updated hero object + * @apiSuccess {Object} data The updated user object */ api.updateHero = { method: 'PUT', diff --git a/website/server/controllers/api-v3/modelsPaths.js b/website/server/controllers/api-v3/modelsPaths.js index dc168b98b4..927087df01 100644 --- a/website/server/controllers/api-v3/modelsPaths.js +++ b/website/server/controllers/api-v3/modelsPaths.js @@ -6,7 +6,7 @@ let tasksModels = ['habit', 'daily', 'todo', 'reward']; let allModels = ['user', 'tag', 'challenge', 'group'].concat(tasksModels); /** - * @api {get} /api/v3/models/:model/paths Get all paths for the specified model. + * @api {get} /api/v3/models/:model/paths Get all paths for the specified model * @apiDescription Doesn't require authentication * @apiVersion 3.0.0 * @apiName GetUserModelPaths diff --git a/website/server/controllers/api-v3/tasks.js b/website/server/controllers/api-v3/tasks.js index 3ed454c666..50e32220bc 100644 --- a/website/server/controllers/api-v3/tasks.js +++ b/website/server/controllers/api-v3/tasks.js @@ -54,7 +54,7 @@ async function _createTasks (req, res, user, challenge) { } /** - * @api {post} /api/v3/tasks/user Create a new task belonging to the user. + * @api {post} /api/v3/tasks/user Create a new task belonging to the user * @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiVersion 3.0.0 * @apiName CreateUserTasks @@ -73,13 +73,13 @@ api.createUserTasks = { }; /** - * @api {post} /api/v3/tasks/challenge/:challengeId Create a new task belonging to a challenge. + * @api {post} /api/v3/tasks/challenge/:challengeId Create a new task belonging to a challenge * @apiDescription Can be passed an object to create a single task or an array of objects to create multiple tasks. * @apiVersion 3.0.0 * @apiName CreateChallengeTasks * @apiGroup Task * - * @apiParam {UUID} challengeId The id of the challenge the new task(s) will belong to. + * @apiParam {UUID} challengeId The id of the challenge the new task(s) will belong to * * @apiSuccess data An object if a single task was created, otherwise an array of tasks */ @@ -171,7 +171,7 @@ async function _getTasks (req, res, user, challenge) { * @apiName GetUserTasks * @apiGroup Task * - * @apiParam {string="habits","dailys","todos","rewards","completedTodos"} type Optional query parameter to return just a type of tasks. By default all types will be returned except completed todos that requested separately. + * @apiParam {string="habits","dailys","todos","rewards","completedTodos"} type Optional query parameter to return just a type of tasks. By default all types will be returned except completed todos that must be requested separately. * * @apiSuccess {Array} data An array of tasks */ @@ -197,7 +197,7 @@ api.getUserTasks = { * @apiName GetChallengeTasks * @apiGroup Task * - * @apiParam {UUID} challengeId The id of the challenge from which to retrieve the tasks. + * @apiParam {UUID} challengeId The id of the challenge from which to retrieve the tasks * @apiParam {string="habits","dailys","todos","rewards"} type Optional query parameter to return just a type of tasks * * @apiSuccess {Array} data An array of tasks @@ -427,9 +427,9 @@ api.scoreTask = { }, }; -// completed todos cannot be moved, they'll be returned ordered by date of completion /** * @api {post} /api/v3/tasks/:taskId/move/to/:position Move a task to a new position + * @apiDescription Note: completed To-Dos are not sortable, do not appear in user.tasksOrder.todos, and are ordered by date of completion. * @apiVersion 3.0.0 * @apiName MoveTask * @apiGroup Task diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 32cc1fb7bb..2e22a6b662 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -142,7 +142,7 @@ let checkPreferencePurchase = (user, path, item) => { }; /** - * @api {put} /api/v3/user Update the user. + * @api {put} /api/v3/user Update the user * @apiDescription Example body: {'stats.hp':50, 'preferences.background': 'beach'} * @apiVersion 3.0.0 * @apiName UserUpdate @@ -305,13 +305,13 @@ api.getUserAnonymized = { const partyMembersFields = 'profile.name stats achievements items.special'; /** - * @api {post} /api/v3/user/class/cast/:spellId Cast a spell on a target. + * @api {post} /api/v3/user/class/cast/:spellId Cast a skill (spell) on a target * @apiVersion 3.0.0 * @apiName UserCast * @apiGroup User * - * @apiParam {string} spellId The spell to cast. - * @apiParam {UUID} targetId Optional query parameter, the id of the target when casting a spell on a party member or a task. + * @apiParam {string} spellId The skill to cast + * @apiParam {UUID} targetId Optional query parameter, the id of the target when casting a skill on a party member or a task * * @apiSuccess data Will return the modified targets. For party members only the necessary fields will be populated. The user is always returned. */ @@ -443,7 +443,7 @@ api.castSpell = { }; /** - * @api {post} /api/v3/user/sleep Put the user in the inn. + * @api {post} /api/v3/user/sleep Make the user start / stop sleeping (resting in the Inn) * @apiVersion 3.0.0 * @apiName UserSleep * @apiGroup User @@ -463,7 +463,7 @@ api.sleep = { }; /** - * @api {post} /api/v3/user/allocate Allocate an attribute point. + * @api {post} /api/v3/user/allocate Allocate an attribute point * @apiVersion 3.0.0 * @apiName UserAllocate * @apiGroup User @@ -485,7 +485,8 @@ api.allocate = { }; /** - * @api {post} /api/v3/user/allocate-now Allocate all attribute points. + * @api {post} /api/v3/user/allocate-now Allocate all attribute points + * @apiDescription Uses the user's chosen automatic allocation method, or if none, assigns all to STR. * @apiVersion 3.0.0 * @apiName UserAllocateNow * @apiGroup User @@ -511,7 +512,7 @@ api.allocateNow = { * @apiName UserBuy * @apiGroup User * - * @apiParam {string} key The item to buy. + * @apiParam {string} key The item to buy */ api.buy = { method: 'POST', @@ -526,12 +527,12 @@ api.buy = { }; /** - * @api {post} /user/buy-gear/:key Buy a piece of gear. + * @api {post} /user/buy-gear/:key Buy a piece of gear * @apiVersion 3.0.0 * @apiName UserBuyGear * @apiGroup User * - * @apiParam {string} key The item to buy. + * @apiParam {string} key The item to buy * * @apiSuccess {object} data.items user.items * @apiSuccess {object} data.flags user.flags @@ -552,12 +553,12 @@ api.buyGear = { }; /** - * @api {post} /user/buy-armoire Buy an armoire item. + * @api {post} /user/buy-armoire Buy an armoire item * @apiVersion 3.0.0 * @apiName UserBuyArmoire * @apiGroup User * - * @apiParam {string} key The item to buy. + * @apiParam {string} key The item to buy * * @apiSuccess {object} data.items user.items * @apiSuccess {object} data.flags user.flags @@ -600,12 +601,12 @@ api.buyPotion = { }; /** - * @api {post} /user/buy-mystery-set/:key Buy a mystery set. + * @api {post} /user/buy-mystery-set/:key Buy a mystery set * @apiVersion 3.0.0 * @apiName UserBuyMysterySet * @apiGroup User * - * @apiParam {string} key The mystery set to buy. + * @apiParam {string} key The mystery set to buy * * @apiSuccess {Object} data.items user.items * @apiSuccess {Object} data.purchasedPlanConsecutive user.purchased.plan.consecutive @@ -624,12 +625,12 @@ api.buyMysterySet = { }; /** - * @api {post} /api/v3/user/buy-quest/:key Buy a quest with gold. + * @api {post} /api/v3/user/buy-quest/:key Buy a quest with gold * @apiVersion 3.0.0 * @apiName UserBuyQuest * @apiGroup User * - * @apiParam {string} key The quest spell to buy. + * @apiParam {string} key The quest scroll to buy * * @apiSuccess {Object} data `user.items.quests` * @apiSuccess {string} message Success message @@ -647,12 +648,13 @@ api.buyQuest = { }; /** - * @api {post} /api/v3/user/buy-special-spell/:key Buy special spell. + * @api {post} /api/v3/user/buy-special-spell/:key Buy special "spell" item + * @apiDescription Includes gift cards (e.g., birthday card), and avatar Transformation Items and their antidotes (e.g., Snowball item and Salt reward). * @apiVersion 3.0.0 * @apiName UserBuySpecialSpell * @apiGroup User * - * @apiParam {string} key The special spell to buy. + * @apiParam {string} key The special item to buy. Must be one of the keys from "content.special", such as birthday, snowball, salt. * * @apiSuccess {Object} data.stats user.stats * @apiSuccess {Object} data.items user.items @@ -671,13 +673,13 @@ api.buySpecialSpell = { }; /** - * @api {post} /api/v3/user/hatch/:egg/:hatchingPotion Hatch a pet. + * @api {post} /api/v3/user/hatch/:egg/:hatchingPotion Hatch a pet * @apiVersion 3.0.0 * @apiName UserHatch * @apiGroup User * - * @apiParam {string} egg The egg to use. - * @apiParam {string} hatchingPotion The hatching potion to use. + * @apiParam {string} egg The egg to use + * @apiParam {string} hatchingPotion The hatching potion to use * * @apiSuccess {Object} data user.items * @apiSuccess {string} message @@ -743,13 +745,13 @@ api.feed = { }; /** -* @api {post} /api/v3/user/change-class Change class. +* @api {post} /api/v3/user/change-class Change class * @apiDescription User must be at least level 10. If ?class is defined and user.flags.classSelected is false it'll change the class. If user.preferences.disableClasses it'll enable classes, otherwise it sets user.flags.classSelected to false (costs 3 gems) * @apiVersion 3.0.0 * @apiName UserChangeClass * @apiGroup User * -* @apiParam {string} class Query parameter - ?class={warrior|rogue|wizard|healer}. +* @apiParam {string} class Query parameter - ?class={warrior|rogue|wizard|healer} * * @apiSuccess {object} data.flags user.flags * @apiSuccess {object} data.stats user.stats @@ -769,7 +771,7 @@ api.changeClass = { }; /** -* @api {post} /api/v3/user/disable-classes Disable classes. +* @api {post} /api/v3/user/disable-classes Disable classes * @apiVersion 3.0.0 * @apiName UserDisableClasses * @apiGroup User @@ -791,13 +793,13 @@ api.disableClasses = { }; /** -* @api {post} /api/v3/user/purchase/:type/:key Purchase Gem Items. +* @api {post} /api/v3/user/purchase/:type/:key Purchase Gem or Gem-purchasable item * @apiVersion 3.0.0 * @apiName UserPurchase * @apiGroup User * -* @apiParam {string} type Type of item to purchase. Must be one of: gem, gems, eggs, hatchingPotions, food, quests or gear -* @apiParam {string} key Item's key +* @apiParam {string} type Type of item to purchase. Must be one of: gems, eggs, hatchingPotions, food, quests, or gear +* @apiParam {string} key Item's key (use "gem" for purchasing gems) * * @apiSuccess {object} data.items user.items * @apiSuccess {number} data.balance user.balance @@ -816,7 +818,7 @@ api.purchase = { }; /** -* @api {post} /api/v3/user/purchase-hourglass/:type/:key Purchase Hourglass. +* @api {post} /api/v3/user/purchase-hourglass/:type/:key Purchase Hourglass-purchasable item * @apiVersion 3.0.0 * @apiName UserPurchaseHourglass * @apiGroup User @@ -841,7 +843,7 @@ api.userPurchaseHourglass = { }; /** -* @api {post} /api/v3/user/read-card/:cardType Reads a card. +* @api {post} /api/v3/user/read-card/:cardType Reads a card * @apiVersion 3.0.0 * @apiName UserReadCard * @apiGroup User @@ -865,7 +867,7 @@ api.readCard = { }; /** -* @api {post} /api/v3/user/open-mystery-item Open the mystery item. +* @api {post} /api/v3/user/open-mystery-item Open the Mystery Item box * @apiVersion 3.0.0 * @apiName UserOpenMysteryItem * @apiGroup User @@ -955,7 +957,7 @@ api.deleteWebhook = { }; -/* @api {post} /api/v3/user/release-pets Releases pets. +/* @api {post} /api/v3/user/release-pets Release pets * @apiVersion 3.0.0 * @apiName UserReleasePets * @apiGroup User @@ -976,7 +978,7 @@ api.userReleasePets = { }; /* -* @api {post} /api/v3/user/release-both Releases Pets and Mounts and grants Triad Bingo. +* @api {post} /api/v3/user/release-both Release pets and mounts and grants Triad Bingo * @apiVersion 3.0.0 * @apiName UserReleaseBoth * @apiGroup User @@ -999,7 +1001,7 @@ api.userReleaseBoth = { }; /* -* @api {post} /api/v3/user/release-mounts Released mounts. +* @api {post} /api/v3/user/release-mounts Release mounts * @apiVersion 3.0.0 * @apiName UserReleaseMounts * @apiGroup User @@ -1020,12 +1022,12 @@ api.userReleaseMounts = { }; /* -* @api {post} /api/v3/user/sell/:type/:key Sells a gold item owned by the user. +* @api {post} /api/v3/user/sell/:type/:key Sell a gold-sellable item owned by the user * @apiVersion 3.0.0 * @apiName UserSell * @apiGroup User * -* @apiParam {string} type The type of item to sell. Acceptable types are eggs, hatchingPotions, food +* @apiParam {string} type The type of item to sell. Must be one of: eggs, hatchingPotions, or food * @apiParam {string} key The key of the item * * @apiSuccess {Object} data.stats @@ -1045,7 +1047,7 @@ api.userSell = { }; /* -* @api {post} /api/v3/user/unlock Unlocks items by purchase. +* @api {post} /api/v3/user/unlock Unlock item or set of items by purchase * @apiVersion 3.0.0 * @apiName UserUnlock * @apiGroup User @@ -1053,9 +1055,9 @@ api.userSell = { * @apiParam {string} path Query parameter. The path to unlock * * @apiSuccess {Object} data.purchased -* @apiSuccess {Object} data.items` -* @apiSuccess {Object} data.preferences` -* @apiSuccess {string} message` +* @apiSuccess {Object} data.items +* @apiSuccess {Object} data.preferences +* @apiSuccess {string} message */ api.userUnlock = { method: 'POST', @@ -1070,7 +1072,7 @@ api.userUnlock = { }; /** -* @api {post} /api/v3/user/revive Revives user from death. +* @api {post} /api/v3/user/revive Revive user from death * @apiVersion 3.0.0 * @apiName UserRevive * @apiGroup User @@ -1128,7 +1130,8 @@ api.userRebirth = { }; /** - * @api {post} /api/v3/user/block/:uuid Blocks and unblocks a user + * @api {post} /api/v3/user/block/:uuid Block and unblock a user + * @apiDescription Must be an admin to make this request. * @apiVersion 3.0.0 * @apiName BlockUser * @apiGroup User @@ -1212,7 +1215,7 @@ api.markPmsRead = { }; /* -* @api {post} /api/v3/user/reroll Rerolls a user. +* @api {post} /api/v3/user/reroll Reroll a user using the Fortify Potion * @apiVersion 3.0.0 * @apiName UserReroll * @apiGroup User @@ -1248,7 +1251,7 @@ api.userReroll = { }; /* -* @api {post} /api/v3/user/addPushDevice Adds a push device to a user. +* @api {post} /api/v3/user/addPushDevice Add a push device to a user * @apiVersion 3.0.0 * @apiName UserAddPushDevice * @apiGroup User @@ -1274,7 +1277,7 @@ api.userAddPushDevice = { }; /* -* @api {post} /api/v3/user/reset Resets a user. +* @api {post} /api/v3/user/reset Reset user * @apiVersion 3.0.0 * @apiName UserReset * @apiGroup User diff --git a/website/server/controllers/top-level/dataexport.js b/website/server/controllers/top-level/dataexport.js index 10519807e3..477c8ea829 100644 --- a/website/server/controllers/top-level/dataexport.js +++ b/website/server/controllers/top-level/dataexport.js @@ -26,8 +26,8 @@ const BASE_URL = nconf.get('BASE_URL'); let api = {}; /** - * @api {get} /export/history.csv Export user tasks history in CSV format. - * @apiDescription History is only available for habits and dailys so todos and rewards won't be included NOTE: Part of the private API that may change at any time. + * @api {get} /export/history.csv Export user tasks history in CSV format + * @apiDescription History is only available for habits and dailys so todos and rewards won't be included. NOTE: Part of the private API that may change at any time. * @apiVersion 3.0.0 * @apiName ExportUserHistory * @apiGroup DataExport @@ -94,7 +94,7 @@ async function _getUserDataForExport (user) { } /** - * @api {get} /export/userdata.json Export user data in JSON format. + * @api {get} /export/userdata.json Export user data in JSON format * @apiVersion 3.0.0 * @apiName ExportUserDataJson * @apiGroup DataExport From cfb5e2be60bb23aa9f261fe33feae1ea4e7c15b3 Mon Sep 17 00:00:00 2001 From: Alys Date: Tue, 17 May 2016 02:13:49 +1000 Subject: [PATCH 08/10] fix bug in Rebirth test, add new tests, adjust apidocs (#7293) --- .../user/POST-user_rebirth.test.js | 1 + test/common/ops/rebirth.js | 62 ++++++++++++++++--- website/server/controllers/api-v3/user.js | 4 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/test/api/v3/integration/user/POST-user_rebirth.test.js b/test/api/v3/integration/user/POST-user_rebirth.test.js index 3591844c3c..21fbed0b8d 100644 --- a/test/api/v3/integration/user/POST-user_rebirth.test.js +++ b/test/api/v3/integration/user/POST-user_rebirth.test.js @@ -31,6 +31,7 @@ describe('POST /user/rebirth', () => { let daily = await generateDaily({ text: 'test habit', type: 'daily', + value: 1, streak: 1, userId: user._id, }); diff --git a/test/common/ops/rebirth.js b/test/common/ops/rebirth.js index 0ab66674b3..9916144669 100644 --- a/test/common/ops/rebirth.js +++ b/test/common/ops/rebirth.js @@ -3,7 +3,9 @@ import i18n from '../../../common/script/i18n'; import { MAX_LEVEL } from '../../../common/script/constants'; import { generateUser, + generateHabit, generateDaily, + generateTodo, generateReward, } from '../../helpers/common.helper'; import { @@ -19,7 +21,7 @@ describe('shared.ops.rebirth', () => { beforeEach(() => { user = generateUser(); user.balance = 2; - tasks = [generateDaily(), generateReward()]; + tasks = [generateHabit(), generateDaily(), generateTodo(), generateReward()]; }); it('returns an error when user balance is too low and user is less than max level', (done) => { @@ -49,22 +51,35 @@ describe('shared.ops.rebirth', () => { expect(message).to.equal(i18n.t('rebirthComplete')); }); - it('resets user\'s taks values except for rewards to 0', () => { + it('rebirths a user with not enough gems but more than max level', () => { + user.balance = 0; + user.stats.lvl = MAX_LEVEL + 1; + + let [, message] = rebirth(user); + + expect(message).to.equal(i18n.t('rebirthComplete')); + }); + + it('resets user\'s tasks values except for rewards to 0', () => { tasks[0].value = 1; tasks[1].value = 1; + tasks[2].value = 1; + tasks[3].value = 1; // Reward rebirth(user, tasks); expect(tasks[0].value).to.equal(0); - expect(tasks[1].value).to.equal(1); + expect(tasks[1].value).to.equal(0); + expect(tasks[2].value).to.equal(0); + expect(tasks[3].value).to.equal(1); // Reward }); it('resets user\'s daily streaks to 0', () => { - tasks[0].streak = 1; + tasks[1].streak = 1; // Daily rebirth(user, tasks); - expect(tasks[0].streak).to.equal(0); + expect(tasks[1].streak).to.equal(0); }); it('resets a user\'s buffs', () => { @@ -156,7 +171,7 @@ describe('shared.ops.rebirth', () => { expect(user.flags.dropsEnabled).to.be.false; expect(user.flags.classSelected).to.be.false; expect(user.flags.rebirthEnabled).to.be.false; - expect(user.flags.levelDrops).to.be.emtpy; + expect(user.flags.levelDrops).to.be.empty; }); it('does not reset rebirthEnabled if user has beastMaster', () => { @@ -175,7 +190,7 @@ describe('shared.ops.rebirth', () => { expect(user.achievements.rebirthLevel).to.equal(user.stats.lvl); }); - it('increments rebirth achievemnts', () => { + it('increments rebirth achievements', () => { user.stats.lvl = 2; user.achievements.rebirths = 1; user.achievements.rebirthLevel = 1; @@ -185,4 +200,37 @@ describe('shared.ops.rebirth', () => { expect(user.achievements.rebirths).to.equal(2); expect(user.achievements.rebirthLevel).to.equal(2); }); + + it('does not increment rebirth achievements when level is lower than previous', () => { + user.stats.lvl = 2; + user.achievements.rebirths = 1; + user.achievements.rebirthLevel = 3; + + rebirth(user); + + expect(user.achievements.rebirths).to.equal(1); + expect(user.achievements.rebirthLevel).to.equal(3); + }); + + it('always increments rebirth achievements when level is MAX_LEVEL', () => { + user.stats.lvl = MAX_LEVEL; + user.achievements.rebirths = 1; + user.achievements.rebirthLevel = MAX_LEVEL + 1; // this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test + + rebirth(user); + + expect(user.achievements.rebirths).to.equal(2); + expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL); + }); + + it('always increments rebirth achievements when level is greater than MAX_LEVEL', () => { + user.stats.lvl = MAX_LEVEL + 1; + user.achievements.rebirths = 1; + user.achievements.rebirthLevel = MAX_LEVEL + 2; // this value is not actually possible (actually capped at MAX_LEVEL) but makes a good test + + rebirth(user); + + expect(user.achievements.rebirths).to.equal(2); + expect(user.achievements.rebirthLevel).to.equal(MAX_LEVEL); + }); }); diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 2e22a6b662..789444ea6c 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -1093,12 +1093,12 @@ api.userRevive = { }; /* -* @api {post} /api/v3/user/rebirth Resets a user. +* @api {post} /api/v3/user/rebirth Use Orb of Rebirth on user * @apiVersion 3.0.0 * @apiName UserRebirth * @apiGroup User * -* @apiSuccess {Object} data.userr +* @apiSuccess {Object} data.user * @apiSuccess {array} data.tasks User's modified tasks (no rewards) * @apiSuccess {string} message Success message */ From dbd6daeca013c65eb3185ff3e74bc867c7c303c1 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Mon, 16 May 2016 13:12:46 -0500 Subject: [PATCH 09/10] Updated task model to allow setting streak (#7306) --- test/api/v3/integration/tasks/POST-tasks_user.test.js | 4 +--- website/server/models/task.js | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/api/v3/integration/tasks/POST-tasks_user.test.js b/test/api/v3/integration/tasks/POST-tasks_user.test.js index b39e640480..623d9bb4a4 100644 --- a/test/api/v3/integration/tasks/POST-tasks_user.test.js +++ b/test/api/v3/integration/tasks/POST-tasks_user.test.js @@ -108,7 +108,7 @@ describe('POST /tasks/user', () => { }); it(`ignores setting userId, history, createdAt, - updatedAt, challenge, completed, streak, + updatedAt, challenge, completed, dateCompleted fields`, async () => { let task = await user.post('/tasks/user', { text: 'test daily', @@ -119,7 +119,6 @@ describe('POST /tasks/user', () => { updatedAt: 'tomorrow', challenge: 'no', completed: true, - streak: 25, dateCompleted: 'never', value: 324, // ignored because not a reward }); @@ -130,7 +129,6 @@ describe('POST /tasks/user', () => { expect(task.updatedAt).not.to.equal('tomorrow'); expect(task.challenge).not.to.equal('no'); expect(task.completed).to.equal(false); - expect(task.streak).to.equal(0); expect(task.streak).not.to.equal('never'); expect(task.value).not.to.equal(324); }); diff --git a/website/server/models/task.js b/website/server/models/task.js index 0459a74580..27f356efa3 100644 --- a/website/server/models/task.js +++ b/website/server/models/task.js @@ -56,7 +56,7 @@ export let TaskSchema = new Schema({ }, discriminatorOptions)); TaskSchema.plugin(baseModel, { - noSet: ['challenge', 'userId', 'completed', 'history', 'streak', 'dateCompleted', 'completed'], + noSet: ['challenge', 'userId', 'completed', 'history', 'dateCompleted', 'completed'], sanitizeTransform (taskObj) { if (taskObj.type && taskObj.type !== 'reward') { // value should be settable directly only for rewards delete taskObj.value; From e6b5fe4013638f0333875f2bef0cc6217bd75609 Mon Sep 17 00:00:00 2001 From: Blade Barringer Date: Mon, 16 May 2016 13:45:04 -0500 Subject: [PATCH 10/10] fix: Correct missing * in apidoc comments --- website/server/controllers/api-v3/user.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/website/server/controllers/api-v3/user.js b/website/server/controllers/api-v3/user.js index 789444ea6c..6358d5d25b 100644 --- a/website/server/controllers/api-v3/user.js +++ b/website/server/controllers/api-v3/user.js @@ -887,7 +887,7 @@ api.userOpenMysteryItem = { }, }; -/* +/** * @api {post} /api/v3/user/webhook Create a new webhook * @apiVersion 3.0.0 * @apiName UserAddWebhook @@ -910,7 +910,7 @@ api.addWebhook = { }, }; -/* +/** * @api {put} /api/v3/user/webhook/:id Edit a webhook * @apiVersion 3.0.0 * @apiName UserUpdateWebhook @@ -934,7 +934,7 @@ api.updateWebhook = { }, }; -/* +/** * @api {delete} /api/v3/user/webhook/:id Delete a webhook * @apiVersion 3.0.0 * @apiName UserDeleteWebhook @@ -977,7 +977,7 @@ api.userReleasePets = { }, }; -/* +/** * @api {post} /api/v3/user/release-both Release pets and mounts and grants Triad Bingo * @apiVersion 3.0.0 * @apiName UserReleaseBoth @@ -1000,7 +1000,7 @@ api.userReleaseBoth = { }, }; -/* +/** * @api {post} /api/v3/user/release-mounts Release mounts * @apiVersion 3.0.0 * @apiName UserReleaseMounts @@ -1021,7 +1021,7 @@ api.userReleaseMounts = { }, }; -/* +/** * @api {post} /api/v3/user/sell/:type/:key Sell a gold-sellable item owned by the user * @apiVersion 3.0.0 * @apiName UserSell @@ -1046,7 +1046,7 @@ api.userSell = { }, }; -/* +/** * @api {post} /api/v3/user/unlock Unlock item or set of items by purchase * @apiVersion 3.0.0 * @apiName UserUnlock @@ -1092,7 +1092,7 @@ api.userRevive = { }, }; -/* +/** * @api {post} /api/v3/user/rebirth Use Orb of Rebirth on user * @apiVersion 3.0.0 * @apiName UserRebirth @@ -1214,7 +1214,7 @@ api.markPmsRead = { }, }; -/* +/** * @api {post} /api/v3/user/reroll Reroll a user using the Fortify Potion * @apiVersion 3.0.0 * @apiName UserReroll @@ -1250,7 +1250,7 @@ api.userReroll = { }, }; -/* +/** * @api {post} /api/v3/user/addPushDevice Add a push device to a user * @apiVersion 3.0.0 * @apiName UserAddPushDevice @@ -1276,7 +1276,7 @@ api.userAddPushDevice = { }, }; -/* +/** * @api {post} /api/v3/user/reset Reset user * @apiVersion 3.0.0 * @apiName UserReset