Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache downstream swagger docs #265

Merged
merged 6 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,20 @@ services.AddSwaggerForOcelot(Configuration,

Note, this does not affect nor checks the swagger document's `securityDefinitions` property.

## Downstream Documentation Caching
If your downstream documentation is too large, the response time may be slow.
To address this issue, you can enable caching of transformed documentation by setting the
`DownstreamDocsCacheExpire` parameter. If this parameter is not provided, the documentation won't be cached.
If there is any change in the downstream documentation, the cache will be refreshed.

```csharp
services.AddSwaggerForOcelot(Configuration,
setup =>
{
setup.DownstreamDocsCacheExpire = TimeSpan.FromMinutes(10);
});
```

## Limitation

- Now, this library support only `{everything}` as a wildcard in routing definition. #68
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,22 @@ public class OcelotSwaggerGenOptions
/// </value>
public bool GenerateDocsForGatewayItSelf { get; set; } = false;

/// <summary>
/// Gets or sets a value indicating downstream docs cache expire duration in seconds.
/// </summary>
/// <value>
/// <c>0</c> if it won't be cached; otherwise, cache expire duration in seconds.
/// </value>
public TimeSpan DownstreamDocsCacheExpire { get; set; } = TimeSpan.Zero;

/// <summary>
/// Generates docs for gateway it self with options.
/// </summary>
/// <param name="options">Gateway itself docs generation options.</param>
public void GenerateDocsDocsForGatewayItSelf(Action<OcelotGatewayItSelfSwaggerGenOptions> options = null)
{
GenerateDocsForGatewayItSelf = true;

OcelotGatewayItSelfSwaggerGenOptions = new OcelotGatewayItSelfSwaggerGenOptions();
options?.Invoke(OcelotGatewayItSelfSwaggerGenOptions);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
using Kros.IO;
using Kros.IO;
using Microsoft.Extensions.Caching.Memory;
using MMLib.SwaggerForOcelot.Configuration;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace MMLib.SwaggerForOcelot.Transformation
{
Expand All @@ -15,14 +19,37 @@ namespace MMLib.SwaggerForOcelot.Transformation
public class SwaggerJsonTransformer : ISwaggerJsonTransformer
{
private readonly OcelotSwaggerGenOptions _ocelotSwaggerGenOptions;
private readonly IMemoryCache _memoryCache;

public SwaggerJsonTransformer(OcelotSwaggerGenOptions ocelotSwaggerGenOptions)
public SwaggerJsonTransformer(OcelotSwaggerGenOptions ocelotSwaggerGenOptions, IMemoryCache memoryCache)
{
_ocelotSwaggerGenOptions = ocelotSwaggerGenOptions;
_memoryCache = memoryCache;
}

/// <inheritdoc/>
public string Transform(string swaggerJson,
public string Transform(
string swaggerJson,
IEnumerable<RouteOptions> routes,
string serverOverride,
SwaggerEndPointOptions endPointOptions)
{
if (_ocelotSwaggerGenOptions.DownstreamDocsCacheExpire == TimeSpan.Zero)
{
return TransformSwaggerOrOpenApi(swaggerJson, routes, serverOverride, endPointOptions);
}

return _memoryCache.GetOrCreate(
ComputeHash(swaggerJson),
entry =>
{
entry.AbsoluteExpirationRelativeToNow = _ocelotSwaggerGenOptions.DownstreamDocsCacheExpire;
return TransformSwaggerOrOpenApi(swaggerJson, routes, serverOverride, endPointOptions);
});
}

private string TransformSwaggerOrOpenApi(
string swaggerJson,
IEnumerable<RouteOptions> routes,
string serverOverride,
SwaggerEndPointOptions endPointOptions)
Expand Down Expand Up @@ -327,5 +354,13 @@ private static void TransformServerPaths(JObject openApi, string serverOverride,
}
}
}

private static string ComputeHash(string input)
{
using var sha256 = SHA256.Create();
byte[] bytes = Encoding.UTF8.GetBytes(input);
byte[] hash = sha256.ComputeHash(bytes);
return Convert.ToBase64String(hash);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using BenchmarkDotNet.Attributes;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Configuration;
using MMLib.SwaggerForOcelot.Transformation;
using System.Collections.Generic;
Expand All @@ -14,6 +16,7 @@ public class SwaggerJsonTransfromerBenchmark
private readonly string _swagger;
private readonly SwaggerJsonTransformer _transformer;
private readonly List<RouteOptions> _routeOptions;
private readonly IMemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));

public SwaggerJsonTransfromerBenchmark()
{
Expand All @@ -40,7 +43,7 @@ public SwaggerJsonTransfromerBenchmark()
}
};

_transformer = new SwaggerJsonTransformer(new OcelotSwaggerGenOptions());
_transformer = new SwaggerJsonTransformer(new OcelotSwaggerGenOptions(), memoryCache);
}

