2021-07-20 22:51:43 +00:00
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
2021-08-09 18:32:16 +00:00
|
|
|
|
using System.IO;
|
2021-07-20 22:51:43 +00:00
|
|
|
|
using System.Net.Http;
|
2021-08-09 18:32:16 +00:00
|
|
|
|
using System.Text.Json;
|
2021-08-05 06:22:19 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2021-08-07 22:20:46 +00:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
2021-07-20 22:51:43 +00:00
|
|
|
|
using Props.Shop.Adafruit.Api;
|
2021-08-09 18:32:16 +00:00
|
|
|
|
using Props.Shop.Adafruit.Persistence;
|
2021-07-20 22:51:43 +00:00
|
|
|
|
using Props.Shop.Framework;
|
|
|
|
|
|
|
|
|
|
namespace Props.Shop.Adafruit
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
public class AdafruitShop : IShop, IDisposable
|
2021-07-20 22:51:43 +00:00
|
|
|
|
{
|
2021-08-09 18:32:16 +00:00
|
|
|
|
private string workspaceDir;
|
2021-08-07 22:20:46 +00:00
|
|
|
|
private ILoggerFactory loggerFactory;
|
|
|
|
|
private ILogger<AdafruitShop> logger;
|
2021-08-05 06:22:19 +00:00
|
|
|
|
private SearchManager searchManager;
|
2021-07-20 22:51:43 +00:00
|
|
|
|
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
|
|
|
|
|
);
|
2021-08-12 04:54:52 +00:00
|
|
|
|
public async ValueTask Initialize(string workspaceDir, ILoggerFactory loggerFactory)
|
2021-07-20 22:51:43 +00:00
|
|
|
|
{
|
2021-08-09 18:32:16 +00:00
|
|
|
|
this.workspaceDir = workspaceDir;
|
2021-08-07 22:20:46 +00:00
|
|
|
|
this.loggerFactory = loggerFactory;
|
2021-08-09 18:32:16 +00:00
|
|
|
|
logger = loggerFactory.CreateLogger<AdafruitShop>();
|
2021-08-05 06:22:19 +00:00
|
|
|
|
http = new HttpClient();
|
|
|
|
|
http.BaseAddress = new Uri("http://www.adafruit.com/api/");
|
2021-08-12 04:54:52 +00:00
|
|
|
|
string configPath = Path.Combine(workspaceDir, Configuration.FILE_NAME);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
configuration = JsonSerializer.Deserialize<Configuration>(File.ReadAllText(configPath));
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
2021-08-12 04:54:52 +00:00
|
|
|
|
catch (JsonException e)
|
2021-08-09 18:32:16 +00:00
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("Could not read JSON file \"{0}\": {1}", configPath, e.Message);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
catch (ArgumentException)
|
|
|
|
|
{
|
|
|
|
|
logger.LogWarning("No working directory path provided.");
|
|
|
|
|
}
|
|
|
|
|
catch (DirectoryNotFoundException)
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("Directory \"{0}\" could not be found.", Path.GetDirectoryName(configPath));
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
catch (FileNotFoundException)
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("File \"{0}\" could not be found.", configPath);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (configuration == null)
|
|
|
|
|
{
|
|
|
|
|
configuration = new Configuration();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ProductListingCacheData listingData = null;
|
2021-08-12 04:54:52 +00:00
|
|
|
|
string cachePath = Path.Combine(workspaceDir, ProductListingCacheData.FILE_NAME);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
try
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
using (Stream fileStream = File.OpenRead(cachePath))
|
2021-08-09 18:32:16 +00:00
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
listingData = await JsonSerializer.DeserializeAsync<ProductListingCacheData>(fileStream);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-12 04:54:52 +00:00
|
|
|
|
catch (JsonException e)
|
2021-08-09 18:32:16 +00:00
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("Could not read JSON file \"{0}\": {1}", cachePath, e.Message);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
catch (ArgumentException)
|
|
|
|
|
{
|
|
|
|
|
logger.LogWarning("No working directory path provided.");
|
|
|
|
|
}
|
|
|
|
|
catch (DirectoryNotFoundException)
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("Directory \"{0}\" could not be found.", Path.GetDirectoryName(cachePath));
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
catch (FileNotFoundException)
|
|
|
|
|
{
|
2021-08-12 04:54:52 +00:00
|
|
|
|
logger.LogWarning("File \"{0}\" could not be found.", cachePath);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (configuration == null)
|
|
|
|
|
{
|
|
|
|
|
configuration = new Configuration();
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-08-12 04:54:52 +00:00
|
|
|
|
LiveProductListingManager productListingManager = new LiveProductListingManager(http, loggerFactory.CreateLogger<LiveProductListingManager>(), listingData, configuration.MinDownloadInterval);
|
2021-08-07 22:20:46 +00:00
|
|
|
|
this.searchManager = new SearchManager(productListingManager, configuration.Similarity);
|
2021-08-09 18:32:16 +00:00
|
|
|
|
productListingManager.StartUpdateTimer(delay: 0, configuration.CacheLifespan);
|
2021-08-07 22:20:46 +00:00
|
|
|
|
|
2021-07-20 22:51:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-09 18:32:16 +00:00
|
|
|
|
public async Task<ProductListing> GetProductFromIdentifier(string identifier)
|
2021-07-20 22:51:43 +00:00
|
|
|
|
{
|
2021-08-09 18:32:16 +00:00
|
|
|
|
return await searchManager.ProductListingManager.GetProductListingFromIdentifier(identifier);
|
2021-07-20 22:51:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-07 22:20:46 +00:00
|
|
|
|
public IAsyncEnumerable<ProductListing> Search(string query, Filters filters)
|
2021-07-20 22:51:43 +00:00
|
|
|
|
{
|
2021-08-05 06:22:19 +00:00
|
|
|
|
return searchManager.Search(query);
|
2021-07-20 22:51:43 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-08-12 04:54:52 +00:00
|
|
|
|
public async ValueTask SaveData()
|
|
|
|
|
{
|
|
|
|
|
if (workspaceDir != null)
|
|
|
|
|
{
|
|
|
|
|
logger.LogDebug("Saving data in \"{0}\"...", workspaceDir);
|
2021-08-17 07:59:01 +00:00
|
|
|
|
string configurationPath = Path.Combine(workspaceDir, Configuration.FILE_NAME);
|
|
|
|
|
File.Delete(configurationPath);
|
2021-08-12 04:54:52 +00:00
|
|
|
|
await File.WriteAllTextAsync(Path.Combine(workspaceDir, Configuration.FILE_NAME), JsonSerializer.Serialize(configuration));
|
2021-08-17 07:59:01 +00:00
|
|
|
|
|
|
|
|
|
string productListingCachePath = Path.Combine(workspaceDir, ProductListingCacheData.FILE_NAME);
|
|
|
|
|
File.Delete(productListingCachePath);
|
|
|
|
|
using (Stream fileStream = File.OpenWrite(productListingCachePath))
|
2021-08-12 04:54:52 +00:00
|
|
|
|
{
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-20 22:51:43 +00:00
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if (!disposedValue)
|
|
|
|
|
{
|
|
|
|
|
if (disposing)
|
|
|
|
|
{
|
|
|
|
|
http.Dispose();
|
2021-08-05 06:22:19 +00:00
|
|
|
|
searchManager.Dispose();
|
2021-07-20 22:51:43 +00:00
|
|
|
|
}
|
2021-08-12 04:54:52 +00:00
|
|
|
|
|
2021-07-20 22:51:43 +00:00
|
|
|
|
disposedValue = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
|
|
|
|
Dispose(disposing: true);
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|