This commit is contained in:
2020-09-02 20:53:34 -04:00
parent 85d99d2615
commit d06925204d
38 changed files with 913 additions and 679 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
[*.cs]
# CA2007: Consider calling ConfigureAwait on the awaited task
dotnet_diagnostic.CA2007.severity = silent

View File

@@ -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<IExceptionHandlerPathFeature>();
if (exceptionFeature != null)
{
string route = exceptionFeature.Path;
Exception ex = exceptionFeature.Error;
//TODO: Logging
}
return StatusCode(StatusCodes.Status500InternalServerError);
}
}
}

View File

@@ -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;

View File

@@ -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]

View File

@@ -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<ActionResult> GetVehicleTypes()
{
NexpartVehiclesController vehiclesController = this;
VehicleTypesGetResponse response = await vehiclesController._nexpartService.SendRequest<VehicleTypesGet, VehicleTypesGetResponse>(new VehicleTypesGet());
return vehiclesController.NexpartResponse<VehicleTypesGetResponse, VehicleTypes>(response);
}
[HttpGet]
[Route("makes")]
public async Task<ActionResult> GetMakes()
{
NexpartVehiclesController vehiclesController = this;
MakeSearch requestContent = new MakeSearch()
{
VehicleTypeId = new int[] { 5, 6, 7 }
};
MakeSearchResponse response = await vehiclesController._nexpartService.SendRequest<MakeSearch, MakeSearchResponse>(requestContent);
return vehiclesController.NexpartResponse<MakeSearchResponse, Makes>(response);
}
[HttpGet]
[Route("makes/vehicletypeid/{vehicleTypeId}")]
public async Task<ActionResult> GetMakes(int vehicleTypeId)
{
NexpartVehiclesController vehiclesController = this;
MakeSearch requestContent = new MakeSearch()
{
VehicleTypeId = new int[] { vehicleTypeId }
};
MakeSearchResponse response = await vehiclesController._nexpartService.SendRequest<MakeSearch, MakeSearchResponse>(requestContent);
return vehiclesController.NexpartResponse<MakeSearchResponse, Makes>(response);
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}")]
public async Task<ActionResult> 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<ModelSearch, ModelSearchResponse>(requestContent);
return vehiclesController.NexpartResponse<ModelSearchResponse, Models[]>(response);
}
[HttpGet]
[Route("models/makeid/{makeId}/modelyear/{year}/vehicletypeid/{vehicleTypeId}")]
public async Task<ActionResult> 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<ModelSearch, ModelSearchResponse>(requestContent);
return vehiclesController.NexpartResponse<ModelSearchResponse, Models[]>(response);
}
[HttpGet]
[Route("basevehicle/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetBaseVehicle(int makeId, int modelId, int year)
{
BaseVehicleDetailLookup requestContent = new BaseVehicleDetailLookup()
{
MakeId = makeId,
ModelId = modelId,
Year = year
};
BaseVehicleDetailLookupResponse response = await _nexpartService.SendRequest<BaseVehicleDetailLookup, BaseVehicleDetailLookupResponse>(requestContent);
return NexpartResponse<BaseVehicleDetailLookupResponse, BaseVehicleDetail>(response);
}
[HttpGet]
[Route("engines/basevehicleid/{baseVehicleId}")]
[Route("engines/basevehicleid/{baseVehicleId}/submodelid/{subModelId}")]
public async Task<ActionResult> GetEngines(int baseVehicleId, int? subModelId = null)
{
EngineSearch requestContent = new EngineSearch()
{
VehicleIdentifier = new VehicleIdentifier()
{
BaseVehicleId = baseVehicleId
},
SubModelId = subModelId
};
EngineSearchResponse response = await _nexpartService.SendRequest<EngineSearch, EngineSearchResponse>(requestContent);
return NexpartResponse<EngineSearchResponse, Engines>(response);
}
[HttpGet]
[Route("trim/makeid/{makeId}/modelid/{modelId}/modelyear/{year}")]
public async Task<ActionResult> GetTrim(int makeId, int modelId, int year)
{
SubModelSearch requestContent = new SubModelSearch()
{
MakeId = makeId,
ModelId = modelId,
Year = year,
RegionId = 2
};
SubModelSearchResponse response = await _nexpartService.SendRequest<SubModelSearch, SubModelSearchResponse>(requestContent);
return NexpartResponse<SubModelSearchResponse, SubModels>(response);
}
[HttpGet]
[Route("/detail/basevehicleid/{baseVehicleId}/submodelid/{subModelId}/engineconfigid/{engineConfigId}")]
public async Task<ActionResult> 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<VehicleIdSearch, VehicleIdSearchResponse>(requestContent);
return NexpartResponse<VehicleIdSearchResponse, VehicleDetail>(response);
}
}
}

