using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Text.Json; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Props.Shop.Adafruit.Api; using Props.Shop.Adafruit.Persistence; using Props.Shop.Framework; namespace Props.Shop.Adafruit { public class AdafruitShop : IShop, IDisposable { private string workspaceDir; private ILoggerFactory loggerFactory; private ILogger logger; private SearchManager searchManager; private Configuration configuration; private HttpClient http; private bool disposedValue; public string ShopName => "Adafruit"; public string ShopDescription => "A electronic component online hardware company."; public string ShopModuleAuthor => "Reslate"; public SupportedFeatures SupportedFeatures => new SupportedFeatures( false, false, false, false, true ); public async ValueTask Initialize(string workspaceDir, ILoggerFactory loggerFactory) { workspaceDir = workspaceDir ?? ""; this.workspaceDir = workspaceDir; this.loggerFactory = loggerFactory; logger = loggerFactory.CreateLogger(); http = new HttpClient(); http.BaseAddress = new Uri("http://www.adafruit.com/api/"); string configPath = Path.Combine(workspaceDir, Configuration.FILE_NAME); try { configuration = JsonSerializer.Deserialize(File.ReadAllText(configPath)); } catch (JsonException e) { logger.LogWarning("Could not read JSON file \"{0}\": {1}", configPath, e.Message); } catch (ArgumentException) { logger.LogWarning("No working directory path provided."); } catch (DirectoryNotFoundException) { logger.LogWarning("Directory \"{0}\" could not be found.", Path.GetDirectoryName(configPath)); } catch (FileNotFoundException) { logger.LogWarning("File \"{0}\" could not be found.", configPath); } finally { if (configuration == null) { configuration = new Configuration(); } } ProductListingCacheData listingData = null; string cachePath = Path.Combine(workspaceDir, ProductListingCacheData.FILE_NAME); try { using (Stream fileStream = File.OpenRead(cachePath)) { listingData = await JsonSerializer.DeserializeAsync(fileStream); } } catch (JsonException e) { logger.LogWarning("Could not read JSON file \"{0}\": {1}", cachePath, e.Message); } catch (ArgumentException) { logger.LogWarning("No working directory path provided."); } catch (DirectoryNotFoundException) { logger.LogWarning("Directory \"{0}\" could not be found.", Path.GetDirectoryName(cachePath)); } catch (FileNotFoundException) { logger.LogWarning("File \"{0}\" could not be found.", cachePath); } finally { if (configuration == null) { configuration = new Configuration(); } } LiveProductListingManager productListingManager = new LiveProductListingManager(http, loggerFactory.CreateLogger(), listingData, configuration.MinDownloadInterval); this.searchManager = new SearchManager(productListingManager, configuration.Similarity); productListingManager.StartUpdateTimer(delay: 0, configuration.CacheLifespan); } public async Task GetProductFromIdentifier(string identifier) { return await searchManager.ProductListingManager.GetProductListingFromIdentifier(identifier); } public IAsyncEnumerable Search(string query, Filters filters) { return searchManager.Search(query); } public async ValueTask SaveData() { if (workspaceDir != null) { logger.LogDebug("Saving data in \"{0}\"...", workspaceDir); string configurationPath = Path.Combine(workspaceDir, Configuration.FILE_NAME); File.Delete(configurationPath); await File.WriteAllTextAsync(Path.Combine(workspaceDir, Configuration.FILE_NAME), JsonSerializer.Serialize(configuration)); string productListingCachePath = Path.Combine(workspaceDir, ProductListingCacheData.FILE_NAME); File.Delete(productListingCachePath); using (Stream fileStream = File.OpenWrite(productListingCachePath)) { await JsonSerializer.SerializeAsync(fileStream, new ProductListingCacheData(await searchManager.ProductListingManager.ProductListings)); } logger.LogDebug("Completed saving data."); } } public async ValueTask DisposeAsync() { Dispose(true); await DisposeAsyncCore(); } protected virtual async ValueTask DisposeAsyncCore() { await SaveData(); } protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing) { http.Dispose(); searchManager.Dispose(); } disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }