using Flow.Launcher.Infrastructure.Http; using Flow.Launcher.Infrastructure.Logger; using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Net.Http.Json; using System.Threading; using System.Threading.Tasks; namespace Flow.Launcher.Core.ExternalPlugins { public record CommunityPluginSource(string ManifestFileUrl) { private string latestEtag = ""; private List plugins = new(); /// /// Fetch and deserialize the contents of a plugins.json file found at . /// We use conditional http requests to keep repeat requests fast. /// /// /// This method will only return plugin details when the underlying http request is successful (200 or 304). /// In any other case, an exception is raised /// public async Task> FetchAsync(CancellationToken token) { Log.Info(nameof(CommunityPluginSource), $"Loading plugins from {ManifestFileUrl}"); var request = new HttpRequestMessage(HttpMethod.Get, ManifestFileUrl); request.Headers.Add("If-None-Match", latestEtag); using var response = await Http.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, token).ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.OK) { this.plugins = await response.Content.ReadFromJsonAsync>(cancellationToken: token).ConfigureAwait(false); this.latestEtag = response.Headers.ETag.Tag; Log.Info(nameof(CommunityPluginSource), $"Loaded {this.plugins.Count} plugins from {ManifestFileUrl}"); return this.plugins; } else if (response.StatusCode == HttpStatusCode.NotModified) { Log.Info(nameof(CommunityPluginSource), $"Resource {ManifestFileUrl} has not been modified."); return this.plugins; } else { Log.Warn(nameof(CommunityPluginSource), $"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); throw new Exception($"Failed to load resource {ManifestFileUrl} with response {response.StatusCode}"); } } } }