mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-17 22:57:21 +01:00
Merge branch 'api-v3-hourglass-purchase' of https://github.com/TheHollidayInn/habitrpg into TheHollidayInn-api-v3-hourglass-purchase2
This commit is contained in:
@@ -136,5 +136,6 @@
|
||||
"notAccteptedType": "Type must be in [eggs, hatchingPotions, food, quests, gear]",
|
||||
"contentKeyNotFound": "Key not found for Content <%= type %>",
|
||||
"plusOneGem": "+1 Gem",
|
||||
"purchased": "You purchsed a <%= key %> <%= type %>"
|
||||
"purchased": "You purchsed a <%= key %> <%= type %>",
|
||||
"notAllowedHourglass": "Pet/Mount not available for purchase with Mystic Hourglass."
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
"hourglassBuyItemConfirm": "Buy this item for 1 Mystic Hourglass?",
|
||||
"petsAlreadyOwned": "Pet already owned.",
|
||||
"mountsAlreadyOwned": "Mount already owned.",
|
||||
"typeNotAllowedHourglass": "Item type not supported for purchase with Mystic Hourglass. Allowed types: ",
|
||||
"typeNotAllowedHourglass": "Item type not supported for purchase with Mystic Hourglass. Allowed types: <%= allowedTypes %>",
|
||||
"petsNotAllowedHourglass": "Pet not available for purchase with Mystic Hourglass.",
|
||||
"mountsNotAllowedHourglass": "Mount not available for purchase with Mystic Hourglass.",
|
||||
"hourglassPurchase": "Purchased an item using a Mystic Hourglass!",
|
||||
|
||||
@@ -115,6 +115,7 @@ import equip from './ops/equip';
|
||||
import changeClass from './ops/changeClass';
|
||||
import disableClasses from './ops/disableClasses';
|
||||
import purchase from './ops/purchase';
|
||||
import purchaseHourglass from './ops/hourglassPurchase';
|
||||
|
||||
api.ops = {
|
||||
scoreTask,
|
||||
@@ -131,6 +132,7 @@ api.ops = {
|
||||
changeClass,
|
||||
disableClasses,
|
||||
purchase,
|
||||
purchaseHourglass,
|
||||
};
|
||||
|
||||
import handleTwoHanded from './fns/handleTwoHanded';
|
||||
|
||||
@@ -3,53 +3,58 @@ import i18n from '../i18n';
|
||||
import _ from 'lodash';
|
||||
import splitWhitespace from '../libs/splitWhitespace';
|
||||
import pickDeep from '../libs/pickDeep';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../libs/errors';
|
||||
|
||||
module.exports = function purchaseHourglass (user, req = {}, analytics) {
|
||||
let key = _.get(req, 'params.key');
|
||||
if (!key) throw new BadRequest(i18n.t('missingKeyParam', req.language));
|
||||
|
||||
let type = _.get(req, 'params.type');
|
||||
if (!type) throw new BadRequest(i18n.t('missingTypeParam', req.language));
|
||||
|
||||
module.exports = function(user, req, cb, analytics) {
|
||||
var analyticsData, key, ref, type;
|
||||
ref = req.params, type = ref.type, key = ref.key;
|
||||
if (!content.timeTravelStable[type]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('typeNotAllowedHourglass', req.language) + JSON.stringify(_.keys(content.timeTravelStable))
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('typeNotAllowedHourglass', {allowedTypes: _.keys(content.timeTravelStable).toString()}, req.language));
|
||||
}
|
||||
|
||||
if (!_.contains(_.keys(content.timeTravelStable[type]), key)) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t(type + 'NotAllowedHourglass', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t('notAllowedHourglass', req.language));
|
||||
}
|
||||
|
||||
if (user.items[type][key]) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t(type + 'AlreadyOwned', req.language)
|
||||
}) : void 0;
|
||||
throw new NotAuthorized(i18n.t(`${type}AlreadyOwned`, req.language));
|
||||
}
|
||||
if (!(user.purchased.plan.consecutive.trinkets > 0)) {
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 403,
|
||||
message: i18n.t('notEnoughHourglasses', req.language)
|
||||
}) : void 0;
|
||||
|
||||
if (user.purchased.plan.consecutive.trinkets <= 0) {
|
||||
throw new NotAuthorized(i18n.t('notEnoughHourglasses', req.language));
|
||||
}
|
||||
|
||||
user.purchased.plan.consecutive.trinkets--;
|
||||
|
||||
if (type === 'pets') {
|
||||
user.items.pets[key] = 5;
|
||||
}
|
||||
|
||||
if (type === 'mounts') {
|
||||
user.items.mounts[key] = true;
|
||||
}
|
||||
analyticsData = {
|
||||
|
||||
if (analytics) {
|
||||
analytics.track('acquire item', {
|
||||
uuid: user._id,
|
||||
itemKey: key,
|
||||
itemType: type,
|
||||
acquireMethod: 'Hourglass',
|
||||
category: 'behavior'
|
||||
};
|
||||
if (analytics != null) {
|
||||
analytics.track('acquire item', analyticsData);
|
||||
category: 'behavior',
|
||||
});
|
||||
}
|
||||
return typeof cb === "function" ? cb({
|
||||
code: 200,
|
||||
message: i18n.t('hourglassPurchase', req.language)
|
||||
}, pickDeep(user, splitWhitespace('items purchased.plan.consecutive'))) : void 0;
|
||||
|
||||
let res = {
|
||||
data: pickDeep(user, splitWhitespace('items purchased.plan.consecutive')),
|
||||
message: i18n.t('hourglassPurchase', req.language),
|
||||
};
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
@@ -28,7 +28,6 @@ const COMMON_FILES = [
|
||||
'!./common/script/ops/deleteWebhook.js',
|
||||
'!./common/script/ops/getTag.js',
|
||||
'!./common/script/ops/getTags.js',
|
||||
'!./common/script/ops/hourglassPurchase.js',
|
||||
'!./common/script/ops/openMysteryItem.js',
|
||||
'!./common/script/ops/readCard.js',
|
||||
'!./common/script/ops/rebirth.js',
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
generateUser,
|
||||
translate as t,
|
||||
} from '../../../../helpers/api-integration/v3';
|
||||
|
||||
describe('POST /user/purchase-hourglass/:type/:key', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(async () => {
|
||||
user = await generateUser({
|
||||
'purchased.plan.consecutive.trinkets': 2,
|
||||
});
|
||||
});
|
||||
|
||||
// More tests in common code unit tests
|
||||
|
||||
it('buys a hourglass pet', async () => {
|
||||
let response = await user.post('/user/purchase-hourglass/pets/MantisShrimp-Base');
|
||||
await user.sync();
|
||||
|
||||
expect(response.message).to.eql(t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({'MantisShrimp-Base': 5});
|
||||
});
|
||||
});
|
||||
145
test/common/ops/hourglassPurchase.js
Normal file
145
test/common/ops/hourglassPurchase.js
Normal file
@@ -0,0 +1,145 @@
|
||||
import hourglassPurchase from '../../../common/script/ops/hourglassPurchase';
|
||||
import {
|
||||
BadRequest,
|
||||
NotAuthorized,
|
||||
} from '../../../common/script/libs/errors';
|
||||
import i18n from '../../../common/script/i18n';
|
||||
import content from '../../../common/script/content/index';
|
||||
import {
|
||||
generateUser,
|
||||
} from '../../helpers/common.helper';
|
||||
|
||||
describe('user.ops.hourglassPurchase', () => {
|
||||
let user;
|
||||
|
||||
beforeEach(() => {
|
||||
user = generateUser();
|
||||
});
|
||||
|
||||
context('failure conditions', () => {
|
||||
it('return error when key is not provided', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.eql(i18n.t('missingKeyParam'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when type is not provided', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {key: 'Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(BadRequest);
|
||||
expect(err.message).to.eql(i18n.t('missingTypeParam'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('returns error when inccorect type is provided', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'notAType', key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('typeNotAllowedHourglass', {allowedTypes: _.keys(content.timeTravelStable).toString()}));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant to pets without Mystic Hourglasses', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'pets', key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant to mounts without Mystic Hourglasses', (done) => {
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notEnoughHourglasses'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant pet that is not part of the Time Travel Stable', (done) => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'pets', key: 'Wolf-Veteran'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant mount that is not part of the Time Travel Stable', (done) => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'Orca-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('notAllowedHourglass'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant pet that has already been purchased', (done) => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
user.items.pets = {
|
||||
'MantisShrimp-Base': true,
|
||||
};
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'pets', key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('petsAlreadyOwned'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
|
||||
it('does not grant mount that has already been purchased', (done) => {
|
||||
user.purchased.plan.consecutive.trinkets = 1;
|
||||
user.items.mounts = {
|
||||
'MantisShrimp-Base': true,
|
||||
};
|
||||
|
||||
try {
|
||||
hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
} catch (err) {
|
||||
expect(err).to.be.an.instanceof(NotAuthorized);
|
||||
expect(err.message).to.eql(i18n.t('mountsAlreadyOwned'));
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
context('successful purchases', () => {
|
||||
it('buys a pet', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 2;
|
||||
|
||||
let response = hourglassPurchase(user, {params: {type: 'pets', key: 'MantisShrimp-Base'}});
|
||||
|
||||
expect(response.message).to.eql(i18n.t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.pets).to.eql({'MantisShrimp-Base': 5});
|
||||
});
|
||||
|
||||
it('buys a mount', () => {
|
||||
user.purchased.plan.consecutive.trinkets = 2;
|
||||
|
||||
let response = hourglassPurchase(user, {params: {type: 'mounts', key: 'MantisShrimp-Base'}});
|
||||
expect(response.message).to.eql(i18n.t('hourglassPurchase'));
|
||||
expect(user.purchased.plan.consecutive.trinkets).to.eql(1);
|
||||
expect(user.items.mounts).to.eql({'MantisShrimp-Base': true});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -699,4 +699,27 @@ api.purchase = {
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* @api {post} /user/purchase-hourglass/:type/:key Purchase Hourglass.
|
||||
* @apiVersion 3.0.0
|
||||
* @apiName UserPurchaseHourglass
|
||||
* @apiGroup User
|
||||
*
|
||||
* @apiParam {string} type {pets|mounts}. The type of item to purchase
|
||||
* @apiParam {string} key Ex: {MantisShrimp-Base}. The key for the mount/pet
|
||||
*
|
||||
* @apiSuccess {Object} data `items purchased.plan.consecutive`
|
||||
*/
|
||||
api.userPurchaseHourglass = {
|
||||
method: 'POST',
|
||||
middlewares: [authWithHeaders(), cron],
|
||||
url: '/user/purchase-hourglass/:type/:key',
|
||||
async handler (req, res) {
|
||||
let user = res.locals.user;
|
||||
let purchaseHourglassResponse = common.ops.purchaseHourglass(user, req, res.analytics);
|
||||
await user.save();
|
||||
res.respond(200, purchaseHourglassResponse);
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = api;
|
||||
|
||||
Reference in New Issue
Block a user