using System.Collections.Concurrent; using System.Reflection; using System; using System.Linq; using System.Collections.Generic; using System.Collections; using Newtonsoft.Json.Linq; namespace SpotifyAPI.Web { public abstract class RequestParams { private static readonly ConcurrentDictionary> _bodyParamsCache = new ConcurrentDictionary>(); public JObject BuildBodyParams() { // Make sure everything is okay before building body params CustomEnsure(); var body = new JObject(); var type = GetType(); if (!_bodyParamsCache.IsEmpty && _bodyParamsCache.ContainsKey(type)) { foreach (var (info, attribute) in _bodyParamsCache[type]) { AddBodyParam(body, info, attribute); } } else { var bodyProps = GetType() .GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public) .Where(prop => prop.GetCustomAttributes(typeof(BodyParamAttribute), true).Length > 0); _bodyParamsCache[type] = new List<(PropertyInfo, BodyParamAttribute)>(); foreach (var prop in bodyProps) { var attribute = (BodyParamAttribute)prop.GetCustomAttribute(typeof(BodyParamAttribute)); _bodyParamsCache[type].Add((prop, attribute)); AddBodyParam(body, prop, attribute); } } return body; } private void AddBodyParam(JObject body, PropertyInfo prop, BodyParamAttribute attribute) { object value = prop.GetValue(this); if (value != null) { body[attribute.Key ?? prop.Name] = JToken.FromObject(value); } } private static readonly ConcurrentDictionary> _queryParamsCache = new ConcurrentDictionary>(); public Dictionary BuildQueryParams() { // Make sure everything is okay before building query params CustomEnsure(); var queryParams = new Dictionary(); var type = GetType(); if (!_queryParamsCache.IsEmpty && _queryParamsCache.ContainsKey(type)) { foreach (var (info, attribute) in _queryParamsCache[type]) { AddQueryParam(queryParams, info, attribute); } } else { var queryProps = GetType().GetProperties(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public) .Where(prop => prop.GetCustomAttributes(typeof(QueryParamAttribute), true).Length > 0); _queryParamsCache[type] = new List<(PropertyInfo, QueryParamAttribute)>(); foreach (var prop in queryProps) { var attribute = (QueryParamAttribute)prop.GetCustomAttribute(typeof(QueryParamAttribute)); _queryParamsCache[type].Add((prop, attribute)); AddQueryParam(queryParams, prop, attribute); } } AddCustomQueryParams(queryParams); return queryParams; } private void AddQueryParam(Dictionary queryParams, PropertyInfo prop, QueryParamAttribute attribute) { object value = prop.GetValue(this); if (value != null) { if (value is IList list && list.Count > 0) { var str = string.Join(",", list); queryParams.Add(attribute.Key ?? prop.Name, str); } else if (value is bool valueAsBool) { queryParams.Add(attribute.Key ?? prop.Name, valueAsBool ? "true" : "false"); } else if (value is Enum valueAsEnum) { var enumType = valueAsEnum.GetType(); var valueList = new List(); foreach (Enum enumVal in Enum.GetValues(valueAsEnum.GetType())) { if (valueAsEnum.HasFlag(enumVal)) { if (enumType .GetMember(enumVal.ToString())[0] .GetCustomAttributes(typeof(StringAttribute)) .FirstOrDefault() is StringAttribute stringAttr) { valueList.Add(stringAttr.Value); } } } queryParams.Add(attribute.Key ?? prop.Name, string.Join(",", valueList)); } else { queryParams.Add(attribute.Key ?? prop.Name, value.ToString()); } } } protected virtual void CustomEnsure() { } protected virtual void AddCustomQueryParams(Dictionary queryParams) { } } [AttributeUsage(AttributeTargets.Property)] public class QueryParamAttribute : Attribute { public string Key { get; } public QueryParamAttribute(string key) { Key = key; } } [AttributeUsage(AttributeTargets.Property)] public class BodyParamAttribute : Attribute { public string Key { get; } public BodyParamAttribute(string key) { Key = key; } } }