minor refactor
This commit is contained in:
parent
5366b11f55
commit
0ee2be969f
6 changed files with 138 additions and 136 deletions
10
Program.cs
10
Program.cs
|
@ -1,6 +1,5 @@
|
||||||
using Configuration;
|
using DTO;
|
||||||
using DTO;
|
using static Configuration.ApplicationConfigurationService;
|
||||||
using static Configuration.DemoApplicationConfiguration;
|
|
||||||
|
|
||||||
namespace OrderParser
|
namespace OrderParser
|
||||||
{
|
{
|
||||||
|
@ -10,11 +9,12 @@ namespace OrderParser
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var filesInfoConfig = DemoApplicationConfiguration.InitializeFrom(args);
|
var filesInfoConfig = Configuration.ApplicationConfigurationService.InitializeFrom(args);
|
||||||
var csvFileWriter = new CsvFileWriter(filesInfoConfig.OutputFile);
|
var csvFileWriter = new CsvFileWriterService(filesInfoConfig.OutputFile);
|
||||||
|
|
||||||
foreach (var jsonFile in filesInfoConfig.JsonFilesInfo)
|
foreach (var jsonFile in filesInfoConfig.JsonFilesInfo)
|
||||||
{
|
{
|
||||||
|
// Could have easily been another service, but only do that when it needs to be scaled.
|
||||||
var orderDTO = Newtonsoft.Json.JsonConvert.DeserializeObject<OrderDto>(File.ReadAllText(jsonFile.FullName));
|
var orderDTO = Newtonsoft.Json.JsonConvert.DeserializeObject<OrderDto>(File.ReadAllText(jsonFile.FullName));
|
||||||
|
|
||||||
var orderModel = new OrderModel(orderDTO);
|
var orderModel = new OrderModel(orderDTO);
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
## Infrastructure disclaimer
|
## Infrastructure disclaimer
|
||||||
I come from a background of developing mobile applications in Dart/Flutter, with a different set of conventions (such for file naming etc). I have done some research into doing it and tried sticking to it. But in the end, the architecture is still heavily influenced by practices in mobile development.
|
I come from a background of developing mobile applications in Dart/Flutter, with a different set of conventions (such for file naming etc). I have done some research into doing it and tried sticking to it. But in the end, the architecture is still heavily influenced by practices in mobile development.
|
||||||
|
|
||||||
|
Additionally, I haven't focussed too much on Exception handling, since I feel that the scope of this assignment, is just a simple PoC. I do have some basic safeguards for input validation, and basic exception printing.
|
||||||
|
|
||||||
### Structure
|
### Structure
|
||||||
|
|
||||||
#### Assets
|
#### Assets
|
||||||
|
@ -19,8 +21,8 @@ There ae 2 JSON files in the `assets` directory. These can be used as input as c
|
||||||
2. [OrderModel](models/OrderModel.cs): Captures the usable part of the `OrderDTO` that will be written out to the CSV.
|
2. [OrderModel](models/OrderModel.cs): Captures the usable part of the `OrderDTO` that will be written out to the CSV.
|
||||||
|
|
||||||
#### Services
|
#### Services
|
||||||
1. [DemoApplicationConfiguration](services/DemoApplicationConfiguration.cs): Stores the configuration that the program ran with. Such as the command-line args passed (and hence the mode), the actual input data, and the ouput destination. Along with the file-existnece checks
|
1. [ApplicationConfigurationService](services/ApplicationConfigurationService.cs): Stores the configuration that the program ran with. Such as the command-line args passed (and hence the mode), the actual input data, and the ouput destination. Along with the file-existnece checks
|
||||||
2. [CsvWriter](services/CsvWriter.cs): Writes an order to a file.
|
2. [CsvWriterService](services/CsvWriterService.cs): Writes an order to a file.
|
||||||
3. Deserializing the JSON was handled by a package. Could have made this into a service ideally, for better scaling up.
|
3. Deserializing the JSON was handled by a package. Could have made this into a service ideally, for better scaling up.
|
||||||
|
|
||||||
#### Packages used
|
#### Packages used
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"ProductId": "PID4",
|
"ProductId": "PID4",
|
||||||
"Description": "pid4 description",
|
"Description": "pid4 description",
|
||||||
"Amount": 2.5,
|
"Amount": 2.5,
|
||||||
"price": 169.0
|
"price": 69.0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
244
review.cs
244
review.cs
|
@ -1,144 +1,144 @@
|
||||||
using SollicitantReview.Models;
|
// using SollicitantReview.Models;
|
||||||
using SollicitantReview.Services;
|
// using SollicitantReview.Services;
|
||||||
using Microsoft.Extensions.Options;
|
// using Microsoft.Extensions.Options;
|
||||||
using System;
|
// using System;
|
||||||
using System.Collections.Generic;
|
// using System.Collections.Generic;
|
||||||
using System.IO;
|
// using System.IO;
|
||||||
using System.Linq;
|
// using System.Linq;
|
||||||
using System.Text;
|
// using System.Text;
|
||||||
using System.Threading;
|
// using System.Threading;
|
||||||
using System.Threading.Tasks;
|
// using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace SollicitantReview
|
// namespace SollicitantReview
|
||||||
{
|
// {
|
||||||
public class SollicitantReview
|
// public class SollicitantReview
|
||||||
{
|
// {
|
||||||
private SollicitantReviewSettings options;
|
// private SollicitantReviewSettings options;
|
||||||
private IWriter writer;
|
// private IWriter writer;
|
||||||
private Queue<List<ReviewExportLine>> exportLineQueue;
|
// private Queue<List<ReviewExportLine>> exportLineQueue;
|
||||||
|
|
||||||
public SollicitantReview(IOptions<SollicitantReviewSettings> options, IWriter writer)
|
// public SollicitantReview(IOptions<SollicitantReviewSettings> options, IWriter writer)
|
||||||
{
|
// {
|
||||||
this.options = options.Value;
|
// this.options = options.Value;
|
||||||
this.writer = writer;
|
// this.writer = writer;
|
||||||
this.exportLineQueue = new Queue<List<ReviewExportLine>>();
|
// this.exportLineQueue = new Queue<List<ReviewExportLine>>();
|
||||||
|
|
||||||
var processExportLinesThread = new Thread(new ThreadStart(ProcessReviewExportLines));
|
// var processExportLinesThread = new Thread(new ThreadStart(ProcessReviewExportLines));
|
||||||
processExportLinesThread.Start();
|
// processExportLinesThread.Start();
|
||||||
}
|
// }
|
||||||
|
|
||||||
public void RegisterOrder(Order order)
|
// public void RegisterOrder(Order order)
|
||||||
{
|
// {
|
||||||
DeleteOrder(order);
|
// DeleteOrder(order);
|
||||||
exportLineQueue.Enqueue(ConvertOrderToExportLines(order));
|
// exportLineQueue.Enqueue(ConvertOrderToExportLines(order));
|
||||||
}
|
// }
|
||||||
|
|
||||||
private string FilterString(string value)
|
// private string FilterString(string value)
|
||||||
{
|
// {
|
||||||
return value.Replace(";", ":");
|
// return value.Replace(";", ":");
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void DeleteOrder(Order order)
|
// private void DeleteOrder(Order order)
|
||||||
{
|
// {
|
||||||
var exportLines = new List<ReviewExportLine>()
|
// var exportLines = new List<ReviewExportLine>()
|
||||||
{
|
// {
|
||||||
new ReviewExportLine()
|
// new ReviewExportLine()
|
||||||
{
|
// {
|
||||||
Type = "9",
|
// Type = "9",
|
||||||
OrderNumber = FilterString(order.OrderNumber),
|
// OrderNumber = FilterString(order.OrderNumber),
|
||||||
Amount = "1",
|
// Amount = "1",
|
||||||
UserId = FilterString(order.User),
|
// UserId = FilterString(order.User),
|
||||||
JournalPostIndication = "Y"
|
// JournalPostIndication = "Y"
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
exportLineQueue.Enqueue(exportLines);
|
// exportLineQueue.Enqueue(exportLines);
|
||||||
}
|
// }
|
||||||
|
|
||||||
private List<ReviewExportLine> ConvertOrderToExportLines(Order order, string userId = null)
|
// private List<ReviewExportLine> ConvertOrderToExportLines(Order order, string userId = null)
|
||||||
{
|
// {
|
||||||
var exportLines = new List<ReviewExportLine>();
|
// var exportLines = new List<ReviewExportLine>();
|
||||||
|
|
||||||
foreach (var orderLine in order.OrderLines)
|
// foreach (var orderLine in order.OrderLines)
|
||||||
{
|
// {
|
||||||
exportLines.Add(new ReviewExportLine()
|
// exportLines.Add(new ReviewExportLine()
|
||||||
{
|
// {
|
||||||
OrderNumber = FilterString(order.OrderNumber),
|
// OrderNumber = FilterString(order.OrderNumber),
|
||||||
Name = FilterString(orderLine.Name),
|
// Name = FilterString(orderLine.Name),
|
||||||
Amount = FilterString(orderLine.Amount),
|
// Amount = FilterString(orderLine.Amount),
|
||||||
ProductNumber = FilterString(orderLine.Number),
|
// ProductNumber = FilterString(orderLine.Number),
|
||||||
ProductDescription = FilterString(orderLine.Description),
|
// ProductDescription = FilterString(orderLine.Description),
|
||||||
UserId = userId == null ? FilterString(order.User) : userId,
|
// UserId = userId == null ? FilterString(order.User) : userId,
|
||||||
Location = FilterString(orderLine.Location),
|
// Location = FilterString(orderLine.Location),
|
||||||
JournalPostIndication = "Y"
|
// JournalPostIndication = "Y"
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
return exportLines;
|
// return exportLines;
|
||||||
}
|
// }
|
||||||
|
|
||||||
private void ProcessReviewExportLines()
|
// private void ProcessReviewExportLines()
|
||||||
{
|
// {
|
||||||
while(true)
|
// while(true)
|
||||||
{
|
// {
|
||||||
if (exportLineQueue.Any() && IsFolderEmpty(options.ReviewFolderPath))
|
// if (exportLineQueue.Any() && IsFolderEmpty(options.ReviewFolderPath))
|
||||||
{
|
// {
|
||||||
var exportLines = exportLineQueue.Dequeue();
|
// var exportLines = exportLineQueue.Dequeue();
|
||||||
var randomStringLength = 7;
|
// var randomStringLength = 7;
|
||||||
|
|
||||||
writer.OpenFile($"{options.ReviewFolderPath}\\{GenerateRandomString(randomStringLength)}.txt");
|
// writer.OpenFile($"{options.ReviewFolderPath}\\{GenerateRandomString(randomStringLength)}.txt");
|
||||||
|
|
||||||
foreach (var exportLine in exportLines)
|
// foreach (var exportLine in exportLines)
|
||||||
{
|
// {
|
||||||
writer.WriteLine($"{exportLine.Type};" +
|
// writer.WriteLine($"{exportLine.Type};" +
|
||||||
$"{exportLine.OrderNumber};" +
|
// $"{exportLine.OrderNumber};" +
|
||||||
$"{exportLine.Name};" +
|
// $"{exportLine.Name};" +
|
||||||
$"{exportLine.Amount};" +
|
// $"{exportLine.Amount};" +
|
||||||
$"{exportLine.ProductNumber};" +
|
// $"{exportLine.ProductNumber};" +
|
||||||
$"{exportLine.ProductDescription};" +
|
// $"{exportLine.ProductDescription};" +
|
||||||
$"{exportLine.UserId};" +
|
// $"{exportLine.UserId};" +
|
||||||
$"{exportLine.Location};" +
|
// $"{exportLine.Location};" +
|
||||||
$"{exportLine.JournalPostIndication};" +
|
// $"{exportLine.JournalPostIndication};" +
|
||||||
$"{exportLine.Info2}");
|
// $"{exportLine.Info2}");
|
||||||
}
|
// }
|
||||||
|
|
||||||
writer.CloseFile();
|
// writer.CloseFile();
|
||||||
}
|
// }
|
||||||
|
|
||||||
Thread.Sleep(2000);
|
// Thread.Sleep(2000);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private bool IsFolderEmpty(string path)
|
// private bool IsFolderEmpty(string path)
|
||||||
{
|
// {
|
||||||
DirectoryInfo dirInfo = new DirectoryInfo(path);
|
// DirectoryInfo dirInfo = new DirectoryInfo(path);
|
||||||
|
|
||||||
if (!dirInfo.Exists)
|
// if (!dirInfo.Exists)
|
||||||
{
|
// {
|
||||||
throw new CouldNotFindReviewDirectory();
|
// throw new CouldNotFindReviewDirectory();
|
||||||
}
|
// }
|
||||||
|
|
||||||
return !dirInfo.GetFiles("*.txt").Any();
|
// return !dirInfo.GetFiles("*.txt").Any();
|
||||||
}
|
// }
|
||||||
|
|
||||||
private string GenerateRandomString(int length)
|
// private string GenerateRandomString(int length)
|
||||||
{
|
// {
|
||||||
StringBuilder stringBuilder = new StringBuilder();
|
// StringBuilder stringBuilder = new StringBuilder();
|
||||||
Random random = new Random();
|
// Random random = new Random();
|
||||||
var ASCIIOffset = 65;
|
// var ASCIIOffset = 65;
|
||||||
var ASCIIRange = 25;
|
// var ASCIIRange = 25;
|
||||||
|
|
||||||
char letter;
|
// char letter;
|
||||||
|
|
||||||
for (int i = 0; i < length; i++)
|
// for (int i = 0; i < length; i++)
|
||||||
{
|
// {
|
||||||
double randomDouble = random.NextDouble();
|
// double randomDouble = random.NextDouble();
|
||||||
int shift = Convert.ToInt32(Math.Floor(ASCIIRange * randomDouble));
|
// int shift = Convert.ToInt32(Math.Floor(ASCIIRange * randomDouble));
|
||||||
letter = Convert.ToChar(shift + ASCIIOffset);
|
// letter = Convert.ToChar(shift + ASCIIOffset);
|
||||||
stringBuilder.Append(letter);
|
// stringBuilder.Append(letter);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return stringBuilder.ToString();
|
// return stringBuilder.ToString();
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
namespace Configuration
|
namespace Configuration
|
||||||
{
|
{
|
||||||
public sealed class DemoApplicationConfiguration
|
public sealed class ApplicationConfigurationService
|
||||||
{
|
{
|
||||||
private FileInfo[] jsonFiles;
|
private FileInfo[] jsonFiles;
|
||||||
public FileInfo[] JsonFilesInfo
|
public FileInfo[] JsonFilesInfo
|
||||||
|
@ -22,7 +22,7 @@ namespace Configuration
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DemoApplicationConfiguration InitializeFrom(string[] args)
|
public static ApplicationConfigurationService InitializeFrom(string[] args)
|
||||||
{
|
{
|
||||||
if (args.Length < 3)
|
if (args.Length < 3)
|
||||||
throw new DemoApplicationConfigurationException("Must have atleast: 1 flag, 1 path, 1 output filename");
|
throw new DemoApplicationConfigurationException("Must have atleast: 1 flag, 1 path, 1 output filename");
|
||||||
|
@ -41,14 +41,14 @@ namespace Configuration
|
||||||
// Assert only a single 'd' was passed
|
// Assert only a single 'd' was passed
|
||||||
if ( givenFlags.Length == 1 && givenFlags[0].Equals("-d") ) {
|
if ( givenFlags.Length == 1 && givenFlags[0].Equals("-d") ) {
|
||||||
string[] filesInDirectory = Directory.GetFiles(givenFilePaths[0]);
|
string[] filesInDirectory = Directory.GetFiles(givenFilePaths[0]);
|
||||||
return new DemoApplicationConfiguration {
|
return new ApplicationConfigurationService {
|
||||||
OutputFile = outputFileName,
|
OutputFile = outputFileName,
|
||||||
JsonFilesInfo = filesInDirectory.Select(name => new FileInfo(name)).ToArray(),
|
JsonFilesInfo = filesInDirectory.Select(name => new FileInfo(name)).ToArray(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Assert only '-f' were passed
|
// Assert only '-f' were passed
|
||||||
else if (givenFlags.All((string flag) => flag.Equals("-f"))) {
|
else if (givenFlags.All((string flag) => flag.Equals("-f"))) {
|
||||||
return new DemoApplicationConfiguration {
|
return new ApplicationConfigurationService {
|
||||||
OutputFile = outputFileName,
|
OutputFile = outputFileName,
|
||||||
JsonFilesInfo = givenFilePaths.Select(name => new FileInfo(name)).ToArray(),
|
JsonFilesInfo = givenFilePaths.Select(name => new FileInfo(name)).ToArray(),
|
||||||
};
|
};
|
|
@ -1,12 +1,12 @@
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
|
|
||||||
class CsvFileWriter
|
class CsvFileWriterService
|
||||||
{
|
{
|
||||||
private TextWriter textWriter;
|
private TextWriter textWriter;
|
||||||
private CsvWriter csvWriter;
|
private CsvWriter csvWriter;
|
||||||
|
|
||||||
public CsvFileWriter(FileInfo fileInfo) {
|
public CsvFileWriterService(FileInfo fileInfo) {
|
||||||
textWriter = new StreamWriter(fileInfo.FullName);
|
textWriter = new StreamWriter(fileInfo.FullName);
|
||||||
|
|
||||||
csvWriter = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
|
csvWriter = new CsvWriter(textWriter, CultureInfo.InvariantCulture);
|
Loading…
Reference in a new issue