diff --git a/src/MetaDataAPI/DefaultServiceProvider.cs b/src/MetaDataAPI/DefaultServiceProvider.cs index daed75c6..0664e641 100644 --- a/src/MetaDataAPI/DefaultServiceProvider.cs +++ b/src/MetaDataAPI/DefaultServiceProvider.cs @@ -5,6 +5,7 @@ using MetaDataAPI.Services.Strapi; using GraphQL.Client.Abstractions; using MetaDataAPI.Services.ChainsInfo; +using Poolz.Finance.CSharp.Polly.Extensions; using Microsoft.Extensions.DependencyInjection; using poolz.finance.csharp.contracts.LockDealNFT; using MediatR.Extensions.FluentValidation.AspNetCore; @@ -21,6 +22,7 @@ public static class DefaultServiceProvider serviceCollection.AddFluentValidation([Assembly.GetExecutingAssembly()]); serviceCollection.AddSingleton(); + serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); serviceCollection.AddSingleton(); diff --git a/src/MetaDataAPI/MetaDataAPI.csproj b/src/MetaDataAPI/MetaDataAPI.csproj index 9fb9a19c..01314fd4 100644 --- a/src/MetaDataAPI/MetaDataAPI.csproj +++ b/src/MetaDataAPI/MetaDataAPI.csproj @@ -21,6 +21,7 @@ + diff --git a/src/MetaDataAPI/Routing/Requests/GetMetadataRequestHandler.cs b/src/MetaDataAPI/Routing/Requests/GetMetadataRequestHandler.cs index 37c7d3a2..f06ca55f 100644 --- a/src/MetaDataAPI/Routing/Requests/GetMetadataRequestHandler.cs +++ b/src/MetaDataAPI/Routing/Requests/GetMetadataRequestHandler.cs @@ -5,6 +5,7 @@ using MetaDataAPI.Models.Errors; using MetaDataAPI.Services.Http; using MetaDataAPI.Services.ChainsInfo; +using Poolz.Finance.CSharp.Polly.Extensions; using poolz.finance.csharp.contracts.LockDealNFT; namespace MetaDataAPI.Routing.Requests; @@ -13,7 +14,8 @@ public class GetMetadataRequestHandler( IServiceProvider serviceProvider, IChainManager chainManager, ILockDealNFTService lockDealNft, - IWeb3Factory web3Factory + IWeb3Factory web3Factory, + IRetryExecutor retry ) : IRequestHandler { public Task Handle(GetMetadataRequest request, CancellationToken cancellationToken) @@ -28,12 +30,14 @@ public Task Handle(GetMetadataRequest request, CancellationToken } lockDealNft.Initialize(web3Factory.Create(chainId.ToRpcUrl()), chainInfo.LockDealNFT); - if (!lockDealNft.IsPoolIdInSupplyRange(poolId)) + var isPoolIdInSupplyRange = retry.Execute(_ => lockDealNft.IsPoolIdInSupplyRange(poolId), ct: cancellationToken); + + if (!isPoolIdInSupplyRange) { return Task.FromResult(new PoolIdNotInSupplyRangeResponse(poolId)); } - var poolsInfo = lockDealNft.FetchPoolInfo(poolId); + var poolsInfo = retry.Execute(_ => lockDealNft.FetchPoolInfo(poolId), ct: cancellationToken); var provider = AbstractProvider.CreateFromPoolInfo(poolsInfo, chainInfo, serviceProvider); var metadata = provider.GetErc721Metadata(); diff --git a/src/MetaDataAPI/Services/Erc20/Erc20Provider.cs b/src/MetaDataAPI/Services/Erc20/Erc20Provider.cs index f44b8d8a..2b46dc0b 100644 --- a/src/MetaDataAPI/Services/Erc20/Erc20Provider.cs +++ b/src/MetaDataAPI/Services/Erc20/Erc20Provider.cs @@ -4,11 +4,16 @@ using MetaDataAPI.Services.Http; using EnvironmentManager.Extensions; using MetaDataAPI.Services.ChainsInfo; +using Poolz.Finance.CSharp.Polly.Extensions; using Net.Cache.DynamoDb.ERC20.DynamoDb.Models; namespace MetaDataAPI.Services.Erc20; -public class Erc20Provider(IErc20CacheService erc20Cache, IWeb3Factory web3Factory) : IErc20Provider +public class Erc20Provider( + IErc20CacheService erc20Cache, + IWeb3Factory web3Factory, + IRetryExecutor retry +) : IErc20Provider { public Erc20Token GetErc20Token(ChainInfo chainInfo, EthereumAddress address) { @@ -17,11 +22,14 @@ public Erc20Token GetErc20Token(ChainInfo chainInfo, EthereumAddress address) public Erc20Token GetErc20Token(string rpcUrl, long chainId, EthereumAddress address) { - var cache = erc20Cache.GetOrAddAsync( - new HashKey(chainId, address), - () => Task.FromResult(web3Factory.Create(rpcUrl)), - () => Task.FromResult(new EthereumAddress(Env.MULTI_CALL_V3_ADDRESS.GetRequired())) - ).GetAwaiter().GetResult(); + var cache = retry.Execute(_ => + { + return erc20Cache.GetOrAddAsync( + new HashKey(chainId, address), + () => Task.FromResult(web3Factory.Create(rpcUrl)), + () => Task.FromResult(new EthereumAddress(Env.MULTI_CALL_V3_ADDRESS.GetRequired())) + ).GetAwaiter().GetResult(); + }); return new Erc20Token(cache); } } \ No newline at end of file diff --git a/src/MetaDataAPI/Services/Http/FailureOnlyLoggingHandler.cs b/src/MetaDataAPI/Services/Http/FailureOnlyLoggingHandler.cs index 32d79965..0d66f056 100644 --- a/src/MetaDataAPI/Services/Http/FailureOnlyLoggingHandler.cs +++ b/src/MetaDataAPI/Services/Http/FailureOnlyLoggingHandler.cs @@ -1,6 +1,6 @@ namespace MetaDataAPI.Services.Http; -public class FailureOnlyLoggingHandler(HttpMessageHandler inner) : DelegatingHandler(inner) +public class FailureOnlyLoggingHandler(HttpMessageHandler innerHandler) : DelegatingHandler(innerHandler) { protected override async Task SendAsync(HttpRequestMessage req, CancellationToken ct) { diff --git a/src/MetaDataAPI/Services/Strapi/StrapiClient.cs b/src/MetaDataAPI/Services/Strapi/StrapiClient.cs index 8edf8c03..9b385d66 100644 --- a/src/MetaDataAPI/Services/Strapi/StrapiClient.cs +++ b/src/MetaDataAPI/Services/Strapi/StrapiClient.cs @@ -4,10 +4,11 @@ using Net.Utils.GraphQL.Extensions; using MetaDataAPI.Services.ChainsInfo; using MetaDataAPI.Services.Strapi.Models; +using Poolz.Finance.CSharp.Polly.Extensions; namespace MetaDataAPI.Services.Strapi; -public class StrapiClient(IGraphQLClient graphQlClient) : IStrapiClient +public class StrapiClient(IGraphQLClient graphQlClient, IRetryExecutor retry) : IStrapiClient { private const string NameOfLockDealNFT = "LockDealNFT"; @@ -55,7 +56,9 @@ public class StrapiClient(IGraphQLClient graphQlClient) : IStrapiClient .WithParameter(chainFilter) .Build(); - var response = await graphQlClient.SendQueryAsync(new GraphQLQuery(query)); + var response = await retry.ExecuteAsync(async token => + await graphQlClient.SendQueryAsync(new GraphQLQuery(query), cancellationToken: token) + ); var data = response.EnsureNoErrors(); diff --git a/tests/MetaDataAPI.Tests/Services/Erc20/Erc20ProviderTests.cs b/tests/MetaDataAPI.Tests/Services/Erc20/Erc20ProviderTests.cs index 6d0de437..9cca7f4e 100644 --- a/tests/MetaDataAPI.Tests/Services/Erc20/Erc20ProviderTests.cs +++ b/tests/MetaDataAPI.Tests/Services/Erc20/Erc20ProviderTests.cs @@ -10,6 +10,7 @@ using MetaDataAPI.Services.ChainsInfo; using Net.Cache.DynamoDb.ERC20.Rpc.Models; using Net.Cache.DynamoDb.ERC20.DynamoDb.Models; +using Poolz.Finance.CSharp.Polly.Extensions; namespace MetaDataAPI.Tests.Services.Erc20; @@ -26,6 +27,7 @@ internal void ShouldReceiveExpectedErc20FromDynamoDb() Environment.SetEnvironmentVariable(nameof(Env.BASE_URL_OF_RPC), baseRpcUrl); Environment.SetEnvironmentVariable(nameof(Env.MULTI_CALL_V3_ADDRESS), EthereumAddress.ZeroAddress); + var retryExecutor = new Mock(); var cacheProvider = new Mock(); var web3Factory = new Mock(); var web3 = new Mock(); @@ -47,7 +49,15 @@ internal void ShouldReceiveExpectedErc20FromDynamoDb() )) .ReturnsAsync(cacheItem); - var provider = new Erc20Provider(cacheProvider.Object, web3Factory.Object); + retryExecutor + .Setup(x => x.Execute( + It.IsAny>(), + It.IsAny>(), + It.IsAny() + )) + .Returns((Func action, DefaultRetryStrategyOptions _, CancellationToken token) => action(token)); + + var provider = new Erc20Provider(cacheProvider.Object, web3Factory.Object, retryExecutor.Object); var result = provider.GetErc20Token( new ChainInfo(chainId, EthereumAddress.ZeroAddress),