From b0935e9214d6119181e233c79a97138bc48cc305 Mon Sep 17 00:00:00 2001 From: Tom Raterman Date: Mon, 25 Jan 2021 09:47:50 -0500 Subject: [PATCH] Migrating to Ratermania.Automation --- .editorconfig | 6 + PartSource.Automation/Factories/JobFactory.cs | 8 +- PartSource.Automation/Jobs/TestJob.cs | 35 +++-- PartSource.Automation/Jobs/UpdatePricing.cs | 93 +++++------ .../PartSource.Automation.csproj | 11 +- PartSource.Automation/Program.cs | 146 +++++++----------- PartSource.Automation/Services/SsisService.cs | 1 - PartSource.Automation/appsettings.json | 3 + PartSource.Data/PartSource.Data.csproj | 4 - PartSource.sln | 40 ++--- 10 files changed, 154 insertions(+), 193 deletions(-) diff --git a/.editorconfig b/.editorconfig index 0f71132..136d44f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -2,3 +2,9 @@ # CA2007: Consider calling ConfigureAwait on the awaited task dotnet_diagnostic.CA2007.severity = silent + +# Default severity for analyzer diagnostics with category 'Design' +dotnet_analyzer_diagnostic.category-Design.severity = silent + +# Default severity for analyzer diagnostics with category 'Globalization' +dotnet_analyzer_diagnostic.category-Globalization.severity = silent diff --git a/PartSource.Automation/Factories/JobFactory.cs b/PartSource.Automation/Factories/JobFactory.cs index 11428f1..75ad368 100644 --- a/PartSource.Automation/Factories/JobFactory.cs +++ b/PartSource.Automation/Factories/JobFactory.cs @@ -32,14 +32,14 @@ namespace PartSource.Automation.Factories case nameof(StatusCheck): return _serviceProvider.GetService(); - case nameof(TestJob): - return new TestJob(); + //case nameof(TestJob): + // return new TestJob(); case nameof(UpdateFitment): return _serviceProvider.GetService(); - case nameof(UpdatePricing): - return _serviceProvider.GetService(); + //case nameof(UpdatePricing): + // return _serviceProvider.GetService(); case nameof(UpdatePositioning): return _serviceProvider.GetService(); diff --git a/PartSource.Automation/Jobs/TestJob.cs b/PartSource.Automation/Jobs/TestJob.cs index 3da7984..2a45b64 100644 --- a/PartSource.Automation/Jobs/TestJob.cs +++ b/PartSource.Automation/Jobs/TestJob.cs @@ -1,21 +1,32 @@ -using PartSource.Automation.Jobs.Interfaces; +using Ratermania.Automation.Interfaces; using PartSource.Automation.Models; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace PartSource.Automation.Jobs { - public class TestJob : IAutomationJob - { - public async Task Run() - { - return new AutomationJobResult - { - Message = "Test job ran successfully from the new server", - IsSuccess = true - }; - } - } + public class TestJob : IAutomationJob + { + private readonly ILogger _logger; + + public TestJob(ILogger logger) + { + _logger = logger; + } + +#pragma warning disable CS1998, CA1303 + public async Task Run() + { + await Task.Delay(5000); + + _logger.LogInformation("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc scelerisque congue euismod. Curabitur enim eros, sollicitudin ac purus eget, dignissim mattis augue. In quam sapien, tincidunt et elementum vitae, interdum vitae sem."); + _logger.LogWarning("Praesent feugiat sapien non suscipit faucibus. Mauris fermentum ut augue a feugiat. Integer felis sem, laoreet et augue at, finibus maximus ex. Fusce sit amet erat non tortor porta condimentum condimentum quis ipsum."); + _logger.LogError("Sed fringilla placerat turpis, sed tristique mi malesuada quis. Sed a justo erat. In iaculis, orci pulvinar tempor accumsan, mi leo rutrum lorem, ut egestas arcu ligula sodales dolor."); + _logger.LogCritical("Donec pulvinar vehicula massa. Praesent non erat tortor. Duis posuere tortor sed odio iaculis, sit amet eleifend est tincidunt. Suspendisse rhoncus eros id purus aliquet, ut porttitor lectus eleifend."); + } +#pragma warning restore CS1998, CA1303 + } } diff --git a/PartSource.Automation/Jobs/UpdatePricing.cs b/PartSource.Automation/Jobs/UpdatePricing.cs index b538d18..c687d1b 100644 --- a/PartSource.Automation/Jobs/UpdatePricing.cs +++ b/PartSource.Automation/Jobs/UpdatePricing.cs @@ -1,6 +1,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging.Abstractions; -using PartSource.Automation.Jobs.Interfaces; +using Ratermania.Automation.Interfaces; using PartSource.Automation.Models; using PartSource.Data; using PartSource.Data.Models; @@ -11,22 +11,24 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Logging; namespace PartSource.Automation.Jobs { public class UpdatePricing : IAutomationJob { + private readonly ILogger _logger; private readonly PartSourceContext _partSourceContext; private readonly ShopifyClient _shopifyClient; - public UpdatePricing(PartSourceContext partSourceContext, ShopifyClient shopifyClient) + public UpdatePricing(ILogger logger, PartSourceContext partSourceContext, ShopifyClient shopifyClient) { + _logger = logger; _partSourceContext = partSourceContext; _shopifyClient = shopifyClient; - } - public async Task Run() + public async Task Run() { IEnumerable products = null; IEnumerable prices = null; @@ -40,12 +42,8 @@ namespace PartSource.Automation.Jobs catch (Exception ex) { - // TODO: Logging - return new AutomationJobResult - { - Message = "Failed to get products from Shopify", - IsSuccess = false - }; + _logger.LogError(ex, "Failed to get the initial set of products from Shopify."); + return; } while (products != null && products.Any()) @@ -54,7 +52,6 @@ namespace PartSource.Automation.Jobs { if (product.Variants.Length > 0) { - Variant variant = product.Variants[0]; PartPrice partPrice = prices.Where(p => p.SKU == variant.Sku).FirstOrDefault(); @@ -63,74 +60,54 @@ namespace PartSource.Automation.Jobs continue; } - try + + if (product.Variants[0].Price != partPrice.Your_Price.Value || product.Variants[0].CompareAtPrice != partPrice.Compare_Price.Value) { - if (product.Variants[0].Price != partPrice.Your_Price.Value || product.Variants[0].CompareAtPrice != partPrice.Compare_Price.Value) + product.Variants[0].Price = partPrice.Your_Price.Value; + product.Variants[0].CompareAtPrice = partPrice.Compare_Price.Value; + + product.PublishedAt = partPrice.Active.Trim().ToUpperInvariant() == "Y" ? (DateTime?)DateTime.Now : null; + product.PublishedScope = PublishedScope.Global; + + Metafield metafield = new Metafield { - product.Variants[0].Price = partPrice.Your_Price.Value; - product.Variants[0].CompareAtPrice = partPrice.Compare_Price.Value; + Namespace = "Pricing", + Key = "CorePrice", + Value = partPrice.Core_Price.HasValue ? partPrice.Core_Price.Value.ToString() : "0.00", + ValueType = "string", + OwnerResource = "product", + OwnerId = product.Id + }; - product.PublishedAt = partPrice.Active.Trim().ToUpperInvariant() == "Y" ? (DateTime?)DateTime.Now : null; - product.PublishedScope = PublishedScope.Global; + try + { + await _shopifyClient.Metafields.Add(metafield); + await _shopifyClient.Products.Update(product); - Metafield metafield = new Metafield - { - Namespace = "Pricing", - Key = "CorePrice", - Value = partPrice.Core_Price.HasValue ? partPrice.Core_Price.Value.ToString() : "0.00", - ValueType = "string", - OwnerResource = "product", - OwnerId = product.Id - }; - - try - { - await _shopifyClient.Metafields.Add(metafield); - await _shopifyClient.Products.Update(product); - - updateCount++; - - - } - - catch (Exception ex) - { - Console.WriteLine("bad update"); - } + updateCount++; } - } - catch (Exception ex) - { - Console.WriteLine("failed getting parts"); + catch (Exception ex) + { + _logger.LogWarning(ex, $"Failed to update pricing for SKU {variant.Sku}"); + } } } } - // _partSourceContext.SaveChanges(); - try { - //await _shopifyClient.Products.SaveChanges(); - products = await _shopifyClient.Products.GetNext(); - Console.SetCursorPosition(0, 2); - Console.Clear(); - Console.Write($"Updated: {updateCount} "); + _logger.LogInformation($"Total updated: {updateCount}"); } catch (Exception ex) { + _logger.LogWarning(ex, "Failed to get the next set of products. Retrying"); products = await _shopifyClient.Products.GetPrevious(); } } - - return new AutomationJobResult - { - Message = $"The nightly pricing update has completed. {updateCount} products were updated.", - IsSuccess = true - }; } } } \ No newline at end of file diff --git a/PartSource.Automation/PartSource.Automation.csproj b/PartSource.Automation/PartSource.Automation.csproj index a73fa91..76850a6 100644 --- a/PartSource.Automation/PartSource.Automation.csproj +++ b/PartSource.Automation/PartSource.Automation.csproj @@ -13,13 +13,16 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + + + + - + diff --git a/PartSource.Automation/Program.cs b/PartSource.Automation/Program.cs index c7374a3..8299c9d 100644 --- a/PartSource.Automation/Program.cs +++ b/PartSource.Automation/Program.cs @@ -2,6 +2,8 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using PartSource.Automation.Factories; using PartSource.Automation.Jobs; using PartSource.Automation.Jobs.Interfaces; @@ -11,107 +13,71 @@ using PartSource.Automation.Services; using PartSource.Data; using PartSource.Data.AutoMapper; using PartSource.Services; +using Ratermania.Automation; +using Ratermania.Automation.DependencyInjection; +using Ratermania.Automation.Logging; using Ratermania.Shopify; using Ratermania.Shopify.DependencyInjection; using System; using System.IO; +using System.Threading.Tasks; namespace PartSource.Automation -{ internal class Program - { +{ + class Program + { - private static void Main(string[] args) - { - IServiceProvider serviceProvider = ConfigureServices(); + static async Task Main(string[] args) + { + using IHost host = CreateHostBuilder().Build(); - JobFactory jobFactory = serviceProvider.GetService(); - EmailService emailService = serviceProvider.GetService(); + await host.StartAsync(); + } - foreach (string arg in args) - { - Console.Write($"Running job {arg}... "); - - try - { - IAutomationJob job = jobFactory.Build(arg); - AutomationJobResult result = job.Run().Result; - - if (result.IsSuccess) - { - emailService.Send($"{arg} Completed Successfully", result.Message); - } - - else - { - emailService.Send($"{arg} Failed", result.Message); - } - - Console.WriteLine("Done"); - } - - catch (Exception ex) - { - Console.WriteLine(ex.Message); - } - } - } - - private static IServiceProvider ConfigureServices() - { - string environment = Environment.GetEnvironmentVariable("PS_AUTOMATION_ENVIRONMENT"); - - IConfigurationBuilder builder = new ConfigurationBuilder() - .SetBasePath(Directory.GetCurrentDirectory()) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true); - - IConfigurationRoot configuration = builder.Build(); - - EmailConfiguration emailConfiguration = new EmailConfiguration(); - FtpConfiguration ftpConfiguration = new FtpConfiguration(); - SsisConfiguration ssisConfiguration = new SsisConfiguration(); - - configuration.Bind("emailConfiguration", emailConfiguration); - configuration.Bind("ftpConfiguration", ftpConfiguration); - configuration.Bind("ssisConfiguration", ssisConfiguration); - - ServiceProvider serviceProvider = new ServiceCollection() - .AddDbContext(options => - options.UseSqlServer(configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) - ) - - .AddShopify(options => + private static IHostBuilder CreateHostBuilder() + { + return Host.CreateDefaultBuilder() + .ConfigureAppConfiguration(builder => { - options.ApiKey = configuration["Shopify:ApiKey"]; - options.ApiSecret = configuration["Shopify:ApiSecret"]; - options.ApiVersion = "2020-01"; - options.ShopDomain = configuration["Shopify:ShopDomain"]; - }) + string environment = Environment.GetEnvironmentVariable("AUTOMATION_ENVIRONMENT"); - .AddAutoMapper(typeof(PartSourceProfile)) + builder.SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{environment}.json", optional: true, reloadOnChange: true); + }) + .ConfigureServices((builder, services) => + { + services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) + ) - .AddSingleton(emailConfiguration) - .AddSingleton(ftpConfiguration) - .AddSingleton(ssisConfiguration) - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - //.AddSingleton() - //.AddSingleton() - //.AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .BuildServiceProvider(); + .AddShopify(options => + { + options.ApiKey = builder.Configuration["Shopify:ApiKey"]; + options.ApiSecret = builder.Configuration["Shopify:ApiSecret"]; + options.ApiVersion = "2020-01"; + options.ShopDomain = builder.Configuration["Shopify:ShopDomain"]; + }) - return serviceProvider; - } - } + .AddAutomation(options => + { + options.HasBaseInterval(new TimeSpan(0, 1, 0)) + .HasMaxFailures(5) + .HasJob(options => + { + options.HasInterval(new TimeSpan(0, 5, 0)); + }) + .AddApiServer(); + }) + + .AddAutoMapper(typeof(PartSourceProfile)); + }) + .ConfigureLogging((builder, logging) => + { + logging.AddEventLog(); + + logging.AddProvider(new AutomationLoggerProvider()); + }); + } + } } diff --git a/PartSource.Automation/Services/SsisService.cs b/PartSource.Automation/Services/SsisService.cs index 4cc00ca..b46a2ae 100644 --- a/PartSource.Automation/Services/SsisService.cs +++ b/PartSource.Automation/Services/SsisService.cs @@ -33,7 +33,6 @@ namespace PartSource.Automation.Services CreateNoWindow = false, RedirectStandardOutput = true, RedirectStandardError = true - } }; diff --git a/PartSource.Automation/appsettings.json b/PartSource.Automation/appsettings.json index bc06491..12c17aa 100644 --- a/PartSource.Automation/appsettings.json +++ b/PartSource.Automation/appsettings.json @@ -28,5 +28,8 @@ "ApiKey": "88f931933b566ade1fc92c6a39f04b34", "ApiSecret": "527a3b4213c2c7ecb214728a899052df", "ShopDomain": "partsource.myshopify.com" + }, + "LogLevel": { + "Default": "Information" } } \ No newline at end of file diff --git a/PartSource.Data/PartSource.Data.csproj b/PartSource.Data/PartSource.Data.csproj index 9777abf..4330898 100644 --- a/PartSource.Data/PartSource.Data.csproj +++ b/PartSource.Data/PartSource.Data.csproj @@ -23,9 +23,5 @@ - - - - diff --git a/PartSource.sln b/PartSource.sln index 048370a..7faa82e 100644 --- a/PartSource.sln +++ b/PartSource.sln @@ -11,13 +11,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PartSource.Services", "Part EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PartSource.Automation", "PartSource.Automation\PartSource.Automation.csproj", "{C85D675B-A76C-4F9C-9C57-1E063211C946}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shopify", "..\shopify\Shopify\Shopify.csproj", "{FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1631E7EC-E54D-4F3F-9800-6EE1D5B2CB48}" ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Automation", "..\shopify\Automation\Automation.csproj", "{40E3046C-7B99-4F92-8626-1EF2892DFDCD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Also Debug|Any CPU = Also Debug|Any CPU @@ -103,24 +103,24 @@ Global {C85D675B-A76C-4F9C-9C57-1E063211C946}.Release|x64.Build.0 = Release|Any CPU {C85D675B-A76C-4F9C-9C57-1E063211C946}.Release|x86.ActiveCfg = Release|Any CPU {C85D675B-A76C-4F9C-9C57-1E063211C946}.Release|x86.Build.0 = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|Any CPU.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|x64.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|x64.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|x86.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Also Debug|x86.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|x64.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|x64.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|x86.ActiveCfg = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Debug|x86.Build.0 = Debug|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|Any CPU.Build.0 = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|x64.ActiveCfg = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|x64.Build.0 = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|x86.ActiveCfg = Release|Any CPU - {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|x86.Build.0 = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|Any CPU.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|x64.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|x64.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|x86.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Also Debug|x86.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|x64.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|x64.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|x86.ActiveCfg = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Debug|x86.Build.0 = Debug|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|Any CPU.Build.0 = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|x64.ActiveCfg = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|x64.Build.0 = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|x86.ActiveCfg = Release|Any CPU + {40E3046C-7B99-4F92-8626-1EF2892DFDCD}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE