using System; using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; using Sony.Filtr.Utility.Extensions; namespace Sony.Filtr.ApolloAPI.Models { public enum VendorHeaders { ExecutionTime, Source, Retry } public abstract class VendorHeader { public abstract string Name { get; } public abstract VendorHeaders HeaderType {get;} } public abstract class VendorHeader : VendorHeader where T : VendorHeader { protected double GetDouble(string value) { double d = 0; System.Double.TryParse(value, out d); return d; } public static T Parse(string json) { try { return JsonConvert.DeserializeObject(json); } catch { } return null; } } public class ExecutionTimeHeader : VendorHeader { public override string Name {get {return "x-execution-time";} } public override VendorHeaders HeaderType { get { return VendorHeaders.ExecutionTime; } } public string cache { get; set; } public string api { get; set; } public bool ProcessedFromCache { get { return !(this.GetDouble(this.api) > 0); } } public double TotalProcessTime { get { return this.GetDouble(this.api) + this.GetDouble(this.cache); } } } public class SourceHeader : VendorHeader { public override string Name => "x-data-source"; public override VendorHeaders HeaderType => VendorHeaders.Source; public string source { get; set; } public string ts { get; set; } public DateTime TimeStamp { get { return this.GetDouble(this.ts).FromVendorTimestamp(); } } } public class RetryHeader : VendorHeader { public override string Name => "x-retry-stats"; public override VendorHeaders HeaderType => VendorHeaders.Retry; public int count { get; set; } public string time { get; set; } } public class HeaderTracker : IHeaderTracker { private Dictionary>> requestToHeadersMap = new Dictionary>>(); /* [x-data-source, {\"source\": \"api\", \"ts\": 161675458672}], [x-retry-stats, {\"count\": 0, \"time\": 0}], [x-request-count, {\"count\": 1, \"from\": 161675458657, \"to\": 161675458672}], [x-execution-time, {\"cache\": 0.0, \"api\": 0.151}], */ public void AddHeaders(string uri, IEnumerable> headers) { if (this.requestToHeadersMap.ContainsKey(uri)) { this.requestToHeadersMap.Remove(uri); } this.requestToHeadersMap.Add(uri, headers.ToList()); } public Dictionary>> RequestHeaders { get { return this.requestToHeadersMap; } } public IEnumerable OfType() where T : VendorHeader, new() { T headerTyped = new T(); return this.RequestHeaders.Values .SelectMany(headers => headers) .Where(header => header.Key.ToLower() == headerTyped.Name) .Select(pair => VendorHeader.Parse(pair.Value)) .Where(dto => dto != null) .ToList(); } public string GetRawHeaders() { return String.Join( "###", this.RequestHeaders.Select(urlHeader => $"Url: '{urlHeader.Key}' Headers: {String.Join(";", urlHeader.Value.Select(pair => $"{pair.Key} {pair.Value}"))}") ); } public string GetSummary() { return $"{this.GetExecutionTimeSummary()}:::{this.GetSourceSummary()}:::{this.GetRetrySummary()}"; } private string GetSourceSummary() { return $"Source->{String.Join(", ", this.OfType().Select(h => $"{h.source}:{h.TimeStamp.ToString("yyyy-MM-dd hh-mm-ss")}"))}"; } public string GetRetrySummary() { return $"Retry->{String.Join(", ", this.OfType().Select(h => $"count: {h.count} time:{h.time}"))}"; } private string GetExecutionTimeSummary() { var builder = new System.Text.StringBuilder("Execution time->"); var executionHeaders = this.OfType(); var fromCacheResponses = executionHeaders.Where(h => h.ProcessedFromCache).ToList(); var fromApiResponses = executionHeaders.Where(h => !h.ProcessedFromCache).ToList(); Func, string> getSummary = headers => { return $"#{headers.Count()}. ({String.Join(", ", headers.Select(r => r.TotalProcessTime))})"; }; if (fromCacheResponses.Any()) { builder.Append("From CACHE "); builder.Append(getSummary(fromCacheResponses)); } if (fromApiResponses.Any()) { builder.Append(" From API "); builder.Append(getSummary(fromApiResponses)); } builder.Append($" Total: {executionHeaders.Sum(h => h.TotalProcessTime)}"); return builder.ToString(); } } public class SpotifyPlaylist : HeaderTracker, IRawResponseTracker { public bool collaborative { get; set; } public string description { get; set; } public SpotifyExternalUrls external_urls { get; set; } public SpotifyFollowers followers { get; set; } public string href { get; set; } public string id { get; set; } public List images { get; set; } public string name { get; set; } public SpotifyOwner owner { get; set; } public bool @public { get; set; } public string snapshot_id { get; set; } public SpotifyPlaylistTracks Tracks { get; set; } public string type { get; set; } public string uri { get; set; } public bool HasSnapshotIdChanged { get; set; } private Dictionary rawResponses = new Dictionary(); public IEnumerable> RawResponses => this.rawResponses; public void AddRawResponseToModel(string url, string json) { if(!this.rawResponses.ContainsKey(url)) { this.rawResponses.Add(url, json); } } public string GetImageUrl() { var image = this.images.FirstOrDefault(); return image == null ? null : image.url.ForceUrlHttps(); } public static bool IsValidTrack(SpotifyPlaylistTrackItem track) { return track != null && track.track != null && !String.IsNullOrWhiteSpace(track.track.id) && !String.IsNullOrWhiteSpace(track.track.uri); } } public class SpotifyPlaylistTracks : SpotifyTracks { } }