diff --git a/README.md b/README.md index b68f928..b6be834 100644 --- a/README.md +++ b/README.md @@ -25,3 +25,8 @@ The application calling the service can be a console app. You have total freedom • Write the outputs of the service call to a console window. Create a pull request once you have it working. I will clone your repository, verify that it works, and evaluate it. Please ensure you include any instructions for running that may be required. + + +Instructions: If you have visual studio, there is a debug/run configuration that will start the service and launch the client program. It is +called "Client and Service". Once you have it set, you can go to "Run" -> "Start Without Debugging", and the web service will start and the +client program will launch. \ No newline at end of file diff --git a/ServiceCall/Program.cs b/ServiceCall/Program.cs new file mode 100644 index 0000000..8062ad8 --- /dev/null +++ b/ServiceCall/Program.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading.Tasks; +using Newtonsoft.Json; +using System.Text.RegularExpressions; + +namespace ServiceCall +{ + class Program + { + async static Task Main(string[] args) + { + int option = 0; + do + { + Console.WriteLine(); + Console.WriteLine("Choose an option"); + Console.WriteLine("1) search by order id"); + Console.WriteLine("2) search by msa and status"); + } + while ((!int.TryParse(Console.ReadLine(), out option)) || (option < 1 || option > 2)); + Dictionary dict = new Dictionary(); + Console.WriteLine(); + if (option == 1) + { + dict.Add("OrderID", validateData(() => + { + Console.Write("Enter the order number: "); + return Console.ReadLine(); + }, "[0-9]").AsNumber()); + //dict.Add("OrderID", Console.ReadLine().asNumber()); + } + else + { + dict.Add("MSA", validateData(() => + { + Console.Write("Enter the MSA (value is a number): "); + return Console.ReadLine(); + }, "[0-9]").AsNumber()); + Console.WriteLine(); + dict.Add("Status", validateData(() => + { + Console.Write("Enter the status (value is a number): "); + return Console.ReadLine(); + }, "[0-9]").AsNumber()); + } + Console.WriteLine(); + string date = validateDate(() => validateData(() => { + Console.WriteLine("For the completion date, first give the year (in \"yyyy-mm-dd\" format): "); + return Console.ReadLine(); + }, @"[0-9]{4}\-[0-9]{2}\-[0-9]{2}")); + string time = validateTime(() => validateData(() => { + Console.WriteLine("then the time (in \"hh:mm:ss\" format): "); + return Console.ReadLine(); + }, @"[0-9]{2}\:[0-9]{2}\:[0-9]{2}")); + //dict.Add("CompletionDte", "2018-01-12T05:10:00"); + dict.Add("CompletionDte", $"{date}T{time}"); + Console.WriteLine(); + string pagenumber = validateData(() => + { + Console.Write("What is the page number to start at (leave blank to start at the beginning, page numbers start at 0)? "); + return Console.ReadLine(); + }, "[0-9]", true); + if(!String.IsNullOrEmpty(pagenumber)) + { + dict.Add("page", pagenumber.AsNumber()); + } + Console.WriteLine(); + string pagesize = validateData(() => + { + Console.Write("What is the page size to start at (leave blank for all results or a default size of 25 will be used if a page number was specified and page size was left blank)? "); + return Console.ReadLine(); + }, "[0-9]", true); + if (!String.IsNullOrEmpty(pagesize)) + { + dict.Add("size", pagesize.AsNumber()); + } + Console.WriteLine(); + using (HttpClient client = new HttpClient()) + { + string json = JsonConvert.SerializeObject(dict); + using (HttpContent content = new StringContent(json)) + { + content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); + var result = await client.PostAsync("https://localhost:5001/api/order", content); + Console.WriteLine(await result.Content.ReadAsStringAsync()); + } + + } + System.Threading.Thread.Sleep(10000); + } + + private static string validateData(Func func, string regex, bool allowBlanks = false) + { + string validate; + do + { + validate = func(); + } + while (allowBlanks ? ((!String.IsNullOrEmpty(validate)) && (!Regex.IsMatch(validate, regex))) : (!Regex.IsMatch(validate, regex))); + return validate; + } + + private static string validateDate(Func func) + { + string validate; + //now we check hour, minute, and second values to see if they are kosher + //here, we will only care about 24 hour time + bool verified = false; + do + { + validate = func(); + string[] timesplits = validate.Split('-'); + //we don't care about year because those keep going on and on, but month and day are important + int month = int.Parse(timesplits[1]); + int day = int.Parse(timesplits[2]); + bool leap = (int.Parse(timesplits[0]) % 4) == 0; + //leap years are tricky + if(month == 2 && day > 0 && ((leap && day < 30) || day < 29)) + { + verified = true; + } + else if((month == 4 || month == 6 || month == 9 || month == 11) && day > 0 && day < 31) + { + verified = true; + } + else if((month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12) && day > 0 && day < 32) + { + verified = true; + } + } + while (!verified); + return validate; + } + + private static string validateTime(Func func) + { + string validate; + //now we check hour, minute, and second values to see if they are kosher + //here, we will only care about 24 hour time + bool verified = false; + do + { + validate = func(); + string[] timesplits = validate.Split(':'); + if(int.Parse(timesplits[0]) < 24 && int.Parse(timesplits[1]) < 60 && int.Parse(timesplits[2]) < 60) { + verified = true; + } + } + while (!verified); + return validate; + } + } + + public static class StringExpansions + { + public static int AsNumber(this string s) + { + return int.Parse(s); + } + } +} diff --git a/ServiceCall/ServiceCall.csproj b/ServiceCall/ServiceCall.csproj new file mode 100644 index 0000000..b620416 --- /dev/null +++ b/ServiceCall/ServiceCall.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + README.md + + + diff --git a/simpleOrderSearch/Controllers/OrderController.cs b/simpleOrderSearch/Controllers/OrderController.cs new file mode 100644 index 0000000..3f94bc8 --- /dev/null +++ b/simpleOrderSearch/Controllers/OrderController.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; + +// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860 + +namespace simpleOrderSearch.Controllers +{ + [ApiController] + [Route("api/[controller]")] + public class OrderController : ControllerBase + { + // POST api/values + [HttpPost] + public OrderResult Post([FromBody]IDictionary request) + { + if(!request.ContainsKey("CompletionDte")) + { + throw new Exception("missing date"); + } + //now for finding an item, we can look at either order number or a combination of MSA and Status + //since OrderID is our "primary key", we look for that first before looking for MSA and Status combination + if(request.ContainsKey("OrderID")) + { + var id = request["OrderID"].GetInt64(); + DateTime date = request["CompletionDte"].GetDateTime(); + var page = (request.ContainsKey("page")) ? Math.Max(request["page"].GetInt32(), 0) : 0; + var take = (request.ContainsKey("size")) ? Math.Max(request["size"].GetInt32(), 0) : 0; + return new OrderResult() + { + Results = OrderStore.instance.fetch(id, date, page, take), + PageNumber = page, + PageSize = (take == 0 && page > 0) ? 25 : take + }; + } + else if(request.ContainsKey("MSA") && request.ContainsKey("Status")) + { + long status = request["Status"].GetInt64(); + long msa = request["MSA"].GetInt64(); + DateTime date = request["CompletionDte"].GetDateTime(); + var page = (request.ContainsKey("page")) ? Math.Max(request["page"].GetInt32(), 0) : 0; + var take = (request.ContainsKey("size")) ? Math.Max(request["size"].GetInt32(), 0) : 0; + return new OrderResult() + { + Results = OrderStore.instance.fetch(msa, status, date, page, take), + PageNumber = page, + PageSize = (take == 0 && page > 0) ? 25 : take + }; + } + else + { + throw new Exception("To search, you must provide either an order id or a combination of MSA and status."); + } + } + } +} diff --git a/simpleOrderSearch/Order.cs b/simpleOrderSearch/Order.cs new file mode 100644 index 0000000..efda778 --- /dev/null +++ b/simpleOrderSearch/Order.cs @@ -0,0 +1,37 @@ +using System; +using System.Globalization; +using Newtonsoft.Json; + +namespace simpleOrderSearch +{ + public class Order + { + [JsonProperty("OrderID")] + public long OrderId { get; set; } + + [JsonProperty("ShipperID")] + public long ShipperId { get; set; } + + [JsonProperty("DriverID")] + public long DriverId { get; set; } + + [JsonProperty("CompletionDte")] + public DateTime CompletionDte { get; set; } + + [JsonProperty("Status")] + public long Status { get; set; } + + [JsonProperty("Code")] + public string Code { get; set; } + + [JsonProperty("MSA")] + public long Msa { get; set; } + + [JsonProperty("Duration")] + public string Duration { get; set; } + + [JsonProperty("OfferType")] + public long OfferType { get; set; } + } + +} diff --git a/simpleOrderSearch/OrderResult.cs b/simpleOrderSearch/OrderResult.cs new file mode 100644 index 0000000..9b330c6 --- /dev/null +++ b/simpleOrderSearch/OrderResult.cs @@ -0,0 +1,32 @@ +using System; +using System.Linq; +using System.Collections.Generic; + +namespace simpleOrderSearch +{ + public class OrderResult + { + public IEnumerable Results + { + get; + set; + } + + public int Count + { + get { return Results.Count(); } + } + + public int PageNumber + { + get; + set; + } + + public int PageSize + { + get; + set; + } + } +} diff --git a/simpleOrderSearch/OrderStore.cs b/simpleOrderSearch/OrderStore.cs new file mode 100644 index 0000000..3e8841f --- /dev/null +++ b/simpleOrderSearch/OrderStore.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json; + +namespace simpleOrderSearch +{ + public class OrderStore + { + public static OrderStore instance = new OrderStore(); + private Order[] store; + + private OrderStore() + { + string json = System.IO.File.ReadAllText(@"../data/orderInfo.json"); + store = JsonConvert.DeserializeObject(json); + } + + internal IEnumerable fetch(long id, DateTime date, int page, int size) + { + //size and page of 0 means that the user wants all instances that meet the criteria + if (page == 0 && size == 0) + { + return store.Where(q => q.OrderId == id && q.CompletionDte == date); + } + else + { + //since the user has opted for pagination, we change the number of items taken to the default page size of 25 if page size is not initially given + size = (size == 0) ? 25 : size; + var skipped = page * size; + return store.Where(q => q.OrderId == id && q.CompletionDte == date).Skip(skipped).Take(size); + } + } + + internal IEnumerable fetch(long msa, long status, DateTime date, int page, int size) + { + //size of 0 means that the user wants all instances that meet the criteria + if (page == 0 && size == 0) + { + return store.Where(q => q.Msa == msa && q.Status == status && q.CompletionDte == date); + } + else + { + //since the user has opted for pagination, we change the number of items taken to the default page size of 25 if page size is not initially given + size = (size == 0) ? 25 : size; + var skipped = page * size; + return store.Where(q => q.Msa == msa && q.Status == status && q.CompletionDte == date).Skip(skipped).Take(size); + } + } + } +}