Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 59 additions & 25 deletions src/ShippingRecorder.Api/Controllers/ExportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Microsoft.AspNetCore.Mvc;
using ShippingRecorder.Api.Interfaces;
using ShippingRecorder.Api.Entities;
using ShippingRecorder.Entities.Jobs;

namespace HealthTracker.Api.Controllers
{
Expand All @@ -14,35 +15,55 @@ public class ExportController : Controller
private readonly IBackgroundQueue<CountryExportWorkItem> _countryQueue;
private readonly IBackgroundQueue<LocationExportWorkItem> _locationQueue;
private readonly IBackgroundQueue<OperatorExportWorkItem> _operatorQueue;
private readonly IBackgroundQueue<PortExportWorkItem> _portQueue;
private readonly IBackgroundQueue<SightingExportWorkItem> _sightingQueue;
private readonly IBackgroundQueue<VesselExportWorkItem> _vesselQueue;
private readonly IBackgroundQueue<VesselTypeExportWorkItem> _vesselTypeQueue;
private readonly IBackgroundQueue<VoyageExportWorkItem> _voyageQueue;
private readonly IBackgroundQueue<SightingExportWorkItem> _sightingQueue;
private readonly IBackgroundQueue<PortExportWorkItem> _portQueue;

public ExportController(
IBackgroundQueue<CountryExportWorkItem> countryQueue,
IBackgroundQueue<LocationExportWorkItem> locationQueue,
IBackgroundQueue<OperatorExportWorkItem> operatorQueue,
IBackgroundQueue<PortExportWorkItem> portQueue,
IBackgroundQueue<SightingExportWorkItem> sightingQueue,
IBackgroundQueue<VesselExportWorkItem> vesselQueue,
IBackgroundQueue<VesselTypeExportWorkItem> vesselTypeQueue,
IBackgroundQueue<VoyageExportWorkItem> voyageQueue,
IBackgroundQueue<SightingExportWorkItem> sightingQueue,
IBackgroundQueue<PortExportWorkItem> portQueue)
IBackgroundQueue<VoyageExportWorkItem> voyageQueue)
{
_countryQueue = countryQueue;
_locationQueue = locationQueue;
_operatorQueue = operatorQueue;
_portQueue = portQueue;
_sightingQueue = sightingQueue;
_vesselQueue = vesselQueue;
_vesselTypeQueue = vesselTypeQueue;
_voyageQueue = voyageQueue;
_sightingQueue = sightingQueue;
_portQueue = portQueue;
}

[HttpPost]
[Route("all")]
public IActionResult ExportAll([FromBody] AllExportWorkItem item)
{
// Get the file name without the extension or path
var fileName = Path.GetFileNameWithoutExtension(item.FileName);

// Queue exports for each data type
_countryQueue.Enqueue(BuildWorkItem<CountryExportWorkItem>("Countries", fileName));
_locationQueue.Enqueue(BuildWorkItem<LocationExportWorkItem>("Locations", fileName));
_operatorQueue.Enqueue(BuildWorkItem<OperatorExportWorkItem>("Operators", fileName));
_portQueue.Enqueue(BuildWorkItem<PortExportWorkItem>("Ports", fileName));
_sightingQueue.Enqueue(BuildWorkItem<SightingExportWorkItem>("Sightings", fileName));
_vesselQueue.Enqueue(BuildWorkItem<VesselExportWorkItem>("Vessels", fileName));
_vesselTypeQueue.Enqueue(BuildWorkItem<VesselTypeExportWorkItem>("Vessel-Types", fileName));
_voyageQueue.Enqueue(BuildWorkItem<VoyageExportWorkItem>("Voyages", fileName));

return Accepted();
}

[HttpPost]
[Route("countries")]
public IActionResult ExportCountris([FromBody] CountryExportWorkItem item)
public IActionResult ExportCountries([FromBody] CountryExportWorkItem item)
{
item.JobName = "Country Export";
_countryQueue.Enqueue(item);
Expand All @@ -67,6 +88,24 @@ public IActionResult ExportOperators([FromBody] OperatorExportWorkItem item)
return Accepted();
}

[HttpPost]
[Route("ports")]
public IActionResult ExportPorts([FromBody] PortExportWorkItem item)
{
item.JobName = "Port Export";
_portQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("sightings")]
public IActionResult ExportSightings([FromBody] SightingExportWorkItem item)
{
item.JobName = "Sighting Export";
_sightingQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("vessels")]
public IActionResult ExportVessels([FromBody] VesselExportWorkItem item)
Expand All @@ -85,15 +124,6 @@ public IActionResult ExportVesselTypes([FromBody] VesselTypeExportWorkItem item)
return Accepted();
}

