< Summary - Jellyfin

Information
Class: Jellyfin.Server.Filters.AdditionalModelFilter
Assembly: jellyfin
File(s): /srv/git/jellyfin/Jellyfin.Server/Filters/AdditionalModelFilter.cs
Line coverage
100%
Covered lines: 157
Uncovered lines: 0
Coverable lines: 157
Total lines: 239
Line coverage: 100%
Branch coverage
85%
Covered branches: 24
Total branches: 28
Branch coverage: 85.7%
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(...)85.71%2828100%

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), typeof(MediaBrowser.Model.Br
 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        {
 2037            _serverConfigurationManager = serverConfigurationManager;
 2038        }
 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>();
 5887            foreach (var type in webSocketTypes.Where(t => typeof(IOutboundWebSocketMessage).IsAssignableFrom(t)))
 88            {
 2889                var messageType = (SessionMessageType?)type.GetProperty(nameof(WebSocketMessage.MessageType))?.GetCustom
 2890                if (messageType is null)
 91                {
 92                    continue;
 93                }
 94
 2795                var schema = context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository);
 2796                outboundWebSocketSchemas.Add(schema);
 2797                outboundWebSocketDiscriminators.Add(messageType.ToString()!, schema.Reference.ReferenceV3);
 98            }
 99
 100            // Add custom "SyncPlayGroupUpdateMessage" schema because Swashbuckle cannot generate it for us
 1101            var syncPlayGroupUpdateMessageSchema = new OpenApiSchema
 1102            {
 1103                Type = "object",
 1104                Description = "Untyped sync play command.",
 1105                Properties = new Dictionary<string, OpenApiSchema>
 1106                {
 1107                    {
 1108                        "Data", new OpenApiSchema
 1109                        {
 1110                            AllOf =
 1111                            [
 1112                                new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id =
 1113                            ],
 1114                            Description = "Group update data",
 1115                            Nullable = false,
 1116                        }
 1117                    },
 1118                    { "MessageId", new OpenApiSchema { Type = "string", Format = "uuid", Description = "Gets or sets the
 1119                    {
 1120                        "MessageType", new OpenApiSchema
 1121                        {
 1122                            Enum = Enum.GetValues<SessionMessageType>().Select(type => new OpenApiString(type.ToString()
 1123                            AllOf =
 1124                            [
 1125                                new OpenApiSchema { Reference = new OpenApiReference { Type = ReferenceType.Schema, Id =
 1126                            ],
 1127                            Description = "The different kinds of messages that are used in the WebSocket api.",
 1128                            Default = new OpenApiString(nameof(SessionMessageType.SyncPlayGroupUpdate)),
 1129                            ReadOnly = true
 1130                        }
 1131                    },
 1132                },
 1133                AdditionalPropertiesAllowed = false,
 1134                Reference = new OpenApiReference { Type = ReferenceType.Schema, Id = "SyncPlayGroupUpdateMessage" }
 1135            };
 1136            context.SchemaRepository.AddDefinition("SyncPlayGroupUpdateMessage", syncPlayGroupUpdateMessageSchema);
 1137            outboundWebSocketSchemas.Add(syncPlayGroupUpdateMessageSchema);
 1138            outboundWebSocketDiscriminators[nameof(SessionMessageType.SyncPlayGroupUpdate)] = syncPlayGroupUpdateMessage
 139
 1140            var outboundWebSocketMessageSchema = new OpenApiSchema
 1141            {
 1142                Type = "object",
 1143                Description = "Represents the list of possible outbound websocket types",
 1144                Reference = new OpenApiReference
 1145                {
 1146                    Id = nameof(OutboundWebSocketMessage),
 1147                    Type = ReferenceType.Schema
 1148                },
 1149                OneOf = outboundWebSocketSchemas,
 1150                Discriminator = new OpenApiDiscriminator
 1151                {
 1152                    PropertyName = nameof(WebSocketMessage.MessageType),
 1153                    Mapping = outboundWebSocketDiscriminators
 1154                }
 1155            };
 156
 1157            context.SchemaRepository.AddDefinition(nameof(OutboundWebSocketMessage), outboundWebSocketMessageSchema);
 1158            context.SchemaRepository.AddDefinition(
 1159                nameof(WebSocketMessage),
 1160                new OpenApiSchema
 1161                {
 1162                    Type = "object",
 1163                    Description = "Represents the possible websocket types",
 1164                    Reference = new OpenApiReference
 1165                    {
 1166                        Id = nameof(WebSocketMessage),
 1167                        Type = ReferenceType.Schema
 1168                    },
 1169                    OneOf = new[]
 1170                    {
 1171                        inboundWebSocketMessageSchema,
 1172                        outboundWebSocketMessageSchema
 1173                    }
 1174                });
 175
 176            // Manually generate sync play GroupUpdate messages.
 1177            var groupUpdateTypes = typeof(GroupUpdate<>).Assembly.GetTypes()
 1178                .Where(t => t.BaseType != null
 1179                            && t.BaseType.IsGenericType
 1180                            && t.BaseType.GetGenericTypeDefinition() == typeof(GroupUpdate<>))
 1181                .ToList();
 182
 1183            var groupUpdateSchemas = new List<OpenApiSchema>();
 1184            var groupUpdateDiscriminators = new Dictionary<string, string>();
 20185            foreach (var type in groupUpdateTypes)
 186            {
 9187                var groupUpdateType = (GroupUpdateType?)type.GetProperty(nameof(GroupUpdate<object>.Type))?.GetCustomAtt
 9188                if (groupUpdateType is null)
 189                {
 190                    continue;
 191                }
 192
 9193                var schema = context.SchemaGenerator.GenerateSchema(type, context.SchemaRepository);
 9194                groupUpdateSchemas.Add(schema);
 9195                groupUpdateDiscriminators[groupUpdateType.ToString()!] = schema.Reference.ReferenceV3;
 196            }
 197
 1198            var groupUpdateSchema = new OpenApiSchema
 1199            {
 1200                Type = "object",
 1201                Description = "Represents the list of possible group update types",
 1202                Reference = new OpenApiReference
 1203                {
 1204                    Id = nameof(GroupUpdate<object>),
 1205                    Type = ReferenceType.Schema
 1206                },
 1207                OneOf = groupUpdateSchemas,
 1208                Discriminator = new OpenApiDiscriminator
 1209                {
 1210                    PropertyName = nameof(GroupUpdate<object>.Type),
 1211                    Mapping = groupUpdateDiscriminators
 1212                }
 1213            };
 214
 1215            context.SchemaRepository.Schemas[nameof(GroupUpdate<object>)] = groupUpdateSchema;
 216
 1217            context.SchemaGenerator.GenerateSchema(typeof(ServerDiscoveryInfo), context.SchemaRepository);
 218
 20219            foreach (var configuration in _serverConfigurationManager.GetConfigurationStores())
 220            {
 9221                if (_ignoredConfigurations.IndexOf(configuration.ConfigurationType) != -1)
 222                {
 223                    continue;
 224                }
 225
 7226                context.SchemaGenerator.GenerateSchema(configuration.ConfigurationType, context.SchemaRepository);
 227            }
 228
 1229            context.SchemaRepository.AddDefinition(nameof(TranscodeReason), new OpenApiSchema
 1230            {
 1231                Type = "string",
 1232                Enum = Enum.GetNames<TranscodeReason>()
 1233                    .Select(e => new OpenApiString(e))
 1234                    .Cast<IOpenApiAny>()
 1235                    .ToArray()
 1236            });
 1237        }
 238    }
 239}