ci: regenerated with OpenAPI Doc , Speakeasy CLI 1.391.3

This commit is contained in:
speakeasybot
2024-09-06 17:13:00 +00:00
parent 2bed38d2cb
commit d077d5e9f0
610 changed files with 4195 additions and 3384 deletions

View 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;
}
}
}

View 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));
}
}
}

View 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));
}
}
}

View 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));
}
}
}

View 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();
}
}

View 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);
}
}
}
}

View 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));
}
}
}

View 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;
}
}
}
}

View 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);
}
}
}

View 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;
}
}
}

View 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.");
}
}
}
}
}

View 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;
}
}
}

View 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}");
}
}
}

View 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;
}
}
}

View 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);
}
}
}
}
}
}

View 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;
}
}
}

View 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;
}
}
}