diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..31aeb6d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,341 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUNIT
+*.VisualState.xml
+TestResult.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# JustCode is a .NET coding add-in
+.JustCode
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- Backup*.rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# JetBrains Rider
+.idea/
+*.sln.iml
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+/.vscode
diff --git a/README.md b/README.md
index b68f928..ddcb7ad 100644
--- a/README.md
+++ b/README.md
@@ -1,27 +1,20 @@
# simpleOrderSearch
simpleOrderSearch
+Solution Built Using:
-I want to assess your ability to create an application and REST API service. It truly is the bare minimum of knowledge necessary to be successful in this position. I don't want you to spend a lot of time on this. You should be able to do this in an hour or so if the job is right for you.
+.NET Core 2.2 |
+.NET Standard 2.0 |
+Visual Studio 2019 |
+WPF |
+RX.NET |
+GraphQL
-Order Search
-This programming task consists of building a simple console application to search for orders. Fork this repository and create your application. It should take this input from the user:
+Instructions:
-(Order Number || (MSA && Status)) && CompletionDte
+Download solution as zip and extract.
+Set SimpleOrderSearch.Desktop and SimpleOrderSearch.Service as startup projects. Ensuring that the service is the first project to start up.
+Run the service solution and the client solution.
+The UI should be easy to navigate.
-The console application will call a service that you create using C#. I have provided some sample data for the application in the JSON file in the data folder.
-
-
-
-The file contains an array whose elements represent orders. The data should be defined as a model in your service.
-
-The application calling the service can be a console app. You have total freedom to do what you want but make sure it can do these three things:
-
-• Validate that the user has provided the right criteria to make a search
-
-• Provide an offset and page value for pagination.
-
-• 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.
diff --git a/SimpleOrderSearch.Desktop/App.config b/SimpleOrderSearch.Desktop/App.config
new file mode 100644
index 0000000..81a268a
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/App.config
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/App.xaml b/SimpleOrderSearch.Desktop/App.xaml
new file mode 100644
index 0000000..b90de89
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/App.xaml
@@ -0,0 +1,9 @@
+
+
+
+
+
diff --git a/SimpleOrderSearch.Desktop/App.xaml.cs b/SimpleOrderSearch.Desktop/App.xaml.cs
new file mode 100644
index 0000000..39da15c
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/App.xaml.cs
@@ -0,0 +1,17 @@
+using System;
+using System.Collections.Generic;
+using System.Configuration;
+using System.Data;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows;
+
+namespace SimpleOrderSearch.Desktop
+{
+ ///
+ /// Interaction logic for App.xaml
+ ///
+ public partial class App : Application
+ {
+ }
+}
diff --git a/SimpleOrderSearch.Desktop/FodyWeavers.xml b/SimpleOrderSearch.Desktop/FodyWeavers.xml
new file mode 100644
index 0000000..e12df67
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/FodyWeavers.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/FodyWeavers.xsd b/SimpleOrderSearch.Desktop/FodyWeavers.xsd
new file mode 100644
index 0000000..f3ac476
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/FodyWeavers.xsd
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+ 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.
+
+
+
+
+ A comma-separated list of error codes that can be safely ignored in assembly verification.
+
+
+
+
+ 'false' to turn off automatic generation of the XML Schema file.
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/MainViewModel.cs b/SimpleOrderSearch.Desktop/MainViewModel.cs
new file mode 100644
index 0000000..ccddcf9
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/MainViewModel.cs
@@ -0,0 +1,217 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reactive.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using ReactiveUI;
+using ReactiveUI.Fody.Helpers;
+using DynamicData.Binding;
+using System.Collections.ObjectModel;
+using SimpleOrderSearch.Model;
+//using System.Reactive;
+using SimpleOrderSearch.Desktop.ProxyClient;
+using DynamicData;
+using ReactiveUI.Validation.Abstractions;
+using ReactiveUI.Validation.Contexts;
+using ReactiveUI.Validation.Extensions;
+using ReactiveUI.Validation.Helpers;
+using System.Reactive;
+
+namespace SimpleOrderSearch.Desktop
+{
+ public class MainViewModel : ReactiveValidationObject
+ {
+ List _cursorList = new List();
+
+ public ValidationContext OrderParametersRule { get; private set; }
+
+ public ValidationHelper CompletionDateRule { get; private set; }
+ public ValidationHelper OrderNumberRule { get; private set; }
+ public ValidationHelper StatusRule { get; private set; }
+ public ValidationHelper MSARule { get; private set; }
+
+ [Reactive] public string OrderNo { get; set; }
+ [Reactive] public string MSA { get; set; }
+ [Reactive] public string Status { get; set; }
+ [Reactive] public DateTime? CompletionDate { get; set; } = DateTime.Now;
+
+ [Reactive] public int PageSize { get; set; } = 5;
+ [Reactive] public int PageNo { get; set; } = 1;
+ [Reactive] public bool HasValidCriteria { get; set; } = true;
+ [Reactive] public bool CanPageUp { get; set; }
+ [Reactive] public bool CanPageDown { get; set; }
+ [Reactive] public string ErrorMsg { get; set; }
+
+ [Reactive] public PageInfo CurrentPageInfo { get; set; } = new PageInfo();
+ private string CurrentCursor { get; set; } = string.Empty;
+
+
+ public ObservableCollection Orders { get; set; } = new ObservableCollection();
+
+ ReactiveCommand _rxSearchCommand;
+ ReactiveCommand _rxCommandPageUp;
+ ReactiveCommand _rxCommandPageDown;
+ public ReactiveCommand RxSearchCommad => _rxSearchCommand;
+ public ReactiveCommand RxCommandPageUp => _rxCommandPageUp;
+ public ReactiveCommand RxCommandPageDown => _rxCommandPageDown;
+
+ public MainViewModel()
+ {
+ this.WhenAnyValue(x => x.MSA, x => x.Status, x => x.OrderNo, x => x.CompletionDate).Do(OnNextParamter).Subscribe();
+ var canSearch = this.WhenAnyValue(x => x.HasValidCriteria);
+
+ //var canPageUp = this.WhenAnyValue(x => x.HasValidCriteria, x => x.Orders.Count, x => x.CanPageUp, x => x.PageNo).Select(x => x.Item1 && x.Item2 > 0 && !x.Item3 && x.Item4 > 0);
+ //var canPageDown = this.WhenAnyValue(x => x.HasValidCriteria, x => x.Orders.Count, x => x.CanPageDown, x => x.PageNo).Select(x => x.Item1 && x.Item2 > 0 && !x.Item3 && x.Item4 > 0);
+
+ var canPageUp = this.WhenAnyValue(x => x.CurrentPageInfo).Select(x => x.HasNextPage);
+ var canPageDown = this.WhenAnyValue(x => x.CurrentPageInfo).Select(x => x.HasPreviousPage);
+
+
+
+ _rxSearchCommand = ReactiveCommand.Create(() => OnSearch(true), canSearch);
+ _rxCommandPageUp = ReactiveCommand.Create(() => OnPageUp(), canPageUp);
+ _rxCommandPageDown = ReactiveCommand.Create(() => OnPageDown(), canPageDown);
+
+ ////OrderNumberRule = this.ValidationRule(vm => vm.OrderNo,
+ //// orderid =>
+ //// {
+ //// var val = NumericValidation(orderid);
+ //// return val.isValid && val.parsedValue > 0 && !NumericValidation(this.MSA).isValid && !NumericValidation(this.Status).isValid;
+ //// } ,
+ //// orderid => $"Order Number { (string.IsNullOrWhiteSpace(orderid) ? null : orderid) } Is Invalid.");
+
+ ////MSARule = this.ValidationRule(vm => vm.MSA,
+ //// orderid =>
+ //// {
+ //// var val = NumericValidation(orderid);
+ //// return val.isValid && val.parsedValue > 0 && !NumericValidation(this.MSA).isValid && !NumericValidation(this.Status).isValid;
+ //// },
+ //// orderid => $"Order Number { (string.IsNullOrWhiteSpace(orderid) ? null : orderid) } Is Invalid.");
+
+ ////StatusRule = this.ValidationRule(vm => vm.Status,
+ //// orderid =>
+ //// {
+ //// var val = NumericValidation(orderid);
+ //// return val.isValid && val.parsedValue > 0 && !NumericValidation(this.MSA).isValid && !NumericValidation(this.Status).isValid;
+ //// },
+ //// orderid => $"Order Number { (string.IsNullOrWhiteSpace(orderid) ? null : orderid) } Is Invalid.");
+
+ }
+
+ private void OnPageDown()
+ {
+ PageNo -= 1;
+ OnSearch(false, false);
+ }
+
+ private void OnPageUp()
+ {
+ PageNo += 1;
+ OnSearch(false, true);
+ }
+
+ private async void OnSearch(bool isCursorReset = true, bool? isPageUp = null)
+ {
+ if (isCursorReset)
+ _cursorList.Clear();
+ else
+ {
+ if (isPageUp.HasValue && isPageUp.Value == false)
+ {
+ CurrentCursor = _cursorList[this.PageNo];
+ }
+ }
+
+ OrderSearchQuery query = new OrderSearchQuery()
+ {
+ CompletionDate = this.CompletionDate,
+ MSA = MSA.ToNullableValue(),
+ OrderNumber = OrderNo.ToNullableValue(),
+ Page = this.PageNo,
+ PageLimit = this.PageSize,
+ Status = Status.ToNullableValue(),
+ Cursor = isCursorReset ? string.Empty : CurrentCursor,
+ IsPageUp = isPageUp.HasValue == false ? (bool?)null : isPageUp.HasValue && isPageUp.Value
+ };
+
+ ////var orderResults = OrderSearchProxy.GetOrders(query);
+ var results = await OrderSearchProxy.PostOrderGraphQLQuery(query);
+ if (results != null)
+ {
+ if (results.PageInfo != null)
+ {
+ results.PageInfo.HasPreviousPage = this.PageNo > 1;
+
+ CurrentPageInfo = results.PageInfo;
+
+ string cursor = results.Edges.LastOrDefault()?.Cursor ?? string.Empty;
+
+ CurrentCursor = cursor;
+
+ if (isCursorReset || (isPageUp != null && !_cursorList.Contains(cursor)))
+ _cursorList.Add(cursor);
+
+ Orders.Clear();
+
+ foreach (var node in results.Edges.Select(p => p.Node))
+ {
+ Orders.Add(node);
+ }
+ }
+ }
+
+ //this.CanPageDown = orderResults.IsStart;
+ //this.CanPageUp = orderResults.IsEnd;
+ //Orders.Clear();
+
+ //if (graphQlQryResults != null)
+ //{
+ // graphQlQryResults.ForEach((order) => { Orders.Add(order); });
+ //}
+
+ ////if (orderResults.IsValid)
+ ////{
+ //// foreach (var result in orderResults?.Orders)
+ //// {
+ //// Orders.Add(result);
+ //// }
+ ////}
+ ////else if (orderResults.ErrorResponse != null && orderResults.ErrorResponse.Errors.Any())
+ ////{
+ //// ErrorMsg = string.Join("|", orderResults.ErrorResponse.Errors.Select(p => p.Message));
+ ////}
+ }
+
+ private void OnNextParamter((string msa, string status, string orderno, DateTime? completionDate) obj)
+ {
+ List errors = new List();
+
+ if (!obj.completionDate.HasValue)
+ errors.Add("Valid CompletionDate Required.");
+
+ var orderObj = NumericValidation(obj.orderno);
+ var msaObj = NumericValidation(obj.msa);
+ var statusObj = NumericValidation(obj.status);
+
+ if (!orderObj.isValid && !msaObj.isValid && !statusObj.isValid)
+ errors.Add("Valid Numeric OrderNo or Valid Numeric Status and MSA value Required.");
+ else if (!orderObj.isValid)
+ {
+ if (msaObj.isValid && !statusObj.isValid)
+ errors.Add("Numeric Status Value Required.");
+ else if (!msaObj.isValid && statusObj.isValid)
+ errors.Add("Numeric MSA Value Required.");
+ }
+ HasValidCriteria = !errors.Any();
+ ErrorMsg = string.Join("|", errors);
+ }
+
+ private (object orgValue, int parsedValue, bool isValid) NumericValidation(object o)
+ {
+ int parsedValue;
+ bool isValid = int.TryParse(o == null ? string.Empty : o.ToString(), out parsedValue);
+ return (o, parsedValue, isValid);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/MainWindow.xaml b/SimpleOrderSearch.Desktop/MainWindow.xaml
new file mode 100644
index 0000000..2dc0750
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/MainWindow.xaml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SimpleOrderSearch.Desktop/MainWindow.xaml.cs b/SimpleOrderSearch.Desktop/MainWindow.xaml.cs
new file mode 100644
index 0000000..8e0c3ad
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/MainWindow.xaml.cs
@@ -0,0 +1,46 @@
+using ReactiveUI.Validation.Extensions;
+using ReactiveUI;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+using System.Reactive.Disposables;
+
+namespace SimpleOrderSearch.Desktop
+{
+ ///
+ /// Interaction logic for MainWindow.xaml
+ ///
+ //public partial class MainWindow : ReactiveWindow
+ public partial class MainWindow : Window
+ {
+ public MainWindow()
+ {
+ InitializeComponent();
+ ////this.ViewModel = new MainViewModel();
+ ////this.DataContext = this.ViewModel;
+ this.DataContext = new MainViewModel();
+
+ ////this.WhenActivated(disposable =>
+ ////{
+ //// this.Bind(ViewModel,
+ //// viewModel => viewModel.OrderNo,
+ //// view => view.TxtBoxOrderNo.Text)
+ //// .DisposeWith(disposable);
+
+ //// this.BindValidation(ViewModel, vm => vm.OrderNumberRule, v => v.TxtBoxOrderNo.Text)
+ //// .DisposeWith(disposable);
+ ////});
+ }
+ }
+}
diff --git a/SimpleOrderSearch.Desktop/Properties/AssemblyInfo.cs b/SimpleOrderSearch.Desktop/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..04e22ed
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/Properties/AssemblyInfo.cs
@@ -0,0 +1,55 @@
+using System.Reflection;
+using System.Resources;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Windows;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SimpleOrderSearch.Desktop")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Hewlett-Packard")]
+[assembly: AssemblyProduct("SimpleOrderSearch.Desktop")]
+[assembly: AssemblyCopyright("Copyright © Hewlett-Packard 2019")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+//In order to begin building localizable applications, set
+//CultureYouAreCodingWith in your .csproj file
+//inside a . For example, if you are using US english
+//in your source files, set the to en-US. Then uncomment
+//the NeutralResourceLanguage attribute below. Update the "en-US" in
+//the line below to match the UICulture setting in the project file.
+
+//[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]
+
+
+[assembly: ThemeInfo(
+ ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
+ //(used if a resource is not found in the page,
+ // or application resource dictionaries)
+ ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
+ //(used if a resource is not found in the page,
+ // app, or any theme specific resource dictionaries)
+)]
+
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/SimpleOrderSearch.Desktop/Properties/Resources.Designer.cs b/SimpleOrderSearch.Desktop/Properties/Resources.Designer.cs
new file mode 100644
index 0000000..f09a9ba
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SimpleOrderSearch.Desktop.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SimpleOrderSearch.Desktop.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/SimpleOrderSearch.Desktop/Properties/Resources.resx b/SimpleOrderSearch.Desktop/Properties/Resources.resx
new file mode 100644
index 0000000..af7dbeb
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/Properties/Settings.Designer.cs b/SimpleOrderSearch.Desktop/Properties/Settings.Designer.cs
new file mode 100644
index 0000000..cfa82dd
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace SimpleOrderSearch.Desktop.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/SimpleOrderSearch.Desktop/Properties/Settings.settings b/SimpleOrderSearch.Desktop/Properties/Settings.settings
new file mode 100644
index 0000000..033d7a5
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/ProxyClient/OrderSearchProxy.cs b/SimpleOrderSearch.Desktop/ProxyClient/OrderSearchProxy.cs
new file mode 100644
index 0000000..9bdf890
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/ProxyClient/OrderSearchProxy.cs
@@ -0,0 +1,111 @@
+using GraphQL.Client;
+using GraphQL.Common.Request;
+using RestSharp;
+using SimpleOrderSearch.Model;
+using System;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Desktop.ProxyClient
+{
+ public class OrderSearchProxy
+ {
+ private const string uri = "https://localhost:44345/api/ordersearch";
+ private const string graphQluri = "https://localhost:44345/graphql";
+
+ public static PagedOrderInfo GetOrders(OrderSearchQuery searchQuery)
+ {
+ var client = new RestClient(uri);
+ var request = new RestRequest(Method.GET);
+ request.AddParameter("OrderNo", searchQuery.OrderNumber);
+ request.AddParameter("Status", searchQuery.Status);
+ request.AddParameter("MSA", searchQuery.MSA);
+ request.AddParameter("Page", searchQuery.Page);
+ request.AddParameter("CompletionDate", searchQuery.CompletionDate);
+ request.AddParameter("PageLimit", searchQuery.PageLimit);
+ IRestResponse response = client.Execute(request);
+
+ RestSharp.Serialization.Json.JsonDeserializer jsonDeserializer = new RestSharp.Serialization.Json.JsonDeserializer();
+ var pagedOrderInfo = jsonDeserializer.Deserialize(response);
+ return pagedOrderInfo;
+ }
+
+ public static PagedOrderInfo PostOrdersQuery(OrderSearchQuery searchQuery)
+ {
+ var client = new RestClient(uri);
+ var request = new RestRequest(Method.POST);
+ request.AddJsonBody(searchQuery);
+ IRestResponse response = client.ExecuteAsPost(request, Method.POST.ToString());
+ RestSharp.Serialization.Json.JsonDeserializer jsonDeserializer = new RestSharp.Serialization.Json.JsonDeserializer();
+ var orders = jsonDeserializer.Deserialize(response);
+ return orders;
+ }
+
+
+ //public static async Task> PostOrderGraphQLQuery(OrderSearchQuery searchQuery)
+ public static async Task> PostOrderGraphQLQuery(OrderSearchQuery searchQuery)
+ {
+ var graphQlClient = new GraphQLClient(new Uri(graphQluri));
+ var request = new GraphQLRequest
+ {
+ //OperationName = "OrderQuery",
+ //Query = @"query OrderQuery($orderId: Int!, $msa: Int, $status: Int, $completionDate: Date) {
+ // orders(orderId: $orderId, msa: $msa, status: $status, completionDate: $completionDate) {
+ // orderID
+ // mSA
+ // status
+ // completionDte
+ // offerType
+ // driverID
+ // duration
+ // code
+ // }
+ // }",
+
+ OperationName = "OrderQuery",
+ Query = @"query OrderQuery($orderId: ID!, $msa: Int, $status: Int, $completionDate: Date, $first: Int, $after: String) {
+ ordersConnection(orderId: $orderId, msa: $msa, status: $status, completionDate: $completionDate, first:$first, after:$after, pageSize: 1) {
+ totalCount
+ edges {
+ node {
+ code
+ completionDte
+ driverID
+ duration
+ mSA
+ offerType
+ orderID
+ shipperID
+ status
+ }
+ cursor
+ }
+ pageInfo{
+ startCursor
+ endCursor
+ hasNextPage
+ hasPreviousPage
+
+
+ }
+ }
+ }",
+
+ Variables = new
+ {
+ orderId = searchQuery.OrderNumber.Value,
+ msa = searchQuery.MSA,
+ status = searchQuery.Status,
+ completionDate = searchQuery.CompletionDate,
+ first = searchQuery.PageLimit ,
+ after = searchQuery.IsPageUp.HasValue && searchQuery.IsPageUp.Value ? searchQuery.Cursor : string.Empty,
+ before = searchQuery.IsPageUp.HasValue && !searchQuery.IsPageUp.Value ? searchQuery.Cursor : string.Empty
+ }
+ };
+
+ var response = await graphQlClient.PostAsync(request);
+ ////return response.GetDataFieldAs>("orders");
+ return response.GetDataFieldAs>("ordersConnection");
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/SimpleOrderSearch.Desktop.csproj b/SimpleOrderSearch.Desktop/SimpleOrderSearch.Desktop.csproj
new file mode 100644
index 0000000..c78f8f5
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/SimpleOrderSearch.Desktop.csproj
@@ -0,0 +1,203 @@
+
+
+
+
+
+ Debug
+ AnyCPU
+ {0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}
+ WinExe
+ SimpleOrderSearch.Desktop
+ SimpleOrderSearch.Desktop
+ v4.7.1
+ 512
+ {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
+ 4
+ true
+ true
+
+
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\DynamicData.6.14.1\lib\net461\DynamicData.dll
+
+
+ ..\packages\FluentValidation.8.6.0\lib\net45\FluentValidation.dll
+
+
+ ..\packages\GraphQL-Parser.3.0.0\lib\net45\GraphQL-Parser.dll
+
+
+ ..\packages\GraphQL.Client.1.0.3\lib\netstandard2.0\GraphQL.Client.dll
+
+
+ ..\packages\GraphQL.Common.1.0.3\lib\netstandard2.0\GraphQL.Common.dll
+
+
+ ..\packages\MaterialDesignThemes.3.0.0\lib\net45\MaterialDesignThemes.Wpf.dll
+
+
+ ..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll
+
+
+ ..\packages\ReactiveUI.11.0.6\lib\net461\ReactiveUI.dll
+
+
+ ..\packages\ReactiveUI.Fody.10.5.43\lib\net461\ReactiveUI.Fody.Helpers.dll
+
+
+ ..\packages\ReactiveUI.Validation.1.4.6\lib\net461\ReactiveUI.Validation.dll
+
+
+ ..\packages\ReactiveUI.WPF.11.0.6\lib\net461\ReactiveUI.WPF.dll
+
+
+ ..\packages\RestSharp.106.6.10\lib\net452\RestSharp.dll
+
+
+ ..\packages\Splat.9.3.3\lib\net461\Splat.dll
+
+
+ ..\packages\Splat.Drawing.9.3.3\lib\net461\Splat.Drawing.dll
+
+
+
+ ..\packages\System.Buffers.4.5.0\lib\netstandard2.0\System.Buffers.dll
+
+
+ ..\packages\System.ComponentModel.Annotations.4.4.1\lib\net461\System.ComponentModel.Annotations.dll
+
+
+
+
+
+ ..\packages\System.Drawing.Primitives.4.3.0\lib\net45\System.Drawing.Primitives.dll
+ True
+ True
+
+
+ ..\packages\System.Reactive.4.3.2\lib\net46\System.Reactive.dll
+
+
+ ..\packages\System.Reactive.Core.4.3.2\lib\net46\System.Reactive.Core.dll
+
+
+ ..\packages\System.Reactive.Interfaces.3.1.1\lib\net45\System.Reactive.Interfaces.dll
+
+
+ ..\packages\System.Reactive.Linq.4.3.2\lib\net46\System.Reactive.Linq.dll
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
+
+
+ ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll
+
+
+
+
+
+
+
+
+
+
+
+ 4.0
+
+
+
+
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+
+ MSBuild:Compile
+ Designer
+
+
+ App.xaml
+ Code
+
+
+
+ MainWindow.xaml
+ Code
+
+
+
+
+ Code
+
+
+ True
+ True
+ Resources.resx
+
+
+ True
+ Settings.settings
+ True
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+
+
+
+
+
+ {f127e637-335e-4d3a-82a7-33451706382f}
+ SimpleOrderSearch.Model
+
+
+
+
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.Desktop/packages.config b/SimpleOrderSearch.Desktop/packages.config
new file mode 100644
index 0000000..9402ae3
--- /dev/null
+++ b/SimpleOrderSearch.Desktop/packages.config
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SimpleOrderSearch.ModelStandard/ErrorModel.cs b/SimpleOrderSearch.ModelStandard/ErrorModel.cs
new file mode 100644
index 0000000..471777b
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/ErrorModel.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Model
+{
+ public class ErrorModel
+ {
+ public string FieldName { get; set; }
+
+ public string Message { get; set; }
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/ErrorResponse.cs b/SimpleOrderSearch.ModelStandard/ErrorResponse.cs
new file mode 100644
index 0000000..8c27198
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/ErrorResponse.cs
@@ -0,0 +1,12 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Model
+{
+ public class ErrorResponse
+ {
+ public List Errors { get; set; } = new List();
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/Extensions.cs b/SimpleOrderSearch.ModelStandard/Extensions.cs
new file mode 100644
index 0000000..4362f77
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/Extensions.cs
@@ -0,0 +1,46 @@
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Text;
+
+namespace SimpleOrderSearch.Model
+{
+ public static class Extensions
+ {
+ public static Nullable ToNullableValue(this string input) where T : struct
+ {
+ if (string.IsNullOrEmpty(input))
+ return null;
+
+ Nullable result = new Nullable();
+ try
+ {
+ IConvertible convertibleString = (IConvertible)input;
+ result = new Nullable((T)convertibleString.ToType(typeof(T), CultureInfo.CurrentCulture));
+ }
+ catch (InvalidCastException)
+ {
+
+ }
+ catch (FormatException)
+ {
+
+ }
+
+ return result;
+ }
+
+
+ public static string Base64Encode(this string plainText)
+ {
+ var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
+ return System.Convert.ToBase64String(plainTextBytes);
+ }
+
+ public static string Base64Decode(this string base64EncodedData)
+ {
+ var base64EncodedBytes = System.Convert.FromBase64String(base64EncodedData);
+ return System.Text.Encoding.UTF8.GetString(base64EncodedBytes);
+ }
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/GraphQLQuery.cs b/SimpleOrderSearch.ModelStandard/GraphQLQuery.cs
new file mode 100644
index 0000000..2ad5aa9
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/GraphQLQuery.cs
@@ -0,0 +1,19 @@
+using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Xml.Linq;
+
+namespace SimpleOrderSearch.Model
+{
+ public class GraphQLQuery
+ {
+ public string OperationName { get; set; }
+
+ public string NamedQuery { get; set; }
+
+ public string Query { get; set; }
+
+ public JObject Variables { get; set; }
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/GraphQLResponse.cs b/SimpleOrderSearch.ModelStandard/GraphQLResponse.cs
new file mode 100644
index 0000000..2d58ef8
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/GraphQLResponse.cs
@@ -0,0 +1,27 @@
+using System.Collections.Generic;
+
+namespace SimpleOrderSearch.Model
+{
+ public class GraphQLResponse
+ {
+ public PageInfo PageInfo { get; set; }
+
+ public List> Edges { get; set; }
+
+ public int TotalCount { get; set; }
+ }
+
+ public class PageInfo
+ {
+ public bool HasNextPage { get; set; }
+ public bool HasPreviousPage { get; set; }
+ public string StartCursor { get; set; }
+ public string EndCursor { get; set; }
+ }
+
+ public class Edge
+ {
+ public string Cursor { get; set; }
+ public T Node { get; set; }
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/OrderInfo.cs b/SimpleOrderSearch.ModelStandard/OrderInfo.cs
new file mode 100644
index 0000000..e83743c
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/OrderInfo.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace SimpleOrderSearch.Model
+{
+ public class OrderInfo
+ {
+ public int OrderID { get; set; }
+
+ public int ShipperID { get; set; }
+
+ public int DriverID { get; set; }
+
+ public DateTime CompletionDte { get; set; }
+
+ public int Status { get; set; }
+
+ public string Code { get; set; }
+
+ public int MSA { get; set; }
+
+ public string Duration { get; set; }
+
+ public int OfferType { get; set; }
+
+
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/OrderSearchQuery.cs b/SimpleOrderSearch.ModelStandard/OrderSearchQuery.cs
new file mode 100644
index 0000000..5a25264
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/OrderSearchQuery.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SimpleOrderSearch.Model
+{
+ public class OrderSearchQuery
+ {
+ public OrderSearchQuery()
+ {
+
+ }
+ public int? OrderNumber { get; set; }
+
+ public int? MSA { get; set; }
+
+ public int? Status { get; set; }
+
+ public DateTime? CompletionDate { get; set; }
+
+ public int Page { get; set; }
+
+ public int Offset { get; set; }
+
+ public int PageLimit { get; set; }
+
+ public PageInfo PageInfo { get; set; }
+
+ public string Cursor { get; set; }
+ //public string AfterCursor { get; set; }
+
+ //public string BeforeCursor { get; set; }
+
+ public bool? IsPageUp { get; set; } = null;
+ }
+}
diff --git a/SimpleOrderSearch.ModelStandard/PagedOrderInfo.cs b/SimpleOrderSearch.ModelStandard/PagedOrderInfo.cs
new file mode 100644
index 0000000..60b5110
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/PagedOrderInfo.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace SimpleOrderSearch.Model
+{
+ public class PagedOrderInfo
+ {
+ public IEnumerable Orders { get; set; }
+
+ public ErrorResponse ErrorResponse { get; set; }
+
+ public int PageNumber { get; set; } = 1;
+
+ public int PageLimit { get; set; } = 5;
+
+ public int Offset { get; set; } = 0;
+
+ public bool IsStart { get; set; }
+
+ public bool IsEnd { get; set; }
+
+ public bool IsValid { get; set; } = true;
+ }
+}
+
diff --git a/SimpleOrderSearch.ModelStandard/SimpleOrderSearch.Model.csproj b/SimpleOrderSearch.ModelStandard/SimpleOrderSearch.Model.csproj
new file mode 100644
index 0000000..a9007ee
--- /dev/null
+++ b/SimpleOrderSearch.ModelStandard/SimpleOrderSearch.Model.csproj
@@ -0,0 +1,12 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
diff --git a/SimpleOrderSearch.Tests/SimpleOrderSearch.Tests.csproj b/SimpleOrderSearch.Tests/SimpleOrderSearch.Tests.csproj
new file mode 100644
index 0000000..d45c579
--- /dev/null
+++ b/SimpleOrderSearch.Tests/SimpleOrderSearch.Tests.csproj
@@ -0,0 +1,15 @@
+
+
+
+ netcoreapp2.2
+
+ false
+
+
+
+
+
+
+
+
+
diff --git a/SimpleOrderSearch.Tests/UnitTest1.cs b/SimpleOrderSearch.Tests/UnitTest1.cs
new file mode 100644
index 0000000..eec28e2
--- /dev/null
+++ b/SimpleOrderSearch.Tests/UnitTest1.cs
@@ -0,0 +1,13 @@
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace SimpleOrderSearch.Tests
+{
+ [TestClass]
+ public class UnitTest1
+ {
+ [TestMethod]
+ public void TestMethod1()
+ {
+ }
+ }
+}
diff --git a/SimpleOrderSearch.sln b/SimpleOrderSearch.sln
new file mode 100644
index 0000000..a0facde
--- /dev/null
+++ b/SimpleOrderSearch.sln
@@ -0,0 +1,43 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29020.237
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleOrderSearch.Service", "SimpleOrderSearch\SimpleOrderSearch.Service.csproj", "{439B4E29-5435-401B-BB52-9DD39C3FE0D9}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleOrderSearch.Tests", "SimpleOrderSearch.Tests\SimpleOrderSearch.Tests.csproj", "{641D0232-B4F6-416A-9A4B-B5D32B3FB08E}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SimpleOrderSearch.Desktop", "SimpleOrderSearch.Desktop\SimpleOrderSearch.Desktop.csproj", "{0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleOrderSearch.Model", "SimpleOrderSearch.ModelStandard\SimpleOrderSearch.Model.csproj", "{F127E637-335E-4D3A-82A7-33451706382F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {439B4E29-5435-401B-BB52-9DD39C3FE0D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {439B4E29-5435-401B-BB52-9DD39C3FE0D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {439B4E29-5435-401B-BB52-9DD39C3FE0D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {439B4E29-5435-401B-BB52-9DD39C3FE0D9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {641D0232-B4F6-416A-9A4B-B5D32B3FB08E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {641D0232-B4F6-416A-9A4B-B5D32B3FB08E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {641D0232-B4F6-416A-9A4B-B5D32B3FB08E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {641D0232-B4F6-416A-9A4B-B5D32B3FB08E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {0B79DF84-F3E1-49B0-ADE4-70BC0A8E22A4}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F127E637-335E-4D3A-82A7-33451706382F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F127E637-335E-4D3A-82A7-33451706382F}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F127E637-335E-4D3A-82A7-33451706382F}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F127E637-335E-4D3A-82A7-33451706382F}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {4AB4F81F-2ED6-4A08-968D-05DC83094CEC}
+ EndGlobalSection
+EndGlobal
diff --git a/SimpleOrderSearch/Contracts/IDataAccessor.cs b/SimpleOrderSearch/Contracts/IDataAccessor.cs
new file mode 100644
index 0000000..55ad383
--- /dev/null
+++ b/SimpleOrderSearch/Contracts/IDataAccessor.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Service.Contracts
+{
+ ///
+ ///
+ ///
+ ///
+ public interface IDataAccessor
+ {
+ ///
+ ///
+ ///
+ ///
+ ///
+ T GetById(int id);
+
+ ///
+ ///
+ ///
+ ///
+ IEnumerable GetAll();
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ IEnumerable GetAllRequested(Func predicate);
+ }
+}
diff --git a/SimpleOrderSearch/Contracts/IOrderSearchHandler.cs b/SimpleOrderSearch/Contracts/IOrderSearchHandler.cs
new file mode 100644
index 0000000..1633979
--- /dev/null
+++ b/SimpleOrderSearch/Contracts/IOrderSearchHandler.cs
@@ -0,0 +1,16 @@
+using FluentValidation;
+using LaYumba.Functional;
+using SimpleOrderSearch.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Service.Contracts
+{
+ public interface IOrderSearchHandler
+ {
+ Validation> HandleSearchOrderRequest(OrderSearchQuery query, AbstractValidator queryValidator, IDataAccessor dataAccessor);
+
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch/Contracts/JsonDataAccessor.cs b/SimpleOrderSearch/Contracts/JsonDataAccessor.cs
new file mode 100644
index 0000000..58c6cdc
--- /dev/null
+++ b/SimpleOrderSearch/Contracts/JsonDataAccessor.cs
@@ -0,0 +1,56 @@
+using SimpleOrderSearch.Model;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Newtonsoft.Json;
+
+namespace SimpleOrderSearch.Service.Contracts
+{
+ public class JsonDataAccessor : IDataAccessor
+ {
+ private List allOrders = new List();
+
+ public IEnumerable GetAll()
+ {
+ return GetData();
+ }
+
+ ///
+ /// Returns data based using a HOF as a predicate.
+ ///
+ ///
+ ///
+ public IEnumerable GetAllRequested(Func predicate)
+ {
+ return GetData().Where(predicate); ;
+ }
+
+ public OrderInfo GetById(int id)
+ {
+ return GetData().ToList().Find(p => p.OrderID == id);
+ }
+
+ public OrderInfo GetDataFromSource()
+ {
+ throw new NotImplementedException();
+ }
+
+ private IEnumerable GetData()
+ {
+ string text = null;
+
+ try
+ {
+ text = File.ReadAllText(@"..\SimpleOrderSearch\Data\orderInfo.json");
+ }
+ catch
+ {
+ throw;
+ }
+ var orders = JsonConvert.DeserializeObject>(text);
+ return orders;
+ }
+ }
+}
diff --git a/SimpleOrderSearch/Controllers/GraphQLController.cs b/SimpleOrderSearch/Controllers/GraphQLController.cs
new file mode 100644
index 0000000..225dc8c
--- /dev/null
+++ b/SimpleOrderSearch/Controllers/GraphQLController.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using GraphQL;
+using GraphQL.Types;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using SimpleOrderSearch.Service.GraphQL.Query;
+using SimpleOrderSearch.Service.GraphQL.Types;
+
+namespace SimpleOrderSearch.Service.Controllers
+{
+ ///
+ /// GraphQLController
+ ///
+ ////[Route("api/[controller]")]
+ [Route("graphql")]
+ [ApiController]
+ public class GraphQLController : ControllerBase
+ {
+ private readonly IDataAccessor dataAccessor;
+
+ ///
+ /// Constructor
+ ///
+ ///
+ public GraphQLController(IDataAccessor dataAccessor) => this.dataAccessor = dataAccessor;
+
+ public async Task Post([FromBody] GraphQLQuery query)
+ {
+ var inputs = query.Variables.ToInputs();
+
+ var schema = new Schema
+ {
+ Query = new OrderGraphQLQuery(this.dataAccessor)
+ };
+ schema.RegisterType();
+
+ var result = await new DocumentExecuter().ExecuteAsync(_ =>
+ {
+ _.Schema = schema;
+ _.Query = query.Query;
+ _.OperationName = query.OperationName;
+ _.Inputs = inputs;
+ });
+
+ if (result.Errors?.Count > 0)
+ {
+ return BadRequest(result.Errors.Select(p => p.Message));
+ }
+
+ return Ok(result);
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch/Controllers/OrderSearchController.cs b/SimpleOrderSearch/Controllers/OrderSearchController.cs
new file mode 100644
index 0000000..e5a838f
--- /dev/null
+++ b/SimpleOrderSearch/Controllers/OrderSearchController.cs
@@ -0,0 +1,71 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Web.Http;
+using FluentValidation;
+using FluentValidation.Results;
+using LaYumba.Functional;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Mvc;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using static LaYumba.Functional.Either;
+using RouteAttribute = Microsoft.AspNetCore.Mvc.RouteAttribute;
+
+namespace SimpleOrderSearch.Service.Controllers
+{
+ ///
+ /// Order Search COntroller
+ ///
+ [Route("api/[controller]")]
+ [ApiController]
+ public class OrderSearchController : ControllerBase
+ {
+ IDataAccessor dataAccessor;
+ AbstractValidator queryValidator;
+ IOrderSearchHandler handler;
+
+
+ ///
+ /// Constructor
+ ///
+ ///
+ ///
+ public OrderSearchController(IDataAccessor dataAccessor, AbstractValidator queryValidator, IOrderSearchHandler handler)
+ {
+ this.dataAccessor = dataAccessor;
+ this.queryValidator = queryValidator;
+ this.handler = handler;
+ }
+
+
+ // GET: api/OrderSearch
+ ///
+ /// Gets searched orders results by criteria. Results are returned paged.
+ ///
+ /// Not required but must be greater than zero (0) if specified.
+ /// Not required. If specified must be in combination with msa.
+ /// Not required. If specified must be in combination with status.
+ /// Always required
+ /// Indicates number page to be retrived.
+ /// Indicates max number of results to be returned per page.
+ /// PagedOrderInfo
+ [Microsoft.AspNetCore.Mvc.HttpGet]
+ public IActionResult Get([FromQuery] int? orderNo, int? status, int? msa, DateTime? completionDate, int page = 1, int pageLimit = 5) =>
+ this.handler.HandleSearchOrderRequest(new OrderSearchQuery() // this can be improved.
+ {
+ MSA = msa,
+ Status = status,
+ OrderNumber = orderNo,
+ CompletionDate = completionDate,
+ Page = page,
+ PageLimit = pageLimit
+ },
+ this.queryValidator,
+ this.dataAccessor)
+ .Match(Invalid: (p) => BadRequest(p.Select(x => x.Message)),
+ Valid: result => result.Match(Exception: (e) => StatusCode(500, "Exception Occurred In Service."),
+ Success: (pagedResult) => Ok(pagedResult)));
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch/Data/orderInfo.json b/SimpleOrderSearch/Data/orderInfo.json
new file mode 100644
index 0000000..e9a6a59
--- /dev/null
+++ b/SimpleOrderSearch/Data/orderInfo.json
@@ -0,0 +1,136 @@
+[
+ {
+ "OrderID": 36,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-12T05:10:00",
+ "Status": 10,
+ "Code": "R4C877FF",
+ "MSA": 1,
+ "Duration": "92.00",
+ "OfferType": 1
+
+ },
+ {
+ "OrderID": 37,
+ "ShipperID": 4,
+ "DriverID": 243,
+ "CompletionDte": "2018-02-15T05:10:00",
+ "Status": 10,
+ "Code": "R47077FF",
+ "MSA": 1,
+ "Duration": "43.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 38,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R6453FF",
+ "MSA": 2,
+ "Duration": "120.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 39,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R4C877DS",
+ "MSA": 4,
+ "Duration": "15.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 40,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R4C9999F",
+ "MSA": 1,
+ "Duration": "111.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 41,
+ "ShipperID": 67,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R4C87S32",
+ "MSA": 1,
+ "Duration": "54.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 42,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R4C87123",
+ "MSA": 1,
+ "Duration": "92.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 43,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R42G77FF",
+ "MSA": 1,
+ "Duration": "40.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 44,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 10,
+ "Code": "R4002WFF",
+ "MSA": 1,
+ "Duration": "92.00",
+ "OfferType": 2
+ },
+ {
+ "OrderID": 45,
+ "ShipperID": 4,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 20,
+ "Code": "R400KHFF",
+ "MSA": 3,
+ "Duration": "23.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 46,
+ "ShipperID": 24,
+ "DriverID": 35,
+ "CompletionDte": "2018-01-31T05:10:00",
+ "Status": 61,
+ "Code": "R4C437FF",
+ "MSA": 1,
+ "Duration": "92.00",
+ "OfferType": 1
+ },
+ {
+ "OrderID": 47,
+ "ShipperID": 121,
+ "DriverID": 35,
+ "CompletionDte": "2018-03-1T05:10:00",
+ "Status": 10,
+ "Code": "R422AQF",
+ "MSA": 1,
+ "Duration": "66.00",
+ "OfferType": 2
+ }
+
+]
\ No newline at end of file
diff --git a/SimpleOrderSearch/Filters/ValidationFilter.cs b/SimpleOrderSearch/Filters/ValidationFilter.cs
new file mode 100644
index 0000000..c6a7767
--- /dev/null
+++ b/SimpleOrderSearch/Filters/ValidationFilter.cs
@@ -0,0 +1,23 @@
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.AspNetCore.Mvc.Filters;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Service.Filters
+{
+ public class ValidationFilter : IActionFilter
+ {
+ public void OnActionExecuted(ActionExecutedContext context)
+ {
+ }
+
+ public void OnActionExecuting(ActionExecutingContext context)
+ {
+ //throw new NotImplementedException();
+ }
+ }
+}
diff --git a/SimpleOrderSearch/GraphQL/Query/OrderGraphQLQuery.cs b/SimpleOrderSearch/GraphQL/Query/OrderGraphQLQuery.cs
new file mode 100644
index 0000000..8cdcbbf
--- /dev/null
+++ b/SimpleOrderSearch/GraphQL/Query/OrderGraphQLQuery.cs
@@ -0,0 +1,85 @@
+using GraphQL.Types;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using SimpleOrderSearch.Service.GraphQL.Types;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using GraphQL.Types.Relay.DataObjects;
+using GraphQL.Relay.Types;
+
+namespace SimpleOrderSearch.Service.GraphQL.Query
+{
+ ///
+ /// encapsulates querying for an order using graphql.
+ ///
+ public class OrderGraphQLQuery : ObjectGraphType
+ {
+ private const string OrderIdArgName = "orderId";
+ private const string MsaArgName = "msa";
+ private const string StatusArgName = "status";
+ private const string CompletionDateArgName = "completionDate";
+ private const string FirstArgName = "first";
+ private const string cursorArgName = "cursor";
+
+ ///
+ /// constructor
+ ///
+ ///
+ public OrderGraphQLQuery(IDataAccessor dataAccessor)
+ {
+ Field>("Orders", arguments: new QueryArguments(new QueryArgument { Name = OrderIdArgName, Description = "OrderId" },
+ new QueryArgument { Name = MsaArgName, Description = "MSA" },
+ new QueryArgument { Name = StatusArgName, Description = "Status" },
+ new QueryArgument { Name = CompletionDateArgName, Description = "CompletionDate" },
+ new QueryArgument { Name = cursorArgName, Description = "Cursor" },
+ new QueryArgument { Name = FirstArgName, Description = "First order to return." }), resolve: ctx =>
+ {
+ var completionDate = ctx.GetArgument(CompletionDateArgName);
+ var msa = ctx.GetArgument(MsaArgName);
+ var status = ctx.GetArgument(StatusArgName);
+ var orderId = ctx.GetArgument(OrderIdArgName);
+
+ Func predicate = p => completionDate.HasValue &&
+ p.CompletionDte.Date == completionDate.Value.Date && ((orderId.HasValue && p.OrderID == orderId.Value) ||
+ msa.HasValue && status.HasValue && msa.Value == p.MSA && status.Value == p.Status);
+
+ return dataAccessor.GetAllRequested(predicate);
+ });
+
+ Connection().Name("OrdersConnection")
+ .Bidirectional()
+ .Argument(name: OrderIdArgName, description: "OrderId")
+ .Argument(name: MsaArgName, description: "MSA")
+ .Argument(name: StatusArgName, description: "Status")
+ .Argument(name: CompletionDateArgName, description: "Completion Date")
+ .Argument(name: "first", description: "first")
+ .Argument(name: "after", description: "after")
+ .Argument(name: "pageSize", description: "page size")
+ ////.Argument(name: cursorArgName, description: "cursors of orders")
+ .Resolve(ctx =>
+ {
+ var f = ctx.First;
+ var a = ctx.After;
+ var c = ctx.PageSize;
+ var src = ctx.Source;
+ var tc = ctx.TotalCount;
+ var sz = ctx.PageSize;
+
+ var completionDate = ctx.GetArgument(CompletionDateArgName);
+ var msa = ctx.GetArgument(MsaArgName);
+ var status = ctx.GetArgument(StatusArgName);
+ var orderId = ctx.GetArgument(OrderIdArgName);
+
+ Func predicate = p => completionDate.HasValue &&
+ p.CompletionDte.Date == completionDate.Value.Date && ((orderId.HasValue && p.OrderID == orderId.Value) ||
+ msa.HasValue && status.HasValue && msa.Value == p.MSA && status.Value == p.Status);
+ // return dataAccessor.GetAllRequested(predicate);
+ var results = dataAccessor.GetAllRequested(predicate).OrderBy(p => p.OrderID);
+ //.SkipWhile(p => p.OrderID != ctx.First.Value);
+ return ConnectionUtils.ToConnection(results, ctx);
+ });
+ }
+ }
+}
diff --git a/SimpleOrderSearch/GraphQL/Types/OrderType.cs b/SimpleOrderSearch/GraphQL/Types/OrderType.cs
new file mode 100644
index 0000000..24a4cbe
--- /dev/null
+++ b/SimpleOrderSearch/GraphQL/Types/OrderType.cs
@@ -0,0 +1,63 @@
+using GraphQL.Types;
+using GraphQL.Types.Relay.DataObjects;
+using SimpleOrderSearch.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using GraphQL.Types.Relay;
+using GraphQL.Relay.Types;
+using SimpleOrderSearch.Service.Contracts;
+
+namespace SimpleOrderSearch.Service.GraphQL.Types
+{
+ ///
+ /// GraphQl Order Type
+ ///
+ public class OrderType : ObjectGraphType
+ ////public class OrderType : NodeGraphType
+ {
+ private readonly IDataAccessor data;
+
+ ///
+ /// GraphQl OrderType over OrderInfo object.
+ ///
+ public OrderType()
+ ////public OrderType()
+ {
+ Name = "Order";
+
+ ////Id(p => p.OrderID);
+ Field(x => x.OrderID, type: typeof(IdGraphType)).Description("Id of the order");
+ Field(x => x.MSA, type: typeof(IntGraphType)).Description("MSA");
+ Field(x => x.Status, type: typeof(IntGraphType)).Description("Status");
+ Field(x => x.CompletionDte, type: typeof(DateGraphType)).Description("The completion date of the order");
+ Field(x => x.DriverID, type: typeof(IntGraphType)).Description("Id of the driver");
+ Field(x => x.Duration, type: typeof(DecimalGraphType)).Description("The duration of the order.");
+ Field(x => x.OfferType, type: typeof(IntGraphType)).Description("Offer type");
+ Field(x => x.ShipperID, type: typeof(IntGraphType)).Description("The shipper Id.");
+ Field(x => x.Code, type: typeof(StringGraphType)).Description("code.");
+ //this.data = data;
+ }
+
+ ////public override OrderInfo GetById(string id)
+ ////{
+ //// throw new NotImplementedException();
+ ////}
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ //public override OrderInfo GetById(string id)
+ //{
+ // int parsedId;
+ // //if (int.TryParse(id, out parsedId))
+ // // return this.data.GetById(parsedId);
+ // //else return null;
+
+ // return int.TryParse(id, out parsedId) ? this.data.GetById(parsedId) : null;
+ //}
+ }
+}
diff --git a/SimpleOrderSearch/Handlers/OrderSearchHandler.cs b/SimpleOrderSearch/Handlers/OrderSearchHandler.cs
new file mode 100644
index 0000000..49b4ef2
--- /dev/null
+++ b/SimpleOrderSearch/Handlers/OrderSearchHandler.cs
@@ -0,0 +1,82 @@
+using FluentValidation;
+using LaYumba.Functional;
+using static LaYumba.Functional.F;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Service.Handlers
+{
+ ///
+ /// OrderSearchHandler
+ ///
+ public class OrderSearchHandler : IOrderSearchHandler
+ {
+ private IDataAccessor dataAccessor { get; }
+ private AbstractValidator queryValidator { get; }
+
+ ///
+ /// Constructor
+ ///
+ public OrderSearchHandler(IDataAccessor dataAccessor, AbstractValidator queryValidator)
+ {
+ this.dataAccessor = dataAccessor;
+ this.queryValidator = queryValidator;
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public Validation> HandleSearchOrderRequest(OrderSearchQuery query, AbstractValidator queryValidator, IDataAccessor dataAccessor)
+ {
+ return ValidateQuery(query, queryValidator)
+ .Map(qry => GetPagedResults(qry, dataAccessor));
+ }
+
+ private Validation ValidateQuery(OrderSearchQuery query, AbstractValidator queryValidator)
+ {
+ var result = queryValidator.Validate(query);
+ if (!result.IsValid)
+ return Error(string.Join(Environment.NewLine, result.Errors));
+ else return query;
+ }
+
+ private Exceptional GetPagedResults(OrderSearchQuery query, IDataAccessor dataAccessor)
+ {
+ Func prediate = p => query.CompletionDate.HasValue &&
+ p.CompletionDte.Date == query.CompletionDate.Value.Date && ((query.OrderNumber.HasValue && p.OrderID == query.OrderNumber.Value) ||
+ query.MSA.HasValue && query.Status.HasValue && query.MSA.Value == p.MSA && query.Status.Value == p.Status);
+
+ IEnumerable pagedResults;
+ int totalResults = 0;
+ try
+ {
+ IEnumerable requestedOrders = dataAccessor.GetAllRequested(prediate); // this is a side effect....make sure this implementation is pure.
+ totalResults = requestedOrders.Count();
+ pagedResults = requestedOrders.OrderBy(p => p.OrderID)
+ .Skip(query.Page - 1 * query.PageLimit + 1)
+ .Take(query.PageLimit);
+ }
+ catch (Exception ex)
+ {
+ return ex;
+ }
+
+ return new PagedOrderInfo()
+ {
+ Orders = pagedResults,
+ PageLimit = query.PageLimit,
+ PageNumber = query.Page,
+ IsStart = query.Page == 1,
+ IsEnd = query.Page * query.PageLimit >= totalResults
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch/Program.cs b/SimpleOrderSearch/Program.cs
new file mode 100644
index 0000000..732776c
--- /dev/null
+++ b/SimpleOrderSearch/Program.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Logging;
+
+namespace SimpleOrderSearch
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateWebHostBuilder(args).Build().Run();
+ }
+
+ public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
+ WebHost.CreateDefaultBuilder(args)
+ .UseStartup();
+ }
+}
diff --git a/SimpleOrderSearch/Properties/launchSettings.json b/SimpleOrderSearch/Properties/launchSettings.json
new file mode 100644
index 0000000..25af388
--- /dev/null
+++ b/SimpleOrderSearch/Properties/launchSettings.json
@@ -0,0 +1,34 @@
+{
+ "iisSettings": {
+ "windowsAuthentication": false,
+ "anonymousAuthentication": true,
+ "iis": {
+ "applicationUrl": "http://localhost/SimpleOrderSearch.Service",
+ "sslPort": 0
+ },
+ "iisExpress": {
+ "applicationUrl": "http://localhost:11882",
+ "sslPort": 44345
+ }
+ },
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "IIS Express": {
+ "commandName": "IISExpress",
+ "launchBrowser": true,
+ "launchUrl": "graphql",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ }
+ },
+ "SimpleOrderSearch": {
+ "commandName": "Project",
+ "launchBrowser": true,
+ "launchUrl": "api/values",
+ "environmentVariables": {
+ "ASPNETCORE_ENVIRONMENT": "Development"
+ },
+ "applicationUrl": "https://localhost:5001;http://localhost:5000"
+ }
+ }
+}
\ No newline at end of file
diff --git a/SimpleOrderSearch/SimpleOrderSearch.Service.csproj b/SimpleOrderSearch/SimpleOrderSearch.Service.csproj
new file mode 100644
index 0000000..9c694d3
--- /dev/null
+++ b/SimpleOrderSearch/SimpleOrderSearch.Service.csproj
@@ -0,0 +1,43 @@
+
+
+
+ netcoreapp2.2
+ InProcess
+ SimpleOrderSearch.Service
+
+
+
+ C:\Users\joseph\Source\Repos\simpleOrderSearch\SimpleOrderSearch\SimpleOrderSearch.Service.xml
+ bin\Debug\netcoreapp2.2\
+
+
+
+ C:\Users\joseph\Source\Repos\simpleOrderSearch\SimpleOrderSearch\SimpleOrderSearch.Service.xml
+ bin\Debug\netcoreapp2.2\
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SimpleOrderSearch/SimpleOrderSearch.Service.xml b/SimpleOrderSearch/SimpleOrderSearch.Service.xml
new file mode 100644
index 0000000..3e8868c
--- /dev/null
+++ b/SimpleOrderSearch/SimpleOrderSearch.Service.xml
@@ -0,0 +1,109 @@
+
+
+
+ SimpleOrderSearch.Service
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ GraphQLController
+
+
+
+
+ Constructor
+
+
+
+
+
+ Order Search COntroller
+
+
+
+
+ Constructor
+
+
+
+
+
+
+ Gets searched orders results by criteria. Results are returned paged.
+
+ Not required but must be greater than zero (0) if specified.
+ Not required. If specified must be in combination with msa.
+ Not required. If specified must be in combination with status.
+ Always required
+ Indicates number page to be retrived.
+ Indicates max number of results to be returned per page.
+ PagedOrderInfo
+
+
+
+ encapsulates querying for an order using graphql.
+
+
+
+
+ constructor
+
+
+
+
+
+ GraphQl Order Type
+
+
+
+
+ GraphQl OrderType over OrderInfo object.
+
+
+
+
+ OrderSearchHandler
+
+
+
+
+ Constructor
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/SimpleOrderSearch/Startup.cs b/SimpleOrderSearch/Startup.cs
new file mode 100644
index 0000000..23b77b6
--- /dev/null
+++ b/SimpleOrderSearch/Startup.cs
@@ -0,0 +1,110 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.HttpsPolicy;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using SimpleOrderSearch.Model;
+using SimpleOrderSearch.Service.Contracts;
+using FluentValidation.AspNetCore;
+using FluentValidation;
+using SimpleOrderSearch.Service.Validators;
+using SimpleOrderSearch.Service.Filters;
+using Swashbuckle.AspNetCore.Swagger;
+using System.Reflection;
+using System.IO;
+using GraphiQl;
+using GraphQL.Types.Relay;
+using GraphQL.Relay.Types;
+using SimpleOrderSearch.Service.GraphQL.Types;
+using SimpleOrderSearch.Service.Handlers;
+
+namespace SimpleOrderSearch
+{
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ // This method gets called by the runtime. Use this method to add services to the container.
+ public void ConfigureServices(IServiceCollection services)
+ {
+
+ services.AddMvc(setupAction: options =>
+ {
+ options.Filters.Add();
+ })
+ .AddFluentValidation(mvcconfig => mvcconfig.RegisterValidatorsFromAssemblyContaining())
+ .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
+
+ services.AddSwaggerGen(x =>
+ {
+ x.SwaggerDoc("v1", new Info() { Title = "", Version = "v1" });
+ var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
+ var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
+ x.IncludeXmlComments(xmlPath);
+ });
+
+ services.AddTransient, JsonDataAccessor>();
+ services.AddTransient();
+ services.AddSingleton, OrderSearchQueryValidator>();
+ services.AddTransient(typeof(ConnectionType<>));
+ services.AddTransient(typeof(EdgeType<>));
+ services.AddTransient();
+ services.AddTransient();
+
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ // middlewares
+ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+ else
+ {
+ // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
+ app.UseHsts();
+ }
+
+
+ app.UseHttpsRedirection();
+ app.UseGraphiQl("/graphql");
+ app.UseMvc();
+ var swaggerOptions = new SwaggerOptions();
+ Configuration.GetSection(nameof(SwaggerOptions)).Bind(swaggerOptions); // gets section from json settings file.
+ //app.UseSwagger(option =>
+ //{
+ // //option.RouteTemplate = ""
+ //});
+
+ app.UseSwagger();
+ app.UseSwaggerUI(c =>
+ {
+ c.SwaggerEndpoint("/swagger/v1/swagger.json", "Search Orders API");
+ //c.RoutePrefix = string.Empty;
+ });
+
+
+
+ }
+
+ private string GetXmlCommentsPath()
+ {
+ var app = System.AppContext.BaseDirectory;
+ return System.IO.Path.Combine(app, "ASPNETCoreSwaggerDemo.xml");
+ }
+ }
+}
diff --git a/SimpleOrderSearch/Validators/OrderSearchValidator.cs b/SimpleOrderSearch/Validators/OrderSearchValidator.cs
new file mode 100644
index 0000000..54c0896
--- /dev/null
+++ b/SimpleOrderSearch/Validators/OrderSearchValidator.cs
@@ -0,0 +1,53 @@
+using FluentValidation;
+using SimpleOrderSearch.Model;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace SimpleOrderSearch.Service.Validators
+{
+ public class OrderSearchQueryValidator : AbstractValidator
+ {
+ public OrderSearchQueryValidator()
+ {
+ // CompletionDate set as top level validation rule. If completion date validation fails the dependent validations won't run.
+ this.RuleFor(query => query.CompletionDate)
+ .Cascade(CascadeMode.StopOnFirstFailure)
+ .NotNull()
+ .NotEqual(DateTime.MinValue)
+ .DependentRules(() =>
+ {
+ RuleFor(q => q).Must(HaveValidQuery).WithMessage(q => $"Invalid Order Number ({GetValue(q.OrderNumber)}) or Invalid MSA ({GetValue(q.MSA)}) and Status ({GetValue(q.Status)}) combination");
+ });
+ }
+
+ private string GetValue(object obj) => obj == null ? "null" : obj.ToString();
+
+ private bool HaveValidQuery(OrderSearchQuery query)
+ {
+ bool isValid = HaveValidOrderNumber(query) || (HaveValidMSA(query) && HaveValidStatus(query));
+
+ return isValid;
+ }
+
+ private bool HaveValidOrderNumber(OrderSearchQuery arg)
+ {
+ bool isValid = arg.OrderNumber.HasValue && arg.OrderNumber.Value > 0;
+
+ return isValid;
+ }
+
+ private bool HaveValidMSA(OrderSearchQuery qry)
+ {
+ bool isValid = qry == null ? false : qry.MSA.HasValue;
+ return isValid;
+ }
+
+ private bool HaveValidStatus(OrderSearchQuery qry)
+ {
+ bool isValid = qry == null ? false : qry.Status.HasValue;
+ return isValid;
+ }
+ }
+}
diff --git a/SimpleOrderSearch/appsettings.Development.json b/SimpleOrderSearch/appsettings.Development.json
new file mode 100644
index 0000000..89dcc04
--- /dev/null
+++ b/SimpleOrderSearch/appsettings.Development.json
@@ -0,0 +1,10 @@
+{
+ "Logging": {
+ "LogLevel": {
+ "Default": "Debug",
+ "System": "Information",
+ "Microsoft": "Information"
+ }
+ },
+ "DefaultConnection": ""
+}
diff --git a/SimpleOrderSearch/appsettings.json b/SimpleOrderSearch/appsettings.json
new file mode 100644
index 0000000..a9e3221
--- /dev/null
+++ b/SimpleOrderSearch/appsettings.json
@@ -0,0 +1,14 @@
+{
+ "SwaggerOptions": {
+ "JsonRoute": "swagger/{documentname}/swagger.json",
+ "Description": "Search Orders Service API",
+ "UIEndpint": "v1/swagger.json"
+ },
+ "Logging": {
+ "LogLevel": {
+ "Default": "Warning"
+ }
+ },
+ "AllowedHosts": "*",
+ "DefaultConnection": ""
+}