From 0f3fff1c81ecf2fa4b3697ff3aa198d0d9fc4a0c Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:26:24 +0100 Subject: [PATCH 1/8] implement CombatPoint, Crystal + CrystalPerPrice Calculation --- .../Lib9c.GraphQL.Tests.csproj | 2 +- .../Extensions/EquipmentExtensions.cs | 12 ++ .../ActionHandler/ProductStateHandler.cs | 182 ++++++++---------- 3 files changed, 98 insertions(+), 98 deletions(-) create mode 100644 Lib9c.Models/Extensions/EquipmentExtensions.cs diff --git a/Lib9c.GraphQL.Tests/Lib9c.GraphQL.Tests.csproj b/Lib9c.GraphQL.Tests/Lib9c.GraphQL.Tests.csproj index 171baf3c..ec3089cd 100644 --- a/Lib9c.GraphQL.Tests/Lib9c.GraphQL.Tests.csproj +++ b/Lib9c.GraphQL.Tests/Lib9c.GraphQL.Tests.csproj @@ -12,7 +12,7 @@ - + diff --git a/Lib9c.Models/Extensions/EquipmentExtensions.cs b/Lib9c.Models/Extensions/EquipmentExtensions.cs new file mode 100644 index 00000000..82f57233 --- /dev/null +++ b/Lib9c.Models/Extensions/EquipmentExtensions.cs @@ -0,0 +1,12 @@ +using Bencodex.Types; +using Lib9c.Models.Items; + +namespace Lib9c.Models.Extensions; + +public static class EquipmentExtensions +{ + public static Nekoyume.Model.Item.Equipment ToNekoyumeEquipment(this Equipment equipment) + { + return new Nekoyume.Model.Item.Equipment((Dictionary)equipment.Bencoded); + } +} \ No newline at end of file diff --git a/Mimir.Worker/ActionHandler/ProductStateHandler.cs b/Mimir.Worker/ActionHandler/ProductStateHandler.cs index a83734f1..9a8fdf73 100644 --- a/Mimir.Worker/ActionHandler/ProductStateHandler.cs +++ b/Mimir.Worker/ActionHandler/ProductStateHandler.cs @@ -1,5 +1,7 @@ using System.Text.RegularExpressions; using Bencodex.Types; +using Lib9c.Models.Extensions; +using Lib9c.Models.Items; using Lib9c.Models.Market; using Libplanet.Crypto; using Mimir.MongoDB.Bson; @@ -9,6 +11,10 @@ using Mimir.Worker.Services; using MongoDB.Bson; using MongoDB.Driver; +using Nekoyume.Battle; +using Nekoyume.Helper; +using Nekoyume.TableData; +using Nekoyume.TableData.Crystal; using Serilog; namespace Mimir.Worker.ActionHandler; @@ -122,7 +128,7 @@ await AddNewProductsAsync( private async Task> GetExistingProductIds(Address productsStateAddress) { var filter = Builders.Filter.Eq( - "Object.ProductsStateAddress", + "ProductsStateAddress", productsStateAddress.ToHex() ); var projection = Builders.Projection.Include("Object.ProductId"); @@ -151,7 +157,7 @@ private async Task>> AddNewProductsAsync( try { var product = await StateGetter.GetProductState(productId, stoppingToken); - var document = CreateProductDocumentAsync( + var document = await CreateProductDocumentAsync( blockIndex, avatarAddress, productsStateAddress, @@ -173,7 +179,7 @@ private async Task>> AddNewProductsAsync( return documents.Select(doc => doc.ToUpdateOneModel()); } - private ProductDocument CreateProductDocumentAsync( + private async Task CreateProductDocumentAsync( long blockIndex, Address avatarAddress, Address productsStateAddress, @@ -186,19 +192,9 @@ Product product case ItemProduct itemProduct: { var unitPrice = CalculateUnitPrice(itemProduct); - // var combatPoint = await CalculateCombatPointAsync(itemProduct); - // var (crystal, crystalPerPrice) = await CalculateCrystalMetricsAsync(itemProduct); - - // return new ProductDocument( - // productAddress, - // avatarAddress, - // productsStateAddress, - // product, - // unitPrice, - // combatPoint, - // crystal, - // crystalPerPrice - // ); + var combatPoint = await CalculateCombatPointAsync(itemProduct); + var (crystal, crystalPerPrice) = await CalculateCrystalMetricsAsync(itemProduct); + return new ProductDocument( blockIndex, productAddress, @@ -206,9 +202,9 @@ Product product productsStateAddress, product, unitPrice, - null, - null, - null + combatPoint, + crystal, + crystalPerPrice ); } case FavProduct favProduct: @@ -249,93 +245,85 @@ private static decimal CalculateUnitPrice(FavProduct favProduct) / decimal.Parse(favProduct.Asset.GetQuantityString()); } - // private async Task CalculateCombatPointAsync(ItemProduct itemProduct) - // { - // try - // { - // var costumeStatSheet = await Store.GetSheetAsync(); - - // if (costumeStatSheet != null) - // { - // int? cp = itemProduct.TradableItem switch - // { - // ItemUsable itemUsable - // => CPHelper.GetCP( - // (Nekoyume.Model.Item.ItemUsable) - // Nekoyume.Model.Item.ItemFactory.Deserialize( - // (Dictionary)itemUsable.Bencoded - // ) - // ), - // Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), - // _ => null - // }; - // return cp; - // } - // } - // catch (Exception ex) - // { - // Logger.Error( - // $"Error calculating combat point for itemProduct {itemProduct.ProductId}: {ex.Message}" - // ); - // } + private async Task CalculateCombatPointAsync(ItemProduct itemProduct) + { + try + { + var costumeStatSheet = await Store.GetSheetAsync(); - // return null; - // } + if (costumeStatSheet != null) + { + int? cp = itemProduct.TradableItem switch + { + ItemUsable itemUsable + => CPHelper.GetCP( + (Nekoyume.Model.Item.ItemUsable) + Nekoyume.Model.Item.ItemFactory.Deserialize( + (Dictionary)itemUsable.Bencoded + ) + ), + Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), + _ => null + }; + return cp; + } + } + catch (Exception ex) + { + Logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } - // private async Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync( - // ItemProduct itemProduct - // ) - // { - // try - // { - // var crystalEquipmentGrindingSheet = - // await Store.GetSheetAsync(); - // var crystalMonsterCollectionMultiplierSheet = - // await Store.GetSheetAsync(); + return null; + } - // if ( - // crystalEquipmentGrindingSheet != null - // && crystalMonsterCollectionMultiplierSheet != null - // && itemProduct.TradableItem is Equipment equipment - // ) - // { - // var rawCrystal = CrystalCalculator.CalculateCrystal( - // [equipment], - // false, - // crystalEquipmentGrindingSheet, - // crystalMonsterCollectionMultiplierSheet, - // 0 - // ); + private async Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync( + ItemProduct itemProduct + ) + { + try + { + var crystalEquipmentGrindingSheet = + await Store.GetSheetAsync(); + var crystalMonsterCollectionMultiplierSheet = + await Store.GetSheetAsync(); - // int crystal = (int)rawCrystal.MajorUnit; - // int crystalPerPrice = (int) - // rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; + if ( + crystalEquipmentGrindingSheet != null + && crystalMonsterCollectionMultiplierSheet != null + && itemProduct.TradableItem is Equipment equipment + ) + { + var rawCrystal = CrystalCalculator.CalculateCrystal( + [equipment.ToNekoyumeEquipment()], + false, + crystalEquipmentGrindingSheet, + crystalMonsterCollectionMultiplierSheet, + 0 + ); - // return (crystal, crystalPerPrice); - // } - // } - // catch (Exception ex) - // { - // Logger.Error( - // $"Error calculating crystal metrics for itemProduct {itemProduct.ProductId}: {ex.Message}" - // ); - // } + int crystal = (int)rawCrystal.MajorUnit; + int crystalPerPrice = (int) + rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; - // return (null, null); - // } + return (crystal, crystalPerPrice); + } + } + catch (Exception ex) + { + Logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + return (null, null); + } + private IEnumerable> RemoveOldProducts(List productsToRemove) { - var ops = new List>(); - foreach (var productId in productsToRemove) - { - var productFilter = Builders.Filter.Eq( - "Object.TradableItem.TradableId", - productId.ToString() + var productFilter = Builders.Filter.In( + "Object.ProductId", + productsToRemove.Select(x=>x.ToString()) ); - ops.Add(new DeleteOneModel(productFilter)); - } - - return ops; + yield return new DeleteManyModel(productFilter); } } From 6becdb1e48108eea8104d8320915d3e46dcc6f10 Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Mon, 2 Dec 2024 23:53:52 +0100 Subject: [PATCH 2/8] don't throw exception on unpublished sheets --- Mimir.Worker/Initializer/TableSheetInitializer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Mimir.Worker/Initializer/TableSheetInitializer.cs b/Mimir.Worker/Initializer/TableSheetInitializer.cs index 02b1d0af..d6155f1e 100644 --- a/Mimir.Worker/Initializer/TableSheetInitializer.cs +++ b/Mimir.Worker/Initializer/TableSheetInitializer.cs @@ -89,7 +89,8 @@ private static async Task SyncSheetStateAsync( var sheetState = await stateService.GetState(sheetAddress, stoppingToken); if (sheetState is not Text sheetValue) { - throw new InvalidCastException($"Expected sheet state to be of type 'Text'."); + logger.Error("Expected sheet state of {SheetTypeName} to be of type {TextType}", sheetType.Name, typeof(Text)); + return; } sheet.Set(sheetValue.Value); From 0edc54851e8afd81a05da46539533e8b29610c40 Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Tue, 10 Dec 2024 18:50:30 +0100 Subject: [PATCH 3/8] add IItemProductCalculationService and ProductMigrator --- Mimir.Initializer/Configuration.cs | 1 + Mimir.Initializer/HostExtensions.cs | 29 ++++++ .../Migrators/ProductMigrator.cs | 67 +++++++++++++ Mimir.Initializer/Mimir.Initializer.csproj | 5 + Mimir.Initializer/Program.cs | 20 ++-- Mimir.Initializer/RunOptions.cs | 11 +++ Mimir.Initializer/appsettings.json | 3 +- .../ActionHandler/ProductStateHandler.cs | 80 +--------------- Mimir.Worker/Program.cs | 2 + .../IItemProductCalculationService.cs | 10 ++ .../Services/ItemProductCalculationService.cs | 94 +++++++++++++++++++ 11 files changed, 239 insertions(+), 83 deletions(-) create mode 100644 Mimir.Initializer/HostExtensions.cs create mode 100644 Mimir.Initializer/Migrators/ProductMigrator.cs create mode 100644 Mimir.Initializer/RunOptions.cs create mode 100644 Mimir.Worker/Services/IItemProductCalculationService.cs create mode 100644 Mimir.Worker/Services/ItemProductCalculationService.cs diff --git a/Mimir.Initializer/Configuration.cs b/Mimir.Initializer/Configuration.cs index 0149ed03..79943ffe 100644 --- a/Mimir.Initializer/Configuration.cs +++ b/Mimir.Initializer/Configuration.cs @@ -10,6 +10,7 @@ public class Configuration public string? MongoDbCAFile { get; init; } public string ChainStorePath { get; init; } public string[] TargetAccounts { get; init; } + public RunOptions RunOptions { get; init; } public Address[] GetTargetAddresses() => TargetAccounts.Select(adr => new Address(adr)).ToArray(); diff --git a/Mimir.Initializer/HostExtensions.cs b/Mimir.Initializer/HostExtensions.cs new file mode 100644 index 00000000..5cb68186 --- /dev/null +++ b/Mimir.Initializer/HostExtensions.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Mimir.Initializer.Initializer; +using Mimir.Initializer.Migrators; + +namespace Mimir.Initializer; + +public static class HostExtensions +{ + public static async Task RunSnapShotInitializerAsync(this IHost host) + { + using var scope = host.Services.CreateScope(); + var initializer = scope.ServiceProvider.GetRequiredService(); + + var stoppingToken = new CancellationTokenSource().Token; + + await initializer.RunAsync(stoppingToken); + } + + public static async Task RunProductMigratorAsync(this IHost host) + { + using var scope = host.Services.CreateScope(); + var productMigrator = scope.ServiceProvider.GetRequiredService(); + + var stoppingToken = new CancellationTokenSource().Token; + + await productMigrator.AddCpAndCrystalsToProduct(stoppingToken); + } +} \ No newline at end of file diff --git a/Mimir.Initializer/Migrators/ProductMigrator.cs b/Mimir.Initializer/Migrators/ProductMigrator.cs new file mode 100644 index 00000000..a9c4e77e --- /dev/null +++ b/Mimir.Initializer/Migrators/ProductMigrator.cs @@ -0,0 +1,67 @@ +using Mimir.MongoDB; +using Mimir.MongoDB.Bson; +using Mimir.Worker.Services; +using MongoDB.Bson; +using MongoDB.Bson.Serialization; +using MongoDB.Driver; +using Nekoyume.Model.Market; +using ItemProduct = Lib9c.Models.Market.ItemProduct; + +namespace Mimir.Initializer.Migrators; + +public class ProductMigrator +{ + private readonly MongoDbService _dbService; + private readonly IItemProductCalculationService _itemProductCalculationService; + + public ProductMigrator(MongoDbService dbService, IItemProductCalculationService itemProductCalculationService) + { + _dbService = dbService; + _itemProductCalculationService = itemProductCalculationService; + } + + public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) + { + var collectionName = CollectionNames.GetCollectionName(); + var collection = _dbService.GetCollection(collectionName); + + var builder = new FilterDefinitionBuilder(); + var filter = builder.Not(builder.Exists(nameof(ProductDocument.Crystal))); + filter |= builder.Not(builder.Exists(nameof(ProductDocument.CombatPoint))); + filter = builder.And(filter, builder.Eq("Object.ProductType", Enum.GetName(ProductType.NonFungible))); + + var asyncCursor = await collection.FindAsync(filter, cancellationToken: cancellationToken); + var bsonDocs = await asyncCursor.ToListAsync(cancellationToken: cancellationToken); + + var updateDocuments = new List>(); + Console.WriteLine($"Updating {bsonDocs.Count} ProductDocuments with CombatPoints and Crystals"); + for (var index = 0; index < bsonDocs.Count; index++) + { + if (cancellationToken.IsCancellationRequested) + { + return; + } + + var productDocument = BsonSerializer.Deserialize(bsonDocs[index]); + if (productDocument?.Object is not ItemProduct itemProduct) + { + continue; + } + + var (crystal, crystalPerPrice) = await _itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); + var cp = await _itemProductCalculationService.CalculateCombatPointAsync(itemProduct); + + var newProductDocument = productDocument with { Crystal = crystal, CrystalPerPrice = crystalPerPrice, CombatPoint = cp }; + updateDocuments.Add(newProductDocument.ToUpdateOneModel()); + + Console.Write($"\rUpdated of {index + 1} of {bsonDocs.Count}."); + } + + Console.WriteLine($"\nSaving changed models to {collectionName}."); + + await _dbService.UpsertStateDataManyAsync( + collectionName, + updateDocuments, + cancellationToken: cancellationToken); + } +} \ No newline at end of file diff --git a/Mimir.Initializer/Mimir.Initializer.csproj b/Mimir.Initializer/Mimir.Initializer.csproj index 153a11cd..d5dcc07f 100644 --- a/Mimir.Initializer/Mimir.Initializer.csproj +++ b/Mimir.Initializer/Mimir.Initializer.csproj @@ -11,4 +11,9 @@ + + + Always + + diff --git a/Mimir.Initializer/Program.cs b/Mimir.Initializer/Program.cs index 24f1f166..8e0ee881 100644 --- a/Mimir.Initializer/Program.cs +++ b/Mimir.Initializer/Program.cs @@ -4,10 +4,11 @@ using Microsoft.Extensions.Options; using Mimir.Initializer; using Mimir.Initializer.Initializer; +using Mimir.Initializer.Migrators; using Mimir.Worker.Services; using Serilog; -var builder = Host.CreateDefaultBuilder(args) +var host = Host.CreateDefaultBuilder(args) .ConfigureAppConfiguration( (hostingContext, config) => { @@ -44,6 +45,9 @@ var targetAccounts = config.GetTargetAddresses(); return new SnapshotInitializer(dbService, config.ChainStorePath, targetAccounts); }); + + services.AddSingleton(); + services.AddSingleton(); } ) .UseSerilog( @@ -54,9 +58,13 @@ ) .Build(); -using var scope = builder.Services.CreateScope(); -var initializer = scope.ServiceProvider.GetRequiredService(); +var config = host.Services.GetRequiredService>().Value; -var stoppingToken = new CancellationTokenSource().Token; - -await initializer.RunAsync(stoppingToken); +if(config.RunOptions.HasFlag(RunOptions.SnapShotInitializer)) +{ + await host.RunSnapShotInitializerAsync(); +} +if(config.RunOptions.HasFlag(RunOptions.ProductMigrator)) +{ + await host.RunProductMigratorAsync(); +} diff --git a/Mimir.Initializer/RunOptions.cs b/Mimir.Initializer/RunOptions.cs new file mode 100644 index 00000000..3aef32bd --- /dev/null +++ b/Mimir.Initializer/RunOptions.cs @@ -0,0 +1,11 @@ +using System.Text.Json.Serialization; + +namespace Mimir.Initializer; + +[Flags] +[JsonConverter(typeof(JsonStringEnumConverter))] +public enum RunOptions +{ + SnapShotInitializer = 1, + ProductMigrator = 2 +} \ No newline at end of file diff --git a/Mimir.Initializer/appsettings.json b/Mimir.Initializer/appsettings.json index f7586cd9..599dcdd3 100644 --- a/Mimir.Initializer/appsettings.json +++ b/Mimir.Initializer/appsettings.json @@ -34,6 +34,7 @@ "000000000000000000000000000000000000001a", "000000000000000000000000000000000000001b" ], - "EnableInitializing": true + "EnableInitializing": true, + "RunOptions" : "SnapShotInitializer, ProductMigrator" } } \ No newline at end of file diff --git a/Mimir.Worker/ActionHandler/ProductStateHandler.cs b/Mimir.Worker/ActionHandler/ProductStateHandler.cs index 9a8fdf73..a9e2ba79 100644 --- a/Mimir.Worker/ActionHandler/ProductStateHandler.cs +++ b/Mimir.Worker/ActionHandler/ProductStateHandler.cs @@ -23,7 +23,8 @@ public class ProductStateHandler( IStateService stateService, MongoDbService store, IHeadlessGQLClient headlessGqlClient, - IInitializerManager initializerManager + IInitializerManager initializerManager, + IItemProductCalculationService itemProductCalculationService ) : BaseActionHandler( stateService, @@ -192,8 +193,8 @@ Product product case ItemProduct itemProduct: { var unitPrice = CalculateUnitPrice(itemProduct); - var combatPoint = await CalculateCombatPointAsync(itemProduct); - var (crystal, crystalPerPrice) = await CalculateCrystalMetricsAsync(itemProduct); + var combatPoint = await itemProductCalculationService.CalculateCombatPointAsync(itemProduct); + var (crystal, crystalPerPrice) = await itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); return new ProductDocument( blockIndex, @@ -244,79 +245,6 @@ private static decimal CalculateUnitPrice(FavProduct favProduct) return decimal.Parse(favProduct.Price.GetQuantityString()) / decimal.Parse(favProduct.Asset.GetQuantityString()); } - - private async Task CalculateCombatPointAsync(ItemProduct itemProduct) - { - try - { - var costumeStatSheet = await Store.GetSheetAsync(); - - if (costumeStatSheet != null) - { - int? cp = itemProduct.TradableItem switch - { - ItemUsable itemUsable - => CPHelper.GetCP( - (Nekoyume.Model.Item.ItemUsable) - Nekoyume.Model.Item.ItemFactory.Deserialize( - (Dictionary)itemUsable.Bencoded - ) - ), - Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), - _ => null - }; - return cp; - } - } - catch (Exception ex) - { - Logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", - itemProduct.ProductId, ex.Message); - } - - return null; - } - - private async Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync( - ItemProduct itemProduct - ) - { - try - { - var crystalEquipmentGrindingSheet = - await Store.GetSheetAsync(); - var crystalMonsterCollectionMultiplierSheet = - await Store.GetSheetAsync(); - - if ( - crystalEquipmentGrindingSheet != null - && crystalMonsterCollectionMultiplierSheet != null - && itemProduct.TradableItem is Equipment equipment - ) - { - var rawCrystal = CrystalCalculator.CalculateCrystal( - [equipment.ToNekoyumeEquipment()], - false, - crystalEquipmentGrindingSheet, - crystalMonsterCollectionMultiplierSheet, - 0 - ); - - int crystal = (int)rawCrystal.MajorUnit; - int crystalPerPrice = (int) - rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; - - return (crystal, crystalPerPrice); - } - } - catch (Exception ex) - { - Logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", - itemProduct.ProductId, ex.Message); - } - - return (null, null); - } private IEnumerable> RemoveOldProducts(List productsToRemove) { diff --git a/Mimir.Worker/Program.cs b/Mimir.Worker/Program.cs index 25787c86..b2664b8c 100644 --- a/Mimir.Worker/Program.cs +++ b/Mimir.Worker/Program.cs @@ -40,6 +40,8 @@ ); }); +builder.Services.AddSingleton(); + builder.ConfigureInitializers(); builder.ConfigureHandlers(); diff --git a/Mimir.Worker/Services/IItemProductCalculationService.cs b/Mimir.Worker/Services/IItemProductCalculationService.cs new file mode 100644 index 00000000..e611ce82 --- /dev/null +++ b/Mimir.Worker/Services/IItemProductCalculationService.cs @@ -0,0 +1,10 @@ +using Lib9c.Models.Market; + +namespace Mimir.Worker.Services; + +public interface IItemProductCalculationService +{ + Task CalculateCombatPointAsync(ItemProduct itemProduct); + + Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync(ItemProduct itemProduct); +} \ No newline at end of file diff --git a/Mimir.Worker/Services/ItemProductCalculationService.cs b/Mimir.Worker/Services/ItemProductCalculationService.cs new file mode 100644 index 00000000..85e4f808 --- /dev/null +++ b/Mimir.Worker/Services/ItemProductCalculationService.cs @@ -0,0 +1,94 @@ +using Bencodex.Types; +using Lib9c.Models.Extensions; +using Lib9c.Models.Items; +using Lib9c.Models.Market; +using Nekoyume.Battle; +using Nekoyume.Helper; +using Nekoyume.TableData; +using Nekoyume.TableData.Crystal; +using ILogger = Serilog.ILogger; + +namespace Mimir.Worker.Services; + +public class ItemProductCalculationService : IItemProductCalculationService +{ + private readonly MongoDbService _store; + private readonly ILogger _logger; + + public ItemProductCalculationService(MongoDbService store, ILogger logger) + { + _store = store; + _logger = logger; + } + + public async Task CalculateCombatPointAsync(ItemProduct itemProduct) + { + try + { + var costumeStatSheet = await _store.GetSheetAsync(); + + if (costumeStatSheet != null) + { + int? cp = itemProduct.TradableItem switch + { + ItemUsable itemUsable + => CPHelper.GetCP( + (Nekoyume.Model.Item.ItemUsable) + Nekoyume.Model.Item.ItemFactory.Deserialize( + (Dictionary)itemUsable.Bencoded + ) + ), + Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), + _ => null + }; + return cp; + } + } + catch (Exception ex) + { + _logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + + return null; + } + + public async Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync(ItemProduct itemProduct) + { + try + { + var crystalEquipmentGrindingSheet = + await _store.GetSheetAsync(); + var crystalMonsterCollectionMultiplierSheet = + await _store.GetSheetAsync(); + + if ( + crystalEquipmentGrindingSheet != null + && crystalMonsterCollectionMultiplierSheet != null + && itemProduct.TradableItem is Equipment equipment + ) + { + var rawCrystal = CrystalCalculator.CalculateCrystal( + [equipment.ToNekoyumeEquipment()], + false, + crystalEquipmentGrindingSheet, + crystalMonsterCollectionMultiplierSheet, + 0 + ); + + int crystal = (int)rawCrystal.MajorUnit; + int crystalPerPrice = (int) + rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; + + return (crystal, crystalPerPrice); + } + } + catch (Exception ex) + { + _logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + + return (null, null); + } +} \ No newline at end of file From 366d97a092d65db1db1658c947e6ae78a7550118 Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:07:45 +0100 Subject: [PATCH 4/8] fix logger injection --- Mimir.Initializer/Migrators/ProductMigrator.cs | 11 +++++++---- Mimir.Worker/Program.cs | 7 ++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/Mimir.Initializer/Migrators/ProductMigrator.cs b/Mimir.Initializer/Migrators/ProductMigrator.cs index a9c4e77e..f0aeee20 100644 --- a/Mimir.Initializer/Migrators/ProductMigrator.cs +++ b/Mimir.Initializer/Migrators/ProductMigrator.cs @@ -5,6 +5,7 @@ using MongoDB.Bson.Serialization; using MongoDB.Driver; using Nekoyume.Model.Market; +using Serilog; using ItemProduct = Lib9c.Models.Market.ItemProduct; namespace Mimir.Initializer.Migrators; @@ -13,11 +14,13 @@ public class ProductMigrator { private readonly MongoDbService _dbService; private readonly IItemProductCalculationService _itemProductCalculationService; + private readonly ILogger _logger; - public ProductMigrator(MongoDbService dbService, IItemProductCalculationService itemProductCalculationService) + public ProductMigrator(MongoDbService dbService, IItemProductCalculationService itemProductCalculationService, ILogger logger) { _dbService = dbService; _itemProductCalculationService = itemProductCalculationService; + _logger = logger; } public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) @@ -34,7 +37,7 @@ public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) var bsonDocs = await asyncCursor.ToListAsync(cancellationToken: cancellationToken); var updateDocuments = new List>(); - Console.WriteLine($"Updating {bsonDocs.Count} ProductDocuments with CombatPoints and Crystals"); + _logger.Information("Updating {BsonDocsCount} ProductDocuments with CombatPoints and Crystals", bsonDocs.Count); for (var index = 0; index < bsonDocs.Count; index++) { if (cancellationToken.IsCancellationRequested) @@ -54,10 +57,10 @@ public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) var newProductDocument = productDocument with { Crystal = crystal, CrystalPerPrice = crystalPerPrice, CombatPoint = cp }; updateDocuments.Add(newProductDocument.ToUpdateOneModel()); - Console.Write($"\rUpdated of {index + 1} of {bsonDocs.Count}."); + _logger.Debug("\rUpdated of {Index} of {BsonDocsCount}", index + 1, bsonDocs.Count); } - Console.WriteLine($"\nSaving changed models to {collectionName}."); + _logger.Information("Saving changed models to {CollectionName} collection", collectionName); await _dbService.UpsertStateDataManyAsync( collectionName, diff --git a/Mimir.Worker/Program.cs b/Mimir.Worker/Program.cs index b2664b8c..a0a0ad43 100644 --- a/Mimir.Worker/Program.cs +++ b/Mimir.Worker/Program.cs @@ -40,7 +40,12 @@ ); }); -builder.Services.AddSingleton(); +builder.Services.AddSingleton(serviceProvider => +{ + var logger = Log.ForContext(); + var dbService = serviceProvider.GetRequiredService(); + return new ItemProductCalculationService(dbService, logger); +}); builder.ConfigureInitializers(); builder.ConfigureHandlers(); From b85e445ae22d8fd79b533e2c17682a951a3e911f Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Tue, 10 Dec 2024 19:16:57 +0100 Subject: [PATCH 5/8] Revert "don't throw exception on unpublished sheets" This reverts commit 6becdb1e48108eea8104d8320915d3e46dcc6f10. --- Mimir.Worker/Initializer/TableSheetInitializer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Mimir.Worker/Initializer/TableSheetInitializer.cs b/Mimir.Worker/Initializer/TableSheetInitializer.cs index 0ff7c3fa..2c086c40 100644 --- a/Mimir.Worker/Initializer/TableSheetInitializer.cs +++ b/Mimir.Worker/Initializer/TableSheetInitializer.cs @@ -93,8 +93,7 @@ private static async Task SyncSheetStateAsync( var sheetState = await stateService.GetState(sheetAddress, stoppingToken); if (sheetState is not Text sheetValue) { - logger.Error("Expected sheet state of {SheetTypeName} to be of type {TextType}", sheetType.Name, typeof(Text)); - return; + throw new InvalidCastException($"Expected sheet state to be of type 'Text'."); } sheet.Set(sheetValue.Value); From 0fc0377243687a2b4a8e89c3ed7ab0a78f7a097f Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:20:39 +0100 Subject: [PATCH 6/8] add ExecuteManager --- Mimir.Initializer/ExecuteManager.cs | 24 +++++++++++++++ Mimir.Initializer/HostExtensions.cs | 29 ------------------- Mimir.Initializer/IExecutor.cs | 8 +++++ .../Initializer/SnapshotInitializer.cs | 18 +++++++----- .../Migrators/ProductMigrator.cs | 25 +++++++++++----- Mimir.Initializer/Program.cs | 27 ++++++----------- 6 files changed, 69 insertions(+), 62 deletions(-) create mode 100644 Mimir.Initializer/ExecuteManager.cs delete mode 100644 Mimir.Initializer/HostExtensions.cs create mode 100644 Mimir.Initializer/IExecutor.cs diff --git a/Mimir.Initializer/ExecuteManager.cs b/Mimir.Initializer/ExecuteManager.cs new file mode 100644 index 00000000..b15ca130 --- /dev/null +++ b/Mimir.Initializer/ExecuteManager.cs @@ -0,0 +1,24 @@ +namespace Mimir.Initializer; + +public class ExecuteManager +{ + private readonly IEnumerable _executors; + + public ExecuteManager(IEnumerable executors) + { + _executors = executors; + } + + public async Task ExecuteAsync(CancellationToken cancellationToken) + { + foreach (var executor in _executors) + { + if (!executor.ShouldRun()) + { + continue; + } + + await executor.RunAsync(cancellationToken); + } + } +} \ No newline at end of file diff --git a/Mimir.Initializer/HostExtensions.cs b/Mimir.Initializer/HostExtensions.cs deleted file mode 100644 index 5cb68186..00000000 --- a/Mimir.Initializer/HostExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Mimir.Initializer.Initializer; -using Mimir.Initializer.Migrators; - -namespace Mimir.Initializer; - -public static class HostExtensions -{ - public static async Task RunSnapShotInitializerAsync(this IHost host) - { - using var scope = host.Services.CreateScope(); - var initializer = scope.ServiceProvider.GetRequiredService(); - - var stoppingToken = new CancellationTokenSource().Token; - - await initializer.RunAsync(stoppingToken); - } - - public static async Task RunProductMigratorAsync(this IHost host) - { - using var scope = host.Services.CreateScope(); - var productMigrator = scope.ServiceProvider.GetRequiredService(); - - var stoppingToken = new CancellationTokenSource().Token; - - await productMigrator.AddCpAndCrystalsToProduct(stoppingToken); - } -} \ No newline at end of file diff --git a/Mimir.Initializer/IExecutor.cs b/Mimir.Initializer/IExecutor.cs new file mode 100644 index 00000000..3b7acdfe --- /dev/null +++ b/Mimir.Initializer/IExecutor.cs @@ -0,0 +1,8 @@ +namespace Mimir.Initializer; + +public interface IExecutor +{ + bool ShouldRun(); + + Task RunAsync(CancellationToken stoppingToken); +} \ No newline at end of file diff --git a/Mimir.Initializer/Initializer/SnapshotInitializer.cs b/Mimir.Initializer/Initializer/SnapshotInitializer.cs index 7e9b0156..cc2a3aa6 100644 --- a/Mimir.Initializer/Initializer/SnapshotInitializer.cs +++ b/Mimir.Initializer/Initializer/SnapshotInitializer.cs @@ -5,6 +5,7 @@ using Libplanet.Crypto; using Libplanet.Store; using Libplanet.Store.Trie; +using Microsoft.Extensions.Options; using Mimir.Initializer.Util; using Mimir.MongoDB; using Mimir.MongoDB.Bson; @@ -15,25 +16,28 @@ namespace Mimir.Initializer.Initializer; -public class SnapshotInitializer +public class SnapshotInitializer : IExecutor { private readonly MongoDbService _dbService; private readonly ILogger _logger; private readonly string _chainStorePath; - private Address[] _targetAccounts; + private readonly bool _shouldRun; + private readonly Address[] _targetAccounts; public SnapshotInitializer( - MongoDbService dbService, - string chainStorePath, - Address[] targetAccounts + IOptions configuration, + MongoDbService dbService ) { _dbService = dbService; - _chainStorePath = chainStorePath; - _targetAccounts = targetAccounts; + _chainStorePath = configuration.Value.ChainStorePath; + _targetAccounts = configuration.Value.GetTargetAddresses(); + _shouldRun = configuration.Value.RunOptions.HasFlag(RunOptions.SnapShotInitializer); _logger = Log.ForContext(); } + public bool ShouldRun() => _shouldRun; + public async Task RunAsync(CancellationToken stoppingToken) { var started = DateTime.UtcNow; diff --git a/Mimir.Initializer/Migrators/ProductMigrator.cs b/Mimir.Initializer/Migrators/ProductMigrator.cs index f0aeee20..ed1ea541 100644 --- a/Mimir.Initializer/Migrators/ProductMigrator.cs +++ b/Mimir.Initializer/Migrators/ProductMigrator.cs @@ -1,3 +1,4 @@ +using Microsoft.Extensions.Options; using Mimir.MongoDB; using Mimir.MongoDB.Bson; using Mimir.Worker.Services; @@ -10,20 +11,28 @@ namespace Mimir.Initializer.Migrators; -public class ProductMigrator +public class ProductMigrator : IExecutor { + private readonly IOptions _configuration; private readonly MongoDbService _dbService; private readonly IItemProductCalculationService _itemProductCalculationService; private readonly ILogger _logger; - public ProductMigrator(MongoDbService dbService, IItemProductCalculationService itemProductCalculationService, ILogger logger) + public ProductMigrator(IOptions configuration, MongoDbService dbService, + IItemProductCalculationService itemProductCalculationService, ILogger logger) { + _configuration = configuration; _dbService = dbService; _itemProductCalculationService = itemProductCalculationService; _logger = logger; } - public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) + public bool ShouldRun() + { + return _configuration.Value.RunOptions.HasFlag(RunOptions.ProductMigrator); + } + + public async Task RunAsync(CancellationToken stoppingToken) { var collectionName = CollectionNames.GetCollectionName(); var collection = _dbService.GetCollection(collectionName); @@ -33,18 +42,18 @@ public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) filter |= builder.Not(builder.Exists(nameof(ProductDocument.CombatPoint))); filter = builder.And(filter, builder.Eq("Object.ProductType", Enum.GetName(ProductType.NonFungible))); - var asyncCursor = await collection.FindAsync(filter, cancellationToken: cancellationToken); - var bsonDocs = await asyncCursor.ToListAsync(cancellationToken: cancellationToken); + var asyncCursor = await collection.FindAsync(filter, cancellationToken: stoppingToken); + var bsonDocs = await asyncCursor.ToListAsync(cancellationToken: stoppingToken); var updateDocuments = new List>(); _logger.Information("Updating {BsonDocsCount} ProductDocuments with CombatPoints and Crystals", bsonDocs.Count); for (var index = 0; index < bsonDocs.Count; index++) { - if (cancellationToken.IsCancellationRequested) + if (stoppingToken.IsCancellationRequested) { return; } - + var productDocument = BsonSerializer.Deserialize(bsonDocs[index]); if (productDocument?.Object is not ItemProduct itemProduct) { @@ -65,6 +74,6 @@ public async Task AddCpAndCrystalsToProduct(CancellationToken cancellationToken) await _dbService.UpsertStateDataManyAsync( collectionName, updateDocuments, - cancellationToken: cancellationToken); + cancellationToken: stoppingToken); } } \ No newline at end of file diff --git a/Mimir.Initializer/Program.cs b/Mimir.Initializer/Program.cs index 8e0ee881..5ca9d757 100644 --- a/Mimir.Initializer/Program.cs +++ b/Mimir.Initializer/Program.cs @@ -37,17 +37,12 @@ config.MongoDbCAFile ); }); - - services.AddTransient(serviceProvider => - { - var config = serviceProvider.GetRequiredService>().Value; - var dbService = serviceProvider.GetRequiredService(); - var targetAccounts = config.GetTargetAddresses(); - return new SnapshotInitializer(dbService, config.ChainStorePath, targetAccounts); - }); services.AddSingleton(); - services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); } ) .UseSerilog( @@ -58,13 +53,9 @@ ) .Build(); -var config = host.Services.GetRequiredService>().Value; +using var scope = host.Services.CreateScope(); +var executeManager = scope.ServiceProvider.GetRequiredService(); + +var stoppingToken = new CancellationTokenSource().Token; -if(config.RunOptions.HasFlag(RunOptions.SnapShotInitializer)) -{ - await host.RunSnapShotInitializerAsync(); -} -if(config.RunOptions.HasFlag(RunOptions.ProductMigrator)) -{ - await host.RunProductMigratorAsync(); -} +await executeManager.ExecuteAsync(stoppingToken); From 091d1ecdd394b330835e781f9568fb4c4f244395 Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Wed, 11 Dec 2024 22:34:28 +0100 Subject: [PATCH 7/8] move try/catch upwards --- .../Migrators/ProductMigrator.cs | 27 +++++- .../ActionHandler/ProductStateHandler.cs | 25 ++++- Mimir.Worker/Program.cs | 7 +- .../Services/ItemProductCalculationService.cs | 94 ++++++++----------- 4 files changed, 86 insertions(+), 67 deletions(-) diff --git a/Mimir.Initializer/Migrators/ProductMigrator.cs b/Mimir.Initializer/Migrators/ProductMigrator.cs index ed1ea541..25feef4a 100644 --- a/Mimir.Initializer/Migrators/ProductMigrator.cs +++ b/Mimir.Initializer/Migrators/ProductMigrator.cs @@ -60,10 +60,31 @@ public async Task RunAsync(CancellationToken stoppingToken) continue; } - var (crystal, crystalPerPrice) = await _itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); - var cp = await _itemProductCalculationService.CalculateCombatPointAsync(itemProduct); + int? crystal = null; + int? crystalPerPrice = null; + int? combatPoint = null; + try + { + (crystal, crystalPerPrice) = await _itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); + } + catch (Exception ex) + { + _logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + + try + { + combatPoint = await _itemProductCalculationService.CalculateCombatPointAsync(itemProduct); + } + catch (Exception ex) + { + _logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + - var newProductDocument = productDocument with { Crystal = crystal, CrystalPerPrice = crystalPerPrice, CombatPoint = cp }; + var newProductDocument = productDocument with { Crystal = crystal, CrystalPerPrice = crystalPerPrice, CombatPoint = combatPoint }; updateDocuments.Add(newProductDocument.ToUpdateOneModel()); _logger.Debug("\rUpdated of {Index} of {BsonDocsCount}", index + 1, bsonDocs.Count); diff --git a/Mimir.Worker/ActionHandler/ProductStateHandler.cs b/Mimir.Worker/ActionHandler/ProductStateHandler.cs index a9e2ba79..86aceaf7 100644 --- a/Mimir.Worker/ActionHandler/ProductStateHandler.cs +++ b/Mimir.Worker/ActionHandler/ProductStateHandler.cs @@ -193,8 +193,29 @@ Product product case ItemProduct itemProduct: { var unitPrice = CalculateUnitPrice(itemProduct); - var combatPoint = await itemProductCalculationService.CalculateCombatPointAsync(itemProduct); - var (crystal, crystalPerPrice) = await itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); + + int? crystal = null; + int? crystalPerPrice = null; + int? combatPoint = null; + try + { + (crystal, crystalPerPrice) = await itemProductCalculationService.CalculateCrystalMetricsAsync(itemProduct); + } + catch (Exception ex) + { + Logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } + + try + { + combatPoint = await itemProductCalculationService.CalculateCombatPointAsync(itemProduct); + } + catch (Exception ex) + { + Logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", + itemProduct.ProductId, ex.Message); + } return new ProductDocument( blockIndex, diff --git a/Mimir.Worker/Program.cs b/Mimir.Worker/Program.cs index a0a0ad43..b2664b8c 100644 --- a/Mimir.Worker/Program.cs +++ b/Mimir.Worker/Program.cs @@ -40,12 +40,7 @@ ); }); -builder.Services.AddSingleton(serviceProvider => -{ - var logger = Log.ForContext(); - var dbService = serviceProvider.GetRequiredService(); - return new ItemProductCalculationService(dbService, logger); -}); +builder.Services.AddSingleton(); builder.ConfigureInitializers(); builder.ConfigureHandlers(); diff --git a/Mimir.Worker/Services/ItemProductCalculationService.cs b/Mimir.Worker/Services/ItemProductCalculationService.cs index 85e4f808..e4e5d86a 100644 --- a/Mimir.Worker/Services/ItemProductCalculationService.cs +++ b/Mimir.Worker/Services/ItemProductCalculationService.cs @@ -13,80 +13,62 @@ namespace Mimir.Worker.Services; public class ItemProductCalculationService : IItemProductCalculationService { private readonly MongoDbService _store; - private readonly ILogger _logger; - public ItemProductCalculationService(MongoDbService store, ILogger logger) + public ItemProductCalculationService(MongoDbService store) { _store = store; - _logger = logger; } public async Task CalculateCombatPointAsync(ItemProduct itemProduct) { - try - { - var costumeStatSheet = await _store.GetSheetAsync(); + var costumeStatSheet = await _store.GetSheetAsync(); - if (costumeStatSheet != null) - { - int? cp = itemProduct.TradableItem switch - { - ItemUsable itemUsable - => CPHelper.GetCP( - (Nekoyume.Model.Item.ItemUsable) - Nekoyume.Model.Item.ItemFactory.Deserialize( - (Dictionary)itemUsable.Bencoded - ) - ), - Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), - _ => null - }; - return cp; - } - } - catch (Exception ex) + if (costumeStatSheet != null) { - _logger.Error("Error calculating combat point for itemProduct {ItemProductProductId}: {ExMessage}", - itemProduct.ProductId, ex.Message); + int? cp = itemProduct.TradableItem switch + { + ItemUsable itemUsable + => CPHelper.GetCP( + (Nekoyume.Model.Item.ItemUsable) + Nekoyume.Model.Item.ItemFactory.Deserialize( + (Dictionary)itemUsable.Bencoded + ) + ), + Costume costume => CPHelper.GetCP(new Nekoyume.Model.Item.Costume((Dictionary)costume.Bencoded), costumeStatSheet), + _ => null + }; + return cp; } - + return null; } public async Task<(int? crystal, int? crystalPerPrice)> CalculateCrystalMetricsAsync(ItemProduct itemProduct) { - try - { - var crystalEquipmentGrindingSheet = - await _store.GetSheetAsync(); - var crystalMonsterCollectionMultiplierSheet = - await _store.GetSheetAsync(); + var crystalEquipmentGrindingSheet = + await _store.GetSheetAsync(); + var crystalMonsterCollectionMultiplierSheet = + await _store.GetSheetAsync(); - if ( - crystalEquipmentGrindingSheet != null - && crystalMonsterCollectionMultiplierSheet != null - && itemProduct.TradableItem is Equipment equipment - ) - { - var rawCrystal = CrystalCalculator.CalculateCrystal( - [equipment.ToNekoyumeEquipment()], - false, - crystalEquipmentGrindingSheet, - crystalMonsterCollectionMultiplierSheet, - 0 - ); + if ( + crystalEquipmentGrindingSheet != null + && crystalMonsterCollectionMultiplierSheet != null + && itemProduct.TradableItem is Equipment equipment + ) + { + var rawCrystal = CrystalCalculator.CalculateCrystal( + [equipment.ToNekoyumeEquipment()], + false, + crystalEquipmentGrindingSheet, + crystalMonsterCollectionMultiplierSheet, + 0 + ); - int crystal = (int)rawCrystal.MajorUnit; - int crystalPerPrice = (int) - rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; + int crystal = (int)rawCrystal.MajorUnit; + int crystalPerPrice = (int) + rawCrystal.DivRem(itemProduct.Price.MajorUnit).Quotient.MajorUnit; - return (crystal, crystalPerPrice); - } - } - catch (Exception ex) - { - _logger.Error("Error calculating crystal metrics for itemProduct {ItemProductProductId}: {ExMessage}", - itemProduct.ProductId, ex.Message); + return (crystal, crystalPerPrice); } return (null, null); From 0d9289d8ef1664db1a7f3a3d269f3bd27bc758fd Mon Sep 17 00:00:00 2001 From: Spoomer <65831869+Spoomer@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:24:43 +0100 Subject: [PATCH 8/8] Apply suggestions from code review Co-authored-by: Lee Dogeon --- Mimir.Worker/ActionHandler/ProductStateHandler.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Mimir.Worker/ActionHandler/ProductStateHandler.cs b/Mimir.Worker/ActionHandler/ProductStateHandler.cs index 86aceaf7..1500b18e 100644 --- a/Mimir.Worker/ActionHandler/ProductStateHandler.cs +++ b/Mimir.Worker/ActionHandler/ProductStateHandler.cs @@ -269,10 +269,10 @@ private static decimal CalculateUnitPrice(FavProduct favProduct) private IEnumerable> RemoveOldProducts(List productsToRemove) { - var productFilter = Builders.Filter.In( - "Object.ProductId", - productsToRemove.Select(x=>x.ToString()) + var productFilter = Builders.Filter.In( + "Object.ProductId", + productsToRemove.Select(x=>x.ToString()) ); - yield return new DeleteManyModel(productFilter); + yield return new DeleteManyModel(productFilter); } }