diff options
Diffstat (limited to 'Emby.Server.Implementations/Services')
10 files changed, 297 insertions, 54 deletions
diff --git a/Emby.Server.Implementations/Services/HttpResult.cs b/Emby.Server.Implementations/Services/HttpResult.cs index 91314c15a..296da2f7a 100644 --- a/Emby.Server.Implementations/Services/HttpResult.cs +++ b/Emby.Server.Implementations/Services/HttpResult.cs @@ -15,7 +15,6 @@ namespace Emby.Server.Implementations.Services public HttpResult(object response, string contentType, HttpStatusCode statusCode) { this.Headers = new Dictionary<string, string>(); - this.Cookies = new List<Cookie>(); this.Response = response; this.ContentType = contentType; @@ -26,29 +25,28 @@ namespace Emby.Server.Implementations.Services public IDictionary<string, string> Headers { get; private set; } - public List<Cookie> Cookies { get; private set; } - public int Status { get; set; } public HttpStatusCode StatusCode { - get { return (HttpStatusCode)Status; } - set { Status = (int)value; } + get => (HttpStatusCode)Status; + set => Status = (int)value; } public IRequest RequestContext { get; set; } public async Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken) { - var response = RequestContext != null ? RequestContext.Response : null; + var response = RequestContext == null ? null : RequestContext.Response; - var bytesResponse = this.Response as byte[]; - if (bytesResponse != null) + if (this.Response is byte[] bytesResponse) { var contentLength = bytesResponse.Length; if (response != null) + { response.SetContentLength(contentLength); + } if (contentLength > 0) { diff --git a/Emby.Server.Implementations/Services/RequestHelper.cs b/Emby.Server.Implementations/Services/RequestHelper.cs index 711ba8bbc..24e9cbfa4 100644 --- a/Emby.Server.Implementations/Services/RequestHelper.cs +++ b/Emby.Server.Implementations/Services/RequestHelper.cs @@ -1,7 +1,7 @@ using System; using System.IO; -using Emby.Server.Implementations.HttpServer; using System.Threading.Tasks; +using Emby.Server.Implementations.HttpServer; namespace Emby.Server.Implementations.Services { @@ -49,4 +49,4 @@ namespace Emby.Server.Implementations.Services } } -}
\ No newline at end of file +} diff --git a/Emby.Server.Implementations/Services/ServiceController.cs b/Emby.Server.Implementations/Services/ServiceController.cs index 46af83128..5796956d8 100644 --- a/Emby.Server.Implementations/Services/ServiceController.cs +++ b/Emby.Server.Implementations/Services/ServiceController.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Reflection; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; -using Microsoft.Extensions.Logging; using MediaBrowser.Model.Services; namespace Emby.Server.Implementations.Services @@ -73,7 +72,7 @@ namespace Emby.Server.Implementations.Services public void RegisterRestPaths(HttpListenerHost appHost, Type requestType, Type serviceType) { var attrs = appHost.GetRouteAttributes(requestType); - foreach (RouteAttribute attr in attrs) + foreach (var attr in attrs) { var restPath = new RestPath(appHost.CreateInstance, appHost.GetParseFn, requestType, serviceType, attr.Path, attr.Verbs, attr.IsHidden, attr.Summary, attr.Description); @@ -90,8 +89,7 @@ namespace Emby.Server.Implementations.Services if (restPath.Path.IndexOfAny(InvalidRouteChars) != -1) throw new ArgumentException(string.Format("Route '{0}' on '{1}' contains invalid chars. ", restPath.Path, restPath.RequestType.GetMethodName())); - List<RestPath> pathsAtFirstMatch; - if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out pathsAtFirstMatch)) + if (!RestPathMap.TryGetValue(restPath.FirstMatchHashKey, out List<RestPath> pathsAtFirstMatch)) { pathsAtFirstMatch = new List<RestPath>(); RestPathMap[restPath.FirstMatchHashKey] = pathsAtFirstMatch; diff --git a/Emby.Server.Implementations/Services/ServiceExec.cs b/Emby.Server.Implementations/Services/ServiceExec.cs index 9516c1e38..45c918fa1 100644 --- a/Emby.Server.Implementations/Services/ServiceExec.cs +++ b/Emby.Server.Implementations/Services/ServiceExec.cs @@ -5,7 +5,6 @@ using System.Linq.Expressions; using System.Reflection; using System.Threading.Tasks; using MediaBrowser.Model.Services; -using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.Services { @@ -74,8 +73,7 @@ namespace Emby.Server.Implementations.Services { var actionName = request.Verb ?? "POST"; - ServiceMethod actionContext; - if (ServiceExecGeneral.execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out actionContext)) + if (execMap.TryGetValue(ServiceMethod.Key(serviceType, actionName, requestName), out ServiceMethod actionContext)) { if (actionContext.RequestFilters != null) { diff --git a/Emby.Server.Implementations/Services/ServiceHandler.cs b/Emby.Server.Implementations/Services/ServiceHandler.cs index f5fcb5fe6..7e836e22c 100644 --- a/Emby.Server.Implementations/Services/ServiceHandler.cs +++ b/Emby.Server.Implementations/Services/ServiceHandler.cs @@ -4,8 +4,8 @@ using System.Reflection; using System.Threading; using System.Threading.Tasks; using Emby.Server.Implementations.HttpServer; -using Microsoft.Extensions.Logging; using MediaBrowser.Model.Services; +using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.Services { @@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.Services return deserializer(requestType, httpReq.InputStream); } } - return Task.FromResult(host.CreateInstance(requestType)); + return Task.FromResult(host.CreateInstance(requestType)); } public static RestPath FindMatchingRestPath(string httpMethod, string pathInfo, out string contentType) @@ -62,8 +62,7 @@ namespace Emby.Server.Implementations.Services { if (this.RestPath == null) { - string contentType; - this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out contentType); + this.RestPath = FindMatchingRestPath(httpMethod, pathInfo, out string contentType); if (contentType != null) ResponseContentType = contentType; @@ -137,9 +136,8 @@ namespace Emby.Server.Implementations.Services public static object CreateRequest(IRequest httpReq, RestPath restPath, Dictionary<string, string> requestParams, object requestDto) { - string contentType; var pathInfo = !restPath.IsWildCardPath - ? GetSanitizedPathInfo(httpReq.PathInfo, out contentType) + ? GetSanitizedPathInfo(httpReq.PathInfo, out string contentType) : httpReq.PathInfo; return restPath.CreateRequest(pathInfo, requestParams, requestDto); @@ -239,8 +237,7 @@ namespace Emby.Server.Implementations.Services private static RestPath GetRoute(IRequest req) { - object route; - req.Items.TryGetValue("__route", out route); + req.Items.TryGetValue("__route", out var route); return route as RestPath; } } diff --git a/Emby.Server.Implementations/Services/ServiceMethod.cs b/Emby.Server.Implementations/Services/ServiceMethod.cs index fa2dd43d0..7add72815 100644 --- a/Emby.Server.Implementations/Services/ServiceMethod.cs +++ b/Emby.Server.Implementations/Services/ServiceMethod.cs @@ -1,4 +1,4 @@ -using System; +using System; namespace Emby.Server.Implementations.Services { @@ -14,4 +14,4 @@ namespace Emby.Server.Implementations.Services return serviceType.FullName + " " + method.ToUpper() + " " + requestDtoName; } } -}
\ No newline at end of file +} diff --git a/Emby.Server.Implementations/Services/ServicePath.cs b/Emby.Server.Implementations/Services/ServicePath.cs index ac2af3eaf..7e1993b71 100644 --- a/Emby.Server.Implementations/Services/ServicePath.cs +++ b/Emby.Server.Implementations/Services/ServicePath.cs @@ -4,8 +4,6 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; -using Microsoft.Extensions.Logging; -using MediaBrowser.Model.Extensions; namespace Emby.Server.Implementations.Services { @@ -39,7 +37,7 @@ namespace Emby.Server.Implementations.Services public int PathComponentsCount { get; set; } /// <summary> - /// The total number of segments after subparts have been exploded ('.') + /// The total number of segments after subparts have been exploded ('.') /// e.g. /path/to/here.ext == 4 /// </summary> public int TotalComponentsCount { get; set; } @@ -50,7 +48,7 @@ namespace Emby.Server.Implementations.Services public Type ServiceType { get; private set; } - public string Path { get { return this.restPath; } } + public string Path => this.restPath; public string Summary { get; private set; } public string Description { get; private set; } @@ -58,10 +56,7 @@ namespace Emby.Server.Implementations.Services public int Priority { get; set; } //passed back to RouteAttribute - public IEnumerable<string> PathVariables - { - get { return this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e)); } - } + public IEnumerable<string> PathVariables => this.variablesNames.Where(e => !string.IsNullOrWhiteSpace(e)); public static string[] GetPathPartsForMatching(string pathInfo) { @@ -117,7 +112,7 @@ namespace Emby.Server.Implementations.Services var hasSeparators = new List<bool>(); foreach (var component in this.restPath.Split(PathSeperatorChar)) { - if (String.IsNullOrEmpty(component)) continue; + if (string.IsNullOrEmpty(component)) continue; if (StringContains(component, VariablePrefix) && component.IndexOf(ComponentSeperator) != -1) @@ -311,8 +306,7 @@ namespace Emby.Server.Implementations.Services public int MatchScore(string httpMethod, string[] withPathInfoParts) { - int wildcardMatchCount; - var isMatch = IsMatch(httpMethod, withPathInfoParts, out wildcardMatchCount); + var isMatch = IsMatch(httpMethod, withPathInfoParts, out var wildcardMatchCount); if (!isMatch) { return -1; @@ -354,7 +348,7 @@ namespace Emby.Server.Implementations.Services if (withPathInfoParts.Length != this.PathComponentsCount && !this.IsWildCardPath) { - return false; + return false; } if (!Verbs.Contains(httpMethod, StringComparer.OrdinalIgnoreCase)) @@ -420,10 +414,10 @@ namespace Emby.Server.Implementations.Services return pathIx == withPathInfoParts.Length; } - private bool LiteralsEqual(string str1, string str2) + private static bool LiteralsEqual(string str1, string str2) { // Most cases - if (String.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(str1, str2, StringComparison.OrdinalIgnoreCase)) { return true; } @@ -433,7 +427,7 @@ namespace Emby.Server.Implementations.Services str2 = str2.ToUpperInvariant(); // Invariant IgnoreCase would probably be better but it's not available in PCL - return String.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); + return string.Equals(str1, str2, StringComparison.CurrentCultureIgnoreCase); } private bool ExplodeComponents(ref string[] withPathInfoParts) @@ -442,7 +436,7 @@ namespace Emby.Server.Implementations.Services for (var i = 0; i < withPathInfoParts.Length; i++) { var component = withPathInfoParts[i]; - if (String.IsNullOrEmpty(component)) continue; + if (string.IsNullOrEmpty(component)) continue; if (this.PathComponentsCount != this.TotalComponentsCount && this.componentsWithSeparators[i]) @@ -473,7 +467,7 @@ namespace Emby.Server.Implementations.Services && requestComponents.Length >= this.TotalComponentsCount - this.wildcardCount; if (!isValidWildCardPath) - throw new ArgumentException(String.Format( + throw new ArgumentException(string.Format( "Path Mismatch: Request Path '{0}' has invalid number of components compared to: '{1}'", pathInfo, this.restPath)); } @@ -489,10 +483,9 @@ namespace Emby.Server.Implementations.Services continue; } - string propertyNameOnRequest; - if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out propertyNameOnRequest)) + if (!this.propertyNamesMap.TryGetValue(variableName.ToLower(), out var propertyNameOnRequest)) { - if (String.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals("ignore", variableName, StringComparison.OrdinalIgnoreCase)) { pathIx++; continue; @@ -522,12 +515,12 @@ namespace Emby.Server.Implementations.Services // hits a match for the next element in the definition (which must be a literal) // It may consume 0 or more path parts var stopLiteral = i == this.TotalComponentsCount - 1 ? null : this.literalsToMatch[i + 1]; - if (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) { var sb = new StringBuilder(); sb.Append(value); pathIx++; - while (!String.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) + while (!string.Equals(requestComponents[pathIx], stopLiteral, StringComparison.OrdinalIgnoreCase)) { sb.Append(PathSeperatorChar + requestComponents[pathIx++]); } diff --git a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs index 2233bf918..d13935fba 100644 --- a/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs +++ b/Emby.Server.Implementations/Services/StringMapTypeDeserializer.cs @@ -11,14 +11,14 @@ namespace Emby.Server.Implementations.Services { internal class PropertySerializerEntry { - public PropertySerializerEntry(Action<object,object> propertySetFn, Func<string, object> propertyParseStringFn) + public PropertySerializerEntry(Action<object, object> propertySetFn, Func<string, object> propertyParseStringFn) { PropertySetFn = propertySetFn; PropertyParseStringFn = propertyParseStringFn; } public Action<object, object> PropertySetFn; - public Func<string,object> PropertyParseStringFn; + public Func<string, object> PropertyParseStringFn; public Type PropertyType; } diff --git a/Emby.Server.Implementations/Services/SwaggerService.cs b/Emby.Server.Implementations/Services/SwaggerService.cs new file mode 100644 index 000000000..9bceeabec --- /dev/null +++ b/Emby.Server.Implementations/Services/SwaggerService.cs @@ -0,0 +1,259 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MediaBrowser.Model.Services; + +namespace Emby.Server.Implementations.Services +{ + [Route("/swagger", "GET", Summary = "Gets the swagger specifications")] + [Route("/swagger.json", "GET", Summary = "Gets the swagger specifications")] + public class GetSwaggerSpec : IReturn<SwaggerSpec> + { + } + + public class SwaggerSpec + { + public string swagger { get; set; } + public string[] schemes { get; set; } + public SwaggerInfo info { get; set; } + public string host { get; set; } + public string basePath { get; set; } + public SwaggerTag[] tags { get; set; } + public IDictionary<string, Dictionary<string, SwaggerMethod>> paths { get; set; } + public Dictionary<string, SwaggerDefinition> definitions { get; set; } + public SwaggerComponents components { get; set; } + } + + public class SwaggerComponents + { + public Dictionary<string, SwaggerSecurityScheme> securitySchemes { get; set; } + } + + public class SwaggerSecurityScheme + { + public string name { get; set; } + public string type { get; set; } + public string @in { get; set; } + } + + public class SwaggerInfo + { + public string description { get; set; } + public string version { get; set; } + public string title { get; set; } + public string termsOfService { get; set; } + + public SwaggerConcactInfo contact { get; set; } + } + + public class SwaggerConcactInfo + { + public string email { get; set; } + public string name { get; set; } + public string url { get; set; } + } + + public class SwaggerTag + { + public string description { get; set; } + public string name { get; set; } + } + + public class SwaggerMethod + { + public string summary { get; set; } + public string description { get; set; } + public string[] tags { get; set; } + public string operationId { get; set; } + public string[] consumes { get; set; } + public string[] produces { get; set; } + public SwaggerParam[] parameters { get; set; } + public Dictionary<string, SwaggerResponse> responses { get; set; } + public Dictionary<string, string[]>[] security { get; set; } + } + + public class SwaggerParam + { + public string @in { get; set; } + public string name { get; set; } + public string description { get; set; } + public bool required { get; set; } + public string type { get; set; } + public string collectionFormat { get; set; } + } + + public class SwaggerResponse + { + public string description { get; set; } + + // ex. "$ref":"#/definitions/Pet" + public Dictionary<string, string> schema { get; set; } + } + + public class SwaggerDefinition + { + public string type { get; set; } + public Dictionary<string, SwaggerProperty> properties { get; set; } + } + + public class SwaggerProperty + { + public string type { get; set; } + public string format { get; set; } + public string description { get; set; } + public string[] @enum { get; set; } + public string @default { get; set; } + } + + public class SwaggerService : IService, IRequiresRequest + { + private SwaggerSpec _spec; + + public IRequest Request { get; set; } + + public object Get(GetSwaggerSpec request) + { + return _spec ?? (_spec = GetSpec()); + } + + private SwaggerSpec GetSpec() + { + string host = null; + Uri uri; + if (Uri.TryCreate(Request.RawUrl, UriKind.Absolute, out uri)) + { + host = uri.Host; + } + + var securitySchemes = new Dictionary<string, SwaggerSecurityScheme>(); + + securitySchemes["api_key"] = new SwaggerSecurityScheme + { + name = "api_key", + type = "apiKey", + @in = "query" + }; + + var spec = new SwaggerSpec + { + schemes = new[] { "http" }, + tags = GetTags(), + swagger = "2.0", + info = new SwaggerInfo + { + title = "Jellyfin Server API", + version = "1.0.0", + description = "Explore the Jellyfin Server API", + contact = new SwaggerConcactInfo + { + name = "Jellyfin Community", + url = "https://jellyfin.readthedocs.io/en/latest/user-docs/getting-help/" + } + }, + paths = GetPaths(), + definitions = GetDefinitions(), + basePath = "/jellyfin", + host = host, + + components = new SwaggerComponents + { + securitySchemes = securitySchemes + } + }; + + return spec; + } + + + private SwaggerTag[] GetTags() + { + return new SwaggerTag[] { }; + } + + private Dictionary<string, SwaggerDefinition> GetDefinitions() + { + return new Dictionary<string, SwaggerDefinition>(); + } + + private IDictionary<string, Dictionary<string, SwaggerMethod>> GetPaths() + { + var paths = new SortedDictionary<string, Dictionary<string, SwaggerMethod>>(); + + var all = ServiceController.Instance.RestPathMap.OrderBy(i => i.Key, StringComparer.OrdinalIgnoreCase).ToList(); + + foreach (var current in all) + { + foreach (var info in current.Value) + { + if (info.IsHidden) + { + continue; + } + + if (info.Path.StartsWith("/mediabrowser", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + if (info.Path.StartsWith("/jellyfin", StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + paths[info.Path] = GetPathInfo(info); + } + } + + return paths; + } + + private Dictionary<string, SwaggerMethod> GetPathInfo(RestPath info) + { + var result = new Dictionary<string, SwaggerMethod>(); + + foreach (var verb in info.Verbs) + { + var responses = new Dictionary<string, SwaggerResponse> + { + }; + + responses["200"] = new SwaggerResponse + { + description = "OK" + }; + + var security = new List<Dictionary<string, string[]>>(); + + var apiKeySecurity = new Dictionary<string, string[]>(); + apiKeySecurity["api_key"] = Array.Empty<string>(); + + security.Add(apiKeySecurity); + + result[verb.ToLower()] = new SwaggerMethod + { + summary = info.Summary, + description = info.Description, + produces = new[] + { + "application/json" + }, + consumes = new[] + { + "application/json" + }, + operationId = info.RequestType.Name, + tags = Array.Empty<string>(), + + parameters = new SwaggerParam[] { }, + + responses = responses, + + security = security.ToArray() + }; + } + + return result; + } + } +} diff --git a/Emby.Server.Implementations/Services/UrlExtensions.cs b/Emby.Server.Implementations/Services/UrlExtensions.cs index ba9889c41..8899fbfa3 100644 --- a/Emby.Server.Implementations/Services/UrlExtensions.cs +++ b/Emby.Server.Implementations/Services/UrlExtensions.cs @@ -1,11 +1,11 @@ -using System; +using System; namespace Emby.Server.Implementations.Services { /// <summary> /// Donated by Ivan Korneliuk from his post: /// http://korneliuk.blogspot.com/2012/08/servicestack-reusing-dtos.html - /// + /// /// Modified to only allow using routes matching the supplied HTTP Verb /// </summary> public static class UrlExtensions @@ -30,4 +30,4 @@ namespace Emby.Server.Implementations.Services : strVal.Substring(0, pos); } } -}
\ No newline at end of file +} |
