/* eslint-disable max-classes-per-file */ import get from 'lodash/get'; import i18n from '../../i18n'; import { NotAuthorized, NotImplementedError, BadRequest, } from '../../libs/errors'; import updateUserBalance from '../updateUserBalance'; import updateUserHourglasses from '../updateUserHourglasses'; export class AbstractBuyOperation { /** * @param {User} user - the User-Object * @param {Request} req - the Request-Object */ constructor (user, req) { this.user = user; this.req = req || {}; const quantity = get(req, 'quantity'); this.quantity = quantity ? Number(quantity) : 1; if (this.quantity < 1 || !Number.isInteger(this.quantity)) throw new BadRequest(this.i18n('invalidQuantity')); } /** * Returns the item value * @param item * @returns {number} */ getItemValue (item) { // eslint-disable-line class-methods-use-this return item.value; } /** * Returns the item key * @param item * @returns {String} */ getItemKey (item) { // eslint-disable-line class-methods-use-this return item.key; } /** * Returns the item type * @param item * @returns {String} */ getItemType (item) { // eslint-disable-line class-methods-use-this if (!item.type) throw new NotImplementedError('item doesn\'t have a type property'); return item.type; } /** * Shortcut to get the translated string without passing `req.language` * @param {String} key - translation key * @param {*=} params * @returns {*|string} */ // eslint-disable-next-line no-unused-vars i18n (key, params = {}) { return i18n.t.apply(null, [...arguments, this.req.language]); // eslint-disable-line prefer-rest-params, max-len } /** * If the Operation allows purchasing items by quantity * @returns Boolean */ multiplePurchaseAllowed () { // eslint-disable-line class-methods-use-this throw new NotImplementedError('multiplePurchaseAllowed'); } /** * Method is called to save the params as class-fields in order to access them */ extractAndValidateParams () { // eslint-disable-line class-methods-use-this throw new NotImplementedError('extractAndValidateParams'); } async executeChanges () { // eslint-disable-line class-methods-use-this throw new NotImplementedError('executeChanges'); } async purchase () { if (!this.multiplePurchaseAllowed() && this.quantity > 1) { throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk')); } this.extractAndValidateParams(this.user, this.req); const resultObj = await this.executeChanges(this.user, this.item, this.req); return resultObj; } } export class AbstractGoldItemOperation extends AbstractBuyOperation { canUserPurchase (user, item) { this.item = item; const itemValue = this.getItemValue(item); const userGold = user.stats.gp; if (userGold < itemValue * this.quantity) { throw new NotAuthorized(this.i18n('messageNotEnoughGold')); } if (item && item.canOwn && !item.canOwn(user)) { throw new NotAuthorized(this.i18n('cannotBuyItem')); } } async subtractCurrency (user, item) { const itemValue = this.getItemValue(item); user.stats.gp -= itemValue * this.quantity; } } export class AbstractGemItemOperation extends AbstractBuyOperation { canUserPurchase (user, item) { this.item = item; const itemValue = this.getItemValue(item); if (!item.canBuy(user)) { throw new NotAuthorized(this.i18n('messageNotAvailable')); } if (!user.balance || user.balance < itemValue * this.quantity) { throw new NotAuthorized(this.i18n('notEnoughGems')); } } async subtractCurrency (user, item) { const itemValue = this.getItemValue(item); await updateUserBalance(user, -(itemValue * this.quantity), 'spend', item.key, item.text()); } } export class AbstractHourglassItemOperation extends AbstractBuyOperation { canUserPurchase (user, item) { this.item = item; if (user.purchased.plan.consecutive.trinkets <= 0) { throw new NotAuthorized(this.i18n('notEnoughHourglasses')); } } async subtractCurrency (user, item) { // eslint-disable-line class-methods-use-this await updateUserHourglasses(user, -1, 'spend', item.key); } }