mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-12-08 06:05:01 -05:00
Cache OpenApi document generation Original-merge: 8cd56521570992d8587db5e1f80d4cb826537f31 Merged-by: anthonylavado <anthony@lavado.ca> Backported-by: Bond_009 <bond.009@outlook.com>
90 lines
3.3 KiB
C#
90 lines
3.3 KiB
C#
using System;
|
|
using System.Threading;
|
|
using Microsoft.AspNetCore.Mvc.ApiExplorer;
|
|
using Microsoft.Extensions.Caching.Memory;
|
|
using Microsoft.Extensions.Options;
|
|
using Microsoft.OpenApi.Models;
|
|
using Swashbuckle.AspNetCore.Swagger;
|
|
using Swashbuckle.AspNetCore.SwaggerGen;
|
|
|
|
namespace Jellyfin.Server.Filters;
|
|
|
|
/// <summary>
|
|
/// OpenApi provider with caching.
|
|
/// </summary>
|
|
internal sealed class CachingOpenApiProvider : ISwaggerProvider
|
|
{
|
|
private const string CacheKey = "openapi.json";
|
|
|
|
private static readonly MemoryCacheEntryOptions _cacheOptions = new() { SlidingExpiration = TimeSpan.FromMinutes(5) };
|
|
private static readonly SemaphoreSlim _lock = new(1, 1);
|
|
private static readonly TimeSpan _lockTimeout = TimeSpan.FromSeconds(1);
|
|
|
|
private readonly IMemoryCache _memoryCache;
|
|
private readonly SwaggerGenerator _swaggerGenerator;
|
|
private readonly SwaggerGeneratorOptions _swaggerGeneratorOptions;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="CachingOpenApiProvider"/> class.
|
|
/// </summary>
|
|
/// <param name="optionsAccessor">The options accessor.</param>
|
|
/// <param name="apiDescriptionsProvider">The api descriptions provider.</param>
|
|
/// <param name="schemaGenerator">The schema generator.</param>
|
|
/// <param name="memoryCache">The memory cache.</param>
|
|
public CachingOpenApiProvider(
|
|
IOptions<SwaggerGeneratorOptions> optionsAccessor,
|
|
IApiDescriptionGroupCollectionProvider apiDescriptionsProvider,
|
|
ISchemaGenerator schemaGenerator,
|
|
IMemoryCache memoryCache)
|
|
{
|
|
_swaggerGeneratorOptions = optionsAccessor.Value;
|
|
_swaggerGenerator = new SwaggerGenerator(_swaggerGeneratorOptions, apiDescriptionsProvider, schemaGenerator);
|
|
_memoryCache = memoryCache;
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public OpenApiDocument GetSwagger(string documentName, string? host = null, string? basePath = null)
|
|
{
|
|
if (_memoryCache.TryGetValue(CacheKey, out OpenApiDocument? openApiDocument) && openApiDocument is not null)
|
|
{
|
|
return AdjustDocument(openApiDocument, host, basePath);
|
|
}
|
|
|
|
var acquired = _lock.Wait(_lockTimeout);
|
|
try
|
|
{
|
|
if (_memoryCache.TryGetValue(CacheKey, out openApiDocument) && openApiDocument is not null)
|
|
{
|
|
return AdjustDocument(openApiDocument, host, basePath);
|
|
}
|
|
|
|
if (!acquired)
|
|
{
|
|
throw new InvalidOperationException("OpenApi document is generating");
|
|
}
|
|
|
|
openApiDocument = _swaggerGenerator.GetSwagger(documentName);
|
|
_memoryCache.Set(CacheKey, openApiDocument, _cacheOptions);
|
|
return AdjustDocument(openApiDocument, host, basePath);
|
|
}
|
|
finally
|
|
{
|
|
if (acquired)
|
|
{
|
|
_lock.Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
private OpenApiDocument AdjustDocument(OpenApiDocument document, string? host, string? basePath)
|
|
{
|
|
document.Servers = _swaggerGeneratorOptions.Servers.Count != 0
|
|
? _swaggerGeneratorOptions.Servers
|
|
: string.IsNullOrEmpty(host) && string.IsNullOrEmpty(basePath)
|
|
? []
|
|
: [new OpenApiServer { Url = $"{host}{basePath}" }];
|
|
|
|
return document;
|
|
}
|
|
}
|