From d06925204d70055e725f1c145b2923144538febf Mon Sep 17 00:00:00 2001 From: Tom Raterman Date: Wed, 2 Sep 2020 20:53:34 -0400 Subject: [PATCH] . --- .editorconfig | 4 + PartSource.Api/Controllers/ErrorController.cs | 35 --- .../Controllers/InventoryController.cs | 1 + .../Controllers/LegacyVehiclesController.cs | 3 +- .../Controllers/NexpartVehiclesController.cs | 158 ---------- PartSource.Api/Controllers/PartsController.cs | 1 + .../Controllers/SearchController.cs | 32 -- .../Controllers/StoresController.cs | 47 --- .../Controllers/TemplatesController.cs | 40 --- .../Controllers/VehiclesController.cs | 207 +++++++++---- PartSource.Api/PartSource.Api.csproj | 10 +- PartSource.Api/PartSource.Api.xml | 110 +++++++ PartSource.Api/Startup.cs | 28 +- PartSource.Automation/.editorconfig | 13 - .../Jobs/AddAndUpdateProducts.cs | 68 ++--- PartSource.Automation/Jobs/DeleteProducts.cs | 4 +- .../Jobs/UpdateFitment - Copy.cs | 170 ++++++----- .../Jobs/UpdatePositioning.cs | 5 +- PartSource.Automation/Jobs/UpdatePricing.cs | 10 +- .../PartSource.Automation.csproj | 5 +- PartSource.Automation/Program.cs | 40 ++- .../Properties/launchSettings.json | 2 +- PartSource.Automation/appsettings.json | 4 +- .../AutoMapper/PartSourceProfile.cs | 22 ++ PartSource.Data/Dtos/BaseVehicleDto.cs | 27 ++ PartSource.Data/Dtos/EngineDto.cs | 8 +- PartSource.Data/Dtos/MakeDto.cs | 15 + PartSource.Data/Dtos/ModelDto.cs | 21 ++ PartSource.Data/Dtos/SubmodelDto.cs | 10 +- PartSource.Data/Dtos/VehicleDto.cs | 42 +++ .../Models/{VehicleData.cs => Vehicle.cs} | 0 PartSource.Data/PartSource.Data.csproj | 10 + PartSource.Data/PartSource.Data.xml | 115 ++++++++ .../Extensions/IQueryableExtensions.cs | 4 +- .../Integrations/ShopifyClient.cs | 23 -- .../PartSource.Services.csproj | 6 +- PartSource.Services/VehicleService.cs | 279 +++++++++++------- PartSource.sln | 13 + 38 files changed, 913 insertions(+), 679 deletions(-) create mode 100644 .editorconfig delete mode 100644 PartSource.Api/Controllers/ErrorController.cs delete mode 100644 PartSource.Api/Controllers/NexpartVehiclesController.cs delete mode 100644 PartSource.Api/Controllers/SearchController.cs delete mode 100644 PartSource.Api/Controllers/StoresController.cs delete mode 100644 PartSource.Api/Controllers/TemplatesController.cs create mode 100644 PartSource.Api/PartSource.Api.xml delete mode 100644 PartSource.Automation/.editorconfig create mode 100644 PartSource.Data/AutoMapper/PartSourceProfile.cs create mode 100644 PartSource.Data/Dtos/BaseVehicleDto.cs create mode 100644 PartSource.Data/Dtos/MakeDto.cs create mode 100644 PartSource.Data/Dtos/ModelDto.cs create mode 100644 PartSource.Data/Dtos/VehicleDto.cs rename PartSource.Data/Models/{VehicleData.cs => Vehicle.cs} (100%) create mode 100644 PartSource.Data/PartSource.Data.xml delete mode 100644 PartSource.Services/Integrations/ShopifyClient.cs diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0f71132 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,4 @@ +[*.cs] + +# CA2007: Consider calling ConfigureAwait on the awaited task +dotnet_diagnostic.CA2007.severity = silent diff --git a/PartSource.Api/Controllers/ErrorController.cs b/PartSource.Api/Controllers/ErrorController.cs deleted file mode 100644 index dca9e39..0000000 --- a/PartSource.Api/Controllers/ErrorController.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Diagnostics; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace PartSource.Api.Controllers -{ - [Route("[controller]")] - public class ErrorController : ControllerBase - { - [HttpGet] - [Route("")] - [AllowAnonymous] - - public ActionResult Get() - { - IExceptionHandlerPathFeature exceptionFeature = HttpContext.Features.Get(); - - if (exceptionFeature != null) - { - string route = exceptionFeature.Path; - - Exception ex = exceptionFeature.Error; - - //TODO: Logging - } - - return StatusCode(StatusCodes.Status500InternalServerError); - } - } -} diff --git a/PartSource.Api/Controllers/InventoryController.cs b/PartSource.Api/Controllers/InventoryController.cs index ef7fc59..3071a82 100644 --- a/PartSource.Api/Controllers/InventoryController.cs +++ b/PartSource.Api/Controllers/InventoryController.cs @@ -9,6 +9,7 @@ namespace PartSource.Api.Controllers [Route("[controller]")] [Route("v1/[controller]")] [ApiController] + [ApiExplorerSettings(GroupName = "v1")] public class InventoryController : BaseNexpartController { private readonly PartService _inventoryService; diff --git a/PartSource.Api/Controllers/LegacyVehiclesController.cs b/PartSource.Api/Controllers/LegacyVehiclesController.cs index b90fe43..c789dc7 100644 --- a/PartSource.Api/Controllers/LegacyVehiclesController.cs +++ b/PartSource.Api/Controllers/LegacyVehiclesController.cs @@ -20,6 +20,7 @@ namespace PartSource.Api.Controllers [Route("vehicles")] [Route("v1/vehicles")] [ApiController] + [ApiExplorerSettings(GroupName = "v1")] public class LegacyVehiclesController : BaseNexpartController { private readonly VehicleService _vehicleService; @@ -116,7 +117,7 @@ namespace PartSource.Api.Controllers }); } - return Ok(new { data = new { subModel = submodels } }); + return Ok(new { data = new { subModel = nexpartSubmodels } }); } [HttpGet] diff --git a/PartSource.Api/Controllers/NexpartVehiclesController.cs b/PartSource.Api/Controllers/NexpartVehiclesController.cs deleted file mode 100644 index 8e715d5..0000000 --- a/PartSource.Api/Controllers/NexpartVehiclesController.cs +++ /dev/null @@ -1,158 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using PartSource.Data.Nexpart; -using PartSource.Services; -using System.Threading.Tasks; - -namespace PartSource.Api.Controllers -{ - [Route("[controller]")] - [ApiController] - public class NexpartVehiclesController : BaseNexpartController - { - private readonly NexpartService _nexpartService; - - public NexpartVehiclesController(NexpartService nexpartService) - { - this._nexpartService = nexpartService; - } - - [HttpGet] - [Route("types")] - public async Task GetVehicleTypes() - { - NexpartVehiclesController vehiclesController = this; - VehicleTypesGetResponse response = await vehiclesController._nexpartService.SendRequest(new VehicleTypesGet()); - return vehiclesController.NexpartResponse(response); - } - - [HttpGet] - [Route("makes")] - public async Task GetMakes() - { - NexpartVehiclesController vehiclesController = this; - MakeSearch requestContent = new MakeSearch() - { - VehicleTypeId = new int[] { 5, 6, 7 } - }; - MakeSearchResponse response = await vehiclesController._nexpartService.SendRequest(requestContent); - return vehiclesController.NexpartResponse(response); - } - - [HttpGet] - [Route("makes/vehicletypeid/{vehicleTypeId}")] - public async Task GetMakes(int vehicleTypeId) - { - NexpartVehiclesController vehiclesController = this; - MakeSearch requestContent = new MakeSearch() - { - VehicleTypeId = new int[] { vehicleTypeId } - }; - MakeSearchResponse response = await vehiclesController._nexpartService.SendRequest(requestContent); - return vehiclesController.NexpartResponse(response); - } - - [HttpGet] - [Route("models/makeid/{makeId}/modelyear/{year}")] - public async Task GetModels(int makeId, int year) - { - NexpartVehiclesController vehiclesController = this; - ModelSearch requestContent = new ModelSearch() - { - MakeId = makeId, - Year = year, - VehicleTypeId = new int[] { 5, 6, 7 } - }; - ModelSearchResponse response = await vehiclesController._nexpartService.SendRequest(requestContent); - return vehiclesController.NexpartResponse(response); - } - - [HttpGet] - [Route("models/makeid/{makeId}/modelyear/{year}/vehicletypeid/{vehicleTypeId}")] - public async Task GetModels(int makeId, int year, int vehicleTypeId) - { - NexpartVehiclesController vehiclesController = this; - ModelSearch requestContent = new ModelSearch() - { - MakeId = makeId, - Year = year, - VehicleTypeId = new int[] { vehicleTypeId } - }; - ModelSearchResponse response = await vehiclesController._nexpartService.SendRequest(requestContent); - return vehiclesController.NexpartResponse(response); - } - - [HttpGet] - [Route("basevehicle/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")] - public async Task GetBaseVehicle(int makeId, int modelId, int year) - { - BaseVehicleDetailLookup requestContent = new BaseVehicleDetailLookup() - { - MakeId = makeId, - ModelId = modelId, - Year = year - }; - BaseVehicleDetailLookupResponse response = await _nexpartService.SendRequest(requestContent); - return NexpartResponse(response); - } - - [HttpGet] - [Route("engines/basevehicleid/{baseVehicleId}")] - [Route("engines/basevehicleid/{baseVehicleId}/submodelid/{subModelId}")] - public async Task GetEngines(int baseVehicleId, int? subModelId = null) - { - EngineSearch requestContent = new EngineSearch() - { - VehicleIdentifier = new VehicleIdentifier() - { - BaseVehicleId = baseVehicleId - }, - SubModelId = subModelId - }; - EngineSearchResponse response = await _nexpartService.SendRequest(requestContent); - return NexpartResponse(response); - } - - [HttpGet] - [Route("trim/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")] - public async Task GetTrim(int makeId, int modelId, int year) - { - SubModelSearch requestContent = new SubModelSearch() - { - MakeId = makeId, - ModelId = modelId, - Year = year, - RegionId = 2 - }; - SubModelSearchResponse response = await _nexpartService.SendRequest(requestContent); - return NexpartResponse(response); - } - - [HttpGet] - [Route("/detail/basevehicleid/{baseVehicleId}/submodelid/{subModelId}/engineconfigid/{engineConfigId}")] - public async Task GetVehicleId(int baseVehicleId, int subModelId, int engineConfigId) - { - VehicleIdSearch requestContent = new VehicleIdSearch - { - VehicleIdentifier = new VehicleIdentifier() - { - BaseVehicleId = baseVehicleId, - EngineConfigId = engineConfigId - }, - Criterion = new Criterion[1]{ - new Criterion - { - Attribute = "SUB_MODEL", - Id = subModelId - } - }, - RegionId = new RegionId - { - Value = 2 - }, - }; - - VehicleIdSearchResponse response = await _nexpartService.SendRequest(requestContent); - return NexpartResponse(response); - } - } -} diff --git a/PartSource.Api/Controllers/PartsController.cs b/PartSource.Api/Controllers/PartsController.cs index d91a998..c3cc536 100644 --- a/PartSource.Api/Controllers/PartsController.cs +++ b/PartSource.Api/Controllers/PartsController.cs @@ -10,6 +10,7 @@ namespace PartSource.Api.Controllers { [Route("[controller]")] [ApiController] + [ApiExplorerSettings(GroupName = "v1")] public class PartsController : BaseNexpartController { private readonly NexpartService _nexpartService; diff --git a/PartSource.Api/Controllers/SearchController.cs b/PartSource.Api/Controllers/SearchController.cs deleted file mode 100644 index 18d2cbb..0000000 --- a/PartSource.Api/Controllers/SearchController.cs +++ /dev/null @@ -1,32 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using PartSource.Data.Nexpart; -using PartSource.Services; -using System.Threading.Tasks; - -namespace PartSource.Api.Controllers -{ - [Route("[controller]")] - [ApiController] - public class SearchController : BaseNexpartController - { - private readonly NexpartService _nexpartService; - - public SearchController() - { - this._nexpartService = new NexpartService(); - } - - [HttpGet] - [Route("makes/vehicletypeid/{vehicleTypeId}")] - public async Task GetMakes(int vehicleTypeId) - { - SearchController searchController = this; - MakeSearch requestContent = new MakeSearch() - { - VehicleTypeId = new int[] { vehicleTypeId } - }; - MakeSearchResponse response = await searchController._nexpartService.SendRequest(requestContent); - return searchController.NexpartResponse(response); - } - } -} diff --git a/PartSource.Api/Controllers/StoresController.cs b/PartSource.Api/Controllers/StoresController.cs deleted file mode 100644 index 671c37f..0000000 --- a/PartSource.Api/Controllers/StoresController.cs +++ /dev/null @@ -1,47 +0,0 @@ -//using Microsoft.AspNetCore.Mvc; -//using PartSource.Data.Models; -//using PartSource.Services; -//using System.Web.Http; - -//namespace PartSource.Api.Controllers -//{ -// [Route("stores")] -// public class StoresController : ControllerBase -// { -// private const int Count = 5; -// private const int Page = 1; -// private readonly LocationService _service; - -// public StoresController(LocationService service) -// { -// this._service = service; -// } - -// [HttpGet] -// [Route("nearest/{postal}")] -// [Route("nearest/{postal}/count/{count}")] -// [Route("nearest/{postal}/count/{count}/page/{page}")] -// public ActionResult GetNearestStores(string postal, int count = 5, int page = 1) -// { -// PostalCode postalCodeData = this._service.GetPostalCodeData(postal); -// if (postalCodeData == null) -// return (ActionResult)this.BadRequest("Invalid postal code"); -// return (ActionResult)this.Ok(new -// { -// Data = this._service.GetClosestLocations(postalCodeData, count, page) -// }); -// } - -// [HttpGet] -// [Route("nearest/{postal}/radius/{radius}")] -// [Route("nearest/{postal}/radius/{radius}/count/{count}")] -// [Route("nearest/{postal}/radius/{radius}/count/{count}/page/{page}")] -// public ActionResult GetNearestStores(string postal, double radius, int count = 5, int page = 1) -// { -// return (ActionResult)this.Ok(new -// { -// Data = this._service.GetClosestLocations(this._service.GetPostalCodeData(postal), count, page, radius) -// }); -// } -// } -//} diff --git a/PartSource.Api/Controllers/TemplatesController.cs b/PartSource.Api/Controllers/TemplatesController.cs deleted file mode 100644 index 177eb00..0000000 --- a/PartSource.Api/Controllers/TemplatesController.cs +++ /dev/null @@ -1,40 +0,0 @@ -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Primitives; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace PartSource.Api.Controllers -{ - [Route("[controller]")] - [ApiController] - public class TemplatesController : ControllerBase - { - - [Route("{templateName}/{vehicleId}")] - [HttpGet] - public IActionResult GetTemplate(string templateName, int vehicleId) - { - StringValues contentType = new StringValues("application/liquid"); - Response.Headers.Add("Content-Type", contentType); - - string content = $"{templateName},{vehicleId}"; - - return Ok(content); - } - - //Crappy oauth code to make the app installable on shopify - - //HttpRequest request = HttpContext.Request; - - //string location = "https://ratermaniac.myshopify.com/admin/oauth/authorize?client_id=097de154602f28499e058f66b8653033&scope=read_customers&redirect_uri=https://soundpress.com&state=0.4585849384"; - - //StringValues locationHeader = new StringValues(location); - - // //Response.Headers.Add("Location", location); - - // return Redirect(location); - } -} diff --git a/PartSource.Api/Controllers/VehiclesController.cs b/PartSource.Api/Controllers/VehiclesController.cs index a17de05..4c95031 100644 --- a/PartSource.Api/Controllers/VehiclesController.cs +++ b/PartSource.Api/Controllers/VehiclesController.cs @@ -10,8 +10,12 @@ using System.Threading.Tasks; namespace PartSource.Api.Controllers { + /// + /// This endpoint gets vehicle data from WHI's SEO data. + /// [Route("v2/[controller]")] [ApiController] + [ApiExplorerSettings(GroupName = "v2")] public class VehiclesController : BaseApiController { private readonly VehicleService _vehicleService; @@ -21,17 +25,30 @@ namespace PartSource.Api.Controllers _vehicleService = vehicleService; } - [HttpGet] - [Route("")] - public async Task GetVehicles([FromQuery] Vehicle vehicleQuery) + /// + /// Get Vehicles + /// + /// OK: An array of vehicles matching the query. + /// No Content: The query executed successfully, but no vehicles were found. + [HttpGet("")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + public async Task GetVehicles([FromQuery] VehicleDto vehicleQuery) { IList vehicles = await _vehicleService.GetVehicles(vehicleQuery); return ApiResponse(vehicles); } - [HttpGet] - [Route("{id}")] + /// + /// Get Vehicle by ID + /// + /// A WHI VehicleToEngineConfigId + /// OK: The vehicle with the provided VehicleToEngineConfigId. + /// Not Found: No vehicle was found matching the provided VehicleToEngineConfigId. + [HttpGet("{id}")] + [ProducesResponseType(typeof(VehicleDto), 200)] + [ProducesResponseType(404)] public async Task GetVehicleById(int id) { Vehicle vehicle = await _vehicleService.GetVehicleById(id); @@ -39,94 +56,162 @@ namespace PartSource.Api.Controllers return ApiResponse(vehicle); } - [HttpGet] - [Route("makes")] - public async Task GetMakes([FromQuery] VehicleMake vehicleMake) + /// + /// Get Makes + /// + /// OK: An array of makes matching the query. + /// No Content: The query executed successfully, but no makes were found. + [HttpGet("makes")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + public async Task GetMakes([FromQuery] VehicleDto vehicleQuery) { - IList makes = await _vehicleService.GetMakes(vehicleMake); + IList makes = await _vehicleService.GetMakes(vehicleQuery); return ApiResponse(makes); } - [HttpGet] - [Route("makes/{id}")] + /// + /// Get Make by ID + /// + /// A WHI MakeId + /// OK: The make with the provided MakeId. + /// Not Found: No make was found matching the provided MakeId. + [HttpGet("makes/{id}")] + [ProducesResponseType(typeof(MakeDto), 200)] + [ProducesResponseType(404)] public async Task GetMakeById(int id) { - VehicleMake make = await _vehicleService.GetMakeById(id); + MakeDto make = await _vehicleService.GetMakeById(id); return ApiResponse(make); } - [HttpGet] - [Route("models")] - public async Task GetModels([FromQuery] VehicleModel vehicleModel) + /// + /// Get Models + /// + /// OK: An array of models matching the query. + /// No Content: The query executed successfully, but no models were found. + [HttpGet("models")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + public async Task GetModels([FromQuery] VehicleDto vehicleQuery) { - IList models = await _vehicleService.GetModels(vehicleModel); + IList models = await _vehicleService.GetModels(vehicleQuery); return ApiResponse(models); } - [HttpGet] - [Route("models/{id}")] + /// + /// Get Model by ID + /// + /// A WHI ModelId + /// OK: The model with the provided ModelId. + /// Not Found: No model was found matching the provided ModelId. + [HttpGet("models/{id}")] + [ProducesResponseType(typeof(ModelDto), 200)] + [ProducesResponseType(404)] public async Task GetModelById(int id) { - VehicleModel model = await _vehicleService.GetModelById(id); + ModelDto model = await _vehicleService.GetModelById(id); return ApiResponse(model); } - [HttpGet] - [Route("basevehicles")] - public async Task GetBaseVehicles([FromQuery] BaseVehicle baseVehicle) + /// + /// Get Submodels + /// + /// /// Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model. + /// OK: An array of submodels matching the query. + /// No Content: The query executed successfully, but no submodels were found. + [HttpGet("submodels")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + public async Task GetSubmodels([FromQuery] VehicleDto vehicleQuery) { - IList baseVehicles = await _vehicleService.GetBaseVehicles(baseVehicle); - - return ApiResponse(baseVehicles); - } - - [HttpGet] - [Route("basevehicles/{id}")] - public async Task GetBaseVehicleById(int id) - { - BaseVehicle baseVehicle = await _vehicleService.GetBaseVehicleById(id); - - return ApiResponse(baseVehicle); - } - - [HttpGet] - [Route("engines")] - public async Task GetEngines([FromQuery] Engine engine) - { - IList engines = await _vehicleService.GetEngines(engine); - - return ApiResponse(engines); - } - - [HttpGet] - [Route("engines/{id}")] - public async Task GetEngineById(int id) - { - EngineDto engine = await _vehicleService.GetEngineById(id); - - return ApiResponse(engine); - } - - [HttpGet] - [Route("submodels")] - public async Task GetSubmodels([FromQuery] Submodel submodelQuery) - { - IList submodels = await _vehicleService.GetSubmodels(submodelQuery); + IList submodels = await _vehicleService.GetSubmodels(vehicleQuery); return ApiResponse(submodels); } - [HttpGet] - [Route("submodels/{id}")] + /// + /// Get Submodel by ID + /// + /// Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model. + /// A WHI SubmodelId + /// OK: The submodel with the provided SubmodelId. + /// Not Found: No submodel was found matching the provided SubmodelId. + [HttpGet("submodels/{id}")] + [ProducesResponseType(typeof(SubmodelDto), 200)] + [ProducesResponseType(404)] public async Task GetSubmodels(int id) { SubmodelDto submodel = await _vehicleService.GetSubmodelById(id); return ApiResponse(submodel); } + + /// + /// Get Base Vehicles + /// + /// OK: An array of base vehicles matching the query. + /// No Content: The query executed successfully, but no base vehicles were found. + [HttpGet("basevehicles")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + + public async Task GetBaseVehicles([FromQuery] VehicleDto vehicleQuery) + { + IList baseVehicles = await _vehicleService.GetBaseVehicles(vehicleQuery); + + return ApiResponse(baseVehicles); + } + + /// + /// Get Base Vehicle by ID + /// + /// A WHI BaseVehicleId + /// OK: The base vehicle with the provided BaseVehicleId. + /// Not Found: No base vehicle was found matching the provided BaseVehicleId. + [HttpGet("basevehicles/{id}")] + [ProducesResponseType(typeof(BaseVehicleDto), 200)] + [ProducesResponseType(404)] + public async Task GetBaseVehicleById(int id) + { + BaseVehicleDto baseVehicle = await _vehicleService.GetBaseVehicleById(id); + + return ApiResponse(baseVehicle); + } + + /// + /// Get Engines + /// + /// OK: An array of engines matching the query. + /// No Content: The query executed successfully, but no engines were found. + [HttpGet("engines")] + [ProducesResponseType(typeof(IList), 200)] + [ProducesResponseType(204)] + public async Task GetEngines([FromQuery] VehicleDto vehicleQuery) + { + IList engines = await _vehicleService.GetEngines(vehicleQuery); + + return ApiResponse(engines); + } + + /// + /// Get Engine by ID + /// + /// A WHI EngineConfigId + /// OK: The engine with the provided EngineConfigId. + /// Not Found: No engine was found matching the provided EngineConfigId. + [HttpGet("engines/{id}")] + [ProducesResponseType(typeof(EngineDto), 200)] + [ProducesResponseType(404)] + public async Task GetEngineById(int id) + { + EngineDto engine = await _vehicleService.GetEngineById(id); + + return ApiResponse(engine); + } } } diff --git a/PartSource.Api/PartSource.Api.csproj b/PartSource.Api/PartSource.Api.csproj index 7e08a9e..9edd31e 100644 --- a/PartSource.Api/PartSource.Api.csproj +++ b/PartSource.Api/PartSource.Api.csproj @@ -6,6 +6,11 @@ Debug;Release;Also Debug + + C:\Users\Tommy\source\repos\ratermania\partsource\PartSource.Api\PartSource.Api.xml + 1701;1702;1591 + + @@ -15,15 +20,18 @@ + Always - + + + diff --git a/PartSource.Api/PartSource.Api.xml b/PartSource.Api/PartSource.Api.xml new file mode 100644 index 0000000..a364bd7 --- /dev/null +++ b/PartSource.Api/PartSource.Api.xml @@ -0,0 +1,110 @@ + + + + PartSource.Api + + + + + This controller exists to enable legacy support for the old Nexpart-based vehicle lookup. + + + + + This endpoint gets vehicle data from WHI's SEO data. + + + + + Get Vehicles + + OK: An array of vehicles matching the query. + No Content: The query executed successfully, but no vehicles were found. + + + + Get Vehicle by ID + + A WHI VehicleToEngineConfigId + OK: The vehicle with the provided VehicleToEngineConfigId. + Not Found: No vehicle was found matching the provided VehicleToEngineConfigId. + + + + Get Makes + + OK: An array of makes matching the query. + No Content: The query executed successfully, but no makes were found. + + + + Get Make by ID + + A WHI MakeId + OK: The make with the provided MakeId. + Not Found: No make was found matching the provided MakeId. + + + + Get Models + + OK: An array of models matching the query. + No Content: The query executed successfully, but no models were found. + + + + Get Model by ID + + A WHI ModelId + OK: The model with the provided ModelId. + Not Found: No model was found matching the provided ModelId. + + + + Get Submodels + + /// Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model. + OK: An array of submodels matching the query. + No Content: The query executed successfully, but no submodels were found. + + + + Get Submodel by ID + + Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model. + A WHI SubmodelId + OK: The submodel with the provided SubmodelId. + Not Found: No submodel was found matching the provided SubmodelId. + + + + Get Base Vehicles + + OK: An array of base vehicles matching the query. + No Content: The query executed successfully, but no base vehicles were found. + + + + Get Base Vehicle by ID + + A WHI BaseVehicleId + OK: The base vehicle with the provided BaseVehicleId. + Not Found: No base vehicle was found matching the provided BaseVehicleId. + + + + Get Engines + + OK: An array of engines matching the query. + No Content: The query executed successfully, but no engines were found. + + + + Get Engine by ID + + A WHI EngineConfigId + OK: The engine with the provided EngineConfigId. + Not Found: No engine was found matching the provided EngineConfigId. + + + diff --git a/PartSource.Api/Startup.cs b/PartSource.Api/Startup.cs index 00f7484..7197ffe 100644 --- a/PartSource.Api/Startup.cs +++ b/PartSource.Api/Startup.cs @@ -1,11 +1,16 @@ -using Microsoft.AspNetCore.Builder; +using AutoMapper; +using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc.Formatters; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.OpenApi.Models; using PartSource.Api.Formatters; using PartSource.Data; +using PartSource.Data.AutoMapper; using PartSource.Services; +using System.IO; namespace PartSource.Api { @@ -23,10 +28,20 @@ namespace PartSource.Api { services.AddMvc(options => { - options.OutputFormatters.Add(new LiquidTemplateOutputFormatter()); + options.OutputFormatters.RemoveType(typeof(StringOutputFormatter)); options.EnableEndpointRouting = false; }); + services.AddSwaggerGen(c => + { + c.SwaggerDoc("v2", new OpenApiInfo { Title = "Partsource/WHI Integration API", Version = "v2" }); + + c.IncludeXmlComments(Path.Combine(System.AppContext.BaseDirectory, "PartSource.Api.xml")); + c.IncludeXmlComments(Path.Combine(System.AppContext.BaseDirectory, "PartSource.Data.xml")); + }); + + services.AddAutoMapper(typeof(PartSourceProfile)); + services.AddTransient(); services.AddTransient(); services.AddTransient(); @@ -58,7 +73,14 @@ namespace PartSource.Api app.UseCors("Default"); - // app.UseExceptionHandler("/Error"); + app.UseSwagger(); + app.UseReDoc(c => + { + c.SpecUrl = "/swagger/v2/swagger.json"; + c.ExpandResponses(string.Empty); + }); + + // app.UseExceptionHandler("/Error"); // app.UseHttpsRedirection(); app.UseMvc(); } diff --git a/PartSource.Automation/.editorconfig b/PartSource.Automation/.editorconfig deleted file mode 100644 index b0099bc..0000000 --- a/PartSource.Automation/.editorconfig +++ /dev/null @@ -1,13 +0,0 @@ -[*.cs] - -# CA1307: Specify StringComparison -dotnet_diagnostic.CA1307.severity = suggestion - -# CA1031: Do not catch general exception types -dotnet_diagnostic.CA1031.severity = none - -# CA2007: Consider calling ConfigureAwait on the awaited task -dotnet_diagnostic.CA2007.severity = silent - -# CA1303: Do not pass literals as localized parameters -dotnet_diagnostic.CA1303.severity = silent diff --git a/PartSource.Automation/Jobs/AddAndUpdateProducts.cs b/PartSource.Automation/Jobs/AddAndUpdateProducts.cs index 190b7e3..7a1b8d6 100644 --- a/PartSource.Automation/Jobs/AddAndUpdateProducts.cs +++ b/PartSource.Automation/Jobs/AddAndUpdateProducts.cs @@ -3,8 +3,8 @@ using PartSource.Automation.Jobs.Interfaces; using PartSource.Automation.Models; using PartSource.Data; using PartSource.Data.Models; -using PartSource.Services.Integrations; -using Ratermania.Shopify.Entities; +using Ratermania.Shopify; +using Ratermania.Shopify.Resources; using System; using System.Collections.Generic; using System.Linq; @@ -26,9 +26,11 @@ namespace PartSource.Automation.Jobs public async Task Run() { - //await SyncronizeIdsAndSkus(); + //throw new Exception("You need to add a ProductVariant resource to the Shopify client"); + + await SyncronizeIdsAndSkus(); //await AddSkus(); - await AddVariants(); + // await AddVariants(); return new AutomationJobResult { @@ -49,7 +51,7 @@ namespace PartSource.Automation.Jobs { foreach (Product product in products) { - foreach (ProductVariant variant in product.Variants) + foreach (Variant variant in product.Variants) { ImportData importData = _partSourceContext.ImportData.FirstOrDefault(i => i.VariantSku == variant.Sku); @@ -119,20 +121,20 @@ namespace PartSource.Automation.Jobs items[0].PartNumber, }; - List productVariants = new List(); + //List productVariants = new List(); - //foreach (ImportData itemVariant in items) + ////foreach (ImportData itemVariant in items) + ////{ + //productVariants.Add(new ProductVariant //{ - productVariants.Add(new ProductVariant - { - InventoryPolicy = "Deny", - CompareAtPrice = importData.CompareAt, - Price = importData.Price, - Sku = importData.VariantSku, - Title = importData.VariantTitle ?? importData.Title, - Option1 = importData.VariantTitle, - RequiresShipping = false, - }); + // InventoryPolicy = "Deny", + // CompareAtPrice = importData.CompareAt, + // Price = importData.Price, + // Sku = importData.VariantSku, + // Title = importData.VariantTitle ?? importData.Title, + // Option1 = importData.VariantTitle, + // RequiresShipping = false, + //}); //} @@ -146,7 +148,7 @@ namespace PartSource.Automation.Jobs Published = true, //ProductType = importData.FINELINE_NM, Images = productImages.ToArray(), - Variants = productVariants.ToArray(), + //Variants = productVariants.ToArray(), CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; @@ -234,21 +236,21 @@ namespace PartSource.Automation.Jobs items[0].PartNumber, }; - List productVariants = new List(); + //List productVariants = new List(); - foreach (ImportData itemVariant in items) - { - productVariants.Add(new ProductVariant - { - InventoryPolicy = "Deny", - CompareAtPrice = itemVariant.CompareAt, - Price = itemVariant.Price, - Sku = itemVariant.VariantSku, - Title = itemVariant.VariantTitle, - Option1 = itemVariant.VariantTitle, - RequiresShipping = false, - }); - } + //foreach (ImportData itemVariant in items) + //{ + // productVariants.Add(new ProductVariant + // { + // InventoryPolicy = "Deny", + // CompareAtPrice = itemVariant.CompareAt, + // Price = itemVariant.Price, + // Sku = itemVariant.VariantSku, + // Title = itemVariant.VariantTitle, + // Option1 = itemVariant.VariantTitle, + // RequiresShipping = false, + // }); + //} Product requestData = new Product @@ -260,7 +262,7 @@ namespace PartSource.Automation.Jobs Published = true, //ProductType = importData.FINELINE_NM, Images = productImages.ToArray(), - Variants = productVariants.ToArray(), + //Variants = productVariants.ToArray(), CreatedAt = DateTime.Now, UpdatedAt = DateTime.Now }; diff --git a/PartSource.Automation/Jobs/DeleteProducts.cs b/PartSource.Automation/Jobs/DeleteProducts.cs index 461ab10..9b9c901 100644 --- a/PartSource.Automation/Jobs/DeleteProducts.cs +++ b/PartSource.Automation/Jobs/DeleteProducts.cs @@ -1,8 +1,8 @@ using PartSource.Automation.Jobs.Interfaces; using PartSource.Automation.Models; using PartSource.Data; -using PartSource.Services.Integrations; -using Ratermania.Shopify.Entities; +using Ratermania.Shopify; +using Ratermania.Shopify.Resources; using System; using System.Collections.Generic; using System.Linq; diff --git a/PartSource.Automation/Jobs/UpdateFitment - Copy.cs b/PartSource.Automation/Jobs/UpdateFitment - Copy.cs index 7394b1d..e5bc930 100644 --- a/PartSource.Automation/Jobs/UpdateFitment - Copy.cs +++ b/PartSource.Automation/Jobs/UpdateFitment - Copy.cs @@ -7,7 +7,6 @@ using PartSource.Data; using PartSource.Data.Models; using PartSource.Data.Nexpart; using PartSource.Services; -using PartSource.Services.Integrations; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -18,7 +17,8 @@ using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -using Ratermania.Shopify.Entities; +using Ratermania.Shopify; +using Ratermania.Shopify.Resources; namespace PartSource.Automation.Jobs { @@ -40,110 +40,128 @@ namespace PartSource.Automation.Jobs public async Task Run() { - IList parts = _partSourceContext.ImportData.Where(i => new[] { "Raybestos Brakes", "MotoMaster", "Pro Series OE Plus Brake Pads" }.Contains(i.Vendor)).ToList(); + IList parts = _partSourceContext.ImportData + .Where(p => p.UpdatedAt < DateTime.Now.AddDays(-3)) + .Take(50) + .ToList(); - foreach (ImportData importData in parts) + while (parts != null && parts.Count > 0) { - try + foreach (ImportData importData in parts) { - Product product = await _shopifyClient.Products.GetById((long)importData.ShopifyId); - if (product == null) + try { - continue; - } - - - bool isFitment = false; - - IList vehicles = _vehicleService.GetVehiclesForPart(importData.PartNumber, importData.LineCode); - IList vehicleIdFitment = _vehicleService.GetVehicleIdFitment(vehicles); - if (vehicleIdFitment.Count > 0) - { - isFitment = true; - - string json = JsonConvert.SerializeObject(vehicleIdFitment); - if (json.Length >= 100000) + Product product = await _shopifyClient.Products.GetById(importData.ShopifyId.GetValueOrDefault()); + if (product == null) { continue; } - Metafield vehicleMetafield = new Metafield + + bool isFitment = false; + + IList vehicles = _vehicleService.GetVehiclesForPart(importData.PartNumber, importData.LineCode); + IList vehicleIdFitment = _vehicleService.GetVehicleIdFitment(vehicles); + if (vehicleIdFitment.Count > 0) { - Namespace = "fitment", - Key = "ids", - Value = json, - ValueType = "json_string", - OwnerResource = "product", - OwnerId = product.Id - }; + isFitment = true; - await _shopifyClient.Metafields.Add(vehicleMetafield); - } + string json = JsonConvert.SerializeObject(vehicleIdFitment); + if (json.Length >= 100000) + { + continue; + } - IList ymmFitment = _vehicleService.GetYmmFitment(vehicles); - if (ymmFitment.Count > 0) - { - isFitment = true; + Metafield vehicleMetafield = new Metafield + { + Namespace = "fitment", + Key = "ids", + Value = json, + ValueType = "json_string", + OwnerResource = "product", + OwnerId = product.Id + }; - string json = JsonConvert.SerializeObject(ymmFitment); - if (json.Length >= 100000) - { - continue; + await _shopifyClient.Metafields.Add(vehicleMetafield); } - Metafield ymmMetafield = new Metafield + IList ymmFitment = _vehicleService.GetYmmFitment(vehicles); + if (ymmFitment.Count > 0) { - Namespace = "fitment", - Key = "seo", - Value = json, - ValueType = "json_string", + isFitment = true; + + string json = JsonConvert.SerializeObject(ymmFitment); + if (json.Length >= 100000) + { + continue; + } + + Metafield ymmMetafield = new Metafield + { + Namespace = "fitment", + Key = "seo", + Value = json, + ValueType = "json_string", + OwnerResource = "product", + OwnerId = product.Id + }; + + await _shopifyClient.Metafields.Add(ymmMetafield); + } + + Metafield isFitmentMetafield = new Metafield + { + Namespace = "Flags", + Key = "IsFitment", + Value = isFitment.ToString(), + ValueType = "string", OwnerResource = "product", OwnerId = product.Id }; - await _shopifyClient.Metafields.Add(ymmMetafield); - } + await _shopifyClient.Metafields.Add(isFitmentMetafield); - Metafield isFitmentMetafield = new Metafield - { - Namespace = "Flags", - Key = "IsFitment", - Value = isFitment.ToString(), - ValueType = "string", - OwnerResource = "product", - OwnerId = product.Id - }; - - await _shopifyClient.Metafields.Add(isFitmentMetafield); - - List tags = new List + List tags = new List { importData.LineCode, importData.PartNumber }; - for (int i = 0; i < vehicleIdFitment.Count; i += 25) - { - tags.Add(string.Join('-', vehicleIdFitment.Skip(i).Take(25).Select(i => $"v{i}"))); + for (int j = 0; j < vehicleIdFitment.Count; j += 25) + { + tags.Add(string.Join('-', vehicleIdFitment.Skip(j).Take(25).Select(j => $"v{j}"))); + } + + tags.AddRange(ymmFitment); + + if (tags.Count > 250) + { + tags = tags.Take(250).ToList(); + } + + product.Tags = string.Join(',', tags); + + await _shopifyClient.Products.Update(product); + + importData.UpdatedAt = DateTime.Now; + importData.UpdateType = "Fitment"; } - - tags.AddRange(ymmFitment); - - if (tags.Count > 250) + + catch (Exception ex) { - tags = tags.Take(250).ToList(); + if (!ex.Message.Contains("response content", StringComparison.InvariantCultureIgnoreCase)) + { + Console.WriteLine($"{importData.VariantSku}: {ex.Message}"); + } } - - product.Tags = string.Join(',', tags); - - await _shopifyClient.Products.Update(product); - } - catch (Exception ex) - { - Console.WriteLine($"{importData.VariantSku}: {ex.Message}"); - } + await _partSourceContext.SaveChangesAsync(); + + parts = _partSourceContext.ImportData + .Where(p => p.UpdatedAt < DateTime.Now.AddDays(-3)) + .Take(50) + .ToList(); } diff --git a/PartSource.Automation/Jobs/UpdatePositioning.cs b/PartSource.Automation/Jobs/UpdatePositioning.cs index 01e52e9..0f47758 100644 --- a/PartSource.Automation/Jobs/UpdatePositioning.cs +++ b/PartSource.Automation/Jobs/UpdatePositioning.cs @@ -5,13 +5,12 @@ using PartSource.Automation.Models; using PartSource.Data; using PartSource.Data.Models; using PartSource.Services; -using PartSource.Services.Integrations; -using Ratermania.Shopify.Entities; +using Ratermania.Shopify; +using Ratermania.Shopify.Resources; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; diff --git a/PartSource.Automation/Jobs/UpdatePricing.cs b/PartSource.Automation/Jobs/UpdatePricing.cs index 8afdf0c..a0baf89 100644 --- a/PartSource.Automation/Jobs/UpdatePricing.cs +++ b/PartSource.Automation/Jobs/UpdatePricing.cs @@ -2,8 +2,9 @@ using PartSource.Automation.Models; using PartSource.Data; using PartSource.Data.Models; -using PartSource.Services.Integrations; -using Ratermania.Shopify.Entities; +using Ratermania.Shopify; +using Ratermania.Shopify.Resources; +using Ratermania.Shopify.Resources.Enums; using System; using System.Collections.Generic; using System.Linq; @@ -49,7 +50,7 @@ namespace PartSource.Automation.Jobs { if (product.Variants.Length > 0) { - ProductVariant variant = product.Variants[0]; + Variant variant = product.Variants[0]; PartPrice partPrice = _partSourceContext.PartPrices.Where(p => p.SKU == variant.Sku).FirstOrDefault(); if (partPrice == null || !partPrice.Your_Price.HasValue || !partPrice.Compare_Price.HasValue) @@ -64,6 +65,8 @@ namespace PartSource.Automation.Jobs product.Variants[0].Price = partPrice.Your_Price.Value; product.Variants[0].CompareAtPrice = partPrice.Compare_Price.Value; + product.PublishedAt = partPrice.Active.ToUpperInvariant() == "Y" ? DateTime.Now : default; + Metafield metafield = new Metafield { Namespace = "Pricing", @@ -76,6 +79,7 @@ namespace PartSource.Automation.Jobs try { + await _shopifyClient.Metafields.Add(metafield); await _shopifyClient.Products.Update(product); updateCount++; diff --git a/PartSource.Automation/PartSource.Automation.csproj b/PartSource.Automation/PartSource.Automation.csproj index b43d74e..a73fa91 100644 --- a/PartSource.Automation/PartSource.Automation.csproj +++ b/PartSource.Automation/PartSource.Automation.csproj @@ -3,11 +3,12 @@ Exe netcoreapp3.1 - AnyCPU;x86;x64 Debug;Release;Also Debug + + all runtime; build; native; contentfiles; analyzers; buildtransitive @@ -15,10 +16,10 @@ - + diff --git a/PartSource.Automation/Program.cs b/PartSource.Automation/Program.cs index 869295e..c7374a3 100644 --- a/PartSource.Automation/Program.cs +++ b/PartSource.Automation/Program.cs @@ -1,29 +1,23 @@ -using PartSource.Services; -using PartSource.Automation.Factories; -using PartSource.Automation.Jobs.Interfaces; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Configuration.FileExtensions; -using Microsoft.Extensions.Configuration.Json; - -using PartSource.Data; -using System.IO; +using AutoMapper; using Microsoft.EntityFrameworkCore; -using PartSource.Automation.Models.Configuration; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using PartSource.Automation.Factories; using PartSource.Automation.Jobs; -using PartSource.Automation.Services; -using PartSource.Services.Integrations; - -using Ratermania.Shopify.DependencyInjection; +using PartSource.Automation.Jobs.Interfaces; using PartSource.Automation.Models; +using PartSource.Automation.Models.Configuration; +using PartSource.Automation.Services; +using PartSource.Data; +using PartSource.Data.AutoMapper; +using PartSource.Services; +using Ratermania.Shopify; +using Ratermania.Shopify.DependencyInjection; +using System; +using System.IO; namespace PartSource.Automation -{ - internal class Program +{ internal class Program { private static void Main(string[] args) @@ -86,13 +80,15 @@ namespace PartSource.Automation options.UseSqlServer(configuration.GetConnectionString("PartSourceDatabase"), opts => opts.EnableRetryOnFailure()) ) - .AddShopify(options => { + .AddShopify(options => + { options.ApiKey = configuration["Shopify:ApiKey"]; options.ApiSecret = configuration["Shopify:ApiSecret"]; options.ApiVersion = "2020-01"; options.ShopDomain = configuration["Shopify:ShopDomain"]; }) + .AddAutoMapper(typeof(PartSourceProfile)) .AddSingleton(emailConfiguration) .AddSingleton(ftpConfiguration) diff --git a/PartSource.Automation/Properties/launchSettings.json b/PartSource.Automation/Properties/launchSettings.json index 4e4d7f7..9c692e8 100644 --- a/PartSource.Automation/Properties/launchSettings.json +++ b/PartSource.Automation/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "PartSource.Automation": { "commandName": "Project", - "commandLineArgs": "UpdatePositioning", + "commandLineArgs": "AddAndUpdateProducts", "environmentVariables": { "PS_AUTOMATION_ENVIRONMENT": "development" } diff --git a/PartSource.Automation/appsettings.json b/PartSource.Automation/appsettings.json index 88da9b3..bc06491 100644 --- a/PartSource.Automation/appsettings.json +++ b/PartSource.Automation/appsettings.json @@ -1,7 +1,7 @@ { "ConnectionStrings": { - "PartSourceDatabase": "Server=localhost;Database=ps-whi-stage;Integrated Security=True;" - //"PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" + //"PartSourceDatabase": "Server=localhost;Database=ps-whi-stage;Integrated Security=True;" + "PartSourceDatabase": "Server=tcp:ps-whi.database.windows.net,1433;Initial Catalog=ps-whi-stage;Persist Security Info=False;User ID=ps-whi;Password=9-^*N5dw!6:|.5Q;MultipleActiveResultSets=True;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;" }, "emailConfiguration": { diff --git a/PartSource.Data/AutoMapper/PartSourceProfile.cs b/PartSource.Data/AutoMapper/PartSourceProfile.cs new file mode 100644 index 0000000..29f6f24 --- /dev/null +++ b/PartSource.Data/AutoMapper/PartSourceProfile.cs @@ -0,0 +1,22 @@ +using AutoMapper; +using PartSource.Data.Dtos; +using PartSource.Data.Models; +using System; +using System.Collections.Generic; +using System.Text; + +namespace PartSource.Data.AutoMapper +{ + public class PartSourceProfile : Profile + { + public PartSourceProfile() + { + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + CreateMap(); + } + } +} diff --git a/PartSource.Data/Dtos/BaseVehicleDto.cs b/PartSource.Data/Dtos/BaseVehicleDto.cs new file mode 100644 index 0000000..ac398fa --- /dev/null +++ b/PartSource.Data/Dtos/BaseVehicleDto.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PartSource.Data.Dtos +{ + public class BaseVehicleDto + { + /// 149268 + public int BaseVehicleId { get; set; } + + /// Audi + public string MakeName { get; set; } + + /// 73 + public int MakeId { get; set; } + + /// R8 + public string ModelName { get; set; } + + /// 6039 + public int ModelId { get; set; } + + /// 2020 + public int Year { get; set; } + } +} diff --git a/PartSource.Data/Dtos/EngineDto.cs b/PartSource.Data/Dtos/EngineDto.cs index 4731596..5f924db 100644 --- a/PartSource.Data/Dtos/EngineDto.cs +++ b/PartSource.Data/Dtos/EngineDto.cs @@ -6,12 +6,16 @@ namespace PartSource.Data.Dtos { public class EngineDto { + /// 24294 public int EngineConfigId { get; set; } - public string Description { get; set; } + /// V10-5204cc 5.2L FI DKAA 602HP + public string EngineDescription { get; set; } - public string Make { get; set; } + /// Audi + public string MakeName { get; set; } + /// 73 public int MakeId { get; set; } } } diff --git a/PartSource.Data/Dtos/MakeDto.cs b/PartSource.Data/Dtos/MakeDto.cs new file mode 100644 index 0000000..ea05417 --- /dev/null +++ b/PartSource.Data/Dtos/MakeDto.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PartSource.Data.Dtos +{ + public class MakeDto + { + /// 73 + public int MakeId { get; set; } + + /// Audi + public string MakeName { get; set; } + } +} diff --git a/PartSource.Data/Dtos/ModelDto.cs b/PartSource.Data/Dtos/ModelDto.cs new file mode 100644 index 0000000..39541a6 --- /dev/null +++ b/PartSource.Data/Dtos/ModelDto.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PartSource.Data.Dtos +{ + public class ModelDto + { + /// 6039 + public int ModelId { get; set; } + + /// R8 + public string ModelName { get; set; } + + /// 73 + public int MakeId { get; set; } + + /// Audi + public string MakeName { get; set; } + } +} diff --git a/PartSource.Data/Dtos/SubmodelDto.cs b/PartSource.Data/Dtos/SubmodelDto.cs index 5d7498d..7983ccc 100644 --- a/PartSource.Data/Dtos/SubmodelDto.cs +++ b/PartSource.Data/Dtos/SubmodelDto.cs @@ -6,12 +6,16 @@ namespace PartSource.Data.Dtos { public class SubmodelDto { - public int Id { get; set; } + /// 897 + public int SubmodelId { get; set; } - public string Name { get; set; } + /// Performance + public string SubmodelName { get; set; } - public string Make { get; set; } + /// Audi + public string MakeName { get; set; } + /// 73 public int MakeId { get; set; } } } diff --git a/PartSource.Data/Dtos/VehicleDto.cs b/PartSource.Data/Dtos/VehicleDto.cs new file mode 100644 index 0000000..15fda7c --- /dev/null +++ b/PartSource.Data/Dtos/VehicleDto.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace PartSource.Data.Dtos +{ + public class VehicleDto + { + /// 73 + public int MakeId { get; set; } + + /// Audi + public string MakeName { get; set; } + + /// 6039 + public int ModelId { get; set; } + + /// R8 + public string ModelName { get; set; } + + /// 24294 + public int EngineConfigId { get; set; } + + /// V10-5204cc 5.2L FI DKAA 602HP + public string EngineDescription { get; set; } + + /// 149268 + public int BaseVehicleId { get; set; } + + /// 2020 + public int Year { get; set; } + + /// 897 + public int SubmodelId { get; set; } + + /// Performance + public string SubmodelName { get; set; } + + /// 523387 + public int VehicleToEngineConfigId { get; set; } + } +} diff --git a/PartSource.Data/Models/VehicleData.cs b/PartSource.Data/Models/Vehicle.cs similarity index 100% rename from PartSource.Data/Models/VehicleData.cs rename to PartSource.Data/Models/Vehicle.cs diff --git a/PartSource.Data/PartSource.Data.csproj b/PartSource.Data/PartSource.Data.csproj index 677b20b..9777abf 100644 --- a/PartSource.Data/PartSource.Data.csproj +++ b/PartSource.Data/PartSource.Data.csproj @@ -5,6 +5,11 @@ Debug;Release;Also Debug + + 1701;1702;1591 + C:\Users\Tommy\source\repos\ratermania\partsource\PartSource.Data\PartSource.Data.xml + + @@ -12,10 +17,15 @@ + + + + + diff --git a/PartSource.Data/PartSource.Data.xml b/PartSource.Data/PartSource.Data.xml new file mode 100644 index 0000000..c59d168 --- /dev/null +++ b/PartSource.Data/PartSource.Data.xml @@ -0,0 +1,115 @@ + + + + PartSource.Data + + + + 149268 + + + Audi + + + 73 + + + R8 + + + 6039 + + + 2020 + + + 24294 + + + V10-5204cc 5.2L FI DKAA 602HP + + + Audi + + + 73 + + + 73 + + + Audi + + + 6039 + + + R8 + + + 73 + + + Audi + + + 897 + + + Performance + + + Audi + + + 73 + + + 73 + + + Audi + + + 6039 + + + R8 + + + 24294 + + + V10-5204cc 5.2L FI DKAA 602HP + + + 149268 + + + 2020 + + + 897 + + + Performance + + + 523387 + + + + Utility methods related to CLR Types for SQL Server + + + + + Loads the required native assemblies for the current architecture (x86 or x64) + + + Root path of the current application. Use Server.MapPath(".") for ASP.NET applications + and AppDomain.CurrentDomain.BaseDirectory for desktop applications. + + + + diff --git a/PartSource.Services/Extensions/IQueryableExtensions.cs b/PartSource.Services/Extensions/IQueryableExtensions.cs index 2fc6c28..ffeac5a 100644 --- a/PartSource.Services/Extensions/IQueryableExtensions.cs +++ b/PartSource.Services/Extensions/IQueryableExtensions.cs @@ -13,9 +13,9 @@ namespace PartSource.Services.Extensions { public static class IQueryableExtensions { - public static IQueryable ApplyQueryDto(this IQueryable queryable, T dto) + public static IQueryable ApplyQueryDto(this IQueryable queryable, U dto) { - foreach (PropertyInfo property in typeof(T).GetProperties()) + foreach (PropertyInfo property in typeof(U).GetProperties()) { object value = property.GetValue(dto); diff --git a/PartSource.Services/Integrations/ShopifyClient.cs b/PartSource.Services/Integrations/ShopifyClient.cs deleted file mode 100644 index c05041d..0000000 --- a/PartSource.Services/Integrations/ShopifyClient.cs +++ /dev/null @@ -1,23 +0,0 @@ -//using PartSource.Data.Shopify; -using Ratermania.Shopify; -using Ratermania.Shopify.Entities; -using System; -using System.Collections.Generic; -using System.Text; - -namespace PartSource.Services.Integrations -{ - public class ShopifyClient : BaseShopifyClient - { - public ShopifyClient(ShopifyOptionsBuilder optionsBuilder) : base(optionsBuilder) { } - - public ShopifyResource Products { get; set; } - - public ShopifyResource Metafields { get; set; } - - //public ShopifyResource SmartCollections { get; set; } - - public ShopifyResource ProductImages { get; set; } - } -} - diff --git a/PartSource.Services/PartSource.Services.csproj b/PartSource.Services/PartSource.Services.csproj index 06299f3..a3887df 100644 --- a/PartSource.Services/PartSource.Services.csproj +++ b/PartSource.Services/PartSource.Services.csproj @@ -6,8 +6,8 @@ + - @@ -20,4 +20,8 @@ + + + + diff --git a/PartSource.Services/VehicleService.cs b/PartSource.Services/VehicleService.cs index b74acb7..9c3d3bf 100644 --- a/PartSource.Services/VehicleService.cs +++ b/PartSource.Services/VehicleService.cs @@ -1,4 +1,5 @@ -using Microsoft.EntityFrameworkCore; +using AutoMapper; +using Microsoft.EntityFrameworkCore; using PartSource.Data; using PartSource.Data.Dtos; using PartSource.Data.Models; @@ -14,14 +15,16 @@ namespace PartSource.Services { public class VehicleService { + private readonly IMapper _mapper; private readonly PartSourceContext _partSourceContext; - public VehicleService(PartSourceContext partSourceContext) + public VehicleService(IMapper mapper, PartSourceContext partSourceContext) { + _mapper = mapper; _partSourceContext = partSourceContext; } - public async Task> GetVehicles(Vehicle vehicleQuery) + public async Task> GetVehicles(VehicleDto vehicleQuery) { return await _partSourceContext.Vehicles .ApplyQueryDto(vehicleQuery) @@ -34,6 +37,134 @@ namespace PartSource.Services .FirstOrDefaultAsync(v => v.VehicleToEngineConfigId == vehicleToEngineConfigId); } + public async Task> GetMakes(VehicleDto vehicleQuery) + { + return await _partSourceContext.Vehicles + .ApplyQueryDto(vehicleQuery) + .Select(v => new MakeDto + { + MakeId = v.MakeId, + MakeName = v.MakeName + + }) + .Distinct() + .OrderBy(v => v.MakeName) + .ToListAsync(); + } + + public async Task GetMakeById(int makeId) + { + Vehicle vehicle = await _partSourceContext.Vehicles + .Where(v => v.MakeId == makeId) + .FirstOrDefaultAsync(); + + return _mapper.Map(vehicle); + } + + public async Task> GetModels(VehicleDto vehicleQuery) + { + return await _partSourceContext.Vehicles + .ApplyQueryDto(vehicleQuery) + .Select(v => new ModelDto + { + ModelId = v.ModelId, + ModelName = v.ModelName, + MakeId = v.MakeId, + MakeName = v.MakeName + }) + .Distinct() + .OrderBy(m => m.ModelName) + .ToListAsync(); + } + + public async Task GetModelById(int modelId) + { + Vehicle vehicle = await _partSourceContext.Vehicles + .Where(m => m.ModelId == modelId) + .FirstOrDefaultAsync(); + + return _mapper.Map(vehicle); + } + + + public async Task> GetBaseVehicles(VehicleDto vehicleQuery) + { + return await _partSourceContext.Vehicles + .ApplyQueryDto(vehicleQuery) + .Select(v => new BaseVehicleDto + { + BaseVehicleId = v.BaseVehicleId, + MakeName = v.MakeName, + MakeId = v.MakeId, + ModelName = v.ModelName, + ModelId = v.ModelId, + Year = v.Year + }) + .Distinct() + .ToListAsync(); + } + + public async Task GetBaseVehicleById(int baseVehicleId) + { + Vehicle vehicle = await _partSourceContext.Vehicles + .Where(b => b.BaseVehicleId == baseVehicleId) + .FirstOrDefaultAsync(); + + return _mapper.Map(vehicle); + } + + public async Task> GetEngines(VehicleDto vehicleQuery) + { + return await _partSourceContext.Vehicles + .ApplyQueryDto(vehicleQuery) + .Select(v => new EngineDto + { + EngineConfigId = v.EngineConfigId, + EngineDescription = v.EngineDescription, + MakeName = v.MakeName, + MakeId = v.MakeId + }) + .Distinct() + .ToListAsync(); + } + + public async Task GetEngineById(int engineConfigId) + { + Vehicle vehicle = await _partSourceContext.Vehicles + .Where(e => e.EngineConfigId == engineConfigId) + .FirstOrDefaultAsync(); + + return _mapper.Map(vehicle); + } + + public async Task> GetSubmodels(VehicleDto vehicleQuery) + { + return await _partSourceContext.Vehicles + .ApplyQueryDto(vehicleQuery) + .GroupBy(v => new { v.SubmodelId, v.SubmodelName, v.MakeId, v.MakeName }) + .Select(v => new SubmodelDto + { + SubmodelId = v.Key.SubmodelId, + SubmodelName = v.Key.SubmodelName, + MakeId = v.Key.MakeId, + MakeName = v.Key.MakeName + }) + .Distinct() + .ToListAsync(); + } + + public async Task GetSubmodelById(int submodelId) + { + Vehicle vehicle = await _partSourceContext.Vehicles + .Where(s => s.SubmodelId == submodelId) + .FirstOrDefaultAsync(); + + return _mapper.Map(vehicle); + } + + #region Legacy API Support + + [Obsolete] public async Task GetVehicle(int baseVehicleId, int engineConfigId, int submodelId) { return await _partSourceContext.Vehicles.FirstOrDefaultAsync(d => @@ -43,35 +174,44 @@ namespace PartSource.Services ); } - public async Task> GetMakes() + [Obsolete] + public async Task> GetSubmodels(int makeId, int modelId, int year) { - return await _partSourceContext.VehicleMakes - .OrderBy(m => m.Name) - .ToListAsync(); + return await _partSourceContext.Submodels.Where(s => + s.MakeId == makeId + && s.ModelId == modelId + && s.Year == year + ) + .ToListAsync(); } - public async Task> GetMakes(VehicleMake criteria) + [Obsolete] + public async Task> GetEngines(int baseVehicleId) { - return await _partSourceContext.VehicleMakes - .ApplyQueryDto(criteria) - .OrderBy(m => m.Name) - .ToListAsync(); + return await _partSourceContext.Engines.Where(e => e.BaseVehicleId == baseVehicleId).ToListAsync(); } - public async Task GetMakeById(int makeId) + [Obsolete] + public async Task> GetEngines(int baseVehicleId, int submodelId) { - return await _partSourceContext.VehicleMakes - .FirstOrDefaultAsync(m => m.MakeId == makeId); + return await _partSourceContext.Engines.Where(e => + e.BaseVehicleId == baseVehicleId + && e.SubmodelId == submodelId + ) + .ToListAsync(); } - public async Task> GetModels(VehicleModel criteria) + [Obsolete] + public async Task GetBaseVehicle(int makeId, int modelId, int year) { - return await _partSourceContext.VehicleModels - .ApplyQueryDto(criteria) - .OrderBy(m => m.Name) - .ToListAsync(); + return await _partSourceContext.BaseVehicles.FirstOrDefaultAsync(d => + d.MakeId == makeId + && d.ModelId == modelId + && d.Year == year + ); } - + + [Obsolete] public async Task> GetModels(int makeId, int year) { return await _partSourceContext.VehicleModels @@ -83,101 +223,14 @@ namespace PartSource.Services .ToListAsync(); } - public async Task GetModelById(int modelId) + [Obsolete] + public async Task> GetMakes() { - return await _partSourceContext.VehicleModels - .FirstOrDefaultAsync(m => m.ModelId == modelId); - } - - - public async Task> GetBaseVehicles(BaseVehicle criteria) - { - return await _partSourceContext.BaseVehicles - .ApplyQueryDto(criteria) + return await _partSourceContext.VehicleMakes + .OrderBy(m => m.Name) .ToListAsync(); } - - public async Task GetBaseVehicle(int makeId, int modelId, int year) - { - return await _partSourceContext.BaseVehicles.FirstOrDefaultAsync(d => - d.MakeId == makeId - && d.ModelId == modelId - && d.Year == year - ); - } - - public async Task GetBaseVehicleById(int baseVehicleId) - { - return await _partSourceContext.BaseVehicles - .FirstOrDefaultAsync(b => b.BaseVehicleId == baseVehicleId); - } - - public async Task> GetEngines(Engine criteria) - { - return await _partSourceContext.Engines - .ApplyQueryDto(criteria) - .ToListAsync(); - } - - public async Task> GetEngines(int baseVehicleId) - { - return await _partSourceContext.Engines.Where(e => e.BaseVehicleId == baseVehicleId).ToListAsync(); - } - - public async Task> GetEngines(int baseVehicleId, int submodelId) - { - return await _partSourceContext.Engines.Where(e => - e.BaseVehicleId == baseVehicleId - && e.SubmodelId == submodelId - ) - .ToListAsync(); - } - - public async Task GetEngineById(int engineConfigId) - { - return await _partSourceContext.Engines - .Where(e => e.EngineConfigId == engineConfigId) - .Select(e => new EngineDto - { - EngineConfigId = e.EngineConfigId, - Description = e.Description, - Make = e.Make, - MakeId = e.MakeId - }) - .FirstOrDefaultAsync(); - } - - public async Task> GetSubmodels(Submodel submodelQuery) - { - return await _partSourceContext.Submodels - .ApplyQueryDto(submodelQuery) - .ToListAsync(); - } - - public async Task> GetSubmodels(int makeId, int modelId, int year) - { - return await _partSourceContext.Submodels.Where(s => - s.MakeId == makeId - && s.ModelId == modelId - && s.Year == year - ) - .ToListAsync(); - } - - public async Task GetSubmodelById(int submodelId) - { - return await _partSourceContext.Submodels - .Where(s => s.SubmodelId == submodelId) - .Select(s => new SubmodelDto - { - Id = s.SubmodelId, - Name = s.Name, - Make = s.Make, - MakeId = s.MakeId - }) - .FirstOrDefaultAsync(); - } - + #endregion public IList GetYmmFitment(IList vehicles) { diff --git a/PartSource.sln b/PartSource.sln index f8be63e..9e0c252 100644 --- a/PartSource.sln +++ b/PartSource.sln @@ -11,6 +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 Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Also Debug|Any CPU = Also Debug|Any CPU @@ -42,6 +49,12 @@ Global {C85D675B-A76C-4F9C-9C57-1E063211C946}.Debug|Any CPU.Build.0 = Debug|Any CPU {C85D675B-A76C-4F9C-9C57-1E063211C946}.Release|Any CPU.ActiveCfg = Release|Any CPU {C85D675B-A76C-4F9C-9C57-1E063211C946}.Release|Any CPU.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}.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}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDC22085-4C5A-4CCD-B0DB-9D31F90ECE90}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE