Skip to content

Latest commit

 

History

History
308 lines (232 loc) · 19.1 KB

CHANGELOG.md

File metadata and controls

308 lines (232 loc) · 19.1 KB

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

[2.6.0] - 2024-08-22

  • ⚙ IdempotentAPI.MinimalAPI v3.2.0: Add the option to configure special types in the IIdempotencyOptionsProvider to be excluded from the API actions (e.g., [FromServices] DbContext context). This will solve the self-referencing loop issue. For example, we can configure the ExcludeRequestSpecialTypes in the following way. Thank you, @csimonsson, for reporting issue #81 and help improving the code.

    // Program.cs
    // ...
    builder.Services.AddIdempotentMinimalAPI(new IdempotencyOptionsProvider());
    // ...
    // IdempotencyOptionsProvider.cs
    using IdempotentAPI.Core;
    using IdempotentAPI.MinimalAPI;
    using IdempotentAPI.TestWebMinimalAPIs.ApiContext;
    using Microsoft.EntityFrameworkCore;
    
    namespace IdempotentAPI.TestWebMinimalAPIs
    {
        public class IdempotencyOptionsProvider : IIdempotencyOptionsProvider
        {
            private readonly List<Type> ExcludeRequestSpecialTypes = new()
            {
                typeof(DbContext),
            };
    
            public IIdempotencyOptions GetIdempotencyOptions(IHttpContextAccessor httpContextAccessor)
            {
                // WARNING: This example implementation shows we can provide different IdempotencyOptions per case.
                //switch (httpContextAccessor?.HttpContext?.Request.Path)
                //{
                //    case "/v6/TestingIdempotentAPI/test":
                //        return new IdempotencyOptions()
                //        {
                //            ExpireHours = 1,
                //            ExcludeRequestSpecialTypes = ExcludeRequestSpecialTypes,
                //        };
                //}
    
                return new IdempotencyOptions()
                {
                    ExcludeRequestSpecialTypes = ExcludeRequestSpecialTypes,
                };
            }
        }
    }

[2.5.0] - 2024-07-10

  • 🌟 Support FastEndpoints, a developer-friendly alternative to Minimal APIs and MVC. Thank you, @CaptainPowerTurtle, for reporting issue #72 and @dj-nitehawk for helping me integrate with FastEndpoints 🙏💪.
  • ⚙ Add the option to configure the Newtonsoft SerializerSettings based on our needs. For example, this will enable us to use NodaTime in our DTOs. Thank you, @angularsen, for reporting the issue #74 and sharing your ideas to improve the IdempotentAPI library 🙏.
  • IdempotentAPI.MinimalAPI v3.1.0:
    • ⚙ Configure the idempotent options by implementing the IIdempotencyOptionsProvider to provide the IIdempotencyOptions based on our needs (e.g., per endpoint). For example, we could return the IIdempotencyOptions based on the requested path and register IdempotencyOptionsProvider in the Program.cs. Thank you, @JonasLeetTheWay for reporting issue #73 🙏.
      // Program.cs
      builder.Services.AddIdempotentMinimalAPI(new IdempotencyOptionsProvider());
      public class IdempotencyOptionsProvider : IIdempotencyOptionsProvider
      {
          public IIdempotencyOptions GetIdempotencyOptions(IHttpContextAccessor httpContextAccessor)
          {
              switch (httpContextAccessor?.HttpContext?.Request.Path)
              {
                  case "/v6/TestingIdempotentAPI/test":
                      return new IdempotencyOptions()
                      {
                          ExpireHours = 1,
                      };
              }
      
              return new IdempotencyOptions();
          }
      }
    • ...

[2.4.0] - 2024-04-03

  • Add an extension to register the IIdempotencyOptions that will enable the use of the [Idempotent(UseIdempotencyOption = true)] option. In this way, the attribute will use the predefined IIdempotencyOptions.

  • Thank you, @Jevvry, for your time and implementation. This was an excellent idea (#68) 🙏💪.

    // Register the Core service and the `IIdempotencyOptions`.
    services.AddIdempotentAPI(idempotencyOptions);
    // To use the `IIdempotencyOptions`, set the `UseIdempotencyOption` property to `true`.
    [HttpPost()]
    [Idempotent(UseIdempotencyOption = true)]
    public ActionResult AddMyEntity()
    {
        // ...
    }

[2.3.0] - 2024-03-07 - Minimal APIs (IdempotentAPI.MinimalAPI v3.0.0)

  • 🌟 The AddIdempotentMinimalAPI(...) extension is introduced to simplify the IdempotentAPI.MinimalAPI registration with DI improvements by @hartmark.
    • ❗ IMPORTANT: To use the new extensions, the BREAKING IdempotentAPI.MinimalAPI v3.0.0 should be used.
    • The new extensions register the following services:
    public static IServiceCollection AddIdempotentMinimalAPI(this IServiceCollection serviceCollection, IdempotencyOptions idempotencyOptions)
    {
        serviceCollection.AddSingleton<IIdempotencyAccessCache, IdempotencyAccessCache>();
        serviceCollection.AddSingleton<IIdempotencyOptions>(idempotencyOptions);
        serviceCollection.AddTransient(serviceProvider =>
        {
            var distributedCache = serviceProvider.GetRequiredService<IIdempotencyAccessCache>();
            var logger = serviceProvider.GetRequiredService<ILogger<Idempotency>>();
            var idempotencyOptions = serviceProvider.GetRequiredService<IIdempotencyOptions>();
    
            return new Idempotency(
                distributedCache,
                logger,
                idempotencyOptions.ExpiresInMilliseconds,
                idempotencyOptions.HeaderKeyName,
                idempotencyOptions.DistributedCacheKeysPrefix,
                TimeSpan.FromMilliseconds(idempotencyOptions.DistributedLockTimeoutMilli),
                idempotencyOptions.CacheOnlySuccessResponses,
                idempotencyOptions.IsIdempotencyOptional);
        });
    
        return serviceCollection;
    }
  • ✅ Fix for Minimal API: When the special types (such as HttpRequest) are used as arguments, a Newtonsoft serialization exception for a self-referencing loop is thrown. The primary exception information is the following. Thank you to @hartmark for reporting and investigating this issue (#65) 🙏.
    • Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'ServiceProvider' with type 'Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope'

[2.2.0] - 2023-12-26

Added

  • 🌟 Configure idempotency to be optional by setting the IsIdempotencyOptional option in the attribute. Thank you to @vIceBerg, @morgan35543, and @SebastianBienert for requesting this feature (#56) 🙏❤.
  • 🌟 Use double milliseconds (ExpiresInMilliseconds) to define cache expiration instead of hours in integer.
    • ❗ The ExpireHours option is obsolete and will be deprecated.
    • Thank you @vIceBerg for taking the time to report and implement this new feature (#59) 💪🙏.

Fixed

  • ✅ The request data hash always returns an empty byte array. This results in unwanted behavior because requests with different payloads and the same idempotency key are treated the same way. Thanks to @vaspopinex for identifying and reporting this issue (#58) 🙏.

[2.1.0] - 2023-06-11

Added

  • ❗ If you are updating from version 1.0.*, this is a BREAKING change. In such a case, read the 2.0.0-RC.1 change log below.

  • 🌟 All code uses async to avoid thread pool starvation in high-load scenarios. Thanks to @dimmy-timmy for implementing it (#47) 🙏.

  • 🌟The IdempotentAPI.MinimalAPI is introduced to use IdempotentAPI in Minimal APIs. Just add the IdempotentAPIEndpointFilter in your endpoints. Thank to @hartmark for implementing it (#45) 🙏.

    • app.MapPost("/example",
          ([FromQuery] string yourParam) =>
          {
              return Results.Ok(new ResponseDTOs());
          })
          .AddEndpointFilter<IdempotentAPIEndpointFilter>();
  • ✅ GitHub actions configuration added to run CI build and test on pull requests. Thanks to @dimmy-timmy 💪.

  • ✅ Integration Tests are improved to run on CI using WebApplicationFactory. Thanks to @dimmy-timmy 💪.

[2.0.0-RC.1] - 2022-10-02 - BREAKING

Fixed

  • ✅ There were two cases in which the IdempotentAPI didn't respond as expected. For that reason, we made some corrections and improvements. Thanks to @kvuong for reporting this issue (#37) 💪🙏.

    • When the controller returns a non-successful response (4xx, 5xx, etc.), the IdempotentAPI cache the error response. In some cases, maybe we would like this behavior. For that reason, we have added the CacheOnlySuccessResponses attribute option to set it per case (default value: True).
    • When an exception occurs in the controller, the IdempotentAPI stack is in the in-flight mode by returning a 409 Conflict response in the subsequent requests. Thus, we have made a fix to remove the in-flight request on exceptions.
    • However, as long as a request is in inflight mode (running), all other requests will still get a 409 Conflict response. For that reason, we should be careful when configuring the request timeout.
  • ✅ There was a bug when a request body was big enough (e.g., 30kb+). The cache couldn't appropriately be fetched because of a different hash string. Thanks, @bernardiego, for taking the time to report and provide a fix for this issue (#38) 🙏❤.

  • ✅ Fix a bug in the reconstruction of the ObjectResult responses. Thanks to @MohamadTahir for reporting this issue (#39) and providing a workaround 🙏.

Added

  • ❗ The CacheOnlySuccessResponses attribute option is included (default value: True) to cache only 2xx HTTP responses.

  • 🌟 Support idempotency in a Cluster Environment (i.e., a group of multiple server instances) using Distributed Locks. Refactoring has been performed to include additional abstractions and distinguish the Caching (IIdempotencyCache), Distributed Locks (IDistributedAccessLockProvider), and Accessing of them (IIdempotencyAccessCache). Thanks to @Rast1234 for showing the need for this feature 💪🙏. Currently, we support the following two implementations.

    • 🌟 The DistributedLockTimeoutMilli attribute option is included to configure the time the distributed lock will wait for the lock to be acquired (in milliseconds).
    Supported Technologies DI Registration
    samcook/RedLock.net Redis Redlock services.AddRedLockNetDistributedAccessLock(redisEndpoints);
    madelson/DistributedLock Redis, SqlServer, Postgres and many more. services.AddMadelsonDistributedAccessLock();

Changed

  • IMPORTANT: We should register the IdempotentAPI Core services.

    • services.AddIdempotentAPI();
  • IMPORTANT: The IdempotentAPI.Cache has been renamed to IdempotentAPI.Cache.Abstractions. So, you should remove the IdempotentAPI.Cache NuGet package and use the IdempotentAPI.Cache.Abstractions when needed.

  • Dependency Updates

    • Update Newtonsoft.Json from 12.0.3 to 13.0.1.
    • Update Microsoft.Extensions.Caching.Abstractions from 3.1.21 to 6.0.0.
    • Update Microsoft.Extensions.DependencyInjection.Abstractions from 3.1.22 to 6.0.0.
    • Update ZiggyCreatures.FusionCache from 0.1.7 to 0.13.0.
    • Update ZiggyCreatures.FusionCache.Serialization.NewtonsoftJson from 0.1.7 to 0.13.0.

[1.0.1] - 2022-03-12

Fixed

  • Idempotency did not work as expected when the return type on the controller action was a custom object and not an ActionResult. (#33)
  • Thanks to @MohamadTahir for reporting and investigating this issue 🙏.

[1.0.0-RC.1] - 2022-01-18 - BREAKING

Added

  • 📝 This CHANGELOG.md file quickly shows the notable changes we have performed between the project's releases (versions).

  • 🌟 Support for ASP.NET Core 5.0 and 6.0 by stopping using the obsolete BinaryFormatter and using the Newtonsoft JsonSerializer (recommended action).

  • 🌟 Define the IIdempotencyCache interface to decide which caching implementation is appropriate in each use case. Currently, we support the following two implementations. However, you can use your implementation 😉.

    Support Concurrent Requests Primary Cache 2nd-Level Cache Advanced features
    IdempotentAPI.Cache.DistributedCache (Default) ✔️ IDistributedCache
    IdempotentAPI.Cache.FusionCache ✔️ Memory Cache ✔️
    (IDistributedCache)
    ✔️
  • 🌟 Support the FusionCache, which provides high performance and robust cache with an optional distributed 2nd layer and some advanced features.

    • FusionCache also includes some advanced features like a fail-safe mechanism, cache stampede prevention, fine grained soft/hard timeouts with background factory completion, extensive customizable logging, and more.
  • ⚙ Configure the logging level of the IdempotentAPI logs that we would like to keep. For example, as we can see in the following JSON, we can set IdempotentAPI.Core.Idempotency in the appsettings.json.

    {
      "Logging": {
        "LogLevel": {
          "Default": "Information",
          "IdempotentAPI.Core.Idempotency": "Warning"
        }
      }
    }

Changed

  • The Logger name is changed from IdempotencyAttributeFilter to Idempotency. Thus, in the appsettings.json file we should configure the logging level using the name IdempotentAPI.Core.Idempotency e.g., "IdempotentAPI.Core.Idempotency": "Information".
  • Dependencies of IdempotentAPI:
    • Remove Microsoft.AspNetCore (2.2.0).
    • Remove Microsoft.AspNetCore.Mvc.Abstractions (2.2.0).
    • Remove Microsoft.Extensions.Caching.Abstractions (2.2.0).
    • Update Newtonsoft.Json from 12.0.2 to 12.0.3.

Fixed

  • 🌟 Prevent concurrent requests in our default caching implementation (IdempotentAPI.Cache.DistributedCache).

Thanks to

  • @fjsosa for proposing a workaround to use IdempotentAPI in ASP.NET Core 5.0 and 6.0 in the meantime (#17) 🙏.
  • @william-keller for reporting the #23 and #25 issues 🙏❤.

[0.2.3-beta] - 2021-10-15

Fixed

  • Issue: An Invalid character in chunk size error occurs on the second request when using the Kestrel server (#21). For that purpose, we are not caching the Transfer-Encoding in the first request (excluded from cache).
  • Thanks to @apchenjun and @william-keller for reporting and helping solve this issue 💪🙏.

[0.2.1-beta] - 2021-07-12

Added

  • Handle inflight (concurrent and long-running) requests. In such cases, the subsequent exact requests will get a 409 Conflict response.

Fixed

  • Issue: Duplication on concurrent requests with the same key (#19).
  • Thanks to @lvzhuye and @RichardGreen-IS2 for reporting and fixing the issue 🙏❤.

[0.2.0-beta] - 2021-06-19

Added

  • A sample project is added (using .NET Core 3.1).

Fixed

  • Issue: Accessing form data throws an exception when the Content-Type is not supported (#14).
  • Thanks to @apchenjun for reporting the issue 🙏.

[0.1.0-beta] - 2019-11-04

Added

  • Support idempotency by implementing an ASP.NET Core attribute ([Idempotent()]) by which any HTTP write operations (POST and PATCH) can have effect only once for the given request data.
  • Use the IDistributedCache to cache the appropriate data. In this way, you can register your implementation, such as Memory Cache, SQL Server cache, Redis cache, etc.
  • Set different options per controller or/and action method via the Idempotent attribute, such as:
    • Enabled (default: true): Enable or Disable the Idempotent operation on an API Controller's class or method.
    • ExpireHours (default: 24): The cached idempotent data retention period.
    • HeaderKeyName (default: IdempotencyKey): The name of the Idempotency-Key header.
    • DistributedCacheKeysPrefix (default: IdempAPI_): A prefix for the key names that we will use in the DistributedCache.