2020-05-29 12:06:20 +01:00
|
|
|
using System.Collections.Concurrent;
|
2020-05-01 19:05:28 +01:00
|
|
|
using System.Reflection;
|
|
|
|
using System;
|
|
|
|
using System.Linq;
|
|
|
|
using System.Collections.Generic;
|
2020-05-02 21:48:21 +01:00
|
|
|
using System.Collections;
|
|
|
|
using Newtonsoft.Json.Linq;
|
|
|
|
|
2020-05-01 19:05:28 +01:00
|
|
|
namespace SpotifyAPI.Web
|
|
|
|
{
|
|
|
|
public abstract class RequestParams
|
|
|
|
{
|
2020-06-09 02:04:51 +01:00
|
|
|
private static readonly ConcurrentDictionary<Type, List<(PropertyInfo, BodyParamAttribute)>> _bodyParamsCache =
|
2020-12-26 17:28:44 +00:00
|
|
|
new();
|
2020-06-09 02:04:51 +01:00
|
|
|
|
2020-05-02 21:48:21 +01:00
|
|
|
public JObject BuildBodyParams()
|
|
|
|
{
|
2020-05-07 12:48:31 +01:00
|
|
|
// Make sure everything is okay before building body params
|
2020-05-02 21:48:21 +01:00
|
|
|
CustomEnsure();
|
|
|
|
|
2020-05-29 12:06:20 +01:00
|
|
|
var body = new JObject();
|
2020-06-09 02:04:51 +01:00
|
|
|
var type = GetType();
|
|
|
|
|
|
|
|
if (!_bodyParamsCache.IsEmpty && _bodyParamsCache.ContainsKey(type))
|
2020-05-29 12:06:20 +01:00
|
|
|
{
|
2020-06-09 02:04:51 +01:00
|
|
|
foreach (var (info, attribute) in _bodyParamsCache[type])
|
2020-05-29 12:06:20 +01:00
|
|
|
{
|
2020-06-09 02:04:51 +01:00
|
|
|
AddBodyParam(body, info, attribute);
|
2020-05-29 12:06:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2020-05-02 21:48:21 +01:00
|
|
|
{
|
2020-05-29 12:06:20 +01:00
|
|
|
var bodyProps = GetType()
|
|
|
|
.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
|
|
|
|
.Where(prop => prop.GetCustomAttributes(typeof(BodyParamAttribute), true).Length > 0);
|
|
|
|
|
2020-06-09 02:04:51 +01:00
|
|
|
_bodyParamsCache[type] = new List<(PropertyInfo, BodyParamAttribute)>();
|
2020-05-29 12:06:20 +01:00
|
|
|
foreach (var prop in bodyProps)
|
2020-05-02 21:48:21 +01:00
|
|
|
{
|
2020-11-13 13:43:00 +00:00
|
|
|
var attribute = prop.GetCustomAttribute<BodyParamAttribute>();
|
|
|
|
if (attribute != null)
|
|
|
|
{
|
|
|
|
_bodyParamsCache[type].Add((prop, attribute));
|
|
|
|
AddBodyParam(body, prop, attribute);
|
|
|
|
}
|
2020-05-02 21:48:21 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-29 12:06:20 +01:00
|
|
|
|
|
|
|
return body;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void AddBodyParam(JObject body, PropertyInfo prop, BodyParamAttribute attribute)
|
|
|
|
{
|
2020-11-13 13:43:00 +00:00
|
|
|
object? value = prop.GetValue(this);
|
2020-05-29 12:06:20 +01:00
|
|
|
if (value != null)
|
|
|
|
{
|
|
|
|
body[attribute.Key ?? prop.Name] = JToken.FromObject(value);
|
|
|
|
}
|
2020-05-02 21:48:21 +01:00
|
|
|
}
|
|
|
|
|
2020-06-09 02:04:51 +01:00
|
|
|
private static readonly ConcurrentDictionary<Type, List<(PropertyInfo, QueryParamAttribute)>> _queryParamsCache =
|
2020-12-26 17:28:44 +00:00
|
|
|
new();
|
2020-06-09 02:04:51 +01:00
|
|
|
|
2020-05-01 19:05:28 +01:00
|
|
|
public Dictionary<string, string> BuildQueryParams()
|
|
|
|
{
|
2020-05-02 12:04:26 +01:00
|
|
|
// Make sure everything is okay before building query params
|
2020-05-02 21:48:21 +01:00
|
|
|
CustomEnsure();
|
2020-05-02 12:04:26 +01:00
|
|
|
|
2020-05-29 12:06:20 +01:00
|
|
|
var queryParams = new Dictionary<string, string>();
|
2020-06-09 02:04:51 +01:00
|
|
|
var type = GetType();
|
2020-05-29 12:06:20 +01:00
|
|
|
|
2020-06-09 02:04:51 +01:00
|
|
|
if (!_queryParamsCache.IsEmpty && _queryParamsCache.ContainsKey(type))
|
2020-05-29 12:06:20 +01:00
|
|
|
{
|
2020-06-09 02:04:51 +01:00
|
|
|
foreach (var (info, attribute) in _queryParamsCache[type])
|
2020-05-29 12:06:20 +01:00
|
|
|
{
|
2020-06-09 02:04:51 +01:00
|
|
|
AddQueryParam(queryParams, info, attribute);
|
2020-05-29 12:06:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
var queryProps = GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public)
|
2020-05-01 19:05:28 +01:00
|
|
|
.Where(prop => prop.GetCustomAttributes(typeof(QueryParamAttribute), true).Length > 0);
|
|
|
|
|
2020-06-09 02:04:51 +01:00
|
|
|
_queryParamsCache[type] = new List<(PropertyInfo, QueryParamAttribute)>();
|
2020-05-29 12:06:20 +01:00
|
|
|
foreach (var prop in queryProps)
|
|
|
|
{
|
2020-11-13 13:43:00 +00:00
|
|
|
var attribute = prop.GetCustomAttribute<QueryParamAttribute>();
|
|
|
|
if (attribute != null)
|
|
|
|
{
|
|
|
|
_queryParamsCache[type].Add((prop, attribute));
|
|
|
|
AddQueryParam(queryParams, prop, attribute);
|
|
|
|
}
|
2020-05-29 12:06:20 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AddCustomQueryParams(queryParams);
|
|
|
|
|
|
|
|
return queryParams;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void AddQueryParam(Dictionary<string, string> queryParams, PropertyInfo prop, QueryParamAttribute attribute)
|
|
|
|
{
|
2020-11-13 13:43:00 +00:00
|
|
|
object? value = prop.GetValue(this);
|
2020-05-29 12:06:20 +01:00
|
|
|
if (value != null)
|
2020-05-01 19:05:28 +01:00
|
|
|
{
|
2020-06-29 21:42:35 +01:00
|
|
|
if (value is IList<string> list)
|
2020-05-01 19:05:28 +01:00
|
|
|
{
|
2020-06-29 21:42:35 +01:00
|
|
|
if (list.Count > 0)
|
|
|
|
{
|
|
|
|
var str = string.Join(",", list);
|
|
|
|
queryParams.Add(attribute.Key ?? prop.Name, str);
|
|
|
|
}
|
2020-05-29 12:06:20 +01:00
|
|
|
}
|
2020-06-09 02:04:51 +01:00
|
|
|
else if (value is bool valueAsBool)
|
|
|
|
{
|
|
|
|
queryParams.Add(attribute.Key ?? prop.Name, valueAsBool ? "true" : "false");
|
|
|
|
}
|
2020-05-29 12:06:20 +01:00
|
|
|
else if (value is Enum valueAsEnum)
|
|
|
|
{
|
|
|
|
var enumType = valueAsEnum.GetType();
|
|
|
|
var valueList = new List<string>();
|
2020-05-03 21:34:03 +01:00
|
|
|
|
2020-07-13 14:49:23 +01:00
|
|
|
if (enumType.IsDefined(typeof(FlagsAttribute), false))
|
2020-05-29 12:06:20 +01:00
|
|
|
{
|
2020-07-13 14:49:23 +01:00
|
|
|
foreach (Enum enumVal in Enum.GetValues(valueAsEnum.GetType()))
|
2020-05-03 21:34:03 +01:00
|
|
|
{
|
2020-07-13 14:49:23 +01:00
|
|
|
if (valueAsEnum.HasFlag(enumVal))
|
2020-05-03 21:34:03 +01:00
|
|
|
{
|
2020-07-13 14:49:23 +01:00
|
|
|
if (StringAttribute.GetValue(enumType, enumVal, out var stringVal))
|
|
|
|
{
|
2020-07-13 15:03:19 +01:00
|
|
|
// .netstandard2.0 requires !
|
|
|
|
valueList.Add(stringVal!);
|
2020-07-13 14:49:23 +01:00
|
|
|
}
|
2020-05-03 21:34:03 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-02 21:48:21 +01:00
|
|
|
}
|
2020-07-13 14:49:23 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
if (StringAttribute.GetValue(enumType, valueAsEnum, out var stringVal))
|
|
|
|
{
|
2020-07-13 15:03:19 +01:00
|
|
|
// .netstandard2.0 requires !
|
|
|
|
valueList.Add(stringVal!);
|
2020-07-13 14:49:23 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-29 12:06:20 +01:00
|
|
|
queryParams.Add(attribute.Key ?? prop.Name, string.Join(",", valueList));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-12-26 17:28:44 +00:00
|
|
|
queryParams.Add(attribute.Key ?? prop.Name, value.ToString() ?? throw new APIException("ToString returned null for query parameter"));
|
2020-05-01 19:05:28 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-05-02 12:04:26 +01:00
|
|
|
|
2020-05-02 21:48:21 +01:00
|
|
|
protected virtual void CustomEnsure() { }
|
2020-05-02 12:04:26 +01:00
|
|
|
protected virtual void AddCustomQueryParams(Dictionary<string, string> queryParams) { }
|
2020-05-01 19:05:28 +01:00
|
|
|
}
|
|
|
|
|
2020-05-05 14:30:00 +01:00
|
|
|
[AttributeUsage(AttributeTargets.Property)]
|
2020-12-26 17:28:44 +00:00
|
|
|
public sealed class QueryParamAttribute : Attribute
|
2020-05-01 19:05:28 +01:00
|
|
|
{
|
2020-05-02 13:58:11 +01:00
|
|
|
public string Key { get; }
|
2020-05-01 19:05:28 +01:00
|
|
|
|
|
|
|
public QueryParamAttribute(string key)
|
|
|
|
{
|
|
|
|
Key = key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-05 14:30:00 +01:00
|
|
|
[AttributeUsage(AttributeTargets.Property)]
|
2020-12-26 17:28:44 +00:00
|
|
|
public sealed class BodyParamAttribute : Attribute
|
2020-05-01 19:05:28 +01:00
|
|
|
{
|
2020-05-02 21:48:21 +01:00
|
|
|
public string Key { get; }
|
|
|
|
|
|
|
|
public BodyParamAttribute(string key)
|
|
|
|
{
|
|
|
|
Key = key;
|
|
|
|
}
|
2020-05-01 19:05:28 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-25 17:00:38 +01:00
|
|
|
|