[HttpPost]
[Route("sightings")]
public IActionResult ExportSightings([FromBody] SightingExportWorkItem item)
{
item.JobName = "Sighting Export";
_sightingQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("voyages")]
public IActionResult ExportVoyages([FromBody] VoyageExportWorkItem item)
Expand All @@ -103,13 +133,17 @@ public IActionResult ExportVoyages([FromBody] VoyageExportWorkItem item)
return Accepted();
}

[HttpPost]
[Route("ports")]
public IActionResult ExportPorts([FromBody] PortExportWorkItem item)
{
item.JobName = "Port Export";
_portQueue.Enqueue(item);
return Accepted();
}
/// <summary>
/// Helper method to build a work item for the specified entity type
/// </summary>
/// <param name="entityName"></param>
/// <param name="fileName"></param>
/// <returns></returns>
private T BuildWorkItem<T>(string entityName, string fileName) where T : ExportWorkItem, new()
=> new()
{
JobName = $"{entityName} Export",
FileName = $"{fileName}-{entityName}.csv"
};
}
}
18 changes: 9 additions & 9 deletions src/ShippingRecorder.Api/Controllers/ImportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ public IActionResult ImportPorts([FromBody] PortImportWorkItem item)
return Accepted();
}

[HttpPost]
[Route("sightings")]
public IActionResult ImportSightings([FromBody] SightingImportWorkItem item)
{
item.JobName = "Sighting Import";
_sightingQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("vessels")]
public IActionResult ImportVessels([FromBody] VesselImportWorkItem item)
Expand All @@ -94,15 +103,6 @@ public IActionResult ImportVesselTypes([FromBody] VesselTypeImportWorkItem item)
return Accepted();
}

[HttpPost]
[Route("sightings")]
public IActionResult ImportSightings([FromBody] SightingImportWorkItem item)
{
item.JobName = "Sighting Import";
_sightingQueue.Enqueue(item);
return Accepted();
}

[HttpPost]
[Route("voyages")]
public IActionResult ImportVoyages([FromBody] VoyageImportWorkItem item)
Expand Down
8 changes: 8 additions & 0 deletions src/ShippingRecorder.Api/Entities/AllExportWorkItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using ShippingRecorder.Entities.Jobs;

namespace ShippingRecorder.Api.Entities
{
public class AllExportWorkItem : ExportWorkItem
{
}
}
41 changes: 41 additions & 0 deletions src/ShippingRecorder.Client/ApiClient/ExportClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using ShippingRecorder.Client.Interfaces;
using ShippingRecorder.Entities.Interfaces;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using ShippingRecorder.Entities.Db;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using System.IO;
using System;

