< Summary - Jellyfin

Information
Class: Jellyfin.Server.Filters.AdditionalModelFilter
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Filters/AdditionalModelFilter.cs
Line coverage
99%
Covered lines: 118
Uncovered lines: 1
Coverable lines: 119
Total lines: 200
Line coverage: 99.1%
Branch coverage
88%
Covered branches: 23
Total branches: 26
Branch coverage: 88.4%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
Apply(...)88.46%262699.13%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.ComponentModel;
 4using System.Linq;
 5using System.Reflection;
 6using Jellyfin.Extensions;
 7using Jellyfin.Server.Migrations;
 8using MediaBrowser.Common.Plugins;
 9using MediaBrowser.Controller.Configuration;
 10using MediaBrowser.Controller.Net;
 11using MediaBrowser.Controller.Net.WebSocketMessages;
 12using MediaBrowser.Controller.Net.WebSocketMessages.Outbound;
 13using MediaBrowser.Model.ApiClient;
 14using MediaBrowser.Model.Session;
 15using MediaBrowser.Model.SyncPlay;
 16using Microsoft.OpenApi.Any;
 17using Microsoft.OpenApi.Models;
 18using Swashbuckle.AspNetCore.SwaggerGen;
 19
 20namespace Jellyfin.Server.Filters
 21{
 22    /// <summary>
 23    /// Add models not directly used by the API, but used for discovery and websockets.
 24    /// </summary>
 25    public class AdditionalModelFilter : IDocumentFilter
 26    {
 27        // Array of options that should not be visible in the api spec.
 128        private static readonly Type[] _ignoredConfigurations = { typeof(MigrationOptions) };
 29        private readonly IServerConfigurationManager _serverConfigurationManager;
 30
 31        /// <summary>
 32        /// Initializes a new instance of the <see cref="AdditionalModelFilter"/> class.
 33        /// </summary>
 34        /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/> interface
 35        public AdditionalModelFilter(IServerConfigurationManager serverConfigurationManager)
 36        {
 2137            _serverConfigurationManager = serverConfigurationManager;
 2138        }
 39
 40        /// <inheritdoc />
 41        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
 42        {
 143            context.SchemaGenerator.GenerateSchema(typeof(IPlugin), context.SchemaRepository);
 44
 145            var webSocketTypes = typeof(WebSocketMessage).Assembly.GetTypes()
 146                .Where(t => t.IsSubclassOf(typeof(WebSocketMessage))
 147                            && !t.IsGenericType
 148                            && t != typeof(WebSocketMessageInfo))
 149                .ToList();
 50
 151            var inboundWebSocketSchemas = new List<OpenApiSchema>();
 152            var inboundWebSocketDiscriminators = new Dictionary<string, string>();
 1853            foreach (var type in webSocketTypes.Where(t => typeof(IInboundWebSocketMessage).IsAssignableFrom(t)))
 54            {
 855                var messageType = (SessionMessageType?)type.GetProperty(nameof(WebSocketMessage.MessageType))?.GetCustom
 856                if (messageType is null)
 57                {
 58                    continue;
 59                }
 60
 761                var schema = context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository);
 762                inboundWebSocketSchemas.Add(schema);
 763                inboundWebSocketDiscriminators[messageType.ToString()!] = schema.Reference.ReferenceV3;
 64            }
 65
 166            var inboundWebSocketMessageSchema = new OpenApiSchema
 167            {
 168                Type = "object",
 169                Description = "Represents the list of possible inbound websocket types",
 170                Reference = new OpenApiReference
 171                {
 172                    Id = nameof(InboundWebSocketMessage),
 173                    Type = ReferenceType.Schema
 174                },
 175                OneOf = inboundWebSocketSchemas,
 176                Discriminator = new OpenApiDiscriminator
 177                {
 178                    PropertyName = nameof(WebSocketMessage.MessageType),
 179                    Mapping = inboundWebSocketDiscriminators
 180                }
 181            };
 82
 183            context.SchemaRepository.AddDefinition(nameof(InboundWebSocketMessage), inboundWebSocketMessageSchema);
 84
 185            var outboundWebSocketSchemas = new List<OpenApiSchema>();
 186            var outboundWebSocketDiscriminators = new Dictionary<string, string>();
 6887            foreach (var type in webSocketTypes.Where(t => typeof(IOutboundWebSocketMessage).IsAssignableFrom(t)))
 88            {
 3389                var messageType = (SessionMessageType?)type.GetProperty(nameof(WebSocketMessage.MessageType))?.GetCustom
 3390                if (messageType is null)
 91                {
 92                    continue;
 93                }
 94
 95                // Additional discriminator needed for GroupUpdate models...
 3296                if (messageType == SessionMessageType.SyncPlayGroupUpdate && type != typeof(SyncPlayGroupUpdateCommandMe
 97                {
 98                    continue;
 99                }
 100
 28101                var schema = context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository);
 28102                outboundWebSocketSchemas.Add(schema);
 28103                outboundWebSocketDiscriminators.Add(messageType.ToString()!, schema.Reference.ReferenceV3);
 104            }
 105
 1106            var outboundWebSocketMessageSchema = new OpenApiSchema
 1107            {
 1108                Type = "object",
 1109                Description = "Represents the list of possible outbound websocket types",
 1110                Reference = new OpenApiReference
 1111                {
 1112                    Id = nameof(OutboundWebSocketMessage),
 1113                    Type = ReferenceType.Schema
 1114                },
 1115                OneOf = outboundWebSocketSchemas,
 1116                Discriminator = new OpenApiDiscriminator
 1117                {
 1118                    PropertyName = nameof(WebSocketMessage.MessageType),
 1119                    Mapping = outboundWebSocketDiscriminators
 1120                }
 1121            };
 122
 1123            context.SchemaRepository.AddDefinition(nameof(OutboundWebSocketMessage), outboundWebSocketMessageSchema);
 1124            context.SchemaRepository.AddDefinition(
 1125                nameof(WebSocketMessage),
 1126                new OpenApiSchema
 1127                {
 1128                    Type = "object",
 1129                    Description = "Represents the possible websocket types",
 1130                    Reference = new OpenApiReference
 1131                    {
 1132                        Id = nameof(WebSocketMessage),
 1133                        Type = ReferenceType.Schema
 1134                    },
 1135                    OneOf = new[]
 1136                    {
 1137                        inboundWebSocketMessageSchema,
 1138                        outboundWebSocketMessageSchema
 1139                    }
 1140                });
 141
 142            // Manually generate sync play GroupUpdate messages.
 1143            if (!context.SchemaRepository.Schemas.TryGetValue(nameof(GroupUpdate), out var groupUpdateSchema))
 144            {
 0145                groupUpdateSchema = context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate), context.SchemaRepository
 146            }
 147
 1148            var groupUpdateOfGroupInfoSchema = context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate<GroupInfoDto>),
 1149            var groupUpdateOfGroupStateSchema = context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate<GroupStateUpda
 1150            var groupUpdateOfStringSchema = context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate<string>), context.
 1151            var groupUpdateOfPlayQueueSchema = context.SchemaGenerator.GenerateSchema(typeof(GroupUpdate<PlayQueueUpdate
 152
 1153            groupUpdateSchema.OneOf = new List<OpenApiSchema>
 1154            {
 1155                groupUpdateOfGroupInfoSchema,
 1156                groupUpdateOfGroupStateSchema,
 1157                groupUpdateOfStringSchema,
 1158                groupUpdateOfPlayQueueSchema
 1159            };
 160
 1161            groupUpdateSchema.Discriminator = new OpenApiDiscriminator
 1162            {
 1163                PropertyName = nameof(GroupUpdate.Type),
 1164                Mapping = new Dictionary<string, string>
 1165                {
 1166                    { GroupUpdateType.UserJoined.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 },
 1167                    { GroupUpdateType.UserLeft.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 },
 1168                    { GroupUpdateType.GroupJoined.ToString(), groupUpdateOfGroupInfoSchema.Reference.ReferenceV3 },
 1169                    { GroupUpdateType.GroupLeft.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 },
 1170                    { GroupUpdateType.StateUpdate.ToString(), groupUpdateOfGroupStateSchema.Reference.ReferenceV3 },
 1171                    { GroupUpdateType.PlayQueue.ToString(), groupUpdateOfPlayQueueSchema.Reference.ReferenceV3 },
 1172                    { GroupUpdateType.NotInGroup.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 },
 1173                    { GroupUpdateType.GroupDoesNotExist.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 },
 1174                    { GroupUpdateType.LibraryAccessDenied.ToString(), groupUpdateOfStringSchema.Reference.ReferenceV3 }
 1175                }
 1176            };
 177
 1178            context.SchemaGenerator.GenerateSchema(typeof(ServerDiscoveryInfo), context.SchemaRepository);
 179
 18180            foreach (var configuration in _serverConfigurationManager.GetConfigurationStores())
 181            {
 8182                if (_ignoredConfigurations.IndexOf(configuration.ConfigurationType) != -1)
 183                {
 184                    continue;
 185                }
 186
 7187                context.SchemaGenerator.GenerateSchema(configuration.ConfigurationType, context.SchemaRepository);
 188            }
 189
 1190            context.SchemaRepository.AddDefinition(nameof(TranscodeReason), new OpenApiSchema
 1191            {
 1192                Type = "string",
 1193                Enum = Enum.GetNames<TranscodeReason>()
 1194                    .Select(e => new OpenApiString(e))
 1195                    .Cast<IOpenApiAny>()
 1196                    .ToArray()
 1197            });
 1198        }
 199    }
 200}