diff --git a/common/locales/en/api-v3.json b/common/locales/en/api-v3.json index a9c0a0fb2d..0546d97789 100644 --- a/common/locales/en/api-v3.json +++ b/common/locales/en/api-v3.json @@ -128,5 +128,7 @@ "privateMessageGiftSubscriptionMessage": "<%= numberOfMonths %> months of subscription! ", "cannotSendGemsToYourself": "Cannot send gems to yourself. Try a subscription instead.", "notEnoughGemsToSend": "Amount must be within 0 and your current number of gems.", - "mustPurchaseToSet": "Must purchase <%= val %> to set it on <%= key %>." + "mustPurchaseToSet": "Must purchase <%= val %> to set it on <%= key %>.", + "mysteryItemIsEmpty": "Mystery items are empty", + "mysteryItemOpened": "Mystery item opened." } diff --git a/common/script/index.js b/common/script/index.js index 7f9736044d..3d91749c62 100644 --- a/common/script/index.js +++ b/common/script/index.js @@ -114,6 +114,7 @@ import feed from './ops/feed'; import equip from './ops/equip'; import changeClass from './ops/changeClass'; import disableClasses from './ops/disableClasses'; +import openMysteryItem from './ops/openMysteryItem'; api.ops = { scoreTask, @@ -129,6 +130,7 @@ api.ops = { equip, changeClass, disableClasses, + openMysteryItem, }; import handleTwoHanded from './fns/handleTwoHanded'; diff --git a/common/script/ops/openMysteryItem.js b/common/script/ops/openMysteryItem.js index eb28c605e0..5767ee5f75 100644 --- a/common/script/ops/openMysteryItem.js +++ b/common/script/ops/openMysteryItem.js @@ -1,32 +1,40 @@ import content from '../content/index'; +import i18n from '../i18n'; +import { + BadRequest, +} from '../libs/errors'; +import _ from 'lodash'; + +module.exports = function openMysteryItem (user, req = {}, analytics) { + let item = user.purchased.plan.mysteryItems.shift(); -module.exports = function(user, req, cb, analytics) { - var analyticsData, item, ref, ref1; - item = (ref = user.purchased.plan) != null ? (ref1 = ref.mysteryItems) != null ? ref1.shift() : void 0 : void 0; if (!item) { - return typeof cb === "function" ? cb({ - code: 400, - message: "Empty" - }) : void 0; - } - item = content.gear.flat[item]; - user.items.gear.owned[item.key] = true; - if (typeof user.markModified === "function") { - user.markModified('purchased.plan.mysteryItems'); + throw new BadRequest(i18n.t('mysteryItemIsEmpty', req.language)); } + + item = _.cloneDeep(content.gear.flat[item]); item.notificationType = 'Mystery'; - analyticsData = { - uuid: user._id, - itemKey: item, - itemType: 'Subscriber Gear', - acquireMethod: 'Subscriber', - category: 'behavior' - }; - if (analytics != null) { - analytics.track('open mystery item', analyticsData); + user.items.gear.owned[item.key] = true; + + user.markModified('purchased.plan.mysteryItems'); + + if (analytics) { + analytics.track('open mystery item', { + uuid: user._id, + itemKey: item, + itemType: 'Subscriber Gear', + acquireMethod: 'Subscriber', + category: 'behavior', + }); } + if (typeof window !== 'undefined') { - (user._tmp != null ? user._tmp : user._tmp = {}).drop = item; + if (!user._tmp) user._tmp = {}; + user._tmp.drop = item; } - return typeof cb === "function" ? cb(null, user.items.gear.owned) : void 0; + + return { + message: i18n.t('mysteryItemOpened', req.language), + data: user.items.gear.owned, + }; }; diff --git a/tasks/gulp-eslint.js b/tasks/gulp-eslint.js index 6fb94f6d2b..3aa9f57c75 100644 --- a/tasks/gulp-eslint.js +++ b/tasks/gulp-eslint.js @@ -29,7 +29,6 @@ const COMMON_FILES = [ '!./common/script/ops/getTag.js', '!./common/script/ops/getTags.js', '!./common/script/ops/hourglassPurchase.js', - '!./common/script/ops/openMysteryItem.js', '!./common/script/ops/purchase.js', '!./common/script/ops/readCard.js', '!./common/script/ops/rebirth.js', diff --git a/test/api/v3/integration/user/POST-user_open_mystery_item.test.js b/test/api/v3/integration/user/POST-user_open_mystery_item.test.js new file mode 100644 index 0000000000..2c43445118 --- /dev/null +++ b/test/api/v3/integration/user/POST-user_open_mystery_item.test.js @@ -0,0 +1,26 @@ +import { + generateUser, + translate as t, +} from '../../../../helpers/api-integration/v3'; + +describe('POST /user/open-mystery-item', () => { + let user; + let mysteryItemKey = 'eyewear_special_summerRogue'; + + beforeEach(async () => { + user = await generateUser({ + 'purchased.plan.mysteryItems': [mysteryItemKey], + }); + }); + + // More tests in common code unit tests + + it('opens a mystery item', async () => { + let response = await user.post(`/user/open-mystery-item`); + await user.sync(); + + expect(user.items.gear.owned[mysteryItemKey]).to.be.true; + expect(response.message).to.equal(t('mysteryItemOpened')); + expect(response.data).to.deep.equal(user.items.gear.owned); + }); +}); diff --git a/test/common/ops/openMysteryItem.js b/test/common/ops/openMysteryItem.js new file mode 100644 index 0000000000..c45146a585 --- /dev/null +++ b/test/common/ops/openMysteryItem.js @@ -0,0 +1,38 @@ +import openMysteryItem from '../../../common/script/ops/openMysteryItem'; +import { + generateUser, +} from '../../helpers/common.helper'; +import { + BadRequest, +} from '../../../common/script/libs/errors'; +import i18n from '../../../common/script/i18n'; + +describe('shared.ops.openMysteryItem', () => { + let user; + + beforeEach(() => { + user = generateUser(); + }); + + it('returns error when item key is empty', (done) => { + try { + openMysteryItem(user); + } catch (err) { + expect(err).to.be.an.instanceof(BadRequest); + expect(err.message).to.equal(i18n.t('mysteryItemIsEmpty')); + done(); + } + }); + + it('opens mystery item', () => { + let mysteryItemKey = 'eyewear_special_summerRogue'; + + user.purchased.plan.mysteryItems = [mysteryItemKey]; + + let response = openMysteryItem(user); + + expect(user.items.gear.owned[mysteryItemKey]).to.be.true; + expect(response.message).to.equal(i18n.t('mysteryItemOpened')); + expect(response.data).to.equal(user.items.gear.owned); + }); +}); diff --git a/website/src/controllers/api-v3/user.js b/website/src/controllers/api-v3/user.js index 147f1552c9..a163bcdf34 100644 --- a/website/src/controllers/api-v3/user.js +++ b/website/src/controllers/api-v3/user.js @@ -676,4 +676,24 @@ api.disableClasses = { }, }; +/** +* @api {post} /user/open-mystery-item Open the mystery item. +* @apiVersion 3.0.0 +* @apiName UserOpenMysteryItem +* @apiGroup User +* +* @apiSuccess {Object} data `user.items.gear.owned` +*/ +api.userOpenMysteryItem = { + method: 'POST', + middlewares: [authWithHeaders(), cron], + url: '/user/open-mystery-item', + async handler (req, res) { + let user = res.locals.user; + let openMysteryItemResponse = common.ops.openMysteryItem(user, req, res.analytics); + await user.save(); + res.respond(200, openMysteryItemResponse); + }, +}; + module.exports = api;