namespace ShippingRecorder.Client.ApiClient
{
public class ExportClient : ShippingRecorderClientBase, IExportClient
{
private const string RouteKey = "Export";

public ExportClient(
IShippingRecorderHttpClient client,
IShippingRecorderApplicationSettings settings,
IAuthenticationTokenProvider tokenProvider,
ICacheWrapper cache,
ILogger<ExportClient> logger)
: base(client, settings, tokenProvider, cache, logger)
{
}

/// <summary>
/// Request an export of allexports to a named file in the export allexport
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task ExportAsync(string fileName)
{
dynamic data = new{ FileName = fileName };
var json = Serialize(data);
await SendIndirectAsync(RouteKey, json, HttpMethod.Post);
}
}
}
6 changes: 6 additions & 0 deletions src/ShippingRecorder.Client/Interfaces/IExportClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ShippingRecorder.Client.Interfaces
{
public interface IExportClient : IExporter
{
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public abstract class DataExchangeControllerBase : ShippingRecorderControllerBas

public DataExchangeControllerBase(
ICountryClient countryClient,
IExportClient exportClient,
ILocationClient locationClient,
IOperatorClient operatorClient,
IPortClient portClient,
Expand All @@ -42,6 +43,7 @@ public DataExchangeControllerBase(
_importers.Add(DataExchangeType.VesselTypes, vesselTypeClient);
_importers.Add(DataExchangeType.Voyages, voyageClient);

_exporters.Add(DataExchangeType.All, exportClient);
_exporters.Add(DataExchangeType.Countries, countryClient);
_exporters.Add(DataExchangeType.Locations, locationClient);
_exporters.Add(DataExchangeType.Operators, operatorClient);
Expand Down
2 changes: 2 additions & 0 deletions src/ShippingRecorder.Mvc/Controllers/ExportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public class ExportController : DataExchangeControllerBase
{
public ExportController(
ICountryClient countryClient,
IExportClient exportClient,
ILocationClient locationClient,
IOperatorClient operatorClient,
IPortClient portClient,
Expand All @@ -22,6 +23,7 @@ public ExportController(
IPartialViewToStringRenderer renderer,
ILogger<ExportController> logger) : base(
countryClient,
exportClient,
locationClient,
operatorClient,
portClient,
Expand Down
2 changes: 2 additions & 0 deletions src/ShippingRecorder.Mvc/Controllers/ImportController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public class ImportController : DataExchangeControllerBase

public ImportController(
ICountryClient countryClient,
IExportClient exportClient,
ILocationClient locationClient,
IOperatorClient operatorClient,
IPortClient portClient,
Expand All @@ -25,6 +26,7 @@ public ImportController(
IPartialViewToStringRenderer renderer,
ILogger<ImportController> logger) : base(
countryClient,
exportClient,
locationClient,
operatorClient,
portClient,
Expand Down
1 change: 1 addition & 0 deletions src/ShippingRecorder.Mvc/Entities/DataExchangeType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ namespace ShippingRecorder.Mvc.Enumerations
public enum DataExchangeType
{
None,
All,
Countries,
Locations,
Operators,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public static string ToName(this DataExchangeType type)
return type switch
{
DataExchangeType.None => "",
DataExchangeType.All => "All Data",
DataExchangeType.Countries => "Countries",
DataExchangeType.Locations => "Locations",
DataExchangeType.Operators => "Operators",
Expand Down
2 changes: 1 addition & 1 deletion src/ShippingRecorder.Mvc/Models/ImportViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class ImportViewModel : DataExchangeViewModel

public ImportViewModel()
{
foreach (var importType in Enum.GetValues<DataExchangeType>())
foreach (var importType in Enum.GetValues<DataExchangeType>().Where(x => x != DataExchangeType.All))
{
var importTypeName = importType.ToName();
ImportTypes.Add(new SelectListItem() { Text = $"{importTypeName}", Value = importType.ToString() });
Expand Down
1 change: 1 addition & 0 deletions src/ShippingRecorder.Mvc/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public void ConfigureServices(IServiceCollection services)
services.AddSingleton<IShippingRecorderHttpClient>(provider => ShippingRecorderHttpClient.Instance);
services.AddSingleton<IAuthenticationClient, AuthenticationClient>();
services.AddSingleton<ICountryClient, CountryClient>();
services.AddSingleton<IExportClient, ExportClient>();
services.AddSingleton<ILocationClient, LocationClient>();
services.AddSingleton<IOperatorClient, OperatorClient>();
services.AddSingleton<IPortClient, PortClient>();
Expand Down
4 changes: 4 additions & 0 deletions src/ShippingRecorder.Mvc/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@
{
"Name": "ExportPort",
"Route": "/export/ports"
},
{
"Name": "Export",
"Route": "/export/all"
}
],
"UseCustomErrorPageInDevelopment": true,
Expand Down
70 changes: 70 additions & 0 deletions src/ShippingRecorder.Tests/Client/ExportClientTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Text.Json;
using ShippingRecorder.Client.Interfaces;
using ShippingRecorder.Tests.Mocks;
using Microsoft.Extensions.Logging;
using Moq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using ShippingRecorder.Entities.Config;
using ShippingRecorder.Client.ApiClient;
using System.Threading.Tasks;
using System.Net.Http;
using System.Collections.Generic;
using System.Linq;
using System.IO;

namespace ShippingRecorder.Tests.Client
{
[TestClass]
public class ExportClientTest
{
private readonly string ApiToken = "An API Token";
private readonly MockShippingRecorderHttpClient _httpClient = new();
private IExportClient _client;
private string _filePath;

private readonly ShippingRecorderApplicationSettings _settings = new()
{
ApiUrl = "http://server/",
ApiRoutes = [
new() { Name = "Export", Route = "/export/all" }
]
};

[TestInitialize]
public void Initialise()
{
var provider = new Mock<IAuthenticationTokenProvider>();
provider.Setup(x => x.GetToken()).Returns(ApiToken);
var logger = new Mock<ILogger<ExportClient>>();
var cache = new Mock<ICacheWrapper>();
_client = new ExportClient(_httpClient, _settings, provider.Object, cache.Object, logger.Object);
}

[TestCleanup]
public void CleanUp()
{
if (!string.IsNullOrEmpty(_filePath) && File.Exists(_filePath))
{
File.Delete(_filePath);
}
}

[TestMethod]
public async Task ExportTest()
{
_filePath = Path.ChangeExtension(Path.GetTempFileName(), "csv");
_httpClient.AddResponse("");

var json = JsonSerializer.Serialize(new { FileName = _filePath });
var expectedRoute = _settings.ApiRoutes.First(x => x.Name == "Export").Route;

await _client.ExportAsync(_filePath);

Assert.AreEqual($"Bearer {ApiToken}", _httpClient.DefaultRequestHeaders.Authorization.ToString());
Assert.AreEqual($"{_settings.ApiUrl}", _httpClient.BaseAddress.ToString());
Assert.AreEqual(HttpMethod.Post, _httpClient.Requests[0].Method);
Assert.AreEqual(expectedRoute, _httpClient.Requests[0].Uri);
Assert.AreEqual(json, await _httpClient.Requests[0].Content.ReadAsStringAsync());
}
}
}