chore: switch purchaseService to take inventory document #848

Merged
Sainan merged 1 commits from optimise-purchase into main 2025-01-24 06:24:29 -08:00
5 changed files with 42 additions and 61 deletions

View File

@ -27,18 +27,17 @@ export const getVoidProjectionRewardsController: RequestHandler = async (req, re
logger.debug(`relic rolled`, reward);
response.ParticipantInfo.Reward = reward.type;
// Remove relic
const inventory = await getInventory(accountId);
// Remove relic
addMiscItems(inventory, [
{
ItemType: data.ParticipantInfo.VoidProjection,
ItemCount: -1
}
]);
await inventory.save();
// Give reward
await handleStoreItemAcquisition(reward.type, accountId, reward.itemCount);
await handleStoreItemAcquisition(reward.type, inventory, reward.itemCount);
await inventory.save();
}
res.json(response);
};

View File

@ -2,10 +2,13 @@ import { RequestHandler } from "express";
import { getAccountIdForRequest } from "@/src/services/loginService";
import { IPurchaseRequest } from "@/src/types/purchaseTypes";
import { handlePurchase } from "@/src/services/purchaseService";
import { getInventory } from "@/src/services/inventoryService";
export const purchaseController: RequestHandler = async (req, res) => {
const purchaseRequest = JSON.parse(String(req.body)) as IPurchaseRequest;
const accountId = await getAccountIdForRequest(req);
const response = await handlePurchase(purchaseRequest, accountId);
const inventory = await getInventory(accountId);
const response = await handlePurchase(purchaseRequest, inventory);
await inventory.save();
res.json(response);
coderabbitai[bot] commented 2025-01-21 22:56:36 -08:00 (Migrated from github.com)
Review

⚠️ Potential issue

Add error handling for inventory save operation

Currently, the inventory.save() operation is awaited without error handling. If the save operation fails, it could result in an unhandled promise rejection and potentially crash the server.

Consider wrapping the inventory.save() call in a try-catch block to handle any errors appropriately.

Apply this diff to add error handling:

     const response = await handlePurchase(purchaseRequest, inventory);
-    await inventory.save();
+    try {
+        await inventory.save();
+    } catch (err) {
+        // Handle the error appropriately
+        logger.error('Error saving inventory:', err);
+        return res.status(500).json({ error: 'Failed to save inventory' });
+    }
     res.json(response);

Committable suggestion skipped: line range outside the PR's diff.

_:warning: Potential issue_ **Add error handling for inventory save operation** Currently, the `inventory.save()` operation is awaited without error handling. If the save operation fails, it could result in an unhandled promise rejection and potentially crash the server. Consider wrapping the `inventory.save()` call in a try-catch block to handle any errors appropriately. Apply this diff to add error handling: ```diff const response = await handlePurchase(purchaseRequest, inventory); - await inventory.save(); + try { + await inventory.save(); + } catch (err) { + // Handle the error appropriately + logger.error('Error saving inventory:', err); + return res.status(500).json({ error: 'Failed to save inventory' }); + } res.json(response); ``` > Committable suggestion skipped: line range outside the PR's diff. <!-- This is an auto-generated comment by CodeRabbit -->
};

View File

@ -57,15 +57,15 @@ export const syndicateSacrificeController: RequestHandler = async (request, resp
}
}
await inventory.save();
if (reward) {
combineInventoryChanges(
res.InventoryChanges,
(await handleStoreItemAcquisition(reward, accountId)).InventoryChanges
(await handleStoreItemAcquisition(reward, inventory)).InventoryChanges
);
}
await inventory.save();
response.json(res);
};
coderabbitai[bot] commented 2025-01-21 22:56:37 -08:00 (Migrated from github.com)
Review

⚠️ Potential issue

Add error handling for inventory save operation

The inventory.save() operation is awaited without proper error handling. If the save operation fails, it may result in an unhandled promise rejection and potentially crash the server.

Consider wrapping the inventory.save() call in a try-catch block:

