mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Refactor Purchase API - Part 1 (#9714)
* move to shops/purchase * move files to /buy/ instead of /purchase/ * refactor buy.js - add more itemtypes * revert moving special purchases to buy * only use buyOp from api-routes * fix buying potion client-side * undo import buy instead of purchase * enable potion bulk purchase - use buyGear as fallback (as before) * move quantity purchase inside buyHealthPotion * move quantity purchase inside buyQuest * move quantity purchase inside buySpecialSpell + add analytics * remove unused quantity variable - set req.type on specialKeys * fix `buyKnownKeys` on buy api * test buy-special-spell if not enough gold * more buy ops coverage * fix lint * buyMysterySet: test for window.confirm, buyQuest: check for Masterclassers unlock * fix test & lint * re-create package-lock.json to travis build ? * use global.window instead of method argument * add back canOwn checks * remove buyMysterySet confirm request
This commit is contained in:
@@ -40,4 +40,19 @@ describe('POST /user/buy-special-spell/:key', () => {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
});
|
||||
|
||||
it('returns an error if user does not have enough gold', async () => {
|
||||
let key = 'thankyou';
|
||||
|
||||
await user.update({
|
||||
'stats.gp': 5,
|
||||
});
|
||||
|
||||
await expect(user.post(`/user/buy-special-spell/${key}`))
|
||||
.to.eventually.be.rejected.and.eql({
|
||||
code: 401,
|
||||
error: 'NotAuthorized',
|
||||
message: t('messageNotEnoughGold'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import buy from '../../../../website/common/script/ops/buy';
|
||||
import buy from '../../../../website/common/script/ops/buy/buy';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
@@ -11,6 +11,7 @@ import content from '../../../../website/common/script/content/index';
|
||||
|
||||
describe('shared.ops.buy', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -26,6 +27,12 @@ describe('shared.ops.buy', () => {
|
||||
},
|
||||
stats: { gp: 200 },
|
||||
});
|
||||
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('returns error when key is not provided', (done) => {
|
||||
@@ -40,8 +47,10 @@ describe('shared.ops.buy', () => {
|
||||
|
||||
it('recovers 15 hp', () => {
|
||||
user.stats.hp = 30;
|
||||
buy(user, {params: {key: 'potion'}});
|
||||
buy(user, {params: {key: 'potion'}}, analytics);
|
||||
expect(user.stats.hp).to.eql(45);
|
||||
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('adds equipment to inventory', () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import count from '../../../../website/common/script/count';
|
||||
import buyArmoire from '../../../../website/common/script/ops/buyArmoire';
|
||||
import buyArmoire from '../../../../website/common/script/ops/buy/buyArmoire';
|
||||
import randomVal from '../../../../website/common/script/libs/randomVal';
|
||||
import content from '../../../../website/common/script/content/index';
|
||||
import {
|
||||
@@ -31,6 +31,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
let YIELD_EQUIPMENT = 0.5;
|
||||
let YIELD_FOOD = 0.7;
|
||||
let YIELD_EXP = 0.9;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -45,10 +46,12 @@ describe('shared.ops.buyArmoire', () => {
|
||||
user.items.food = {};
|
||||
|
||||
sandbox.stub(randomVal, 'trueRandom');
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
randomVal.trueRandom.restore();
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -141,7 +144,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(2);
|
||||
|
||||
buyArmoire(user);
|
||||
buyArmoire(user, {}, analytics);
|
||||
|
||||
expect(_.size(user.items.gear.owned)).to.equal(3);
|
||||
|
||||
@@ -149,6 +152,7 @@ describe('shared.ops.buyArmoire', () => {
|
||||
|
||||
expect(armoireCount).to.eql(_.size(getFullArmoire()) - 2);
|
||||
expect(user.stats.gp).to.eql(100);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,15 +4,16 @@ import sinon from 'sinon'; // eslint-disable-line no-shadow
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import buyGear from '../../../../website/common/script/ops/buyGear';
|
||||
import buyGear from '../../../../website/common/script/ops/buy/buyGear';
|
||||
import shared from '../../../../website/common/script';
|
||||
import {
|
||||
NotAuthorized,
|
||||
BadRequest, NotAuthorized, NotFound,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyGear', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -31,18 +32,20 @@ describe('shared.ops.buyGear', () => {
|
||||
|
||||
sinon.stub(shared, 'randomVal');
|
||||
sinon.stub(shared.fns, 'predictableRandom');
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
shared.randomVal.restore();
|
||||
shared.fns.predictableRandom.restore();
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('Gear', () => {
|
||||
it('adds equipment to inventory', () => {
|
||||
user.stats.gp = 31;
|
||||
|
||||
buyGear(user, {params: {key: 'armor_warrior_1'}});
|
||||
buyGear(user, {params: {key: 'armor_warrior_1'}}, analytics);
|
||||
|
||||
expect(user.items.gear.owned).to.eql({
|
||||
weapon_warrior_0: true,
|
||||
@@ -55,6 +58,7 @@ describe('shared.ops.buyGear', () => {
|
||||
eyewear_special_whiteTopFrame: true,
|
||||
eyewear_special_yellowTopFrame: true,
|
||||
});
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('deducts gold from user', () => {
|
||||
@@ -139,6 +143,38 @@ describe('shared.ops.buyGear', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when key is not provided', (done) => {
|
||||
try {
|
||||
buyGear(user);
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when item is not found', (done) => {
|
||||
let params = {key: 'armor_warrior_notExisting'};
|
||||
|
||||
try {
|
||||
buyGear(user, {params});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotFound);
|
||||
expect(err.message).to.equal(i18n.t('itemNotFound', params));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not buyGear equipment without the previous equipment', (done) => {
|
||||
try {
|
||||
buyGear(user, {params: {key: 'armor_warrior_2'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.equal(i18n.t('previousGearNotOwned'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not buyGear equipment if user does not own prior item in sequence', (done) => {
|
||||
user.stats.gp = 200;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import buyHealthPotion from '../../../../website/common/script/ops/buyHealthPotion';
|
||||
import buyHealthPotion from '../../../../website/common/script/ops/buy/buyHealthPotion';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
@@ -10,6 +10,7 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyHealthPotion', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -25,13 +26,19 @@ describe('shared.ops.buyHealthPotion', () => {
|
||||
},
|
||||
stats: { gp: 200 },
|
||||
});
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('Potion', () => {
|
||||
it('recovers 15 hp', () => {
|
||||
user.stats.hp = 30;
|
||||
buyHealthPotion(user);
|
||||
buyHealthPotion(user, {}, analytics);
|
||||
expect(user.stats.hp).to.eql(45);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not increase hp above 50', () => {
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import buyMysterySet from '../../../../website/common/script/ops/buyMysterySet';
|
||||
import buyMysterySet from '../../../../website/common/script/ops/buy/buyMysterySet';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
@@ -12,6 +13,7 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyMysterySet', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser({
|
||||
@@ -23,6 +25,11 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('Mystery Sets', () => {
|
||||
@@ -57,12 +64,22 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when key is not provided', (done) => {
|
||||
try {
|
||||
buyMysterySet(user);
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
context('successful purchases', () => {
|
||||
it('buys Steampunk Accessories Set', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
buyMysterySet(user, {params: {key: '301404'}});
|
||||
buyMysterySet(user, {params: {key: '301404'}}, analytics);
|
||||
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(0);
|
||||
expect(user.items.gear.owned).to.have.property('weapon_warrior_0', true);
|
||||
@@ -70,6 +87,7 @@ describe('shared.ops.buyMysterySet', () => {
|
||||
expect(user.items.gear.owned).to.have.property('armor_mystery_301404', true);
|
||||
expect(user.items.gear.owned).to.have.property('head_mystery_301404', true);
|
||||
expect(user.items.gear.owned).to.have.property('eyewear_mystery_301404', true);
|
||||
expect(analytics.track).to.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import buyQuest from '../../../../website/common/script/ops/buyQuest';
|
||||
import buyQuest from '../../../../website/common/script/ops/buy/buyQuest';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
@@ -10,9 +11,15 @@ import i18n from '../../../../website/common/script/i18n';
|
||||
|
||||
describe('shared.ops.buyQuest', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('buys a Quest scroll', () => {
|
||||
@@ -21,11 +28,12 @@ describe('shared.ops.buyQuest', () => {
|
||||
params: {
|
||||
key: 'dilatoryDistress1',
|
||||
},
|
||||
});
|
||||
}, analytics);
|
||||
expect(user.items.quests).to.eql({
|
||||
dilatoryDistress1: 1,
|
||||
});
|
||||
expect(user.stats.gp).to.equal(5);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('does not buy Quests without enough Gold', (done) => {
|
||||
@@ -62,6 +70,22 @@ describe('shared.ops.buyQuest', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('does not buy the Mystery of the Masterclassers', (done) => {
|
||||
try {
|
||||
buyQuest(user, {
|
||||
params: {
|
||||
key: 'lostMasterclasser1',
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.equal(i18n.t('questUnlockLostMasterclasser'));
|
||||
expect(user.items.quests).to.eql({});
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('does not buy Gem-premium Quests', (done) => {
|
||||
user.stats.gp = 9999;
|
||||
try {
|
||||
@@ -78,4 +102,14 @@ describe('shared.ops.buyQuest', () => {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when key is not provided', (done) => {
|
||||
try {
|
||||
buyQuest(user);
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.equal(i18n.t('missingKeyParam'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import buySpecialSpell from '../../../../website/common/script/ops/buySpecialSpell';
|
||||
import buySpecialSpell from '../../../../website/common/script/ops/buy/buySpecialSpell';
|
||||
import {
|
||||
BadRequest,
|
||||
NotFound,
|
||||
@@ -12,9 +12,15 @@ import content from '../../../../website/common/script/content/index';
|
||||
|
||||
describe('shared.ops.buySpecialSpell', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
it('throws an error if params.key is missing', (done) => {
|
||||
@@ -64,7 +70,7 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
params: {
|
||||
key: 'thankyou',
|
||||
},
|
||||
});
|
||||
}, analytics);
|
||||
|
||||
expect(user.stats.gp).to.equal(1);
|
||||
expect(user.items.special.thankyou).to.equal(1);
|
||||
@@ -75,5 +81,6 @@ describe('shared.ops.buySpecialSpell', () => {
|
||||
expect(message).to.equal(i18n.t('messageBought', {
|
||||
itemText: item.text(),
|
||||
}));
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,25 @@
|
||||
import hourglassPurchase from '../../../website/common/script/ops/hourglassPurchase';
|
||||
import hourglassPurchase from '../../../../website/common/script/ops/buy/hourglassPurchase';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
import content from '../../../website/common/script/content/index';
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import content from '../../../../website/common/script/content/index';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
} from '../../../helpers/common.helper';
|
||||
|
||||
describe('common.ops.hourglassPurchase', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
@@ -126,11 +132,12 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
it('buys a pet', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 2;
|
||||
|
||||
let [, message] = hourglassPurchase(user, {params: {type: 'pets', key: 'MantisShrimp-Base'}});
|
||||
let [, message] = hourglassPurchase(user, {params: {type: 'pets', key: 'MantisShrimp-Base'}}, analytics);
|
||||
|
||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({'MantisShrimp-Base': 5});
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('buys a mount', () => {
|
||||
@@ -1,14 +1,14 @@
|
||||
import purchase from '../../../website/common/script/ops/purchase';
|
||||
import planGemLimits from '../../../website/common/script/libs/planGemLimits';
|
||||
import purchase from '../../../../website/common/script/ops/buy/purchase';
|
||||
import planGemLimits from '../../../../website/common/script/libs/planGemLimits';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../website/common/script/i18n';
|
||||
} from '../../../../website/common/script/libs/errors';
|
||||
import i18n from '../../../../website/common/script/i18n';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
} from '../../../helpers/common.helper';
|
||||
import forEach from 'lodash/forEach';
|
||||
import moment from 'moment';
|
||||
|
||||
@@ -17,11 +17,20 @@ describe('shared.ops.purchase', () => {
|
||||
let user;
|
||||
let goldPoints = 40;
|
||||
let gemsBought = 40;
|
||||
let analytics = {track () {}};
|
||||
|
||||
before(() => {
|
||||
user = generateUser({'stats.class': 'rogue'});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
sinon.stub(analytics, 'track');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
analytics.track.restore();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
it('returns an error when type is not provided', (done) => {
|
||||
try {
|
||||
@@ -129,6 +138,19 @@ describe('shared.ops.purchase', () => {
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
it('returns error when item is not found', (done) => {
|
||||
let params = {key: 'notExisting', type: 'food'};
|
||||
|
||||
try {
|
||||
purchase(user, {params});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotFound);
|
||||
expect(err.message).to.equal(i18n.t('contentKeyNotFound', params));
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
context('successful purchase', () => {
|
||||
@@ -142,12 +164,13 @@ describe('shared.ops.purchase', () => {
|
||||
});
|
||||
|
||||
it('purchases gems', () => {
|
||||
let [, message] = purchase(user, {params: {type: 'gems', key: 'gem'}});
|
||||
let [, message] = purchase(user, {params: {type: 'gems', key: 'gem'}}, analytics);
|
||||
|
||||
expect(message).to.equal(i18n.t('plusOneGem'));
|
||||
expect(user.balance).to.equal(userGemAmount + 0.25);
|
||||
expect(user.purchased.plan.gemsBought).to.equal(1);
|
||||
expect(user.stats.gp).to.equal(goldPoints - planGemLimits.convRate);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('purchases gems with a different language than the default', () => {
|
||||
@@ -163,9 +186,10 @@ describe('shared.ops.purchase', () => {
|
||||
let type = 'eggs';
|
||||
let key = 'Wolf';
|
||||
|
||||
purchase(user, {params: {type, key}});
|
||||
purchase(user, {params: {type, key}}, analytics);
|
||||
|
||||
expect(user.items[type][key]).to.equal(1);
|
||||
expect(analytics.track).to.be.calledOnce;
|
||||
});
|
||||
|
||||
it('purchases hatchingPotions', () => {
|
||||
@@ -1,8 +1,8 @@
|
||||
import axios from 'axios';
|
||||
import buyOp from 'common/script/ops/buy';
|
||||
import buyOp from 'common/script/ops/buy/buy';
|
||||
import content from 'common/script/content/index';
|
||||
import purchaseOp from 'common/script/ops/purchaseWithSpell';
|
||||
import hourglassPurchaseOp from 'common/script/ops/hourglassPurchase';
|
||||
import purchaseOp from 'common/script/ops/buy/purchaseWithSpell';
|
||||
import hourglassPurchaseOp from 'common/script/ops/buy/hourglassPurchase';
|
||||
import sellOp from 'common/script/ops/sell';
|
||||
import unlockOp from 'common/script/ops/unlock';
|
||||
import rerollOp from 'common/script/ops/reroll';
|
||||
@@ -101,7 +101,7 @@ export function purchase (store, params) {
|
||||
|
||||
export function purchaseMysterySet (store, params) {
|
||||
const user = store.state.user.data;
|
||||
let opResult = buyOp(user, {params, noConfirm: true, type: 'mystery'});
|
||||
let opResult = buyOp(user, {params, type: 'mystery'});
|
||||
|
||||
return {
|
||||
result: opResult,
|
||||
|
||||
@@ -138,21 +138,12 @@ import sleep from './ops/sleep';
|
||||
import allocateNow from './ops/stats/allocateNow';
|
||||
import allocate from './ops/stats/allocate';
|
||||
import allocateBulk from './ops/stats/allocateBulk';
|
||||
import buy from './ops/buy';
|
||||
import buyGear from './ops/buyGear';
|
||||
import buyHealthPotion from './ops/buyHealthPotion';
|
||||
import buyArmoire from './ops/buyArmoire';
|
||||
import buyMysterySet from './ops/buyMysterySet';
|
||||
import buyQuest from './ops/buyQuest';
|
||||
import buySpecialSpell from './ops/buySpecialSpell';
|
||||
import buy from './ops/buy/buy';
|
||||
import hatch from './ops/hatch';
|
||||
import feed from './ops/feed';
|
||||
import equip from './ops/equip';
|
||||
import changeClass from './ops/changeClass';
|
||||
import disableClasses from './ops/disableClasses';
|
||||
import purchase from './ops/purchase';
|
||||
import purchaseWithSpell from './ops/purchaseWithSpell';
|
||||
import purchaseHourglass from './ops/hourglassPurchase';
|
||||
import readCard from './ops/readCard';
|
||||
import openMysteryItem from './ops/openMysteryItem';
|
||||
import releasePets from './ops/releasePets';
|
||||
@@ -177,21 +168,12 @@ api.ops = {
|
||||
allocate,
|
||||
allocateBulk,
|
||||
buy,
|
||||
buyGear,
|
||||
buyHealthPotion,
|
||||
buyArmoire,
|
||||
buyMysterySet,
|
||||
buySpecialSpell,
|
||||
buyQuest,
|
||||
allocateNow,
|
||||
hatch,
|
||||
feed,
|
||||
equip,
|
||||
changeClass,
|
||||
disableClasses,
|
||||
purchase,
|
||||
purchaseWithSpell,
|
||||
purchaseHourglass,
|
||||
readCard,
|
||||
openMysteryItem,
|
||||
releasePets,
|
||||
|
||||
@@ -237,7 +237,7 @@ module.exports = function getItemInfo (user, type, item, officialPinnedItems, la
|
||||
notes: item.notes(language),
|
||||
value: item.value,
|
||||
currency: 'gold',
|
||||
purchaseType: 'potions',
|
||||
purchaseType: 'potion',
|
||||
class: `shop_${item.key}`,
|
||||
path: 'potion',
|
||||
pinType: 'potion',
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import i18n from '../i18n';
|
||||
import i18n from '../../i18n';
|
||||
import get from 'lodash/get';
|
||||
import {
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
import buyHealthPotion from './buyHealthPotion';
|
||||
import buyArmoire from './buyArmoire';
|
||||
import buyGear from './buyGear';
|
||||
import buyMysterySet from './buyMysterySet';
|
||||
import buyQuest from './buyQuest';
|
||||
import buySpecialSpell from './buySpecialSpell';
|
||||
import purchaseOp from './purchase';
|
||||
import hourglassPurchase from './hourglassPurchase';
|
||||
|
||||
// @TODO: remove the req option style. Dependency on express structure is an anti-pattern
|
||||
// We should either have more parms or a set structure validated by a Type checker
|
||||
@@ -22,29 +24,43 @@ module.exports = function buy (user, req = {}, analytics) {
|
||||
// @TODO: Slowly remove the need for key and use type instead
|
||||
// This should evenutally be the 'factory' function with vendor classes
|
||||
let type = get(req, 'type');
|
||||
if (!type) type = get(req, 'params.type');
|
||||
if (!type) type = key;
|
||||
|
||||
// @TODO: For now, builk purchasing is here, but we should probably have a parent vendor
|
||||
// class that calls the factory and handles larger operations. If there is more than just bulk
|
||||
let quantity = 1;
|
||||
if (req.quantity) quantity = req.quantity;
|
||||
|
||||
let buyRes;
|
||||
|
||||
for (let i = 0; i < quantity; i += 1) {
|
||||
if (type === 'potion') {
|
||||
buyRes = buyHealthPotion(user, req, analytics);
|
||||
} else if (type === 'armoire') {
|
||||
switch (type) {
|
||||
case 'armoire':
|
||||
buyRes = buyArmoire(user, req, analytics);
|
||||
} else if (type === 'mystery') {
|
||||
break;
|
||||
case 'mystery':
|
||||
buyRes = buyMysterySet(user, req, analytics);
|
||||
} else if (type === 'quest') {
|
||||
break;
|
||||
case 'potion':
|
||||
buyRes = buyHealthPotion(user, req, analytics);
|
||||
break;
|
||||
case 'eggs':
|
||||
case 'hatchingPotions':
|
||||
case 'food':
|
||||
case 'quests':
|
||||
case 'gear':
|
||||
case 'bundles':
|
||||
case 'gems':
|
||||
buyRes = purchaseOp(user, req, analytics);
|
||||
break;
|
||||
case 'pets':
|
||||
case 'mounts':
|
||||
buyRes = hourglassPurchase(user, req, analytics);
|
||||
break;
|
||||
case 'quest':
|
||||
buyRes = buyQuest(user, req, analytics);
|
||||
} else if (type === 'special') {
|
||||
break;
|
||||
case 'special':
|
||||
buyRes = buySpecialSpell(user, req, analytics);
|
||||
} else {
|
||||
break;
|
||||
default:
|
||||
buyRes = buyGear(user, req, analytics);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return buyRes;
|
||||
@@ -1,15 +1,15 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import content from '../../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import filter from 'lodash/filter';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import pick from 'lodash/pick';
|
||||
import count from '../count';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import count from '../../count';
|
||||
import splitWhitespace from '../../libs/splitWhitespace';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
import randomVal from '../libs/randomVal';
|
||||
import { removeItemByPath } from './pinnedGearUtils';
|
||||
} from '../../libs/errors';
|
||||
import randomVal from '../../libs/randomVal';
|
||||
import { removeItemByPath } from '../pinnedGearUtils';
|
||||
|
||||
// TODO this is only used on the server
|
||||
// move out of common?
|
||||
@@ -1,17 +1,17 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import content from '../../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import get from 'lodash/get';
|
||||
import pick from 'lodash/pick';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import splitWhitespace from '../../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
import handleTwoHanded from '../fns/handleTwoHanded';
|
||||
import ultimateGear from '../fns/ultimateGear';
|
||||
} from '../../libs/errors';
|
||||
import handleTwoHanded from '../../fns/handleTwoHanded';
|
||||
import ultimateGear from '../../fns/ultimateGear';
|
||||
|
||||
import { removePinnedGearAddPossibleNewOnes } from './pinnedGearUtils';
|
||||
import { removePinnedGearAddPossibleNewOnes } from '../pinnedGearUtils';
|
||||
|
||||
module.exports = function buyGear (user, req = {}, analytics) {
|
||||
let key = get(req, 'params.key');
|
||||
@@ -1,13 +1,14 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import content from '../../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import {
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
|
||||
module.exports = function buyHealthPotion (user, req = {}, analytics) {
|
||||
let item = content.potion;
|
||||
let quantity = req.quantity || 1;
|
||||
|
||||
if (user.stats.gp < item.value) {
|
||||
if (user.stats.gp < item.value * quantity) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
@@ -23,12 +24,12 @@ module.exports = function buyHealthPotion (user, req = {}, analytics) {
|
||||
throw new NotAuthorized(i18n.t('messageHealthAlreadyMin', req.language));
|
||||
}
|
||||
|
||||
user.stats.hp += 15;
|
||||
user.stats.hp += 15 * quantity;
|
||||
if (user.stats.hp > 50) {
|
||||
user.stats.hp = 50;
|
||||
}
|
||||
|
||||
user.stats.gp -= item.value;
|
||||
user.stats.gp -= item.value * quantity;
|
||||
|
||||
let message = i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
@@ -43,6 +44,7 @@ module.exports = function buyHealthPotion (user, req = {}, analytics) {
|
||||
goldCost: item.value,
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
quantityPurchased: quantity,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import content from '../../content/index';
|
||||
import get from 'lodash/get';
|
||||
import each from 'lodash/each';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
|
||||
module.exports = function buyMysterySet (user, req = {}, analytics) {
|
||||
let key = get(req, 'params.key');
|
||||
@@ -23,10 +23,6 @@ module.exports = function buyMysterySet (user, req = {}, analytics) {
|
||||
throw new NotFound(i18n.t('mysterySetNotFound', req.language));
|
||||
}
|
||||
|
||||
if (typeof window !== 'undefined' && !req.noConfirm && window.confirm) { // TODO move to client
|
||||
if (!window.confirm(i18n.t('hourglassBuyEquipSetConfirm'))) return;
|
||||
}
|
||||
|
||||
each(mysterySet.items, item => {
|
||||
user.items.gear.owned[item.key] = true;
|
||||
if (analytics) {
|
||||
@@ -1,15 +1,17 @@
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import content from '../../content/index';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
import get from 'lodash/get';
|
||||
|
||||
// buy a quest with gold
|
||||
module.exports = function buyQuest (user, req = {}, analytics) {
|
||||
let key = get(req, 'params.key');
|
||||
let quantity = req.quantity || 1;
|
||||
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.quests[key];
|
||||
@@ -22,13 +24,13 @@ module.exports = function buyQuest (user, req = {}, analytics) {
|
||||
if (!(item.category === 'gold' && item.goldValue)) {
|
||||
throw new NotAuthorized(i18n.t('questNotGoldPurchasable', {key}, req.language));
|
||||
}
|
||||
if (user.stats.gp < item.goldValue) {
|
||||
if (user.stats.gp < item.goldValue * quantity) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
|
||||
user.items.quests[item.key] = user.items.quests[item.key] || 0;
|
||||
user.items.quests[item.key]++;
|
||||
user.stats.gp -= item.goldValue;
|
||||
user.items.quests[item.key] += quantity;
|
||||
user.stats.gp -= item.goldValue * quantity;
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
@@ -36,6 +38,7 @@ module.exports = function buyQuest (user, req = {}, analytics) {
|
||||
itemKey: item.key,
|
||||
itemType: 'Market',
|
||||
goldCost: item.goldValue,
|
||||
quantityPurchased: quantity,
|
||||
acquireMethod: 'Gold',
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
47
website/common/script/ops/buy/buySpecialSpell.js
Normal file
47
website/common/script/ops/buy/buySpecialSpell.js
Normal file
@@ -0,0 +1,47 @@
|
||||
import i18n from '../../i18n';
|
||||
import content from '../../content/index';
|
||||
import get from 'lodash/get';
|
||||
import pick from 'lodash/pick';
|
||||
import splitWhitespace from '../../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../../libs/errors';
|
||||
|
||||
module.exports = function buySpecialSpell (user, req = {}, analytics) {
|
||||
let key = get(req, 'params.key');
|
||||
let quantity = req.quantity || 1;
|
||||
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.special[key];
|
||||
if (!item) throw new NotFound(i18n.t('spellNotFound', {spellId: key}, req.language));
|
||||
|
||||
if (user.stats.gp < item.value * quantity) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
user.stats.gp -= item.value * quantity;
|
||||
|
||||
user.items.special[key] += quantity;
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: item.key,
|
||||
itemType: 'Market',
|
||||
goldCost: item.goldValue,
|
||||
quantityPurchased: quantity,
|
||||
acquireMethod: 'Gold',
|
||||
category: 'behavior',
|
||||
headers: req.headers,
|
||||
});
|
||||
}
|
||||
|
||||
return [
|
||||
pick(user, splitWhitespace('items stats')),
|
||||
i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language),
|
||||
];
|
||||
};
|
||||
@@ -1,12 +1,12 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import content from '../../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import get from 'lodash/get';
|
||||
import includes from 'lodash/includes';
|
||||
import keys from 'lodash/keys';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
|
||||
module.exports = function purchaseHourglass (user, req = {}, analytics) {
|
||||
let key = get(req, 'params.key');
|
||||
@@ -1,18 +1,18 @@
|
||||
import content from '../content/index';
|
||||
import i18n from '../i18n';
|
||||
import content from '../../content/index';
|
||||
import i18n from '../../i18n';
|
||||
import get from 'lodash/get';
|
||||
import pick from 'lodash/pick';
|
||||
import forEach from 'lodash/forEach';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import planGemLimits from '../libs/planGemLimits';
|
||||
import splitWhitespace from '../../libs/splitWhitespace';
|
||||
import planGemLimits from '../../libs/planGemLimits';
|
||||
import {
|
||||
NotFound,
|
||||
NotAuthorized,
|
||||
BadRequest,
|
||||
} from '../libs/errors';
|
||||
} from '../../libs/errors';
|
||||
|
||||
import { removeItemByPath } from './pinnedGearUtils';
|
||||
import getItemInfo from '../libs/getItemInfo';
|
||||
import { removeItemByPath } from '../pinnedGearUtils';
|
||||
import getItemInfo from '../../libs/getItemInfo';
|
||||
|
||||
function buyGems (user, analytics, req, key) {
|
||||
let convRate = planGemLimits.convRate;
|
||||
12
website/common/script/ops/buy/purchaseWithSpell.js
Normal file
12
website/common/script/ops/buy/purchaseWithSpell.js
Normal file
@@ -0,0 +1,12 @@
|
||||
import buy from './buy';
|
||||
import get from 'lodash/get';
|
||||
|
||||
module.exports = function purchaseWithSpell (user, req = {}, analytics) {
|
||||
const type = get(req.params, 'type');
|
||||
|
||||
if (type === 'spells') {
|
||||
req.type = 'special';
|
||||
}
|
||||
|
||||
return buy(user, req, analytics);
|
||||
};
|
||||
@@ -1,32 +0,0 @@
|
||||
import i18n from '../i18n';
|
||||
import content from '../content/index';
|
||||
import get from 'lodash/get';
|
||||
import pick from 'lodash/pick';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
NotFound,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function buySpecialSpell (user, req = {}) {
|
||||
let key = get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let item = content.special[key];
|
||||
if (!item) throw new NotFound(i18n.t('spellNotFound', {spellId: key}, req.language));
|
||||
|
||||
if (user.stats.gp < item.value) {
|
||||
throw new NotAuthorized(i18n.t('messageNotEnoughGold', req.language));
|
||||
}
|
||||
user.stats.gp -= item.value;
|
||||
|
||||
user.items.special[key]++;
|
||||
|
||||
return [
|
||||
pick(user, splitWhitespace('items stats')),
|
||||
i18n.t('messageBought', {
|
||||
itemText: item.text(req.language),
|
||||
}, req.language),
|
||||
];
|
||||
};
|
||||
@@ -18,19 +18,10 @@ import clearPMs from './clearPMs';
|
||||
import deletePM from './deletePM';
|
||||
import blockUser from './blockUser';
|
||||
import feed from './feed';
|
||||
import buySpecialSpell from './buySpecialSpell';
|
||||
import purchase from './purchase';
|
||||
import purchaseWithSpell from './purchaseWithSpell';
|
||||
import releasePets from './releasePets';
|
||||
import releaseMounts from './releaseMounts';
|
||||
import releaseBoth from './releaseBoth';
|
||||
import buy from './buy';
|
||||
import buyGear from './buyGear';
|
||||
import buyHealthPotion from './buyHealthPotion';
|
||||
import buyArmoire from './buyArmoire';
|
||||
import buyQuest from './buyQuest';
|
||||
import buyMysterySet from './buyMysterySet';
|
||||
import hourglassPurchase from './hourglassPurchase';
|
||||
import buy from './buy/purchase';
|
||||
import sell from './sell';
|
||||
import equip from './equip';
|
||||
import hatch from './hatch';
|
||||
@@ -63,19 +54,10 @@ module.exports = {
|
||||
deletePM,
|
||||
blockUser,
|
||||
feed,
|
||||
buySpecialSpell,
|
||||
purchase,
|
||||
purchaseWithSpell,
|
||||
releasePets,
|
||||
releaseMounts,
|
||||
releaseBoth,
|
||||
buy,
|
||||
buyGear,
|
||||
buyHealthPotion,
|
||||
buyArmoire,
|
||||
buyQuest,
|
||||
buyMysterySet,
|
||||
hourglassPurchase,
|
||||
sell,
|
||||
equip,
|
||||
hatch,
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import buy from './buy';
|
||||
import purchaseOp from './purchase';
|
||||
import get from 'lodash/get';
|
||||
|
||||
module.exports = function purchaseWithSpell (user, req = {}, analytics) {
|
||||
const type = get(req.params, 'type');
|
||||
|
||||
// Set up type for buy function - different than the above type.
|
||||
req.type = 'special';
|
||||
|
||||
return type === 'spells' ? buy(user, req, analytics) : purchaseOp(user, req, analytics);
|
||||
};
|
||||
@@ -744,6 +744,9 @@ api.sleep = {
|
||||
},
|
||||
};
|
||||
|
||||
const buySpecialKeys = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam'];
|
||||
const buyKnownKeys = ['armoire', 'mystery', 'potion', 'quest', 'special'];
|
||||
|
||||
/**
|
||||
* @api {post} /api/v3/user/buy/:key Buy gear, armoire or potion
|
||||
* @apiDescription Under the hood uses UserBuyGear, UserBuyPotion and UserBuyArmoire
|
||||
@@ -781,14 +784,13 @@ api.buy = {
|
||||
let user = res.locals.user;
|
||||
|
||||
let buyRes;
|
||||
let specialKeys = ['snowball', 'spookySparkles', 'shinySeed', 'seafoam'];
|
||||
|
||||
// @TODO: Remove this when mobile passes type in body
|
||||
let type = req.params.key;
|
||||
if (specialKeys.indexOf(req.params.key) !== -1) {
|
||||
type = 'special';
|
||||
if (buySpecialKeys.indexOf(type) !== -1) {
|
||||
req.type = 'special';
|
||||
} else if (buyKnownKeys.indexOf(type) === -1) {
|
||||
req.type = 'marketGear';
|
||||
}
|
||||
req.type = type;
|
||||
|
||||
// @TODO: right now common follow express structure, but we should decouple the dependency
|
||||
if (req.body.type) req.type = req.body.type;
|
||||
@@ -1267,7 +1269,7 @@ api.purchase = {
|
||||
if (req.body.quantity) quantity = req.body.quantity;
|
||||
req.quantity = quantity;
|
||||
|
||||
let purchaseRes = common.ops.purchaseWithSpell(user, req, res.analytics);
|
||||
let purchaseRes = common.ops.buy(user, req, res.analytics);
|
||||
await user.save();
|
||||
res.respond(200, ...purchaseRes);
|
||||
},
|
||||
@@ -1298,7 +1300,7 @@ api.userPurchaseHourglass = {
|
||||
url: '/user/purchase-hourglass/:type/:key',
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let purchaseHourglassRes = common.ops.purchaseHourglass(user, req, res.analytics);
|
||||
let purchaseHourglassRes = common.ops.buy(user, req, res.analytics);
|
||||
await user.save();
|
||||
res.respond(200, ...purchaseHourglassRes);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user