[Benchmark]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using JsonDiffPatchDotNet;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using MMLib.SwaggerForOcelot.Configuration;
using MMLib.SwaggerForOcelot.Middleware;
Expand All @@ -23,6 +24,7 @@
using Swashbuckle.AspNetCore.Swagger;
using Xunit;
using Xunit.Sdk;
using Options = Microsoft.Extensions.Options.Options;

namespace MMLib.SwaggerForOcelot.Tests
{
Expand All @@ -35,6 +37,7 @@ public async Task AllowVersionPlaceholder()
const string version = "v1";
const string key = "projects";
HttpContext httpContext = GetHttpContext(requestPath: $"/{version}/{key}");
IMemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));

var next = new TestRequestDelegate();

Expand Down Expand Up @@ -82,7 +85,7 @@ public async Task AllowVersionPlaceholder()
string swaggerJson,
IEnumerable<RouteOptions> routeOptions,
string serverOverride,
SwaggerEndPointOptions options) => new SwaggerJsonTransformer(OcelotSwaggerGenOptions.Default)
SwaggerEndPointOptions options) => new SwaggerJsonTransformer(OcelotSwaggerGenOptions.Default, memoryCache)
.Transform(swaggerJson, routeOptions, serverOverride, options));
var swaggerForOcelotMiddleware = new SwaggerForOcelotMiddleware(
next.Invoke,
Expand Down Expand Up @@ -167,6 +170,7 @@ public async Task RespectDownstreamHttpVersionRouteSetting()
const string version = "v1";
const string key = "projects";
HttpContext httpContext = GetHttpContext(requestPath: $"/{version}/{key}");
IMemoryCache memoryCache = new MemoryCache(Options.Create(new MemoryCacheOptions()));

var next = new TestRequestDelegate();

Expand Down Expand Up @@ -200,7 +204,7 @@ public async Task RespectDownstreamHttpVersionRouteSetting()
string swaggerJson,
IEnumerable<RouteOptions> routeOptions,
string serverOverride,
SwaggerEndPointOptions options) => new SwaggerJsonTransformer(OcelotSwaggerGenOptions.Default)
SwaggerEndPointOptions options) => new SwaggerJsonTransformer(OcelotSwaggerGenOptions.Default, memoryCache)
.Transform(swaggerJson, routeOptions, serverOverride, options));
var swaggerForOcelotMiddleware = new SwaggerForOcelotMiddleware(
next.Invoke,
Expand Down
7 changes: 4 additions & 3 deletions tests/MMLib.SwaggerForOcelot.Tests/SwaggerForOcelotShould.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using JsonDiffPatchDotNet;
using Microsoft.Extensions.Caching.Memory;
using MMLib.SwaggerForOcelot.Configuration;
using MMLib.SwaggerForOcelot.Transformation;
using Newtonsoft.Json.Linq;
Expand All @@ -19,22 +20,22 @@ public class SwaggerForOcelotShould
public void TransferDownstreamSwaggerToUpstreamFormat(TestCase testData)
{
var options = new OcelotSwaggerGenOptions();
IMemoryCache memoryCache = new MemoryCache(Microsoft.Extensions.Options.Options.Create(new MemoryCacheOptions()));

foreach ((string key, string value) in testData.AuthenticationProviderKeyMap)
{
options.AuthenticationProviderKeyMap.Add(key, value);
}

var transformer = new SwaggerJsonTransformer(options);
var transformer = new SwaggerJsonTransformer(options, memoryCache);

string transformed = transformer.Transform(
testData.DownstreamSwagger.ToString(),
testData.Routes,
testData.HostOverride,
new SwaggerEndPointOptions()
{
TakeServersFromDownstreamService = testData.TakeServersFromDownstreamService,
RemoveUnusedComponentsFromScheme = testData.RemoveUnusedComponentsFromScheme
TakeServersFromDownstreamService = testData.TakeServersFromDownstreamService, RemoveUnusedComponentsFromScheme = testData.RemoveUnusedComponentsFromScheme
});

AreEqual(transformed, testData.ExpectedTransformedSwagger, testData.FileName);
Expand Down