-    await inventory.save();
+    try {
+        await inventory.save();
+    } catch (err) {
+        // Handle the error appropriately
+        logger.error('Error saving inventory:', err);
+        return response.status(500).json({ error: 'Failed to save inventory' });
+    }

Committable suggestion skipped: line range outside the PR's diff.

_:warning: Potential issue_ **Add error handling for inventory save operation** The `inventory.save()` operation is awaited without proper error handling. If the save operation fails, it may result in an unhandled promise rejection and potentially crash the server. Consider wrapping the `inventory.save()` call in a try-catch block: ```diff - await inventory.save(); + try { + await inventory.save(); + } catch (err) { + // Handle the error appropriately + logger.error('Error saving inventory:', err); + return response.status(500).json({ error: 'Failed to save inventory' }); + } ``` > Committable suggestion skipped: line range outside the PR's diff. <!-- This is an auto-generated comment by CodeRabbit -->

View File

@ -949,10 +949,9 @@ export const addMissionComplete = (inventory: TInventoryDatabaseDocument, { Tag,
}
};
export const addBooster = async (ItemType: string, time: number, accountId: string): Promise<void> => {
export const addBooster = (ItemType: string, time: number, inventory: TInventoryDatabaseDocument): void => {
const currentTime = Math.floor(Date.now() / 1000) - 129600; // Value is wrong without 129600. Figure out why, please. :)
const inventory = await getInventory(accountId);
const { Boosters } = inventory;
const itemIndex = Boosters.findIndex(booster => booster.ItemType === ItemType);
@ -964,8 +963,6 @@ export const addBooster = async (ItemType: string, time: number, accountId: stri
} else {
Boosters.push({ ItemType, ExpiryDate: currentTime + time });
}
await inventory.save();
};
export const updateSyndicate = (

View File

@ -5,8 +5,7 @@ import {
addItem,
addMiscItems,
combineInventoryChanges,
getInventory,
updateCurrencyByAccountId,
updateCurrency,
updateSlots
} from "@/src/services/inventoryService";
import { getRandomWeightedReward } from "@/src/services/rngService";
@ -25,6 +24,7 @@ import {
TRarity
} from "warframe-public-export-plus";
import { config } from "./configService";
import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel";
export const getStoreItemCategory = (storeItem: string): string => {
const storeItemString = getSubstringFromKeyword(storeItem, "StoreItems/");
@ -43,7 +43,7 @@ export const getStoreItemTypesCategory = (typesItem: string): string => {
export const handlePurchase = async (
purchaseRequest: IPurchaseRequest,
accountId: string
inventory: TInventoryDatabaseDocument
): Promise<IPurchaseResponse> => {
logger.debug("purchase request", purchaseRequest);
@ -58,8 +58,8 @@ export const handlePurchase = async (
throw new Error(`unknown vendor offer: ${ItemId}`);
}
if (offer.ItemPrices) {
await handleItemPrices(
accountId,
handleItemPrices(
inventory,
offer.ItemPrices,
purchaseRequest.PurchaseParams.Quantity,
inventoryChanges
@ -73,17 +73,17 @@ export const handlePurchase = async (
const purchaseResponse = await handleStoreItemAcquisition(
purchaseRequest.PurchaseParams.StoreItem,
accountId,
inventory,
purchaseRequest.PurchaseParams.Quantity
);
combineInventoryChanges(purchaseResponse.InventoryChanges, inventoryChanges);
if (!purchaseResponse) throw new Error("purchase response was undefined");
const currencyChanges = await updateCurrencyByAccountId(
const currencyChanges = updateCurrency(
inventory,
purchaseRequest.PurchaseParams.ExpectedPrice,
purchaseRequest.PurchaseParams.UsePremium,
accountId
purchaseRequest.PurchaseParams.UsePremium
);
purchaseResponse.InventoryChanges = {
...currencyChanges,
@ -95,7 +95,6 @@ export const handlePurchase = async (
{
const syndicateTag = purchaseRequest.PurchaseParams.SyndicateTag!;
if (purchaseRequest.PurchaseParams.UseFreeFavor!) {
const inventory = await getInventory(accountId);
const affiliation = inventory.Affiliations.find(x => x.Tag == syndicateTag)!;
affiliation.FreeFavorsUsed ??= [];
const lastTitle = affiliation.FreeFavorsEarned![affiliation.FreeFavorsUsed.length];
@ -106,7 +105,6 @@ export const handlePurchase = async (
Title: lastTitle
}
];
await inventory.save();
} else {
const syndicate = ExportSyndicates[syndicateTag];
if (syndicate) {
@ -114,7 +112,6 @@ export const handlePurchase = async (
x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem
);
if (favour) {
const inventory = await getInventory(accountId);
const affiliation = inventory.Affiliations.find(x => x.Tag == syndicateTag);
if (affiliation) {
purchaseResponse.Standing = [
@ -124,7 +121,6 @@ export const handlePurchase = async (
}
];
affiliation.Standing -= favour.standingCost;
await inventory.save();
}
}
}
@ -136,8 +132,8 @@ export const handlePurchase = async (
const vendor = ExportVendors[purchaseRequest.PurchaseParams.SourceId!];
const offer = vendor.items.find(x => x.storeItem == purchaseRequest.PurchaseParams.StoreItem);
if (offer && offer.itemPrices) {
await handleItemPrices(
accountId,
handleItemPrices(
inventory,
offer.itemPrices,
purchaseRequest.PurchaseParams.Quantity,
purchaseResponse.InventoryChanges
@ -157,7 +153,6 @@ export const handlePurchase = async (
x => x.ItemType == purchaseRequest.PurchaseParams.StoreItem
);
if (offer) {
const inventory = await getInventory(accountId);
if (offer.RegularPrice) {
const invItem: IMiscItem = {
ItemType: "/Lotus/Types/Items/MiscItems/SchismKey",
@ -171,7 +166,6 @@ export const handlePurchase = async (
} else if (!config.infiniteRegalAya) {
inventory.PrimeTokens -= offer.PrimePrice! * purchaseRequest.PurchaseParams.Quantity;
}
await inventory.save();
}
break;
}
@ -180,13 +174,12 @@ export const handlePurchase = async (
return purchaseResponse;
};
const handleItemPrices = async (
accountId: string,
const handleItemPrices = (
inventory: TInventoryDatabaseDocument,
itemPrices: IMiscItem[],
purchaseQuantity: number,
inventoryChanges: IInventoryChanges
): Promise<void> => {
const inventory = await getInventory(accountId);
): void => {
for (const item of itemPrices) {
const invItem: IMiscItem = {
ItemType: item.ItemType,
@ -203,12 +196,11 @@ const handleItemPrices = async (
(inventoryChanges.MiscItems as IMiscItem[]).push(invItem);
}
}
await inventory.save();
};
export const handleStoreItemAcquisition = async (
storeItemName: string,
accountId: string,
inventory: TInventoryDatabaseDocument,
quantity: number = 1,
durability: TRarity = "COMMON",
ignorePurchaseQuantity: boolean = false
@ -226,7 +218,7 @@ export const handleStoreItemAcquisition = async (
(
await handleStoreItemAcquisition(
component.typeName,
accountId,
inventory,
component.purchaseQuantity * quantity,
component.durability,
true
@ -247,16 +239,14 @@ export const handleStoreItemAcquisition = async (
}
switch (storeCategory) {
default: {
const inventory = await getInventory(accountId);
purchaseResponse = await addItem(inventory, internalName, quantity);
await inventory.save();
break;
}
case "Types":
purchaseResponse = await handleTypesPurchase(internalName, accountId, quantity);
purchaseResponse = await handleTypesPurchase(internalName, inventory, quantity);
break;
case "Boosters":
purchaseResponse = await handleBoostersPurchase(internalName, accountId, durability);
purchaseResponse = handleBoostersPurchase(internalName, inventory, durability);
break;
}
}
@ -280,11 +270,11 @@ export const slotPurchaseNameToSlotName: SlotPurchase = {
// // new slot above base = extra + 1 and slots +1
// // new frame = slots -1
// // number of frames = extra - slots + 2
const handleSlotPurchase = async (
const handleSlotPurchase = (
slotPurchaseNameFull: string,
accountId: string,
inventory: TInventoryDatabaseDocument,
quantity: number
): Promise<IPurchaseResponse> => {
): IPurchaseResponse => {
logger.debug(`slot name ${slotPurchaseNameFull}`);
const slotPurchaseName = parseSlotPurchaseName(
slotPurchaseNameFull.substring(slotPurchaseNameFull.lastIndexOf("/") + 1)
@ -294,9 +284,7 @@ const handleSlotPurchase = async (
const slotName = slotPurchaseNameToSlotName[slotPurchaseName].name;
const slotsPurchased = slotPurchaseNameToSlotName[slotPurchaseName].slotsPerPurchase * quantity;
const inventory = await getInventory(accountId);
updateSlots(inventory, slotName, slotsPurchased, slotsPurchased);
await inventory.save();
logger.debug(`added ${slotsPurchased} slot ${slotName}`);
@ -314,7 +302,7 @@ const handleSlotPurchase = async (
const handleBoosterPackPurchase = async (
typeName: string,
accountId: string,
inventory: TInventoryDatabaseDocument,
quantity: number
): Promise<IPurchaseResponse> => {
const pack = ExportBoosterPacks[typeName];
@ -325,7 +313,6 @@ const handleBoosterPackPurchase = async (
BoosterPackItems: "",
InventoryChanges: {}
};
const inventory = await getInventory(accountId);
for (let i = 0; i != quantity; ++i) {
for (const weights of pack.rarityWeightsPerRoll) {
const result = getRandomWeightedReward(pack.components, weights);
@ -340,29 +327,24 @@ const handleBoosterPackPurchase = async (
}
}
}
await inventory.save();
return purchaseResponse;
};
//TODO: change to getInventory, apply changes then save at the end
const handleTypesPurchase = async (
typesName: string,
accountId: string,
inventory: TInventoryDatabaseDocument,
quantity: number
): Promise<IPurchaseResponse> => {
const typeCategory = getStoreItemTypesCategory(typesName);
logger.debug(`type category ${typeCategory}`);
switch (typeCategory) {
default: {
const inventory = await getInventory(accountId);
const resp = await addItem(inventory, typesName, quantity);
await inventory.save();
return resp;
}
default:
return await addItem(inventory, typesName, quantity);
case "BoosterPacks":
return await handleBoosterPackPurchase(typesName, accountId, quantity);
return handleBoosterPackPurchase(typesName, inventory, quantity);
case "SlotItems":
return await handleSlotPurchase(typesName, accountId, quantity);
return handleSlotPurchase(typesName, inventory, quantity);
}
};
@ -380,11 +362,11 @@ const boosterDuration: Record<TRarity, number> = {
LEGENDARY: 90 * 86400
};
const handleBoostersPurchase = async (
const handleBoostersPurchase = (
boosterStoreName: string,
accountId: string,
inventory: TInventoryDatabaseDocument,
durability: TRarity
): Promise<{ InventoryChanges: IInventoryChanges }> => {
): { InventoryChanges: IInventoryChanges } => {
const ItemType = boosterStoreName.replace("StoreItem", "");
if (!boosterCollection.find(x => x == ItemType)) {
logger.error(`unknown booster type: ${ItemType}`);
@ -393,7 +375,7 @@ const handleBoostersPurchase = async (
const ExpiryDate = boosterDuration[durability];
await addBooster(ItemType, ExpiryDate, accountId);
addBooster(ItemType, ExpiryDate, inventory);
return {
InventoryChanges: {