mirror of
https://github.com/LukeHagar/plexcsharp.git
synced 2025-12-09 12:37:46 +00:00
ci: regenerated with OpenAPI Doc , Speakeasy CLI 1.391.3
This commit is contained in:
94
LukeHagar/PlexAPI/SDK/Utils/AnyDeserializer.cs
Normal file
94
LukeHagar/PlexAPI/SDK/Utils/AnyDeserializer.cs
Normal file
@@ -0,0 +1,94 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class AnyDeserializer : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return (objectType == typeof(Dictionary<string, object>));
|
||||
}
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public override object ReadJson(
|
||||
JsonReader reader,
|
||||
Type objectType,
|
||||
object? existingValue,
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
if (reader.TokenType == JsonToken.StartObject) {
|
||||
return ParseTokenIntoDictionary(JToken.Load(reader));
|
||||
}
|
||||
throw new JsonSerializationException($"Could not deserialize token into dictionary");
|
||||
}
|
||||
|
||||
private Dictionary<string, object?> ParseTokenIntoDictionary(JToken token)
|
||||
{
|
||||
var dict = new Dictionary<string, object?>();
|
||||
|
||||
foreach (var child in token.Children<JProperty>())
|
||||
{
|
||||
|
||||
object? val = null;
|
||||
if (child.Value is JObject)
|
||||
{
|
||||
val = ParseTokenIntoDictionary(child.Value);
|
||||
}
|
||||
else if (child.Value is JArray)
|
||||
{
|
||||
val = ParseTokenIntoList(child.Value);
|
||||
}
|
||||
else if (child.Value != null)
|
||||
{
|
||||
val = ((JValue)child.Value).Value;
|
||||
}
|
||||
|
||||
dict[child.Name] = val;
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
private List<object?> ParseTokenIntoList(JToken token)
|
||||
{
|
||||
var list = new List<object?>();
|
||||
|
||||
foreach (var child in token.Children())
|
||||
{
|
||||
if (child is JObject)
|
||||
{
|
||||
list.Add((object)ParseTokenIntoDictionary(child));
|
||||
}
|
||||
else if (child is JArray)
|
||||
{
|
||||
list.Add((object)ParseTokenIntoList(child));
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(((JValue)child).Value);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
61
LukeHagar/PlexAPI/SDK/Utils/BigIntStrConverter.cs
Normal file
61
LukeHagar/PlexAPI/SDK/Utils/BigIntStrConverter.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
internal class BigIntStrConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
var nullableType = Nullable.GetUnderlyingType(objectType);
|
||||
if (nullableType != null)
|
||||
{
|
||||
return nullableType == typeof(BigInteger);
|
||||
}
|
||||
|
||||
return objectType == typeof(BigInteger);
|
||||
}
|
||||
|
||||
public override object? ReadJson(
|
||||
JsonReader reader,
|
||||
Type objectType,
|
||||
object? existingValue,
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return BigInteger.Parse(reader.Value.ToString()!);
|
||||
} catch (System.FormatException ex) {
|
||||
throw new Newtonsoft.Json.JsonSerializationException("Could not parse BigInteger", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteValue("null");
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteValue(((BigInteger)value).ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
60
LukeHagar/PlexAPI/SDK/Utils/DecimalStrConverter.cs
Normal file
60
LukeHagar/PlexAPI/SDK/Utils/DecimalStrConverter.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
internal class DecimalStrConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
var nullableType = Nullable.GetUnderlyingType(objectType);
|
||||
if (nullableType != null)
|
||||
{
|
||||
return nullableType == typeof(Decimal);
|
||||
}
|
||||
|
||||
return objectType == typeof(Decimal);
|
||||
}
|
||||
|
||||
public override object? ReadJson(
|
||||
JsonReader reader,
|
||||
Type objectType,
|
||||
object? existingValue,
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Decimal.Parse(reader.Value.ToString()!);
|
||||
} catch (System.FormatException ex) {
|
||||
throw new Newtonsoft.Json.JsonSerializationException("Could not parse Decimal", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteValue("null");
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteValue(((Decimal)value).ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
84
LukeHagar/PlexAPI/SDK/Utils/EnumConverter.cs
Normal file
84
LukeHagar/PlexAPI/SDK/Utils/EnumConverter.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
using System;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
internal class EnumConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(System.Type objectType)
|
||||
{
|
||||
var nullableType = Nullable.GetUnderlyingType(objectType);
|
||||
if (nullableType != null)
|
||||
{
|
||||
return nullableType.IsEnum;
|
||||
}
|
||||
|
||||
return objectType.IsEnum;
|
||||
}
|
||||
|
||||
public override object? ReadJson(
|
||||
JsonReader reader,
|
||||
System.Type objectType,
|
||||
object? existingValue,
|
||||
JsonSerializer serializer
|
||||
)
|
||||
{
|
||||
if (reader.Value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var extensionType = System.Type.GetType(objectType.FullName + "Extension");
|
||||
|
||||
if (Nullable.GetUnderlyingType(objectType) != null) {
|
||||
objectType = Nullable.GetUnderlyingType(objectType)!;
|
||||
extensionType = System.Type.GetType(objectType!.FullName + "Extension");
|
||||
}
|
||||
|
||||
if (extensionType == null)
|
||||
{
|
||||
return Enum.ToObject(objectType, reader.Value);
|
||||
}
|
||||
|
||||
var method = extensionType.GetMethod("ToEnum");
|
||||
if (method == null)
|
||||
{
|
||||
throw new Exception($"Unable to find ToEnum method on {extensionType.FullName}");
|
||||
}
|
||||
|
||||
try {
|
||||
return method.Invoke(null, new[] { (string)reader.Value });
|
||||
} catch(System.Reflection.TargetInvocationException e) {
|
||||
throw new Newtonsoft.Json.JsonSerializationException("Unable to convert value to enum", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteValue("null");
|
||||
return;
|
||||
}
|
||||
|
||||
var extensionType = System.Type.GetType(value.GetType().FullName + "Extension");
|
||||
if (extensionType == null)
|
||||
{
|
||||
writer.WriteValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
writer.WriteValue(Utilities.ToString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
43
LukeHagar/PlexAPI/SDK/Utils/FlexibleObjectDeserializer.cs
Normal file
43
LukeHagar/PlexAPI/SDK/Utils/FlexibleObjectDeserializer.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
internal class FlexibleObjectDeserializer: JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType) =>
|
||||
objectType == typeof(object);
|
||||
|
||||
public override bool CanWrite => false;
|
||||
|
||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
|
||||
{
|
||||
var token = JToken.ReadFrom(reader);
|
||||
|
||||
if (token is JArray)
|
||||
{
|
||||
return new List<object>(token.Select(t =>
|
||||
{
|
||||
return t.ToString();
|
||||
}));
|
||||
}
|
||||
|
||||
return token.ToObject(objectType);
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) =>
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
128
LukeHagar/PlexAPI/SDK/Utils/HeaderSerializer.cs
Normal file
128
LukeHagar/PlexAPI/SDK/Utils/HeaderSerializer.cs
Normal file
@@ -0,0 +1,128 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
|
||||
internal static class HeaderSerializer
|
||||
{
|
||||
public static void PopulateHeaders(ref HttpRequestMessage httpRequest, object? request)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var props = request.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(request);
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetHeaderMetadata();
|
||||
if (metadata == null || metadata.Name == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var headerValue = SerializeHeader(val, metadata.Explode);
|
||||
if (headerValue != "")
|
||||
{
|
||||
httpRequest.Headers.Add(metadata.Name, headerValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static string SerializeHeader(object value, bool explode)
|
||||
{
|
||||
if (Utilities.IsClass(value))
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
var props = value.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(value);
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetHeaderMetadata();
|
||||
if (metadata == null || metadata.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
items.Add($"{metadata.Name}={Utilities.ValueToString(val)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(metadata.Name);
|
||||
items.Add(Utilities.ValueToString(val));
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(",", items);
|
||||
}
|
||||
else if (Utilities.IsDictionary(value))
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (DictionaryEntry entry in (IDictionary)value)
|
||||
{
|
||||
var key = entry.Key?.ToString();
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
items.Add($"{key}={Utilities.ValueToString(entry.Value)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(key);
|
||||
items.Add(Utilities.ValueToString(entry.Value));
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join(",", items);
|
||||
}
|
||||
else if (Utilities.IsList(value))
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var item in (IList)value)
|
||||
{
|
||||
items.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
|
||||
return string.Join(",", items);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Utilities.ValueToString(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
48
LukeHagar/PlexAPI/SDK/Utils/IsoDateTimeSerializer.cs
Normal file
48
LukeHagar/PlexAPI/SDK/Utils/IsoDateTimeSerializer.cs
Normal file
@@ -0,0 +1,48 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
internal class IsoDateTimeSerializer: JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
var nullableType = Nullable.GetUnderlyingType(objectType);
|
||||
if (nullableType != null)
|
||||
{
|
||||
return nullableType == typeof(DateTime);
|
||||
}
|
||||
|
||||
return objectType == typeof(DateTime);
|
||||
}
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
writer.WriteValue("null");
|
||||
return;
|
||||
}
|
||||
|
||||
DateTime time = (DateTime)value;
|
||||
// The built-in Iso converter coerces to local time;
|
||||
// This standardizes to UTC.
|
||||
writer.WriteValue(time.ToUniversalTime().ToString("o", CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
536
LukeHagar/PlexAPI/SDK/Utils/RequestBodySerializer.cs
Normal file
536
LukeHagar/PlexAPI/SDK/Utils/RequestBodySerializer.cs
Normal file
@@ -0,0 +1,536 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
|
||||
internal class RequestBodySerializer
|
||||
{
|
||||
|
||||
public static HttpContent? Serialize(
|
||||
object? request,
|
||||
string requestFieldName,
|
||||
string serializationMethod,
|
||||
bool nullable = false,
|
||||
bool optional = false,
|
||||
string format = ""
|
||||
)
|
||||
{
|
||||
if (request == null)
|
||||
{
|
||||
if (!nullable && !optional)
|
||||
{
|
||||
throw new ArgumentNullException("request body is required");
|
||||
}
|
||||
else if (nullable && serializationMethod == "json")
|
||||
{
|
||||
return new StringContent("null", Encoding.UTF8, "application/json");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (Utilities.IsClass(request))
|
||||
{
|
||||
var prop = GetPropertyInfo(request, requestFieldName);
|
||||
|
||||
if (prop != null)
|
||||
{
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetRequestMetadata();
|
||||
if (metadata != null)
|
||||
{
|
||||
var fieldValue = prop.GetValue(request);
|
||||
if (fieldValue == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return TrySerialize(
|
||||
fieldValue,
|
||||
requestFieldName,
|
||||
serializationMethod,
|
||||
metadata.MediaType ?? ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not an object or flattened request
|
||||
return TrySerialize(request, requestFieldName, serializationMethod, "", format);
|
||||
}
|
||||
|
||||
private static HttpContent? TrySerialize(
|
||||
object request,
|
||||
string requestFieldName,
|
||||
string serializationMethod,
|
||||
string mediaType = "",
|
||||
string format = ""
|
||||
)
|
||||
{
|
||||
if (mediaType == "")
|
||||
{
|
||||
mediaType = new Dictionary<string, string>()
|
||||
{
|
||||
{ "json", "application/json" },
|
||||
{ "form", "application/x-www-form-urlencoded" },
|
||||
{ "multipart", "multipart/form-data" },
|
||||
{ "raw", "application/octet-stream" },
|
||||
{ "string", "text/plain" },
|
||||
}[serializationMethod];
|
||||
}
|
||||
|
||||
switch (serializationMethod)
|
||||
{
|
||||
case "json":
|
||||
return SerializeJson(request, mediaType, format);
|
||||
case "form":
|
||||
return SerializeForm(request, requestFieldName, mediaType);
|
||||
case "multipart":
|
||||
return SerializeMultipart(request, mediaType);
|
||||
default:
|
||||
// if request is a byte array, use it directly otherwise encode
|
||||
if (request.GetType() == typeof(byte[]))
|
||||
{
|
||||
return SerializeRaw((byte[])request, mediaType);
|
||||
}
|
||||
else if (request.GetType() == typeof(string))
|
||||
{
|
||||
return SerializeString((string)request, mediaType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(
|
||||
"Cannot serialize request body of type "
|
||||
+ request.GetType().Name
|
||||
+ " with serialization method "
|
||||
+ serializationMethod
|
||||
+ ""
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static HttpContent SerializeJson(object request, string mediaType, string format = "")
|
||||
{
|
||||
return new StringContent(Utilities.SerializeJSON(request, format), Encoding.UTF8, mediaType);
|
||||
}
|
||||
|
||||
private static HttpContent SerializeForm(
|
||||
object request,
|
||||
string requestFieldName,
|
||||
string mediaType
|
||||
)
|
||||
{
|
||||
Dictionary<string, List<string>> form = new Dictionary<string, List<string>>();
|
||||
|
||||
if (Utilities.IsClass(request))
|
||||
{
|
||||
var props = request.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(request);
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetFormMetadata();
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (metadata.Json)
|
||||
{
|
||||
var key = metadata.Name ?? prop.Name;
|
||||
if (key == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!form.ContainsKey(key))
|
||||
{
|
||||
form.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
form[key].Add(Utilities.SerializeJSON(val));
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (metadata.Style)
|
||||
{
|
||||
case "form":
|
||||
SerializeFormValue(
|
||||
metadata.Name ?? prop.Name,
|
||||
metadata.Explode,
|
||||
val,
|
||||
ref form
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Unsupported form style " + metadata.Style);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsDictionary(request))
|
||||
{
|
||||
foreach (var k in ((IDictionary)request).Keys)
|
||||
{
|
||||
var key = k?.ToString();
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!form.ContainsKey(key))
|
||||
{
|
||||
form.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
form[key].Add(Utilities.ValueToString(((IDictionary)request)[key]));
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsList(request))
|
||||
{
|
||||
foreach (var item in (IList)request)
|
||||
{
|
||||
if (!form.ContainsKey(requestFieldName))
|
||||
{
|
||||
form.Add(requestFieldName, new List<string>());
|
||||
}
|
||||
|
||||
form[requestFieldName].Add(Utilities.ValueToString(item));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception(
|
||||
"Cannot serialize form data from type " + request.GetType().Name
|
||||
);
|
||||
}
|
||||
|
||||
var formData = new List<KeyValuePair<string?, string?>>();
|
||||
|
||||
foreach (var key in form.Keys)
|
||||
{
|
||||
foreach (var val in form[key])
|
||||
{
|
||||
formData.Add(
|
||||
new KeyValuePair<string?, string?>(
|
||||
key + (form[key].Count > 1 ? "[]" : ""),
|
||||
val
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return new FormUrlEncodedContent(formData);
|
||||
}
|
||||
|
||||
private static HttpContent SerializeMultipart(object request, string mediaType)
|
||||
{
|
||||
var formData = new MultipartFormDataContent();
|
||||
|
||||
var properties = request.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
var value = prop.GetValue(request);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetMultipartFormMetadata();
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (metadata.File)
|
||||
{
|
||||
if (!Utilities.IsClass(value))
|
||||
{
|
||||
throw new Exception(
|
||||
"Cannot serialize multipart file from type " + value.GetType().Name
|
||||
);
|
||||
}
|
||||
|
||||
var fileProps = value.GetType().GetProperties();
|
||||
|
||||
byte[]? content = null;
|
||||
string fileName = "";
|
||||
string fieldName = "";
|
||||
|
||||
foreach (var fileProp in fileProps)
|
||||
{
|
||||
var fileMetadata = fileProp
|
||||
.GetCustomAttribute<SpeakeasyMetadata>()
|
||||
?.GetMultipartFormMetadata();
|
||||
if (
|
||||
fileMetadata == null
|
||||
|| (!fileMetadata.Content && fileMetadata.Name == "")
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fileMetadata.Content)
|
||||
{
|
||||
content = (byte[]?)fileProp.GetValue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
fieldName = fileMetadata.Name ?? fileProp.Name;
|
||||
fileName = fileProp.GetValue(value)?.ToString() ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
if (fieldName == "" || fileName == "" || content == null)
|
||||
{
|
||||
throw new Exception("Invalid multipart/form-data file");
|
||||
}
|
||||
|
||||
formData.Add(new ByteArrayContent(content), fieldName, fileName);
|
||||
}
|
||||
else if (metadata.Json)
|
||||
{
|
||||
formData.Add(
|
||||
new StringContent(Utilities.SerializeJSON(value)),
|
||||
metadata.Name ?? prop.Name
|
||||
);
|
||||
}
|
||||
else if (Utilities.IsList(value))
|
||||
{
|
||||
var values = new List<string>();
|
||||
|
||||
foreach (var item in (IList)value)
|
||||
{
|
||||
values.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
|
||||
foreach (var val in values)
|
||||
{
|
||||
formData.Add(new StringContent(val), metadata.Name ?? prop.Name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
formData.Add(
|
||||
new StringContent(Utilities.ValueToString(value)),
|
||||
metadata.Name ?? prop.Name
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return formData;
|
||||
}
|
||||
|
||||
private static HttpContent SerializeRaw(byte[] request, string mediaType)
|
||||
{
|
||||
var content = new ByteArrayContent(request);
|
||||
content.Headers.Add("Content-Type", mediaType);
|
||||
return content;
|
||||
}
|
||||
|
||||
private static HttpContent SerializeString(string request, string mediaType)
|
||||
{
|
||||
return new StringContent(request, Encoding.UTF8, mediaType);
|
||||
}
|
||||
|
||||
private static void SerializeFormValue(
|
||||
string fieldName,
|
||||
bool explode,
|
||||
object value,
|
||||
ref Dictionary<string, List<string>> form
|
||||
)
|
||||
{
|
||||
if (Utilities.IsClass(value))
|
||||
{
|
||||
if (Utilities.IsDate(value))
|
||||
{
|
||||
if (!form.ContainsKey(fieldName))
|
||||
{
|
||||
form[fieldName] = new List<string>();
|
||||
}
|
||||
|
||||
form[fieldName].Add(Utilities.ValueToString(value));
|
||||
}
|
||||
else
|
||||
{
|
||||
var props = value.GetType().GetProperties();
|
||||
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(value);
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetFormMetadata();
|
||||
if (metadata == null || metadata.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
if (!form.ContainsKey(metadata.Name))
|
||||
{
|
||||
form[metadata.Name] = new List<string>();
|
||||
}
|
||||
|
||||
if (Utilities.IsList(val))
|
||||
{
|
||||
foreach(var item in (IEnumerable)val)
|
||||
{
|
||||
form[metadata.Name].Add(Utilities.ValueToString(item));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
form[metadata.Name].Add(Utilities.ValueToString(val));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Utilities.IsList(val))
|
||||
{
|
||||
foreach(var item in (IEnumerable)val)
|
||||
{
|
||||
items.Add($"{metadata.Name},{Utilities.ValueToString(item)}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add($"{metadata.Name},{Utilities.ValueToString(val)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
if (!form.ContainsKey(fieldName))
|
||||
{
|
||||
form[fieldName] = new List<string>();
|
||||
}
|
||||
|
||||
form[fieldName].Add(string.Join(",", items));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsDictionary(value))
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var k in ((IDictionary)value).Keys)
|
||||
{
|
||||
var key = k?.ToString();
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
if (!form.ContainsKey(key))
|
||||
{
|
||||
form[key] = new List<string>();
|
||||
}
|
||||
|
||||
form[key].Add(
|
||||
Utilities.ValueToString(((IDictionary)value)[key])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add($"{key},{Utilities.ValueToString(((IDictionary)value)[key])}");
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
if (!form.ContainsKey(fieldName))
|
||||
{
|
||||
form[fieldName] = new List<string>();
|
||||
}
|
||||
|
||||
form[fieldName].Add(string.Join(",", items));
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsList(value))
|
||||
{
|
||||
var values = new List<string>();
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var item in (IList)value)
|
||||
{
|
||||
if (explode)
|
||||
{
|
||||
values.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
values.Add(string.Join(",", items));
|
||||
}
|
||||
|
||||
foreach (var val in values)
|
||||
{
|
||||
if (!form.ContainsKey(fieldName))
|
||||
{
|
||||
form[fieldName] = new List<string>();
|
||||
}
|
||||
|
||||
form[fieldName].Add(val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!form.ContainsKey(fieldName))
|
||||
{
|
||||
form[fieldName] = new List<string>();
|
||||
}
|
||||
|
||||
form[fieldName].Add(Utilities.ValueToString(value));
|
||||
}
|
||||
}
|
||||
|
||||
private static PropertyInfo? GetPropertyInfo(object value, string propertyName)
|
||||
{
|
||||
try
|
||||
{
|
||||
return value.GetType().GetProperty(propertyName);
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
122
LukeHagar/PlexAPI/SDK/Utils/ResponseBodyDeserializer.cs
Normal file
122
LukeHagar/PlexAPI/SDK/Utils/ResponseBodyDeserializer.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
|
||||
internal class ResponseBodyDeserializer
|
||||
{
|
||||
|
||||
public static T? Deserialize<T>(string json, NullValueHandling nullValueHandling=NullValueHandling.Ignore, MissingMemberHandling missingMemberHandling=MissingMemberHandling.Ignore)
|
||||
{
|
||||
return JsonConvert.DeserializeObject<T>(json, new JsonSerializerSettings(){ NullValueHandling = nullValueHandling, MissingMemberHandling = missingMemberHandling, Converters = Utilities.GetJsonDeserializers(typeof(T))});
|
||||
}
|
||||
|
||||
public sealed class MissingMemberException : Exception
|
||||
{
|
||||
public MissingMemberException() : base("Missing member.") { }
|
||||
}
|
||||
|
||||
public sealed class DeserializationException : Exception
|
||||
{
|
||||
public DeserializationException(Type type) : base($"Could not deserialize into {type} type.") { }
|
||||
}
|
||||
|
||||
public static T? DeserializeUndiscriminatedUnionMember<T>(string json)
|
||||
{
|
||||
try
|
||||
{
|
||||
return Deserialize<T>(json, missingMemberHandling: MissingMemberHandling.Error);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex is Newtonsoft.Json.JsonSerializationException &&
|
||||
ex.Source == "Newtonsoft.Json" &&
|
||||
ex.Message.Contains("Could not find member"))
|
||||
{
|
||||
throw new MissingMemberException();
|
||||
}
|
||||
else if (ex is Newtonsoft.Json.JsonReaderException ||
|
||||
ex is Newtonsoft.Json.JsonSerializationException
|
||||
)
|
||||
{
|
||||
throw new DeserializationException(typeof(T));
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public static object DeserializeUndiscriminatedUnionFallback(System.Type type, object obj, string propertyName, string json)
|
||||
{
|
||||
MethodInfo? method = typeof(ResponseBodyDeserializer).GetMethod("Deserialize");
|
||||
if (method != null)
|
||||
{
|
||||
MethodInfo generic = method!.MakeGenericMethod(type);
|
||||
var args = new object[] { json, NullValueHandling.Ignore, MissingMemberHandling.Ignore };
|
||||
var value = generic.Invoke(null, args);
|
||||
PropertyInfo? propertyInfo = obj.GetType().GetProperty(propertyName!);
|
||||
if (propertyInfo != null && value != null)
|
||||
{
|
||||
propertyInfo!.SetValue(obj, value!);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
throw new DeserializationException(type);
|
||||
}
|
||||
|
||||
public static int MissingJsonProperties(System.Type type, string json)
|
||||
{
|
||||
int missing = 0;
|
||||
|
||||
JObject jo = JObject.Parse(json);
|
||||
|
||||
var jsonPropertyAttributes = type.GetProperties()
|
||||
.Where(prop => Attribute.IsDefined(prop, typeof(JsonPropertyAttribute)))
|
||||
.Select(prop => prop.GetCustomAttribute(typeof(JsonPropertyAttribute)) as JsonPropertyAttribute)
|
||||
.Where(attr => attr != null && attr!.PropertyName != null)
|
||||
.ToList();
|
||||
|
||||
foreach (var attr in jsonPropertyAttributes)
|
||||
{
|
||||
string propertyName = attr!.PropertyName!;
|
||||
if (!jo.TryGetValue(propertyName, out var _value)){
|
||||
missing++;
|
||||
}
|
||||
}
|
||||
|
||||
return missing;
|
||||
}
|
||||
|
||||
public static int CompareFallbackCandidates(System.Type typeA, System.Type typeB, string json)
|
||||
{
|
||||
var missingA = MissingJsonProperties(typeA, json);
|
||||
var missingB = MissingJsonProperties(typeB, json);
|
||||
|
||||
if (missingA == missingB)
|
||||
{
|
||||
return typeB.GetProperties().Count().CompareTo(typeA.GetProperties().Count());
|
||||
}
|
||||
|
||||
return missingA.CompareTo(missingB);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
47
LukeHagar/PlexAPI/SDK/Utils/Retries/BackoffStrategy.cs
Normal file
47
LukeHagar/PlexAPI/SDK/Utils/Retries/BackoffStrategy.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
public class BackoffStrategy
|
||||
{
|
||||
public readonly long InitialIntervalMs;
|
||||
public readonly long MaxIntervalMs;
|
||||
public readonly long MaxElapsedTimeMs;
|
||||
public readonly double BaseFactor;
|
||||
public readonly double JitterFactor;
|
||||
|
||||
/// <summary>
|
||||
/// Configures the exponential backoff strategy.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The duration between consecutive attempts is calculated as follows:
|
||||
/// intervalMs = min(maxIntervalMs, initialIntervalMs*(baseFactor^attempts) +/- r)
|
||||
/// where baseFactor (also referred to as "exponent") is the multiplicative factor
|
||||
/// and r a random value between 0 and jitterFactor*intervalMs.
|
||||
/// </remarks>
|
||||
/// <param name="initialIntervalMs">The initial interval in milliseconds.</param>
|
||||
/// <param name="maxIntervalMs">The maximum interval in milliseconds.</param>
|
||||
/// <param name="maxElapsedTimeMs">The maximum elapsed time in milliseconds.</param>
|
||||
/// <param name="exponent">The base factor used to compute the exponential interval</param>
|
||||
/// <param name="jitterFactor">The jitter factor used to randomize the backoff interval</param>
|
||||
public BackoffStrategy(long initialIntervalMs,
|
||||
long maxIntervalMs,
|
||||
long maxElapsedTimeMs,
|
||||
double exponent,
|
||||
double jitterFactor = 0.5)
|
||||
{
|
||||
InitialIntervalMs = initialIntervalMs;
|
||||
MaxIntervalMs = maxIntervalMs;
|
||||
MaxElapsedTimeMs = maxElapsedTimeMs;
|
||||
BaseFactor = exponent;
|
||||
JitterFactor = jitterFactor;
|
||||
}
|
||||
}
|
||||
}
|
||||
156
LukeHagar/PlexAPI/SDK/Utils/Retries/Retries.cs
Normal file
156
LukeHagar/PlexAPI/SDK/Utils/Retries/Retries.cs
Normal file
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Models.Errors;
|
||||
|
||||
public class Retries
|
||||
{
|
||||
private Func<Task<HttpResponseMessage>> action;
|
||||
private RetryConfig retryConfig;
|
||||
private List<string> statusCodes;
|
||||
|
||||
public Retries(Func<Task<HttpResponseMessage>> action, RetryConfig retryConfig, List<string> statusCodes)
|
||||
{
|
||||
this.action = action;
|
||||
this.retryConfig = retryConfig;
|
||||
this.statusCodes = statusCodes;
|
||||
|
||||
if (statusCodes.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("statusCodes list cannot be empty");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PermanentException : Exception
|
||||
{
|
||||
public PermanentException(Exception innerException) : base("NonRetryable error.", innerException) { }
|
||||
}
|
||||
|
||||
public sealed class RetryableException : Exception
|
||||
{
|
||||
public HttpResponseMessage? Response = null;
|
||||
|
||||
public RetryableException(HttpResponseMessage response) {
|
||||
Response = response;
|
||||
}
|
||||
|
||||
public RetryableException(Exception innerException) : base("An error occurred.", innerException) { }
|
||||
}
|
||||
|
||||
public async Task<HttpResponseMessage> Run()
|
||||
{
|
||||
switch(retryConfig.Strategy) {
|
||||
case RetryConfig.RetryStrategy.BACKOFF:
|
||||
return await retryWithBackoff(retryConfig.RetryConnectionErrors);
|
||||
|
||||
case RetryConfig.RetryStrategy.NONE:
|
||||
return await action();
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Invalid retry strategy");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> GetResponseAsync(bool retryConnectionErrors)
|
||||
{
|
||||
try
|
||||
{
|
||||
var response = await action();
|
||||
|
||||
foreach (var statusCode in statusCodes)
|
||||
{
|
||||
if (statusCode.ToUpper().Contains("X"))
|
||||
{
|
||||
var codeRange = int.Parse(statusCode.Substring(0, 1));
|
||||
var statusMajor = (int)response.StatusCode / 100;
|
||||
if (codeRange == statusMajor)
|
||||
{
|
||||
throw new RetryableException(response);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var code = int.Parse(statusCode);
|
||||
if (code == (int)response.StatusCode)
|
||||
{
|
||||
throw new RetryableException(response);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
catch (RetryableException)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if ((ex is HttpRequestException || ex is TaskCanceledException) && retryConfig.RetryConnectionErrors)
|
||||
{
|
||||
throw new RetryableException(ex);
|
||||
}
|
||||
throw new PermanentException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<HttpResponseMessage> retryWithBackoff(bool retryConnectionErrors)
|
||||
{
|
||||
var backoff = retryConfig.Backoff;
|
||||
if(backoff == null){
|
||||
throw new ArgumentException("Backoff strategy is not defined");
|
||||
}
|
||||
|
||||
var startMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
var numAttempts = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await GetResponseAsync(retryConnectionErrors);
|
||||
}
|
||||
catch (PermanentException ex)
|
||||
{
|
||||
throw ex.InnerException!;
|
||||
}
|
||||
catch (RetryableException ex)
|
||||
{
|
||||
var nowMs = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if (nowMs - startMs > backoff.MaxElapsedTimeMs)
|
||||
{
|
||||
if(ex.Response != null)
|
||||
{
|
||||
return ex.Response;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
|
||||
var intervalMs = backoff.InitialIntervalMs * Math.Pow(backoff.BaseFactor, numAttempts);
|
||||
var jitterMs = backoff.JitterFactor * intervalMs;
|
||||
intervalMs = intervalMs - jitterMs + new Random().NextDouble() * (2 * jitterMs + 1);
|
||||
intervalMs = Math.Min(intervalMs, backoff.MaxIntervalMs);
|
||||
|
||||
await Task.Delay((int)intervalMs);
|
||||
numAttempts += 1;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
throw new Exception("Unexpected error occurred.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
46
LukeHagar/PlexAPI/SDK/Utils/Retries/RetryConfig.cs
Normal file
46
LukeHagar/PlexAPI/SDK/Utils/Retries/RetryConfig.cs
Normal file
@@ -0,0 +1,46 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils.Retries
|
||||
{
|
||||
public class RetryConfig
|
||||
{
|
||||
public enum RetryStrategy
|
||||
{
|
||||
BACKOFF,
|
||||
NONE
|
||||
}
|
||||
public readonly RetryStrategy Strategy;
|
||||
public readonly BackoffStrategy? Backoff;
|
||||
public readonly bool RetryConnectionErrors;
|
||||
|
||||
/// <summary>
|
||||
/// Selects the retry strategy.
|
||||
/// </summary>
|
||||
/// <param name="strategy">The retry strategy.</param>
|
||||
/// <param name="backoff">The backoff strategy configuration (if applicable)</param>
|
||||
/// <param name="retryConnectionErrors">Whether to retry on connection errors.</param>
|
||||
/// <remarks>
|
||||
/// The backoff strategy is only required if the retry strategy is set to BACKOFF.
|
||||
/// To disable retries, set the strategy to NONE.
|
||||
/// </remarks>
|
||||
public RetryConfig(RetryStrategy strategy, BackoffStrategy? backoff = null, bool retryConnectionErrors = false)
|
||||
{
|
||||
|
||||
if (strategy == RetryStrategy.BACKOFF && backoff == null)
|
||||
{
|
||||
throw new System.ArgumentNullException("Backoff strategy configuration was not provided");
|
||||
}
|
||||
|
||||
Strategy = strategy;
|
||||
Backoff = backoff;
|
||||
RetryConnectionErrors = retryConnectionErrors;
|
||||
}
|
||||
}
|
||||
}
|
||||
240
LukeHagar/PlexAPI/SDK/Utils/SecurityMetadata.cs
Normal file
240
LukeHagar/PlexAPI/SDK/Utils/SecurityMetadata.cs
Normal file
@@ -0,0 +1,240 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Web;
|
||||
|
||||
|
||||
internal class SecurityMetadata
|
||||
{
|
||||
private Dictionary<string, string> headerParams { get; } = new Dictionary<string, string>();
|
||||
private Dictionary<string, string> queryParams { get; } = new Dictionary<string, string>();
|
||||
|
||||
public SecurityMetadata(Func<object> securitySource)
|
||||
{
|
||||
ParseSecuritySource(securitySource);
|
||||
}
|
||||
|
||||
public HttpRequestMessage Apply(HttpRequestMessage request)
|
||||
{
|
||||
foreach (var kvp in headerParams)
|
||||
{
|
||||
request.Headers.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
if(request.RequestUri != null)
|
||||
{
|
||||
var uriBuilder = new UriBuilder(request.RequestUri);
|
||||
var query = HttpUtility.ParseQueryString(uriBuilder.Query);
|
||||
foreach (var kvp in queryParams)
|
||||
{
|
||||
query.Add(kvp.Key, kvp.Value);
|
||||
}
|
||||
uriBuilder.Query = query.ToString();
|
||||
request.RequestUri = uriBuilder.Uri;
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
private void ParseSecuritySource(Func<object> securitySource)
|
||||
{
|
||||
if (securitySource == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var security = securitySource();
|
||||
if (security == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var prop in security.GetType().GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(security, null);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var secMetadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetSecurityMetadata();
|
||||
if (secMetadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (secMetadata.Option)
|
||||
{
|
||||
ParseOption(value);
|
||||
}
|
||||
else if (secMetadata.Scheme)
|
||||
{
|
||||
if (secMetadata.SubType == "basic" && !Utilities.IsClass(value))
|
||||
{
|
||||
ParseScheme(secMetadata, security);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseScheme(secMetadata, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
private void ParseOption(object option)
|
||||
{
|
||||
foreach (var prop in option.GetType().GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(option, null);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var secMetadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetSecurityMetadata();
|
||||
if (secMetadata == null || !secMetadata.Scheme)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ParseScheme(secMetadata, value);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseScheme(SpeakeasyMetadata.SecurityMetadata schemeMetadata, object scheme)
|
||||
{
|
||||
if (Utilities.IsClass(scheme))
|
||||
{
|
||||
if (schemeMetadata.Type == "http" && schemeMetadata.SubType == "basic")
|
||||
{
|
||||
ParseBasicAuthScheme(scheme);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var prop in scheme.GetType().GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(scheme, null);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var secMetadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetSecurityMetadata();
|
||||
if (secMetadata == null || secMetadata.Name == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ParseSchemeValue(schemeMetadata, secMetadata, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseSchemeValue(schemeMetadata, schemeMetadata, scheme);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseSchemeValue(
|
||||
SpeakeasyMetadata.SecurityMetadata schemeMetadata,
|
||||
SpeakeasyMetadata.SecurityMetadata valueMetadata,
|
||||
object value
|
||||
)
|
||||
{
|
||||
var key = valueMetadata.Name;
|
||||
if (key == "")
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var valStr = Utilities.ValueToString(value);
|
||||
|
||||
switch (schemeMetadata.Type)
|
||||
{
|
||||
case "apiKey":
|
||||
switch (schemeMetadata.SubType)
|
||||
{
|
||||
case "header":
|
||||
headerParams.Add(key, valStr);
|
||||
break;
|
||||
case "query":
|
||||
queryParams.Add(key, valStr);
|
||||
break;
|
||||
case "cookie":
|
||||
headerParams.Add("cookie", $"{key}={valStr}");
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown apiKey subType: {schemeMetadata.SubType}");
|
||||
}
|
||||
break;
|
||||
case "openIdConnect":
|
||||
headerParams.Add(key, Utilities.PrefixBearer(valStr));
|
||||
break;
|
||||
case "oauth2":
|
||||
headerParams.Add(key, Utilities.PrefixBearer(valStr));
|
||||
break;
|
||||
case "http":
|
||||
switch (schemeMetadata.SubType)
|
||||
{
|
||||
case "bearer":
|
||||
headerParams.Add(key, Utilities.PrefixBearer(valStr));
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown http subType: {schemeMetadata.SubType}");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unknown security type: {schemeMetadata.Type}");
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseBasicAuthScheme(object scheme)
|
||||
{
|
||||
|
||||
string username = "";
|
||||
string password = "";
|
||||
|
||||
foreach (var prop in scheme.GetType().GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(scheme, null);
|
||||
if (value == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var secMetadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetSecurityMetadata();
|
||||
if (secMetadata == null || secMetadata.Name == "")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (secMetadata.Name == "username")
|
||||
{
|
||||
username = Utilities.ValueToString(value);
|
||||
}
|
||||
else if (secMetadata.Name == "password")
|
||||
{
|
||||
password = Utilities.ValueToString(value);
|
||||
}
|
||||
}
|
||||
|
||||
var auth = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{username}:{password}"));
|
||||
headerParams.Add("Authorization", $"Basic {auth}");
|
||||
}
|
||||
}
|
||||
}
|
||||
81
LukeHagar/PlexAPI/SDK/Utils/SpeakeasyHttpClient.cs
Normal file
81
LukeHagar/PlexAPI/SDK/Utils/SpeakeasyHttpClient.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
public interface ISpeakeasyHttpClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Sends an HTTP request asynchronously.
|
||||
/// </summary>
|
||||
/// <param name="request">The HTTP request message to send.</param>
|
||||
/// <returns>The value of the TResult parameter contains the HTTP response message.</returns>
|
||||
Task<HttpResponseMessage> SendAsync(HttpRequestMessage request);
|
||||
|
||||
/// <summary>
|
||||
/// Clones an HTTP request asynchronously.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is used in the context of Retries. It creates a new HttpRequestMessage instance
|
||||
/// as a deep copy of the original request, including its method, URI, content, headers and options.
|
||||
/// </remarks>
|
||||
/// <param name="request">The HTTP request message to clone.</param>
|
||||
/// <returns>The value of the TResult parameter contains the cloned HTTP request message.</returns>
|
||||
Task<HttpRequestMessage> CloneAsync(HttpRequestMessage request);
|
||||
}
|
||||
|
||||
public class SpeakeasyHttpClient : ISpeakeasyHttpClient
|
||||
{
|
||||
protected readonly HttpClient httpClient;
|
||||
|
||||
public SpeakeasyHttpClient()
|
||||
{
|
||||
httpClient = new System.Net.Http.HttpClient();
|
||||
}
|
||||
|
||||
public virtual async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request)
|
||||
{
|
||||
return await httpClient.SendAsync(request);
|
||||
}
|
||||
|
||||
public virtual async Task<HttpRequestMessage> CloneAsync(HttpRequestMessage request)
|
||||
{
|
||||
HttpRequestMessage clone = new HttpRequestMessage(request.Method, request.RequestUri);
|
||||
|
||||
if (request.Content != null)
|
||||
{
|
||||
clone.Content = new ByteArrayContent(await request.Content.ReadAsByteArrayAsync());
|
||||
if (request.Content.Headers != null)
|
||||
{
|
||||
foreach (var h in request.Content.Headers)
|
||||
{
|
||||
clone.Content.Headers.Add(h.Key, h.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, IEnumerable<string>> header in request.Headers)
|
||||
{
|
||||
clone.Headers.TryAddWithoutValidation(header.Key, header.Value);
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, object?> prop in request.Options)
|
||||
{
|
||||
clone.Options.TryAdd(prop.Key, prop.Value);
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
}
|
||||
}
|
||||
242
LukeHagar/PlexAPI/SDK/Utils/SpeakeasyMetadata.cs
Normal file
242
LukeHagar/PlexAPI/SDK/Utils/SpeakeasyMetadata.cs
Normal file
@@ -0,0 +1,242 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
|
||||
internal class SpeakeasyMetadata : Attribute
|
||||
{
|
||||
internal class RequestMetadata
|
||||
{
|
||||
public string? MediaType { get; set; } = null;
|
||||
}
|
||||
|
||||
internal class FormMetadata
|
||||
{
|
||||
public string Style { get; set; } = "form";
|
||||
public bool Explode { get; set; } = true;
|
||||
public bool Json { get; set; } = false;
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
internal class MultipartFormMetadata
|
||||
{
|
||||
public bool File { get; set; } = false;
|
||||
public bool Content { get; set; } = false;
|
||||
public bool Json { get; set; } = false;
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
internal class PathParamMetadata
|
||||
{
|
||||
public string Style { get; set; } = "simple";
|
||||
public bool Explode { get; set; } = false;
|
||||
public string Name { get; set; } = "";
|
||||
public string? Serialization { get; set; } = null;
|
||||
}
|
||||
|
||||
internal class QueryParamMetadata
|
||||
{
|
||||
public string Style { get; set; } = "form";
|
||||
public bool Explode { get; set; } = true;
|
||||
public string Name { get; set; } = "";
|
||||
public string? Serialization { get; set; } = null;
|
||||
}
|
||||
|
||||
internal class HeaderMetadata
|
||||
{
|
||||
public string Style { get; set; } = "simple";
|
||||
public bool Explode { get; set; } = false;
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
internal class SecurityMetadata
|
||||
{
|
||||
public string? Type { get; set; } = null;
|
||||
public string? SubType { get; set; } = null;
|
||||
public bool Option { get; set; } = false;
|
||||
public bool Scheme { get; set; } = false;
|
||||
public string Name { get; set; } = "";
|
||||
}
|
||||
|
||||
public string Value { get; set; }
|
||||
private Dictionary<string, string>? metadata;
|
||||
|
||||
public SpeakeasyMetadata(string value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public RequestMetadata? GetRequestMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("request", out var value))
|
||||
{
|
||||
var metadata = new RequestMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public FormMetadata? GetFormMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("form", out var value))
|
||||
{
|
||||
var metadata = new FormMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public MultipartFormMetadata? GetMultipartFormMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("multipartForm", out var value))
|
||||
{
|
||||
var metadata = new MultipartFormMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public PathParamMetadata? GetPathParamMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("pathParam", out var value))
|
||||
{
|
||||
var metadata = new PathParamMetadata();
|
||||
ParseMetadata(value, ref metadata);
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public QueryParamMetadata? GetQueryParamMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("queryParam", out var value))
|
||||
{
|
||||
var metadata = new QueryParamMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public HeaderMetadata? GetHeaderMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("header", out var value))
|
||||
{
|
||||
var metadata = new HeaderMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public SecurityMetadata? GetSecurityMetadata()
|
||||
{
|
||||
if (GetMetadata().TryGetValue("security", out var value))
|
||||
{
|
||||
var metadata = new SecurityMetadata();
|
||||
|
||||
ParseMetadata(value, ref metadata);
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Dictionary<string, string> GetMetadata()
|
||||
{
|
||||
if (metadata != null)
|
||||
{
|
||||
return metadata;
|
||||
}
|
||||
|
||||
metadata = new Dictionary<string, string>();
|
||||
|
||||
var groups = Value.Split(" ");
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var parts = group.Split(":");
|
||||
|
||||
if (parts.Length != 2)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
metadata.Add(parts[0], parts[1]);
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private void ParseMetadata<T>(string raw, ref T metadata)
|
||||
{
|
||||
Dictionary<string, string> values = new Dictionary<string, string>();
|
||||
|
||||
var groups = raw.Split(",");
|
||||
|
||||
foreach (var group in groups)
|
||||
{
|
||||
var parts = group.Split("=");
|
||||
var val = "";
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
val = parts[1];
|
||||
}
|
||||
|
||||
values.Add(parts[0], val);
|
||||
}
|
||||
|
||||
var props = typeof(T).GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
if (
|
||||
values.TryGetValue(
|
||||
char.ToLower(prop.Name[0]) + prop.Name.Substring(1),
|
||||
out var value
|
||||
)
|
||||
)
|
||||
{
|
||||
if (prop.PropertyType == typeof(bool) || prop.PropertyType == typeof(Boolean))
|
||||
{
|
||||
prop.SetValue(metadata, value == "true" || value == "");
|
||||
}
|
||||
else
|
||||
{
|
||||
prop.SetValue(metadata, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
597
LukeHagar/PlexAPI/SDK/Utils/URLBuilder.cs
Normal file
597
LukeHagar/PlexAPI/SDK/Utils/URLBuilder.cs
Normal file
@@ -0,0 +1,597 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
|
||||
internal static class URLBuilder
|
||||
{
|
||||
public static string Build(string baseUrl, string relativeUrl, object? request)
|
||||
{
|
||||
var url = baseUrl;
|
||||
|
||||
if (url.EndsWith("/"))
|
||||
{
|
||||
url = url.Substring(0, url.Length - 1);
|
||||
}
|
||||
|
||||
var pathAndFragment = relativeUrl.Split('#');
|
||||
if (pathAndFragment.Length > 2)
|
||||
{
|
||||
throw new ArgumentException($"Malformed URL: {relativeUrl}");
|
||||
}
|
||||
|
||||
url += pathAndFragment[0];
|
||||
|
||||
var parameters = GetPathParameters(request);
|
||||
url = ReplaceParameters(url, parameters);
|
||||
|
||||
var queryParams = SerializeQueryParams(TrySerializeQueryParams(request));
|
||||
if (queryParams != "")
|
||||
{
|
||||
url += $"?{queryParams}";
|
||||
}
|
||||
|
||||
if (pathAndFragment.Length == 2)
|
||||
{
|
||||
url += $"#{pathAndFragment[1]}";
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public static string ReplaceParameters(string url, Dictionary<string, string> parameters)
|
||||
{
|
||||
foreach (var key in parameters.Keys)
|
||||
{
|
||||
url = url.Replace($"{{{key}}}", parameters[key]);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
public static string SerializeQueryParams(Dictionary<string, List<string>> queryParams) {
|
||||
var queries = new List<string>();
|
||||
|
||||
foreach (var key in queryParams.Keys)
|
||||
{
|
||||
foreach (var value in queryParams[key])
|
||||
{
|
||||
queries.Add($"{key}={value}");
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("&", queries);
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> GetPathParameters(object? request)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
return parameters;
|
||||
}
|
||||
|
||||
var props = request.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(request);
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetRequestMetadata() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetPathParamMetadata();
|
||||
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (metadata.Serialization != null)
|
||||
{
|
||||
switch (metadata.Serialization)
|
||||
{
|
||||
case "json":
|
||||
parameters.Add(
|
||||
metadata.Name ?? prop.Name,
|
||||
WebUtility.UrlEncode(Utilities.SerializeJSON(val))
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
$"Unknown serialization type: {metadata.Serialization}"
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (metadata.Style)
|
||||
{
|
||||
case "simple":
|
||||
var simpleParams = SerializeSimplePathParams(
|
||||
metadata.Name ?? prop.Name,
|
||||
val,
|
||||
metadata.Explode
|
||||
);
|
||||
foreach (var key in simpleParams.Keys)
|
||||
{
|
||||
parameters.Add(key, simpleParams[key]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unsupported path param style: {metadata.Style}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> TrySerializeQueryParams(object? request)
|
||||
{
|
||||
var parameters = new Dictionary<string, List<string>>();
|
||||
|
||||
if (request == null)
|
||||
{
|
||||
return parameters;
|
||||
}
|
||||
|
||||
var props = request.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(request);
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetRequestMetadata() != null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetQueryParamMetadata();
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (metadata.Serialization != null)
|
||||
{
|
||||
switch (metadata.Serialization)
|
||||
{
|
||||
case "json":
|
||||
if (!parameters.ContainsKey(metadata.Name ?? prop.Name))
|
||||
{
|
||||
parameters.Add(metadata.Name ?? prop.Name, new List<string>());
|
||||
}
|
||||
|
||||
parameters[metadata.Name ?? prop.Name].Add(
|
||||
Utilities.SerializeJSON(val)
|
||||
);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
$"Unknown serialization type: {metadata.Serialization}"
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (metadata.Style)
|
||||
{
|
||||
case "form":
|
||||
var formParams = SerializeFormQueryParams(
|
||||
metadata.Name ?? prop.Name,
|
||||
val,
|
||||
metadata.Explode,
|
||||
","
|
||||
);
|
||||
foreach (var key in formParams.Keys)
|
||||
{
|
||||
if (!parameters.ContainsKey(key))
|
||||
{
|
||||
parameters.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
foreach (var v in formParams[key])
|
||||
{
|
||||
parameters[key].Add(v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "deepObject":
|
||||
var deepObjParams = SerializeDeepObjectQueryParams(
|
||||
metadata.Name ?? prop.Name,
|
||||
val
|
||||
);
|
||||
foreach (var key in deepObjParams.Keys)
|
||||
{
|
||||
if (!parameters.ContainsKey(key))
|
||||
{
|
||||
parameters.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
foreach (var v in deepObjParams[key])
|
||||
{
|
||||
parameters[key].Add(v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "pipeDelimited":
|
||||
var pipeParams = SerializeFormQueryParams(
|
||||
metadata.Name ?? prop.Name,
|
||||
val,
|
||||
metadata.Explode,
|
||||
"|"
|
||||
);
|
||||
foreach (var key in pipeParams.Keys)
|
||||
{
|
||||
if (!parameters.ContainsKey(key))
|
||||
{
|
||||
parameters.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
foreach (var v in pipeParams[key])
|
||||
{
|
||||
parameters[key].Add(v);
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception($"Unsupported query param style: {metadata.Style}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> SerializeSimplePathParams(
|
||||
string parentName,
|
||||
object value,
|
||||
bool explode
|
||||
)
|
||||
{
|
||||
var parameters = new Dictionary<string, string>();
|
||||
|
||||
if (Utilities.IsClass(value))
|
||||
{
|
||||
var vals = new List<string>();
|
||||
|
||||
var props = value.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(value);
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetPathParamMetadata();
|
||||
if (metadata == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
vals.Add($"{metadata.Name}={Utilities.ToString(val)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
vals.Add($"{metadata.Name},{Utilities.ToString(val)}");
|
||||
}
|
||||
}
|
||||
|
||||
parameters.Add(parentName, string.Join(",", vals));
|
||||
}
|
||||
else if (Utilities.IsDictionary(value))
|
||||
{
|
||||
var vals = new List<string>();
|
||||
|
||||
foreach (var key in ((IDictionary)value).Keys)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var val = ((IDictionary)value)[key];
|
||||
|
||||
if (explode)
|
||||
{
|
||||
vals.Add($"{key}={Utilities.ToString(val)}");
|
||||
}
|
||||
else
|
||||
{
|
||||
vals.Add($"{key},{Utilities.ToString(val)}");
|
||||
}
|
||||
}
|
||||
|
||||
parameters.Add(parentName, string.Join(",", vals));
|
||||
}
|
||||
else if (Utilities.IsList(value))
|
||||
{
|
||||
var vals = new List<string>();
|
||||
|
||||
foreach (var val in (IEnumerable)value)
|
||||
{
|
||||
vals.Add(Utilities.ToString(val));
|
||||
}
|
||||
|
||||
parameters.Add(parentName, string.Join(",", vals));
|
||||
}
|
||||
else
|
||||
{
|
||||
parameters.Add(parentName, Utilities.ToString(value));
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> SerializeFormQueryParams(
|
||||
string parentName,
|
||||
object value,
|
||||
bool explode,
|
||||
string delimiter
|
||||
)
|
||||
{
|
||||
var parameters = new Dictionary<string, List<string>>();
|
||||
|
||||
if (Utilities.IsClass(value) && !Utilities.IsDate(value))
|
||||
{
|
||||
var props = value.GetType().GetProperties();
|
||||
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(value);
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetQueryParamMetadata();
|
||||
if (metadata == null || metadata.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
if (!parameters.ContainsKey(metadata.Name))
|
||||
{
|
||||
parameters.Add(metadata.Name, new List<string>());
|
||||
}
|
||||
|
||||
parameters[metadata.Name].Add(
|
||||
Utilities.ToString(val)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(
|
||||
$"{metadata.Name}{delimiter}{Utilities.ValueToString(val)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
if (!parameters.ContainsKey(parentName))
|
||||
{
|
||||
parameters.Add(parentName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[parentName].Add(string.Join(delimiter, items));
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsDictionary(value))
|
||||
{
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var k in ((IDictionary)value).Keys)
|
||||
{
|
||||
var key = k?.ToString();
|
||||
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
if (!parameters.ContainsKey(key))
|
||||
{
|
||||
parameters.Add(key, new List<string>());
|
||||
}
|
||||
|
||||
parameters[key].Add(
|
||||
Utilities.ValueToString(((IDictionary)value)[key])
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(
|
||||
$"{key}{delimiter}{Utilities.ValueToString(((IDictionary)value)[key])}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
if (!parameters.ContainsKey(parentName))
|
||||
{
|
||||
parameters.Add(parentName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[parentName].Add(string.Join(delimiter, items));
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsList(value))
|
||||
{
|
||||
var values = new List<string>();
|
||||
var items = new List<string>();
|
||||
|
||||
foreach (var item in (IList)value)
|
||||
{
|
||||
if (explode)
|
||||
{
|
||||
values.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
else
|
||||
{
|
||||
items.Add(Utilities.ValueToString(item));
|
||||
}
|
||||
}
|
||||
|
||||
if (items.Count > 0)
|
||||
{
|
||||
values.Add(string.Join(delimiter, items));
|
||||
}
|
||||
|
||||
foreach (var val in values)
|
||||
{
|
||||
if (!parameters.ContainsKey(parentName))
|
||||
{
|
||||
parameters.Add(parentName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[parentName].Add(val);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.ContainsKey(parentName))
|
||||
{
|
||||
parameters.Add(parentName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[parentName].Add(Utilities.ValueToString(value));
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
private static Dictionary<string, List<string>> SerializeDeepObjectQueryParams(
|
||||
string parentName,
|
||||
object value
|
||||
)
|
||||
{
|
||||
var parameters = new Dictionary<string, List<string>>();
|
||||
|
||||
if (Utilities.IsClass(value))
|
||||
{
|
||||
var props = value.GetType().GetProperties();
|
||||
|
||||
foreach (var prop in props)
|
||||
{
|
||||
var val = prop.GetValue(value);
|
||||
|
||||
if (val == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var metadata = prop.GetCustomAttribute<SpeakeasyMetadata>()?.GetQueryParamMetadata();
|
||||
if (metadata == null || metadata.Name == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var keyName = $"{parentName}[{metadata.Name}]";
|
||||
|
||||
if (val != null && Utilities.IsList(val))
|
||||
{
|
||||
foreach (var v in (IList)val)
|
||||
{
|
||||
if (!parameters.ContainsKey(keyName))
|
||||
{
|
||||
parameters.Add(keyName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[keyName].Add(
|
||||
Utilities.ValueToString(v)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.ContainsKey(keyName))
|
||||
{
|
||||
parameters.Add(keyName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[keyName].Add(Utilities.ValueToString(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Utilities.IsDictionary(value))
|
||||
{
|
||||
foreach (var key in ((IDictionary)value).Keys)
|
||||
{
|
||||
if (key == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var val = ((IDictionary)value)[key];
|
||||
|
||||
var keyName = $"{parentName}[{key}]";
|
||||
|
||||
if (val != null && Utilities.IsList(val))
|
||||
{
|
||||
foreach (var v in (IList)val)
|
||||
{
|
||||
if (!parameters.ContainsKey(keyName))
|
||||
{
|
||||
parameters.Add(keyName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[keyName].Add(
|
||||
Utilities.ValueToString(v)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!parameters.ContainsKey(keyName))
|
||||
{
|
||||
parameters.Add(keyName, new List<string>());
|
||||
}
|
||||
|
||||
parameters[keyName].Add(Utilities.ValueToString(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
}
|
||||
}
|
||||
323
LukeHagar/PlexAPI/SDK/Utils/Utilities.cs
Normal file
323
LukeHagar/PlexAPI/SDK/Utils/Utilities.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by Speakeasy (https://speakeasy.com). DO NOT EDIT.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost when
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
#nullable enable
|
||||
namespace LukeHagar.PlexAPI.SDK.Utils
|
||||
{
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Numerics;
|
||||
using Newtonsoft.Json;
|
||||
using NodaTime;
|
||||
using System.Collections;
|
||||
|
||||
public class Utilities
|
||||
{
|
||||
public static JsonConverter[] GetDefaultJsonSerializers()
|
||||
{
|
||||
return new JsonConverter[]
|
||||
{
|
||||
new IsoDateTimeSerializer(),
|
||||
new EnumConverter()
|
||||
};
|
||||
}
|
||||
|
||||
public static JsonConverter[] GetDefaultJsonDeserializers()
|
||||
{
|
||||
return new JsonConverter[] {
|
||||
new FlexibleObjectDeserializer(),
|
||||
new EnumConverter(),
|
||||
new AnyDeserializer()
|
||||
};
|
||||
}
|
||||
|
||||
public static JsonSerializerSettings GetDefaultJsonSerializerSettings()
|
||||
{
|
||||
return new JsonSerializerSettings()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Converters = GetDefaultJsonSerializers()
|
||||
};
|
||||
}
|
||||
|
||||
public static JsonSerializerSettings GetDefaultJsonDeserializerSettings()
|
||||
{
|
||||
return new JsonSerializerSettings()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Converters = GetDefaultJsonDeserializers()
|
||||
};
|
||||
}
|
||||
|
||||
public static JsonConverter[] GetJsonSerializers(Type type, string format = "")
|
||||
{
|
||||
if (format == "string")
|
||||
{
|
||||
if (type == typeof(BigInteger))
|
||||
{
|
||||
return new JsonConverter[] { new BigIntStrConverter() };
|
||||
}
|
||||
|
||||
if (type == typeof(Decimal))
|
||||
{
|
||||
return new JsonConverter[] { new DecimalStrConverter() };
|
||||
}
|
||||
}
|
||||
|
||||
return GetDefaultJsonSerializers();
|
||||
}
|
||||
|
||||
public static JsonConverter[] GetJsonDeserializers(Type type)
|
||||
{
|
||||
if (type == typeof(BigInteger))
|
||||
{
|
||||
return new JsonConverter[] { new BigIntStrConverter() };
|
||||
}
|
||||
|
||||
if (type == typeof(Decimal))
|
||||
{
|
||||
return new JsonConverter[] { new DecimalStrConverter() };
|
||||
}
|
||||
|
||||
return GetDefaultJsonDeserializers();
|
||||
}
|
||||
|
||||
public static string SerializeJSON(object obj, string format = "")
|
||||
{
|
||||
var type = obj.GetType();
|
||||
if (IsList(obj))
|
||||
{
|
||||
type = type.GetGenericArguments().Single();
|
||||
}
|
||||
else if (IsDictionary(obj))
|
||||
{
|
||||
type = type.GetGenericArguments().Last();
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(
|
||||
obj,
|
||||
new JsonSerializerSettings()
|
||||
{
|
||||
NullValueHandling = NullValueHandling.Ignore,
|
||||
Converters = GetJsonSerializers(type, format)
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public static bool IsDictionary(object? o)
|
||||
{
|
||||
if (o == null)
|
||||
return false;
|
||||
return o is IDictionary
|
||||
&& o.GetType().IsGenericType
|
||||
&& o.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(Dictionary<,>));
|
||||
}
|
||||
|
||||
public static bool IsList(object? o)
|
||||
{
|
||||
if (o == null)
|
||||
return false;
|
||||
return o is IList
|
||||
&& o.GetType().IsGenericType
|
||||
&& o.GetType().GetGenericTypeDefinition().IsAssignableFrom(typeof(List<>));
|
||||
}
|
||||
|
||||
public static bool IsClass(object? o)
|
||||
{
|
||||
if (o == null)
|
||||
return false;
|
||||
return o.GetType().IsClass && (o.GetType().FullName ?? "").StartsWith("LukeHagar.PlexAPI.SDK.Models");
|
||||
}
|
||||
|
||||
// TODO: code review polyfilled for IsAssignableTo
|
||||
public static bool IsSameOrSubclass(Type potentialBase, Type potentialDescendant)
|
||||
{
|
||||
return potentialDescendant.IsSubclassOf(potentialBase)
|
||||
|| potentialDescendant == potentialBase;
|
||||
}
|
||||
|
||||
public static bool IsString(object? obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
var type = obj.GetType();
|
||||
return IsSameOrSubclass(type, typeof(string));
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsPrimitive(object? obj) => obj != null && obj.GetType().IsPrimitive;
|
||||
|
||||
public static bool IsEnum(object? obj) => obj != null && obj.GetType().IsEnum;
|
||||
|
||||
public static bool IsDate(object? obj) =>
|
||||
obj != null && (obj.GetType() == typeof(DateTime) || obj.GetType() == typeof(LocalDate));
|
||||
|
||||
private static string StripSurroundingQuotes(string input)
|
||||
{
|
||||
Regex surroundingQuotesRegex = new Regex("^\"(.*)\"$");
|
||||
var match = surroundingQuotesRegex.Match(input);
|
||||
if(match.Groups.Values.Count() == 2)
|
||||
{
|
||||
return match.Groups.Values.Last().ToString();
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
public static string ValueToString(object? value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (value.GetType() == typeof(DateTime))
|
||||
{
|
||||
return ((DateTime)value)
|
||||
.ToUniversalTime()
|
||||
.ToString("o", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
else if (value.GetType() == typeof(LocalDate))
|
||||
{
|
||||
return ((LocalDate)value)
|
||||
.ToDateTimeUnspecified()
|
||||
.ToString("yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture);
|
||||
}
|
||||
else if (value.GetType() == typeof(bool))
|
||||
{
|
||||
return (bool)value ? "true" : "false";
|
||||
}
|
||||
else if (IsEnum(value))
|
||||
{
|
||||
var method = Type.GetType(value.GetType().FullName + "Extension")
|
||||
?.GetMethod("Value");
|
||||
if (method == null)
|
||||
{
|
||||
return Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()))?.ToString() ?? "";
|
||||
}
|
||||
return (string)(method.Invoke(null, new[] { value }) ?? "");
|
||||
}
|
||||
else if (IsDictionary(value))
|
||||
{
|
||||
return JsonConvert.SerializeObject(value, GetDefaultJsonSerializerSettings());
|
||||
}
|
||||
|
||||
return value.ToString() ?? "";
|
||||
}
|
||||
|
||||
public static string ToString(object? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
|
||||
if (IsString(obj))
|
||||
{
|
||||
return obj.ToString() ?? "";
|
||||
}
|
||||
|
||||
if (IsPrimitive(obj))
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
}
|
||||
|
||||
if (IsEnum(obj))
|
||||
{
|
||||
var attributes = obj.GetType().GetMember(obj.ToString() ?? "").First().CustomAttributes;
|
||||
if (attributes.Count() == 0)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
}
|
||||
|
||||
var args = attributes.First().ConstructorArguments;
|
||||
if (args.Count() == 0)
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
}
|
||||
return StripSurroundingQuotes(args.First().ToString());
|
||||
}
|
||||
|
||||
if (IsDate(obj))
|
||||
{
|
||||
return StripSurroundingQuotes(
|
||||
JsonConvert.SerializeObject(obj, GetDefaultJsonSerializerSettings())
|
||||
);
|
||||
}
|
||||
|
||||
return JsonConvert.SerializeObject(obj, GetDefaultJsonSerializerSettings());
|
||||
}
|
||||
|
||||
public static bool IsContentTypeMatch(string expected, string? actual)
|
||||
{
|
||||
if (actual == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected == actual || expected == "*" || expected == "*/*")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var mediaType = MediaTypeHeaderValue.Parse(actual).MediaType ?? "";
|
||||
|
||||
if (expected == mediaType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
var parts = mediaType.Split('/');
|
||||
if (parts.Length == 2)
|
||||
{
|
||||
if (parts[0] + "/*" == expected || "*/" + parts[1] == expected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static string PrefixBearer(string authHeaderValue)
|
||||
{
|
||||
if (authHeaderValue.StartsWith("bearer ", StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return authHeaderValue;
|
||||
}
|
||||
|
||||
return $"Bearer {authHeaderValue}";
|
||||
}
|
||||
public static string RemoveSuffix(string inputString, string suffix)
|
||||
{
|
||||
if (!String.IsNullOrEmpty(suffix) && inputString.EndsWith(suffix))
|
||||
{
|
||||
return inputString.Remove(inputString.Length - suffix.Length, suffix.Length);
|
||||
}
|
||||
return inputString;
|
||||
}
|
||||
public static string TemplateUrl(string template, Dictionary<string, string> paramDict)
|
||||
{
|
||||
foreach(KeyValuePair<string, string> entry in paramDict)
|
||||
{
|
||||
template = template.Replace('{' + entry.Key + '}', entry.Value);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user