< Summary - Jellyfin

Information
Class: Jellyfin.Server.Filters.CachingOpenApiProvider
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Filters/CachingOpenApiProvider.cs
Line coverage
87%
Covered lines: 21
Uncovered lines: 3
Coverable lines: 24
Total lines: 79
Line coverage: 87.5%
Branch coverage
50%
Covered branches: 8
Total branches: 16
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 12/4/2025 - 12:11:49 AM Line coverage: 88.8% (24/27) Branch coverage: 55.5% (10/18) Total lines: 8912/10/2025 - 12:13:43 AM Line coverage: 87.5% (21/24) Branch coverage: 50% (8/16) Total lines: 79 12/4/2025 - 12:11:49 AM Line coverage: 88.8% (24/27) Branch coverage: 55.5% (10/18) Total lines: 8912/10/2025 - 12:13:43 AM Line coverage: 87.5% (21/24) Branch coverage: 50% (8/16) Total lines: 79

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
GetSwagger(...)50%121072.72%
AdjustDocument(...)50%66100%

File(s)

/srv/git/jellyfin/Jellyfin.Server/Filters/CachingOpenApiProvider.cs

#LineLine coverage
 1using System;
 2using AsyncKeyedLock;
 3using Microsoft.AspNetCore.Mvc.ApiExplorer;
 4using Microsoft.Extensions.Caching.Memory;
 5using Microsoft.Extensions.Options;
 6using Microsoft.OpenApi.Models;
 7using Swashbuckle.AspNetCore.Swagger;
 8using Swashbuckle.AspNetCore.SwaggerGen;
 9
 10namespace Jellyfin.Server.Filters;
 11
 12/// <summary>
 13/// OpenApi provider with caching.
 14/// </summary>
 15internal sealed class CachingOpenApiProvider : ISwaggerProvider
 16{
 17    private const string CacheKey = "openapi.json";
 18
 119    private static readonly MemoryCacheEntryOptions _cacheOptions = new() { SlidingExpiration = TimeSpan.FromMinutes(5) 
 120    private static readonly AsyncNonKeyedLocker _lock = new(1);
 121    private static readonly TimeSpan _lockTimeout = TimeSpan.FromSeconds(1);
 22
 23    private readonly IMemoryCache _memoryCache;
 24    private readonly SwaggerGenerator _swaggerGenerator;
 25    private readonly SwaggerGeneratorOptions _swaggerGeneratorOptions;
 26
 27    /// <summary>
 28    /// Initializes a new instance of the <see cref="CachingOpenApiProvider"/> class.
 29    /// </summary>
 30    /// <param name="optionsAccessor">The options accessor.</param>
 31    /// <param name="apiDescriptionsProvider">The api descriptions provider.</param>
 32    /// <param name="schemaGenerator">The schema generator.</param>
 33    /// <param name="memoryCache">The memory cache.</param>
 34    public CachingOpenApiProvider(
 35        IOptions<SwaggerGeneratorOptions> optionsAccessor,
 36        IApiDescriptionGroupCollectionProvider apiDescriptionsProvider,
 37        ISchemaGenerator schemaGenerator,
 38        IMemoryCache memoryCache)
 39    {
 16940        _swaggerGeneratorOptions = optionsAccessor.Value;
 16941        _swaggerGenerator = new SwaggerGenerator(_swaggerGeneratorOptions, apiDescriptionsProvider, schemaGenerator);
 16942        _memoryCache = memoryCache;
 16943    }
 44
 45    /// <inheritdoc />
 46    public OpenApiDocument GetSwagger(string documentName, string? host = null, string? basePath = null)
 47    {
 148        if (_memoryCache.TryGetValue(CacheKey, out OpenApiDocument? openApiDocument) && openApiDocument is not null)
 49        {
 050            return AdjustDocument(openApiDocument, host, basePath);
 51        }
 52
 153        using var acquired = _lock.LockOrNull(_lockTimeout);
 154        if (_memoryCache.TryGetValue(CacheKey, out openApiDocument) && openApiDocument is not null)
 55        {
 056            return AdjustDocument(openApiDocument, host, basePath);
 57        }
 58
 159        if (acquired is null)
 60        {
 061            throw new InvalidOperationException("OpenApi document is generating");
 62        }
 63
 164        openApiDocument = _swaggerGenerator.GetSwagger(documentName);
 165        _memoryCache.Set(CacheKey, openApiDocument, _cacheOptions);
 166        return AdjustDocument(openApiDocument, host, basePath);
 167    }
 68
 69    private OpenApiDocument AdjustDocument(OpenApiDocument document, string? host, string? basePath)
 70    {
 171        document.Servers = _swaggerGeneratorOptions.Servers.Count != 0
 172            ? _swaggerGeneratorOptions.Servers
 173            : string.IsNullOrEmpty(host) && string.IsNullOrEmpty(basePath)
 174                ? []
 175                : [new OpenApiServer { Url = $"{host}{basePath}" }];
 76
 177        return document;
 78    }
 79}