mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
Purchase API Refactoring: Mounts [Hourglass] (#10482)
* AbstractGemItemOperation - BuyQuestWithGemOperation * fix purchase
This commit is contained in:
@@ -9,11 +9,18 @@ import {
|
||||
generateUser,
|
||||
} from '../../../helpers/common.helper';
|
||||
import errorMessage from '../../../../website/common/script/libs/errorMessage';
|
||||
import {BuyHourglassMountOperation} from '../../../../website/common/script/ops/buy/buyMount';
|
||||
|
||||
describe('common.ops.hourglassPurchase', () => {
|
||||
let user;
|
||||
let analytics = {track () {}};
|
||||
|
||||
function buyMount (_user, _req, _analytics) {
|
||||
const buyOp = new BuyHourglassMountOperation(_user, _req, _analytics);
|
||||
|
||||
return buyOp.purchase();
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
sinon.stub(analytics, 'track');
|
||||
@@ -66,7 +73,7 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
|
||||
it('does not grant to mounts without Mystic Hourglasses', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
buyMount(user, {params: {key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||
@@ -90,7 +97,7 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'Orca-Base'}});
|
||||
buyMount(user, {params: {key: 'Orca-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
||||
@@ -120,7 +127,7 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
};
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
buyMount(user, {params: {key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('mountsAlreadyOwned'));
|
||||
@@ -144,7 +151,7 @@ describe('common.ops.hourglassPurchase', () => {
|
||||
it('buys a mount', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 2;
|
||||
|
||||
let [, message] = hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
let [, message] = buyMount(user, {params: {key: 'MantisShrimp-Base'}});
|
||||
expect(message).to.eql(i18n.t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.mounts).to.eql({'MantisShrimp-Base': true});
|
||||
|
||||
@@ -194,3 +194,28 @@ export class AbstractGemItemOperation extends AbstractBuyOperation {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class AbstractHourglassItemOperation extends AbstractBuyOperation {
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
|
||||
canUserPurchase (user, item) {
|
||||
this.item = item;
|
||||
|
||||
if (user.purchased.plan.consecutive.trinkets <= 0) {
|
||||
throw new NotAuthorized(this.i18n('notEnoughHourglasses'));
|
||||
}
|
||||
}
|
||||
|
||||
subtractCurrency (user) {
|
||||
user.purchased.plan.consecutive.trinkets--;
|
||||
}
|
||||
|
||||
analyticsData () {
|
||||
return {
|
||||
itemKey: this.item.key,
|
||||
acquireMethod: 'Hourglass',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import hourglassPurchase from './hourglassPurchase';
|
||||
import errorMessage from '../../libs/errorMessage';
|
||||
import {BuyGemOperation} from './buyGem';
|
||||
import {BuyQuestWithGemOperation} from './buyQuestGem';
|
||||
import {BuyHourglassMountOperation} from './buyMount';
|
||||
|
||||
// @TODO: remove the req option style. Dependency on express structure is an anti-pattern
|
||||
// We should either have more params or a set structure validated by a Type checker
|
||||
@@ -72,8 +73,13 @@ module.exports = function buy (user, req = {}, analytics, options = {quantity: 1
|
||||
case 'bundles':
|
||||
buyRes = purchaseOp(user, req, analytics);
|
||||
break;
|
||||
case 'mounts': {
|
||||
const buyOp = new BuyHourglassMountOperation(user, req, analytics);
|
||||
|
||||
buyRes = buyOp.purchase();
|
||||
break;
|
||||
}
|
||||
case 'pets':
|
||||
case 'mounts':
|
||||
buyRes = hourglassPurchase(user, req, analytics);
|
||||
break;
|
||||
case 'quest': {
|
||||
|
||||
59
website/common/script/ops/buy/buyMount.js
Normal file
59
website/common/script/ops/buy/buyMount.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import content from '../../content/index';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../../libs/errors';
|
||||
|
||||
import {AbstractHourglassItemOperation} from './abstractBuyOperation';
|
||||
import get from 'lodash/get';
|
||||
import includes from 'lodash/includes';
|
||||
import keys from 'lodash/keys';
|
||||
|
||||
export class BuyHourglassMountOperation extends AbstractHourglassItemOperation {
|
||||
constructor (user, req, analytics) {
|
||||
super(user, req, analytics);
|
||||
}
|
||||
|
||||
multiplePurchaseAllowed () {
|
||||
return false;
|
||||
}
|
||||
|
||||
extractAndValidateParams (user, req) {
|
||||
let key = this.key = get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(this.i18n('missingKeyParam'));
|
||||
|
||||
|
||||
if (!includes(keys(content.timeTravelStable.mounts), key)) {
|
||||
throw new NotAuthorized(this.i18n('notAllowedHourglass'));
|
||||
}
|
||||
|
||||
if (user.items.mounts[key]) {
|
||||
throw new NotAuthorized(this.i18n('mountsAlreadyOwned'));
|
||||
}
|
||||
|
||||
this.canUserPurchase(user, {
|
||||
key,
|
||||
});
|
||||
}
|
||||
|
||||
executeChanges (user) {
|
||||
user.items.mounts[this.key] = true;
|
||||
|
||||
if (user.markModified) user.markModified('items.mounts');
|
||||
|
||||
this.subtractCurrency(user);
|
||||
|
||||
let message = this.i18n('hourglassPurchase');
|
||||
|
||||
return [
|
||||
{ items: user.items, purchasedPlanConsecutive: user.purchased.plan.consecutive },
|
||||
message,
|
||||
];
|
||||
}
|
||||
|
||||
analyticsData () {
|
||||
let data = super.analyticsData();
|
||||
data.itemType = 'mounts';
|
||||
return data;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user