View File

@@ -10,6 +10,7 @@ namespace PartSource.Api.Controllers
{
[Route("[controller]")]
[ApiController]
[ApiExplorerSettings(GroupName = "v1")]
public class PartsController : BaseNexpartController
{
private readonly NexpartService _nexpartService;

View File

@@ -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<ActionResult> GetMakes(int vehicleTypeId)
{
SearchController searchController = this;
MakeSearch requestContent = new MakeSearch()
{
VehicleTypeId = new int[] { vehicleTypeId }
};
MakeSearchResponse response = await searchController._nexpartService.SendRequest<MakeSearch, MakeSearchResponse>(requestContent);
return searchController.NexpartResponse<MakeSearchResponse, Makes>(response);
}
}
}

View File

@@ -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)
// });
// }
// }
//}

View File

@@ -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);
}
}

View File

@@ -10,8 +10,12 @@ using System.Threading.Tasks;
namespace PartSource.Api.Controllers
{
/// <remarks>
/// This endpoint gets vehicle data from WHI's SEO data.
/// </remarks>
[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<ActionResult> GetVehicles([FromQuery] Vehicle vehicleQuery)
/// <summary>
/// Get Vehicles
/// </summary>
/// <response code="200"><strong>OK:</strong> An array of vehicles matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no vehicles were found.</response>
[HttpGet("")]
[ProducesResponseType(typeof(IList<VehicleDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetVehicles([FromQuery] VehicleDto vehicleQuery)
{
IList<Vehicle> vehicles = await _vehicleService.GetVehicles(vehicleQuery);
return ApiResponse(vehicles);
}
[HttpGet]
[Route("{id}")]
/// <summary>
/// Get Vehicle by ID
/// </summary>
/// <param name="id">A WHI VehicleToEngineConfigId</param>
/// <response code="200"><strong>OK:</strong> The vehicle with the provided VehicleToEngineConfigId.</response>
/// <response code="404"><strong>Not Found:</strong> No vehicle was found matching the provided VehicleToEngineConfigId.</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(VehicleDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> 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<ActionResult> GetMakes([FromQuery] VehicleMake vehicleMake)
/// <summary>
/// Get Makes
/// </summary>
/// <response code="200"><strong>OK:</strong> An array of makes matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no makes were found.</response>
[HttpGet("makes")]
[ProducesResponseType(typeof(IList<MakeDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetMakes([FromQuery] VehicleDto vehicleQuery)
{
IList<VehicleMake> makes = await _vehicleService.GetMakes(vehicleMake);
IList<MakeDto> makes = await _vehicleService.GetMakes(vehicleQuery);
return ApiResponse(makes);
}
[HttpGet]
[Route("makes/{id}")]
/// <summary>
/// Get Make by ID
/// </summary>
/// <param name="id">A WHI MakeId</param>
/// <response code="200"><strong>OK:</strong> The make with the provided MakeId.</response>
/// <response code="404"><strong>Not Found:</strong> No make was found matching the provided MakeId.</response>
[HttpGet("makes/{id}")]
[ProducesResponseType(typeof(MakeDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> GetMakeById(int id)
{
VehicleMake make = await _vehicleService.GetMakeById(id);
MakeDto make = await _vehicleService.GetMakeById(id);
return ApiResponse(make);
}
[HttpGet]
[Route("models")]
public async Task<ActionResult> GetModels([FromQuery] VehicleModel vehicleModel)
/// <summary>
/// Get Models
/// </summary>
/// <response code="200"><strong>OK:</strong> An array of models matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no models were found.</response>
[HttpGet("models")]
[ProducesResponseType(typeof(IList<ModelDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetModels([FromQuery] VehicleDto vehicleQuery)
{
IList<VehicleModel> models = await _vehicleService.GetModels(vehicleModel);
IList<ModelDto> models = await _vehicleService.GetModels(vehicleQuery);
return ApiResponse(models);
}
[HttpGet]
[Route("models/{id}")]
/// <summary>
/// Get Model by ID
/// </summary>
/// <param name="id">A WHI ModelId</param>
/// <response code="200"><strong>OK:</strong> The model with the provided ModelId.</response>
/// <response code="404"><strong>Not Found:</strong> No model was found matching the provided ModelId.</response>
[HttpGet("models/{id}")]
[ProducesResponseType(typeof(ModelDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> GetModelById(int id)
{
VehicleModel model = await _vehicleService.GetModelById(id);
ModelDto model = await _vehicleService.GetModelById(id);
return ApiResponse(model);
}
[HttpGet]
[Route("basevehicles")]
public async Task<ActionResult> GetBaseVehicles([FromQuery] BaseVehicle baseVehicle)
/// <summary>
/// Get Submodels
/// </summary>
/// /// <remarks><em>Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model.</em></remarks>
/// <response code="200"><strong>OK:</strong> An array of submodels matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no submodels were found.</response>
[HttpGet("submodels")]
[ProducesResponseType(typeof(IList<SubmodelDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetSubmodels([FromQuery] VehicleDto vehicleQuery)
{
IList<BaseVehicle> baseVehicles = await _vehicleService.GetBaseVehicles(baseVehicle);
return ApiResponse(baseVehicles);
}
[HttpGet]
[Route("basevehicles/{id}")]
public async Task<ActionResult> GetBaseVehicleById(int id)
{
BaseVehicle baseVehicle = await _vehicleService.GetBaseVehicleById(id);
return ApiResponse(baseVehicle);
}
[HttpGet]
[Route("engines")]
public async Task<ActionResult> GetEngines([FromQuery] Engine engine)
{
IList<Engine> engines = await _vehicleService.GetEngines(engine);
return ApiResponse(engines);
}
[HttpGet]
[Route("engines/{id}")]
public async Task<ActionResult> GetEngineById(int id)
{
EngineDto engine = await _vehicleService.GetEngineById(id);
return ApiResponse(engine);
}
[HttpGet]
[Route("submodels")]
public async Task<ActionResult> GetSubmodels([FromQuery] Submodel submodelQuery)
{
IList<Submodel> submodels = await _vehicleService.GetSubmodels(submodelQuery);
IList<SubmodelDto> submodels = await _vehicleService.GetSubmodels(vehicleQuery);
return ApiResponse(submodels);
}
[HttpGet]
[Route("submodels/{id}")]
/// <summary>
/// Get Submodel by ID
/// </summary>
/// <remarks><em>Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model.</em></remarks>
/// <param name="id">A WHI SubmodelId</param>
/// <response code="200"><strong>OK:</strong> The submodel with the provided SubmodelId.</response>
/// <response code="404"><strong>Not Found:</strong> No submodel was found matching the provided SubmodelId.</response>
[HttpGet("submodels/{id}")]
[ProducesResponseType(typeof(SubmodelDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> GetSubmodels(int id)
{
SubmodelDto submodel = await _vehicleService.GetSubmodelById(id);
return ApiResponse(submodel);
}
/// <summary>
/// Get Base Vehicles
/// </summary>
/// <response code="200"><strong>OK:</strong> An array of base vehicles matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no base vehicles were found.</response>
[HttpGet("basevehicles")]
[ProducesResponseType(typeof(IList<BaseVehicleDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetBaseVehicles([FromQuery] VehicleDto vehicleQuery)
{
IList<BaseVehicleDto> baseVehicles = await _vehicleService.GetBaseVehicles(vehicleQuery);
return ApiResponse(baseVehicles);
}
/// <summary>
/// Get Base Vehicle by ID
/// </summary>
/// <param name="id">A WHI BaseVehicleId</param>
/// <response code="200"><strong>OK:</strong> The base vehicle with the provided BaseVehicleId.</response>
/// <response code="404"><strong>Not Found:</strong> No base vehicle was found matching the provided BaseVehicleId.</response>
[HttpGet("basevehicles/{id}")]
[ProducesResponseType(typeof(BaseVehicleDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> GetBaseVehicleById(int id)
{
BaseVehicleDto baseVehicle = await _vehicleService.GetBaseVehicleById(id);
return ApiResponse(baseVehicle);
}
/// <summary>
/// Get Engines
/// </summary>
/// <response code="200"><strong>OK:</strong> An array of engines matching the query.</response>
/// <response code="204"><strong>No Content:</strong> The query executed successfully, but no engines were found.</response>
[HttpGet("engines")]
[ProducesResponseType(typeof(IList<EngineDto>), 200)]
[ProducesResponseType(204)]
public async Task<ActionResult> GetEngines([FromQuery] VehicleDto vehicleQuery)
{
IList<EngineDto> engines = await _vehicleService.GetEngines(vehicleQuery);
return ApiResponse(engines);
}
/// <summary>
/// Get Engine by ID
/// </summary>
/// <param name="id">A WHI EngineConfigId</param>
/// <response code="200"><strong>OK:</strong> The engine with the provided EngineConfigId.</response>
/// <response code="404"><strong>Not Found:</strong> No engine was found matching the provided EngineConfigId.</response>
[HttpGet("engines/{id}")]
[ProducesResponseType(typeof(EngineDto), 200)]
[ProducesResponseType(404)]
public async Task<ActionResult> GetEngineById(int id)
{
EngineDto engine = await _vehicleService.GetEngineById(id);
return ApiResponse(engine);
}
}
}

View File

@@ -6,6 +6,11 @@
<Configurations>Debug;Release;Also Debug</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DocumentationFile>C:\Users\Tommy\source\repos\ratermania\partsource\PartSource.Api\PartSource.Api.xml</DocumentationFile>
<NoWarn>1701;1702;1591</NoWarn>
</PropertyGroup>
<ItemGroup>
<Content Remove="appsettings.development.json" />
</ItemGroup>
@@ -15,15 +20,18 @@
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
<None Include="appsettings.development.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.App" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Razor.Design" Version="2.2.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.3" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="5.5.1" />
</ItemGroup>
<ItemGroup>

View File

@@ -0,0 +1,110 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>PartSource.Api</name>
</assembly>
<members>
<member name="T:PartSource.Api.Controllers.LegacyVehiclesController">
<summary>
This controller exists to enable legacy support for the old Nexpart-based vehicle lookup.
</summary>
</member>
<member name="T:PartSource.Api.Controllers.VehiclesController">
<remarks>
This endpoint gets vehicle data from WHI's SEO data.
</remarks>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetVehicles(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Vehicles
</summary>
<response code="200"><strong>OK:</strong> An array of vehicles matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no vehicles were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetVehicleById(System.Int32)">
<summary>
Get Vehicle by ID
</summary>
<param name="id">A WHI VehicleToEngineConfigId</param>
<response code="200"><strong>OK:</strong> The vehicle with the provided VehicleToEngineConfigId.</response>
<response code="404"><strong>Not Found:</strong> No vehicle was found matching the provided VehicleToEngineConfigId.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetMakes(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Makes
</summary>
<response code="200"><strong>OK:</strong> An array of makes matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no makes were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetMakeById(System.Int32)">
<summary>
Get Make by ID
</summary>
<param name="id">A WHI MakeId</param>
<response code="200"><strong>OK:</strong> The make with the provided MakeId.</response>
<response code="404"><strong>Not Found:</strong> No make was found matching the provided MakeId.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetModels(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Models
</summary>
<response code="200"><strong>OK:</strong> An array of models matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no models were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetModelById(System.Int32)">
<summary>
Get Model by ID
</summary>
<param name="id">A WHI ModelId</param>
<response code="200"><strong>OK:</strong> The model with the provided ModelId.</response>
<response code="404"><strong>Not Found:</strong> No model was found matching the provided ModelId.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetSubmodels(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Submodels
</summary>
/// <remarks><em>Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model.</em></remarks>
<response code="200"><strong>OK:</strong> An array of submodels matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no submodels were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetSubmodels(System.Int32)">
<summary>
Get Submodel by ID
</summary>
<remarks><em>Note: Submodels can be shared between models. Do not assume a submodel is unique to a given model.</em></remarks>
<param name="id">A WHI SubmodelId</param>
<response code="200"><strong>OK:</strong> The submodel with the provided SubmodelId.</response>
<response code="404"><strong>Not Found:</strong> No submodel was found matching the provided SubmodelId.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetBaseVehicles(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Base Vehicles
</summary>
<response code="200"><strong>OK:</strong> An array of base vehicles matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no base vehicles were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetBaseVehicleById(System.Int32)">
<summary>
Get Base Vehicle by ID
</summary>
<param name="id">A WHI BaseVehicleId</param>
<response code="200"><strong>OK:</strong> The base vehicle with the provided BaseVehicleId.</response>
<response code="404"><strong>Not Found:</strong> No base vehicle was found matching the provided BaseVehicleId.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetEngines(PartSource.Data.Dtos.VehicleDto)">
<summary>
Get Engines
</summary>
<response code="200"><strong>OK:</strong> An array of engines matching the query.</response>
<response code="204"><strong>No Content:</strong> The query executed successfully, but no engines were found.</response>
</member>
<member name="M:PartSource.Api.Controllers.VehiclesController.GetEngineById(System.Int32)">
<summary>
Get Engine by ID
</summary>
<param name="id">A WHI EngineConfigId</param>
<response code="200"><strong>OK:</strong> The engine with the provided EngineConfigId.</response>
<response code="404"><strong>Not Found:</strong> No engine was found matching the provided EngineConfigId.</response>
</member>
</members>
</doc>

View File

@@ -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<PartService>();
services.AddTransient<NexpartService>();
services.AddTransient<SecurityService>();
@@ -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();
}

View File

@@ -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

View File

@@ -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<AutomationJobResult> 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<ProductVariant> productVariants = new List<ProductVariant>();
//List<ProductVariant> productVariants = new List<ProductVariant>();
//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<ProductVariant> productVariants = new List<ProductVariant>();
//List<ProductVariant> productVariants = new List<ProductVariant>();
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
};

View File

@@ -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;

View File

@@ -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<AutomationJobResult> Run()
{
IList<ImportData> parts = _partSourceContext.ImportData.Where(i => new[] { "Raybestos Brakes", "MotoMaster", "Pro Series OE Plus Brake Pads" }.Contains(i.Vendor)).ToList();
IList<ImportData> 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<Vehicle> vehicles = _vehicleService.GetVehiclesForPart(importData.PartNumber, importData.LineCode);
IList<int> 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<Vehicle> vehicles = _vehicleService.GetVehiclesForPart(importData.PartNumber, importData.LineCode);
IList<int> 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<string> 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<string> 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<string> tags = new List<string>
List<string> tags = new List<string>
{
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();
}

View File

@@ -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;

View File

@@ -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++;

View File

@@ -3,11 +3,12 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<Platforms>AnyCPU;x86;x64</Platforms>
<Configurations>Debug;Release;Also Debug</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.0.1" />
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
@@ -15,10 +16,10 @@
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" />
<PackageReference Include="Ratermania.Shopify" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\shopify\Shopify\Shopify.csproj" />
<ProjectReference Include="..\PartSource.Data\PartSource.Data.csproj" />
<ProjectReference Include="..\PartSource.Services\PartSource.Services.csproj" />
</ItemGroup>

View File

@@ -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<ShopifyClient>(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)

View File

@@ -2,7 +2,7 @@
"profiles": {
"PartSource.Automation": {
"commandName": "Project",
"commandLineArgs": "UpdatePositioning",
"commandLineArgs": "AddAndUpdateProducts",
"environmentVariables": {
"PS_AUTOMATION_ENVIRONMENT": "development"
}

View File

@@ -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": {

View File

@@ -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<Vehicle, MakeDto>();
CreateMap<Vehicle, ModelDto>();
CreateMap<Vehicle, SubmodelDto>();
CreateMap<Vehicle, BaseVehicleDto>();
CreateMap<Vehicle, EngineDto>();
CreateMap<Vehicle, VehicleDto>();
}
}
}

View File

@@ -0,0 +1,27 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class BaseVehicleDto
{
/// <example>149268</example>
public int BaseVehicleId { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
/// <example>73</example>
public int MakeId { get; set; }
/// <example>R8</example>
public string ModelName { get; set; }
/// <example>6039</example>
public int ModelId { get; set; }
/// <example>2020</example>
public int Year { get; set; }
}
}

View File

@@ -6,12 +6,16 @@ namespace PartSource.Data.Dtos
{
public class EngineDto
{
/// <example>24294</example>
public int EngineConfigId { get; set; }
public string Description { get; set; }
/// <example>V10-5204cc 5.2L FI DKAA 602HP</example>
public string EngineDescription { get; set; }
public string Make { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
/// <example>73</example>
public int MakeId { get; set; }
}
}

View File

@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class MakeDto
{
/// <example>73</example>
public int MakeId { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
}
}

View File

@@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class ModelDto
{
/// <example>6039</example>
public int ModelId { get; set; }
/// <example>R8</example>
public string ModelName { get; set; }
/// <example>73</example>
public int MakeId { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
}
}

View File

@@ -6,12 +6,16 @@ namespace PartSource.Data.Dtos
{
public class SubmodelDto
{
public int Id { get; set; }
/// <example>897</example>
public int SubmodelId { get; set; }
public string Name { get; set; }
/// <example>Performance</example>
public string SubmodelName { get; set; }
public string Make { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
/// <example>73</example>
public int MakeId { get; set; }
}
}

View File

@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace PartSource.Data.Dtos
{
public class VehicleDto
{
/// <example>73</example>
public int MakeId { get; set; }
/// <example>Audi</example>
public string MakeName { get; set; }
/// <example>6039</example>
public int ModelId { get; set; }
/// <example>R8</example>
public string ModelName { get; set; }
/// <example>24294</example>
public int EngineConfigId { get; set; }
/// <example>V10-5204cc 5.2L FI DKAA 602HP</example>
public string EngineDescription { get; set; }
/// <example>149268</example>
public int BaseVehicleId { get; set; }
/// <example>2020</example>
public int Year { get; set; }
/// <example>897</example>
public int SubmodelId { get; set; }
/// <example>Performance</example>
public string SubmodelName { get; set; }
/// <example>523387</example>
public int VehicleToEngineConfigId { get; set; }
}
}

View File

@@ -5,6 +5,11 @@
<Configurations>Debug;Release;Also Debug</Configurations>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<NoWarn>1701;1702;1591</NoWarn>
<DocumentationFile>C:\Users\Tommy\source\repos\ratermania\partsource\PartSource.Data\PartSource.Data.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Shopify\**" />
<EmbeddedResource Remove="Shopify\**" />
@@ -12,10 +17,15 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\shopify\Shopify\Shopify.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,115 @@
<?xml version="1.0"?>
<doc>
<assembly>
<name>PartSource.Data</name>
</assembly>
<members>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.BaseVehicleId">
<example>149268</example>
</member>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.ModelName">
<example>R8</example>
</member>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.ModelId">
<example>6039</example>
</member>
<member name="P:PartSource.Data.Dtos.BaseVehicleDto.Year">
<example>2020</example>
</member>
<member name="P:PartSource.Data.Dtos.EngineDto.EngineConfigId">
<example>24294</example>
</member>
<member name="P:PartSource.Data.Dtos.EngineDto.EngineDescription">
<example>V10-5204cc 5.2L FI DKAA 602HP</example>
</member>
<member name="P:PartSource.Data.Dtos.EngineDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.EngineDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.MakeDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.MakeDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.ModelDto.ModelId">
<example>6039</example>
</member>
<member name="P:PartSource.Data.Dtos.ModelDto.ModelName">
<example>R8</example>
</member>
<member name="P:PartSource.Data.Dtos.ModelDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.ModelDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.SubmodelDto.SubmodelId">
<example>897</example>
</member>
<member name="P:PartSource.Data.Dtos.SubmodelDto.SubmodelName">
<example>Performance</example>
</member>
<member name="P:PartSource.Data.Dtos.SubmodelDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.SubmodelDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.MakeId">
<example>73</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.MakeName">
<example>Audi</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.ModelId">
<example>6039</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.ModelName">
<example>R8</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.EngineConfigId">
<example>24294</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.EngineDescription">
<example>V10-5204cc 5.2L FI DKAA 602HP</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.BaseVehicleId">
<example>149268</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.Year">
<example>2020</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.SubmodelId">
<example>897</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.SubmodelName">
<example>Performance</example>
</member>
<member name="P:PartSource.Data.Dtos.VehicleDto.VehicleToEngineConfigId">
<example>523387</example>
</member>
<member name="T:SqlServerTypes.Utilities">
<summary>
Utility methods related to CLR Types for SQL Server
</summary>
</member>
<member name="M:SqlServerTypes.Utilities.LoadNativeAssemblies(System.String)">
<summary>
Loads the required native assemblies for the current architecture (x86 or x64)
</summary>
<param name="rootApplicationPath">
Root path of the current application. Use Server.MapPath(".") for ASP.NET applications
and AppDomain.CurrentDomain.BaseDirectory for desktop applications.
</param>
</member>
</members>
</doc>

View File

@@ -13,9 +13,9 @@ namespace PartSource.Services.Extensions
{
public static class IQueryableExtensions
{
public static IQueryable<T> ApplyQueryDto<T>(this IQueryable<T> queryable, T dto)
public static IQueryable<T> ApplyQueryDto<T, U>(this IQueryable<T> queryable, U dto)
{
foreach (PropertyInfo property in typeof(T).GetProperties())
foreach (PropertyInfo property in typeof(U).GetProperties())
{
object value = property.GetValue(dto);

View File

@@ -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<Product> Products { get; set; }
public ShopifyResource<Metafield> Metafields { get; set; }
//public ShopifyResource<SmartCollection> SmartCollections { get; set; }
public ShopifyResource<ProductImage> ProductImages { get; set; }
}
}

View File

@@ -6,8 +6,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="10.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Ratermania.Shopify" Version="1.0.2" />
</ItemGroup>
<ItemGroup>
@@ -20,4 +20,8 @@
</None>
</ItemGroup>
<ItemGroup>
<Folder Include="Integrations\" />
</ItemGroup>
</Project>

View File

@@ -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<IList<Vehicle>> GetVehicles(Vehicle vehicleQuery)
public async Task<IList<Vehicle>> GetVehicles(VehicleDto vehicleQuery)
{
return await _partSourceContext.Vehicles
.ApplyQueryDto(vehicleQuery)
@@ -34,6 +37,134 @@ namespace PartSource.Services
.FirstOrDefaultAsync(v => v.VehicleToEngineConfigId == vehicleToEngineConfigId);
}
public async Task<IList<MakeDto>> 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<MakeDto> GetMakeById(int makeId)
{
Vehicle vehicle = await _partSourceContext.Vehicles
.Where(v => v.MakeId == makeId)
.FirstOrDefaultAsync();
return _mapper.Map<MakeDto>(vehicle);
}
public async Task<IList<ModelDto>> 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<ModelDto> GetModelById(int modelId)
{
Vehicle vehicle = await _partSourceContext.Vehicles
.Where(m => m.ModelId == modelId)
.FirstOrDefaultAsync();
return _mapper.Map<ModelDto>(vehicle);
}
public async Task<IList<BaseVehicleDto>> 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<BaseVehicleDto> GetBaseVehicleById(int baseVehicleId)
{
Vehicle vehicle = await _partSourceContext.Vehicles
.Where(b => b.BaseVehicleId == baseVehicleId)
.FirstOrDefaultAsync();
return _mapper.Map<BaseVehicleDto>(vehicle);
}
public async Task<IList<EngineDto>> 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<EngineDto> GetEngineById(int engineConfigId)
{
Vehicle vehicle = await _partSourceContext.Vehicles
.Where(e => e.EngineConfigId == engineConfigId)
.FirstOrDefaultAsync();
return _mapper.Map<EngineDto>(vehicle);
}
public async Task<IList<SubmodelDto>> 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<SubmodelDto> GetSubmodelById(int submodelId)
{
Vehicle vehicle = await _partSourceContext.Vehicles
.Where(s => s.SubmodelId == submodelId)
.FirstOrDefaultAsync();
return _mapper.Map<SubmodelDto>(vehicle);
}
#region Legacy API Support
[Obsolete]
public async Task<Vehicle> GetVehicle(int baseVehicleId, int engineConfigId, int submodelId)
{
return await _partSourceContext.Vehicles.FirstOrDefaultAsync(d =>
@@ -43,35 +174,44 @@ namespace PartSource.Services
);
}
public async Task<IList<VehicleMake>> GetMakes()
[Obsolete]
public async Task<IList<Submodel>> 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<IList<VehicleMake>> GetMakes(VehicleMake criteria)
[Obsolete]
public async Task<IList<Engine>> 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<VehicleMake> GetMakeById(int makeId)
[Obsolete]
public async Task<IList<Engine>> 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<IList<VehicleModel>> GetModels(VehicleModel criteria)
[Obsolete]
public async Task<BaseVehicle> 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<IList<VehicleModel>> GetModels(int makeId, int year)
{
return await _partSourceContext.VehicleModels
@@ -83,101 +223,14 @@ namespace PartSource.Services
.ToListAsync();
}
public async Task<VehicleModel> GetModelById(int modelId)
[Obsolete]
public async Task<IList<VehicleMake>> GetMakes()
{
return await _partSourceContext.VehicleModels
.FirstOrDefaultAsync(m => m.ModelId == modelId);
}
public async Task<IList<BaseVehicle>> GetBaseVehicles(BaseVehicle criteria)
{
return await _partSourceContext.BaseVehicles
.ApplyQueryDto(criteria)
return await _partSourceContext.VehicleMakes
.OrderBy(m => m.Name)
.ToListAsync();
}
public async Task<BaseVehicle> 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<BaseVehicle> GetBaseVehicleById(int baseVehicleId)
{
return await _partSourceContext.BaseVehicles
.FirstOrDefaultAsync(b => b.BaseVehicleId == baseVehicleId);
}
public async Task<IList<Engine>> GetEngines(Engine criteria)
{
return await _partSourceContext.Engines
.ApplyQueryDto(criteria)
.ToListAsync();
}
public async Task<IList<Engine>> GetEngines(int baseVehicleId)
{
return await _partSourceContext.Engines.Where(e => e.BaseVehicleId == baseVehicleId).ToListAsync();
}
public async Task<IList<Engine>> GetEngines(int baseVehicleId, int submodelId)
{
return await _partSourceContext.Engines.Where(e =>
e.BaseVehicleId == baseVehicleId
&& e.SubmodelId == submodelId
)
.ToListAsync();
}
public async Task<EngineDto> 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<IList<Submodel>> GetSubmodels(Submodel submodelQuery)
{
return await _partSourceContext.Submodels
.ApplyQueryDto(submodelQuery)
.ToListAsync();
}
public async Task<IList<Submodel>> 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<SubmodelDto> 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<string> GetYmmFitment(IList<Vehicle> vehicles)
{

View File

@@ -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