Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
convert buyGear to buyMarketGearOperation + tests
- Loading branch information
Showing
5 changed files
with
250 additions
and
87 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import i18n from '../../i18n'; | ||
import { | ||
NotAuthorized, | ||
} from '../../libs/errors'; | ||
import _merge from 'lodash/merge'; | ||
import _get from 'lodash/get'; | ||
|
||
export class NotImplementedError extends Error { | ||
constructor (str) { | ||
super(`Method: '${str}' not implemented`); | ||
} | ||
} | ||
|
||
export class AbstractBuyOperation { | ||
/** | ||
* @param {User} user - the User-Object | ||
* @param {Request} req - the Request-Object | ||
* @param {analytics} analytics | ||
*/ | ||
constructor (user, req, analytics) { | ||
this.user = user; | ||
this.req = req || {}; | ||
this.analytics = analytics; | ||
|
||
this.quantity = _get(req, 'quantity', 1); | ||
} | ||
|
||
/** | ||
* 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]); | ||
} | ||
|
||
/** | ||
* If the Operation allows purchasing items by quantity | ||
* @returns Boolean | ||
*/ | ||
multiplePurchaseAllowed () { | ||
throw new NotImplementedError('multiplePurchaseAllowed'); | ||
} | ||
|
||
/** | ||
* Method is called to save the params as class-fields in order to access them | ||
*/ | ||
extractAndValidateParams () { | ||
throw new NotImplementedError('extractAndValidateParams'); | ||
} | ||
|
||
executeChanges () { | ||
throw new NotImplementedError('executeChanges'); | ||
} | ||
|
||
analyticsData () { | ||
throw new NotImplementedError('sendToAnalytics'); | ||
} | ||
|
||
purchase () { | ||
if (!this.multiplePurchaseAllowed() && this.quantity > 1) { | ||
throw new NotAuthorized(this.i18n('messageNotAbleToBuyInBulk')); | ||
} | ||
|
||
this.extractAndValidateParams(this.user, this.req); | ||
|
||
let resultObj = this.executeChanges(this.user, this.item, this.req); | ||
|
||
if (this.analytics) { | ||
this.sendToAnalytics(this.analyticsData()); | ||
} | ||
|
||
return resultObj; | ||
} | ||
|
||
sendToAnalytics (additionalData = {}) { | ||
// spread-operator produces an "unexpected token" error | ||
let analyticsData = _merge(additionalData, { | ||
// ...additionalData, | ||
uuid: this.user._id, | ||
category: 'behavior', | ||
headers: this.req.headers, | ||
}); | ||
|
||
if (this.multiplePurchaseAllowed()) { | ||
analyticsData.quantityPurchased = this.quantity; | ||
} | ||
|
||
this.analytics.track('acquire item', analyticsData); | ||
} | ||
} | ||
|
||
export class AbstractGoldItemOperation extends AbstractBuyOperation { | ||
constructor (user, req, analytics) { | ||
super(user, req, analytics); | ||
} | ||
|
||
getItemValue (item) { | ||
return item.value; | ||
} | ||
|
||
canUserPurchase (user, item) { | ||
this.item = item; | ||
let itemValue = this.getItemValue(item); | ||
|
||
let userGold = user.stats.gp; | ||
|
||
if (userGold < itemValue * this.quantity) { | ||
throw new NotAuthorized(this.i18n('messageNotEnoughGold')); | ||
} | ||
|
||
if (item.canOwn && !item.canOwn(user)) { | ||
throw new NotAuthorized(this.i18n('cannotBuyItem')); | ||
} | ||
} | ||
|
||
substractCurrency (user, item, quantity = 1) { | ||
let itemValue = this.getItemValue(item); | ||
|
||
user.stats.gp -= itemValue * quantity; | ||
} | ||
|
||
analyticsData () { | ||
return { | ||
itemKey: this.item.key, | ||
itemType: 'Market', | ||
acquireMethod: 'Gold', | ||
goldCost: this.getItemValue(this.item), | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
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'; | ||
import handleTwoHanded from '../../fns/handleTwoHanded'; | ||
import ultimateGear from '../../fns/ultimateGear'; | ||
|
||
import {removePinnedGearAddPossibleNewOnes} from '../pinnedGearUtils'; | ||
|
||
import { AbstractGoldItemOperation } from './abstractBuyOperation'; | ||
|
||
export class BuyMarketGearOperation extends AbstractGoldItemOperation { | ||
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')); | ||
|
||
let item = content.gear.flat[key]; | ||
|
||
if (!item) throw new NotFound(this.i18n('itemNotFound', {key})); | ||
|
||
super.canUserPurchase(user, item); | ||
|
||
if (user.items.gear.owned[item.key]) { | ||
throw new NotAuthorized(this.i18n('equipmentAlreadyOwned')); | ||
} | ||
|
||
let itemIndex = Number(item.index); | ||
|
||
if (Number.isInteger(itemIndex) && content.classes.includes(item.klass)) { | ||
let previousLevelGear = key.replace(/[0-9]/, itemIndex - 1); | ||
let hasPreviousLevelGear = user.items.gear.owned[previousLevelGear]; | ||
let checkIndexToType = itemIndex > (item.type === 'weapon' || item.type === 'shield' && item.klass === 'rogue' ? 0 : 1); | ||
|
||
if (checkIndexToType && !hasPreviousLevelGear) { | ||
throw new NotAuthorized(this.i18n('previousGearNotOwned')); | ||
} | ||
} | ||
} | ||
|
||
executeChanges (user, item, req) { | ||
let message; | ||
|
||
if (user.preferences.autoEquip) { | ||
user.items.gear.equipped[item.type] = item.key; | ||
message = handleTwoHanded(user, item, undefined, req); | ||
} | ||
|
||
removePinnedGearAddPossibleNewOnes(user, `gear.flat.${item.key}`, item.key); | ||
|
||
if (item.last) ultimateGear(user); | ||
|
||
this.substractCurrency(user, item); | ||
|
||
if (!message) { | ||
message = this.i18n('messageBought', { | ||
itemText: item.text(req.language), | ||
}); | ||
} | ||
|
||
return [ | ||
pick(user, splitWhitespace('items achievements stats flags')), | ||
message, | ||
]; | ||
} | ||
} |