minor refactor

This commit is contained in:
Mguy13 2023-01-06 16:08:26 +01:00
parent 5366b11f55
commit 1cfb8be85b
7 changed files with 139 additions and 137 deletions

View file

@ -12,7 +12,7 @@ This is a program manages the processing of orders in the following way: when an
5. The `DeleteOrder` method enqueues a single ReviewExportLine object with a Type field value of "9" to the queue. It is not clear what the purpose of this line is, or how it is related to the process of deleting an order. 5. The `DeleteOrder` method enqueues a single ReviewExportLine object with a Type field value of "9" to the queue. It is not clear what the purpose of this line is, or how it is related to the process of deleting an order.
6. `GenerateRandomString` method does not check the validity of the returned path: Since it generates these characters randomly, there could be a chaater that is considered illegal to be used as in a system-path. Additionally, 7 characters (which are very bounded) might not prove to be enough to prevent collisions. 6. `GenerateRandomString` method does not check the validity of the returned path: Since it generates these characters randomly, there could be a character that is considered illegal to be used as in a system-path. Additionally, 7 characters (which are very bounded) might not prove to be enough to prevent collisions.
## How to solve them ## How to solve them
1. Add a condition to the loop to terminate it after a certain number of iterations or after a certain amount of time has passed Alternatively, a cancellation token to the method and pass it a token that can be used to cancel the loop. Or used better design-patterns 1. Add a condition to the loop to terminate it after a certain number of iterations or after a certain amount of time has passed Alternatively, a cancellation token to the method and pass it a token that can be used to cancel the loop. Or used better design-patterns

View file

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

View file

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

View file

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

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

View file

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

View file

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