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 index fd5204b..9924c4c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,8 +4,14 @@ # User-specific files *.suo *.user +*.userosscache *.sln.docstates +**/App.config + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + # Build results [Dd]ebug/ [Dd]ebugPublic/ @@ -13,19 +19,21 @@ [Rr]eleases/ x64/ x86/ -build/ bld/ [Bb]in/ [Oo]bj/ +[Ll]og/ -# Roslyn cache directories -*.ide/ +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* -#NUNIT +# NUNIT *.VisualState.xml TestResult.xml @@ -34,6 +42,11 @@ TestResult.xml [Rr]eleasePS/ dlldata.c +# DNX +project.lock.json +project.fragment.lock.json +artifacts/ + *_i.c *_p.c *_i.h @@ -66,14 +79,18 @@ _Chutzpah* ipch/ *.aps *.ncb +*.opendb *.opensdf *.sdf *.cachefile +*.VC.db +*.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx +*.sap # TFS 2012 Local Workspace $tf/ @@ -86,7 +103,7 @@ _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user -# JustCode is a .NET coding addin-in +# JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in @@ -98,6 +115,7 @@ _TeamCity* # NCrunch _NCrunch_* .*crunch*.local.xml +nCrunchTemp_* # MightyMoose *.mm.* @@ -125,39 +143,63 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted -*.pubxml +#*.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 **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ -# If using the old MSBuild-Integrated Package Restore, uncomment this: +# Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets -# Windows Azure Build Output +# Microsoft Azure Build Output csx/ *.build.csdef -# Windows Store app package directory +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ # Others -sql/ -*.Cache ClientBin/ -[Ss]tyle[Cc]op.* ~$* *~ *.dbmdl *.dbproj.schemaview +*.jfm *.pfx *.publishsettings node_modules/ +orleans.codegen.cs + +# 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/ @@ -181,3 +223,41 @@ UpgradeLog*.htm # Microsoft Fakes FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# 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 +.cr/ + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc diff --git a/RetailCrm/ApiClient.cs b/RetailCrm/ApiClient.cs deleted file mode 100644 index 20fd743..0000000 --- a/RetailCrm/ApiClient.cs +++ /dev/null @@ -1,1073 +0,0 @@ -using Newtonsoft.Json; -using RetailCrm.Http; -using RetailCrm.Response; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace RetailCrm -{ - public class ApiClient - { - private const string apiVersion = "v3"; - protected Client client; - - /// - /// Site code - /// - protected string siteCode; - - /// - /// ApiClient creating - /// - /// - /// - /// - public ApiClient(string url, string apiKey, string site = "") - { - if ("/" != url.Substring(url.Length - 1, 1)) - { - url += "/"; - } - - url += "api/" + apiVersion; - - client = new Client(url, new Dictionary() { { "apiKey", apiKey } }); - siteCode = site; - } - - /// - /// Create a order - /// - /// - /// - /// ApiResponse - public ApiResponse ordersCreate(Dictionary order, string site = "") - { - if (order.Count < 1) - { - throw new ArgumentException("Parameter `order` must contains a data"); - } - - return client.makeRequest( - "/orders/create", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "order", JsonConvert.SerializeObject(order) } - } - ) - ); - } - - /// - /// Edit a order - /// - /// - /// - /// - /// ApiResponse - public ApiResponse ordersEdit(Dictionary order, string by = "externalId", string site = "") - { - if (order.Count < 1) - { - throw new ArgumentException("Parameter `order` must contains a data"); - } - - checkIdParameter(by); - - if (order.ContainsKey(by) == false) - { - throw new ArgumentException("Order array must contain the \"" + by + "\" parameter"); - } - - - return client.makeRequest( - "/orders/" + order[by] + "/edit", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "order", JsonConvert.SerializeObject(order) }, - { "by", by } - } - ) - ); - } - - /// - /// Upload array of the orders - /// - /// - /// - /// ApiResponse - public ApiResponse ordersUpload(Dictionary orders, string site = "") - { - if (orders.Count < 1) - { - throw new ArgumentException("Parameter `order` must contains a data"); - } - - return client.makeRequest( - "/orders/upload", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "orders", JsonConvert.SerializeObject(orders) } - } - ) - ); - } - - /// - /// Get order by id or externalId - /// - /// - /// - /// - /// ApiResponse - public ApiResponse ordersGet(string id, string by = "externalId", string site = "") - { - checkIdParameter(by); - - return client.makeRequest( - "/orders/" + id, - Client.METHOD_GET, - this.fillSite( - site, - new Dictionary() { - { "by", by } - } - ) - ); - } - - /// - /// Returns a orders history - /// - /// - /// - /// - /// - /// - /// ApiResponse - public ApiResponse ordersHistory( - DateTime? startDate = null, - DateTime? endDate = null, - int limit = 100, - int offset = 0, - bool skipMyChanges = true - ) - { - Dictionary parameters = new Dictionary(); - - if (startDate != null) - { - parameters.Add("startDate", startDate.Value.ToString("yyyy-MM-dd HH:mm:ss")); - } - if (endDate != null) - { - parameters.Add("endDate", endDate.Value.ToString("yyyy-MM-dd HH:mm:ss")); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - if (offset > 0) - { - parameters.Add("offset", offset); - } - if (skipMyChanges == true) - { - parameters.Add("skipMyChanges", skipMyChanges); - } - - return client.makeRequest("/orders/history", Client.METHOD_GET, parameters); - } - - /// - /// Returns filtered orders list - /// - /// - /// - /// - /// ApiResponse - public ApiResponse ordersList(Dictionary filter = null, int page = 0, int limit = 0) - { - Dictionary parameters = new Dictionary(); - - if (filter.Count > 0) - { - parameters.Add("filter", filter); - } - if (page > 0) - { - parameters.Add("page", page); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - - return client.makeRequest("/orders", Client.METHOD_GET, parameters); - } - - /// - /// Returns statuses of the orders - /// - /// - /// - /// ApiResponse - public ApiResponse ordersStatuses(Dictionary ids = null, Dictionary externalIds = null) - { - Dictionary parameters = new Dictionary(); - - if (ids.Count > 0) - { - parameters.Add("ids", ids); - } - if (externalIds.Count > 0) - { - parameters.Add("externalIds", externalIds); - } - - return client.makeRequest("/orders/statuses", Client.METHOD_GET, parameters); - } - - /// - /// Save order IDs' (id and externalId) association in the CRM - /// - /// - /// ApiResponse - public ApiResponse ordersFixExternalId(Dictionary ids) - { - if (ids.Count < 1) - { - throw new ArgumentException("Method parameter must contains at least one IDs pair"); - } - - return client.makeRequest( - "/orders/fix-external-ids", - Client.METHOD_POST, - new Dictionary() { - { "orders", JsonConvert.SerializeObject(ids) } - } - ); - } - - /// - /// Create a customer - /// - /// - /// - /// ApiResponse - public ApiResponse customersCreate(Dictionary customer, string site = "") - { - if (customer.Count < 1) - { - throw new ArgumentException("Parameter `customer` must contains a data"); - } - - return client.makeRequest( - "/customers/create", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "customer", JsonConvert.SerializeObject(customer) } - } - ) - ); - } - - /// - /// Edit a customer - /// - /// - /// - /// - /// ApiResponse - public ApiResponse customersEdit(Dictionary customer, string by = "externalId", string site = "") - { - if (customer.Count < 1) - { - throw new ArgumentException("Parameter `customer` must contains a data"); - } - - checkIdParameter(by); - - if (customer.ContainsKey(by) == false) - { - throw new ArgumentException("Customer array must contain the \"" + by + "\" parameter"); - } - - - return client.makeRequest( - "/customers/" + customer[by] + "/edit", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "customer", JsonConvert.SerializeObject(customer) }, - { "by", by } - } - ) - ); - } - - /// - /// Upload array of the customers - /// - /// - /// - /// ApiResponse - public ApiResponse customersUpload(Dictionary customers, string site = "") - { - if (customers.Count < 1) - { - throw new ArgumentException("Parameter `customers` must contains a data"); - } - - return client.makeRequest( - "/customers/upload", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "customers", JsonConvert.SerializeObject(customers) } - } - ) - ); - } - - /// - /// Get customer by id or externalId - /// - /// - /// - /// - /// ApiResponse - public ApiResponse customersGet(string id, string by = "externalId", string site = "") - { - checkIdParameter(by); - - return client.makeRequest( - "/customers/" + id, - Client.METHOD_GET, - this.fillSite( - site, - new Dictionary() { - { "by", by } - } - ) - ); - } - - /// - /// Returns filtered customers list - /// - /// - /// - /// - /// ApiResponse - public ApiResponse customersList(Dictionary filter = null, int page = 0, int limit = 0) - { - Dictionary parameters = new Dictionary(); - if (filter.Count > 0) - { - parameters.Add("filter", filter); - } - if (page > 0) - { - parameters.Add("page", page); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - - return client.makeRequest("/customers", Client.METHOD_GET, parameters); - } - - /// - /// Save customer IDs' (id and externalId) association in the CRM - /// - /// - /// ApiResponse - public ApiResponse customersFixExternalIds(Dictionary ids) - { - if (ids.Count < 1) - { - throw new ArgumentException("Method parameter must contains at least one IDs pair"); - } - - return client.makeRequest( - "/customers/fix-external-ids", - Client.METHOD_POST, - new Dictionary() { - { "customers", JsonConvert.SerializeObject(ids) } - } - ); - } - - /// - /// Returns filtered orders packs list - /// - /// - /// - /// - /// ApiResponse - public ApiResponse packsList(Dictionary filter = null, int page = 0, int limit = 0) - { - Dictionary parameters = new Dictionary(); - - if (filter.Count > 0) - { - parameters.Add("filter", filter); - } - if (page > 0) - { - parameters.Add("page", page); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - - return client.makeRequest("/orders/packs", Client.METHOD_GET, parameters); - } - - /// - /// Create a order pack - /// - /// - /// ApiResponse - public ApiResponse packsCreate(Dictionary pack) - { - if (pack.Count < 1) - { - throw new ArgumentException("Parameter `pack` must contains a data"); - } - - return client.makeRequest( - "/orders/packs/create", - Client.METHOD_POST, - new Dictionary() { - { "pack", JsonConvert.SerializeObject(pack) } - } - ); - } - - /// - /// Returns a orders history - /// - /// - /// - /// - /// ApiResponse - public ApiResponse packsHistory(Dictionary filter = null, int page = 0, int limit = 0) - { - Dictionary parameters = new Dictionary(); - - if (filter.Count > 0) - { - parameters.Add("filter", filter); - } - if (page > 0) - { - parameters.Add("page", page); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - - return client.makeRequest("/orders/packs/history", Client.METHOD_GET, parameters); - } - - /// - /// Get order packs by id - /// - /// - /// ApiResponse - public ApiResponse packsGet(string id) - { - return client.makeRequest("/orders/packs/" + id, Client.METHOD_GET); - } - - /// - /// Delete order packs by id - /// - /// - /// ApiResponse - public ApiResponse packsDelete(string id) - { - return client.makeRequest("/orders/packs/" + id + "/delete", Client.METHOD_POST); - } - - /// - /// Edit a order packs - /// - /// - /// - /// ApiResponse - public ApiResponse packsEdit(string id, Dictionary pack) - { - if (pack.Count < 1) - { - throw new ArgumentException("Parameter `pack` must contains a data"); - } - - return client.makeRequest( - "/orders/packs/" + id + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "pack", JsonConvert.SerializeObject(pack) } - } - ); - } - - /// - /// Returns filtered store inventories list - /// - /// - /// - /// - /// ApiResponse - public ApiResponse inventoriesList(Dictionary filter = null, int page = 0, int limit = 0) - { - Dictionary parameters = new Dictionary(); - - if (filter.Count > 0) - { - parameters.Add("filter", filter); - } - if (page > 0) - { - parameters.Add("page", page); - } - if (limit > 0) - { - parameters.Add("limit", limit); - } - - return client.makeRequest("/store/inventories", Client.METHOD_GET, parameters); - } - - /// - /// Upload array of the store inventories - /// - /// - /// - /// ApiResponse - public ApiResponse inventoriesUpload(Dictionary offers, string site = "") - { - if (offers.Count < 1) - { - throw new ArgumentException("Parameter `offers` must contains a data"); - } - - return client.makeRequest( - "/store/inventories/upload", - Client.METHOD_POST, - this.fillSite( - site, - new Dictionary() { - { "offers", JsonConvert.SerializeObject(offers) } - } - ) - ); - } - - /// - /// Returns deliveryServices list - /// - /// ApiResponse - public ApiResponse deliveryServicesList() - { - return client.makeRequest("/reference/delivery-services", Client.METHOD_GET); - } - - /// - /// Returns deliveryTypes list - /// - /// ApiResponse - public ApiResponse deliveryTypesList() - { - return client.makeRequest("/reference/delivery-types", Client.METHOD_GET); - } - - /// - /// Returns orderMethods list - /// - /// ApiResponse - public ApiResponse orderMethodsList() - { - return client.makeRequest("/reference/order-methods", Client.METHOD_GET); - } - - /// - /// Returns orderTypes list - /// - /// ApiResponse - public ApiResponse orderTypesList() - { - return client.makeRequest("/reference/order-types", Client.METHOD_GET); - } - - /// - /// Returns paymentStatuses list - /// - /// ApiResponse - public ApiResponse paymentStatusesList() - { - return client.makeRequest("/reference/payment-statuses", Client.METHOD_GET); - } - - /// - /// Returns paymentTypes list - /// - /// ApiResponse - public ApiResponse paymentTypesList() - { - return client.makeRequest("/reference/payment-types", Client.METHOD_GET); - } - - /// - /// Returns productStatuses list - /// - /// ApiResponse - public ApiResponse productStatusesList() - { - return client.makeRequest("/reference/product-statuses", Client.METHOD_GET); - } - - /// - /// Returns statusGroups list - /// - /// ApiResponse - public ApiResponse statusGroupsList() - { - return client.makeRequest("/reference/status-groups", Client.METHOD_GET); - } - - /// - /// Returns statuses list - /// - /// ApiResponse - public ApiResponse statusesList() - { - return client.makeRequest("/reference/statuses", Client.METHOD_GET); - } - - /// - /// Returns sites list - /// - /// ApiResponse - public ApiResponse sitesList() - { - return client.makeRequest("/reference/sites", Client.METHOD_GET); - } - - /// - /// Returns stores list - /// - /// ApiResponse - public ApiResponse storesList() - { - return client.makeRequest("/reference/stores", Client.METHOD_GET); - } - - /// - /// Returns countries list - /// - /// ApiResponse - public ApiResponse countriesList() - { - return client.makeRequest("/reference/countries", Client.METHOD_GET); - } - - /// - /// Edit deliveryService - /// - /// - /// ApiResponse - public ApiResponse deliveryServicesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/delivery-services/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "deliveryService", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit deliveryType - /// - /// - /// ApiResponse - public ApiResponse deliveryTypesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/delivery-types/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "deliveryType", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit orderMethod - /// - /// - /// ApiResponse - public ApiResponse orderMethodsEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/order-methods/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "orderMethod", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit orderType - /// - /// - /// ApiResponse - public ApiResponse orderTypesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/order-types/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "orderType", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit paymentStatus - /// - /// - /// ApiResponse - public ApiResponse paymentStatusesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/payment-statuses/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "paymentStatus", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit paymentType - /// - /// - /// ApiResponse - public ApiResponse paymentTypesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/payment-types/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "paymentType", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit productStatus - /// - /// - /// ApiResponse - public ApiResponse productStatusesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/product-statuses/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "productStatus", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit order status - /// - /// - /// ApiResponse - public ApiResponse statusesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/statuses/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "status", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit site - /// - /// - /// ApiResponse - public ApiResponse sitesEdit(Dictionary data) - { - if (data.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/sites/" + data["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "site", JsonConvert.SerializeObject(data) } - } - ); - } - - /// - /// Edit stores - /// - /// - /// ApiResponse - public ApiResponse storesEdit(Dictionary store) - { - if (store.ContainsKey("code") == false) - { - throw new ArgumentException("Data must contain \"code\" parameter"); - } - - return client.makeRequest( - "/reference/stores/" + store["code"] + "/edit", - Client.METHOD_POST, - new Dictionary() { - { "store", JsonConvert.SerializeObject(store) } - } - ); - } - - /// - /// Captures events call for the user - /// - /// - /// - /// - /// - /// ApiResponse - public ApiResponse telephonyСallEventCreate(string phone, string type, string code, string hangupStatus) - { - Dictionary parameters = new Dictionary(); - - if (string.IsNullOrEmpty(phone)) - { - throw new ArgumentException("Parameter \"phone\" can not be empty"); - } - - if (string.IsNullOrEmpty(type)) - { - throw new ArgumentException("Option \"type\" can not be empty. Valid values: in, out, hangup."); - } - - if (string.IsNullOrEmpty(code)) - { - throw new ArgumentException("Option \"code\" can not be empty."); - } - - parameters.Add("phone", phone); - parameters.Add("type", type); - parameters.Add("code", code); - - if (!string.IsNullOrEmpty(hangupStatus)) - { - parameters.Add("hangupStatus", hangupStatus); - } - - return client.makeRequest("/telephony/call/event", Client.METHOD_POST, parameters); - } - - /// - /// It allows you to save your call history - /// - /// - /// ApiResponse - public ApiResponse telephonyСallsUpload(Dictionary calls) - { - return client.makeRequest( - "/telephony/calls/upload", - Client.METHOD_POST, - new Dictionary() { - { "calls", JsonConvert.SerializeObject(calls) } - } - ); - } - - /// - /// Returns the responsible manager for the client with the phone - /// - /// - /// - /// ApiResponse - public ApiResponse telephonyManagerGet(string phone, bool details = false) - { - if (string.IsNullOrEmpty(phone)) - { - throw new ArgumentException("Parameter \"phone\" can not be empty"); - } - - return client.makeRequest( - "/telephony/manager", - Client.METHOD_GET, - new Dictionary() { - { "phone", phone }, - { "details", details } - } - ); - } - - /// - /// Allows you to create/activate/deactivate the phone in the system and specify the necessary settings for the job - /// - /// - /// - /// - /// - /// ApiResponse - public ApiResponse telephonySettingEdit(string code, string clientId, string makeCallUrl, bool active = true) - { - Dictionary parameters = new Dictionary(); - - if (string.IsNullOrEmpty(code)) - { - throw new ArgumentException("Parameter \"code\" can not be empty"); - } - - if (string.IsNullOrEmpty(clientId)) - { - throw new ArgumentException("Option \"clientId\" can not be empty."); - } - - parameters.Add("code", code); - parameters.Add("clientId", clientId); - parameters.Add("active", active); - - if (!string.IsNullOrEmpty(makeCallUrl)) - { - parameters.Add("makeCallUrl", makeCallUrl); - } - - return client.makeRequest("/telephony/setting/" + code, Client.METHOD_POST, parameters); - } - - /// - /// Update CRM basic statistic - /// - /// ApiResponse - public ApiResponse statisticUpdate() - { - return client.makeRequest("/statistic/update", Client.METHOD_GET); - } - - /// - /// Return current site - /// - /// string - public string getSite() - { - return this.siteCode; - } - - /// - /// Return current site - /// - public void setSite(string site) - { - this.siteCode = site; - } - - /// - /// Check ID parameter - /// - /// - protected void checkIdParameter(string by) - { - string[] allowedForBy = new string[] { "externalId", "id" }; - if (allowedForBy.Contains(by) == false) - { - throw new ArgumentException("Value \"" + by + "\" for parameter \"by\" is not valid. Allowed values are " + String.Join(", ", allowedForBy)); - } - } - - /// - /// Fill params by site value - /// - /// - /// - /// Dictionary - protected Dictionary fillSite(string site, Dictionary param) - { - if (site.Length > 1) - { - param.Add("site", site); - } - else if (siteCode.Length > 1) - { - param.Add("site", siteCode); - } - - return param; - } - } -} diff --git a/RetailCrm/Exceptions/InvalidJsonException.cs b/RetailCrm/Exceptions/InvalidJsonException.cs deleted file mode 100644 index 12f19d0..0000000 --- a/RetailCrm/Exceptions/InvalidJsonException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace RetailCrm.Exceptions -{ - public class InvalidJsonException : Exception - { - public InvalidJsonException() - { - } - - public InvalidJsonException(string message) - : base(message) - { - } - - public InvalidJsonException(string message, Exception inner) - : base(message, inner) - { - } - } -} diff --git a/RetailCrm/Extra/Tools.cs b/RetailCrm/Extra/Tools.cs deleted file mode 100644 index a0f8c0e..0000000 --- a/RetailCrm/Extra/Tools.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; - -namespace RetailCrm.Extra -{ - class Tools - { - public static string httpBuildQuery(Dictionary data) - { - if (data is Dictionary == false) - { - return String.Empty; - } - - var parts = new List(); - HandleItem(data, parts); - return String.Join("&", parts); - } - - private static void HandleItem(object data, List parts, string prefix = "") - { - if (data == null) return; - - if (data is Dictionary) - { - parts.Add(FormatDictionary((Dictionary)data, prefix)); - } - else - { - parts.Add(String.IsNullOrEmpty(data.ToString()) ? String.Empty : String.Format("{0}={1}", prefix, data.ToString())); - } - } - - private static string FormatDictionary(Dictionary obj, string prefix = "") - { - var parts = new List(); - foreach (KeyValuePair kvp in obj) - { - string newPrefix = string.IsNullOrEmpty(prefix) ? - String.Format("{0}{1}", prefix, kvp.Key) : - String.Format("{0}[{1}]", prefix, kvp.Key); - HandleItem(kvp.Value, parts, newPrefix); - } - - return String.Join("&", parts); - } - - public static Dictionary jsonDecode(string json) - { - return jsonObjectToDictionary((Dictionary)JsonConvert.DeserializeObject>(json)); - } - - private static Dictionary jsonObjectToDictionary(Dictionary data) - { - Dictionary result = new Dictionary(); - foreach (KeyValuePair kvp in data) - { - object valueObj = kvp.Value; - string value = String.Empty; - - value = valueObj.ToString(); - - if (value != "") - { - if (valueObj.GetType() == typeof(JObject)) - { - valueObj = jsonObjectToDictionary((Dictionary)JsonConvert.DeserializeObject>(value)); - result.Add(kvp.Key.ToString(), valueObj); - } - else if (valueObj.GetType() == typeof(JArray)) - { - var items = new List(); - - dynamic dynamicObject = JsonConvert.DeserializeObject(value); - Dictionary newObject = new Dictionary(); - - int j = 0; - foreach (var item in dynamicObject) - { - newObject.Add(j.ToString(), jsonObjectToDictionary(item.ToObject>())); - j++; - } - - result.Add(kvp.Key.ToString(), newObject); - } - else - { - result.Add(kvp.Key.ToString(), valueObj); - } - } - } - return result; - } - } -} diff --git a/RetailCrm/Http/Client.cs b/RetailCrm/Http/Client.cs deleted file mode 100644 index 9cb9810..0000000 --- a/RetailCrm/Http/Client.cs +++ /dev/null @@ -1,111 +0,0 @@ -using RetailCrm.Extra; -using RetailCrm.Response; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; - -namespace RetailCrm.Http -{ - /// - /// HTTP client - /// - public class Client - { - public const string METHOD_GET = "GET"; - public const string METHOD_POST = "POST"; - private const string USER_AGENT = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; - private const string CONTENT_TYPE = "application/x-www-form-urlencoded"; - - protected string url; - protected Dictionary defaultParameter; - - /// - /// Creating HTTP client - /// - /// - /// - public Client(string apiUrl, Dictionary parameters = null) - { - if (apiUrl.IndexOf("https://") == -1) - { - throw new ArgumentException("API schema requires HTTPS protocol"); - } - - url = apiUrl; - defaultParameter = parameters; - } - - /// - /// Make HTTP request - /// - /// - /// - /// - /// - /// - public ApiResponse makeRequest(string path, string method, Dictionary parameters = null, int timeout = 30) - { - string[] allowedMethods = new string[] { METHOD_GET, METHOD_POST }; - if (allowedMethods.Contains(method) == false) - { - throw new ArgumentException("Method \"" + method + "\" is not valid. Allowed methods are " + String.Join(", ", allowedMethods)); - } - if (parameters == null) { - parameters = new Dictionary(); - } - parameters = defaultParameter.Union(parameters).ToDictionary(k => k.Key, v => v.Value); - path = url + path; - string httpQuery = Tools.httpBuildQuery(parameters); - - if (method.Equals(METHOD_GET) && parameters.Count > 0) - { - path += "?" + httpQuery; - } - - Exception exception = null; - - HttpWebRequest request = (HttpWebRequest) WebRequest.Create(path); - request.Method = method; - - if (method.Equals(METHOD_POST)) - { - UTF8Encoding encoding = new UTF8Encoding(); - byte[] bytes = encoding.GetBytes(httpQuery); - request.ContentLength = bytes.Length; - request.ContentType = CONTENT_TYPE; - request.UserAgent = USER_AGENT; - - Stream post = request.GetRequestStream(); - post.Write(bytes, 0, bytes.Length); - post.Flush(); - post.Close(); - } - - HttpWebResponse response = null; - try - { - response = (HttpWebResponse)request.GetResponse(); - } - catch (WebException ex) - { - response = (HttpWebResponse)ex.Response; - exception = ex; - } - - if (request == null || response == null) - { - throw new WebException(exception.ToString(), exception); - } - - StreamReader reader = new StreamReader((Stream) response.GetResponseStream()); - string responseBody = reader.ReadToEnd(); - int statusCode = (int) response.StatusCode; - - return new ApiResponse(statusCode, responseBody); - } - } -} diff --git a/RetailCrm/Response/ApiResponse.cs b/RetailCrm/Response/ApiResponse.cs deleted file mode 100644 index 6344f18..0000000 --- a/RetailCrm/Response/ApiResponse.cs +++ /dev/null @@ -1,94 +0,0 @@ -using Newtonsoft.Json; -using RetailCrm.Exceptions; -using RetailCrm.Extra; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace RetailCrm.Response -{ - /// - /// Response from retailCRM API - /// - public class ApiResponse - { - /// - /// HTTP response status code - /// - protected int statusCode; - - /// - /// Response - /// - protected Dictionary response; - - /// - /// Creating ApiResponse - /// - /// - /// - public ApiResponse(int statusCode, string responseBody = null) - { - this.statusCode = statusCode; - - if (responseBody != null && responseBody.Length > 0) - { - Dictionary response = new Dictionary(); - try - { - response = Tools.jsonDecode(responseBody); - } - catch (JsonReaderException e) - { - throw new InvalidJsonException("Invalid JSON in the API response body. " + e.ToString()); - } - - this.response = response; - } - } - - /// - /// Return HTTP response status code - /// - /// int - public int getStatusCode() - { - return this.statusCode; - } - - /// - /// HTTP request was successful - /// - /// boolean - public bool isSuccessful() - { - return this.statusCode < 400; - } - - /// - /// Return response - /// - /// Dictionary - public object this[string code] - { - get { - if (this.response.ContainsKey(code)) - { - return this.response[code]; - } - else - { - return new Dictionary(); - } - } - set - { - throw new ArgumentException("Property \"" + code + "\" is not writable"); - } - } - - - } -} diff --git a/Retailcrm.sln b/Retailcrm.sln new file mode 100644 index 0000000..6f3e5bf --- /dev/null +++ b/Retailcrm.sln @@ -0,0 +1,34 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.16 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Retailcrm", "Retailcrm\Retailcrm.csproj", "{9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RetailcrmUnitTest", "RetailcrmUnitTest\RetailcrmUnitTest.csproj", "{4069C2BC-C277-48A3-96AE-DA5934A75F4D}" + ProjectSection(ProjectDependencies) = postProject + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41} = {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41} + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41}.Release|Any CPU.Build.0 = Release|Any CPU + {4069C2BC-C277-48A3-96AE-DA5934A75F4D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4069C2BC-C277-48A3-96AE-DA5934A75F4D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4069C2BC-C277-48A3-96AE-DA5934A75F4D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4069C2BC-C277-48A3-96AE-DA5934A75F4D}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {7EB29423-9A10-4953-B3E8-563908B2D206} + EndGlobalSection +EndGlobal diff --git a/Retailcrm/Properties/AssemblyInfo.cs b/Retailcrm/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f0d3f0f --- /dev/null +++ b/Retailcrm/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// Общие сведения об этой сборке предоставляются следующим набором +// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, +// связанные со сборкой. +[assembly: AssemblyTitle("Retailcrm")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Retailcrm")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Установка значения False для параметра ComVisible делает типы в этой сборке невидимыми +// для компонентов COM. Если необходимо обратиться к типу в этой сборке через +// COM, задайте атрибуту ComVisible значение TRUE для этого типа. +[assembly: ComVisible(false)] + +// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM +[assembly: Guid("9c378ef4-9f9b-4214-a9aa-1fc3c44edb41")] + +// Сведения о версии сборки состоят из следующих четырех значений: +// +// Основной номер версии +// Дополнительный номер версии +// Номер сборки +// Редакция +// +// Можно задать все значения или принять номер сборки и номер редакции по умолчанию. +// используя "*", как показано ниже: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Retailcrm/QueryStringBuilder.cs b/Retailcrm/QueryStringBuilder.cs new file mode 100644 index 0000000..2d41932 --- /dev/null +++ b/Retailcrm/QueryStringBuilder.cs @@ -0,0 +1,113 @@ +namespace Retailcrm +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + + public class QueryStringBuilder + { + private readonly List> _keyValuePairs + = new List>(); + + public static string BuildQueryString(object queryData, string argSeperator = "&") + { + var encoder = new QueryStringBuilder(); + encoder.AddEntry(null, queryData, allowObjects: true); + + return encoder.GetUriString(argSeperator); + } + + private string GetUriString(string argSeperator) + { + return String.Join(argSeperator, + _keyValuePairs.Select(kvp => + { + var key = Uri.EscapeDataString(kvp.Key); + var value = Uri.EscapeDataString(kvp.Value.ToString()); + return $"{key}={value}"; + })); + } + + private void AddEntry(string prefix, object instance, bool allowObjects) + { + var dictionary = instance as IDictionary; + var collection = instance as ICollection; + + if (dictionary != null) + { + Add(prefix, GetDictionaryAdapter(dictionary)); + } + else if (collection != null) + { + Add(prefix, GetArrayAdapter(collection)); + } + else if (allowObjects) + { + Add(prefix, GetObjectAdapter(instance)); + } + else + { + _keyValuePairs.Add(new KeyValuePair(prefix, instance)); + } + } + + private void Add(string prefix, IEnumerable datas) + { + foreach (var item in datas) + { + var newPrefix = String.IsNullOrEmpty(prefix) + ? item.Key + : $"{prefix}[{item.Key}]"; + + AddEntry(newPrefix, item.Value, allowObjects: false); + } + } + + private struct Entry + { + public string Key; + public object Value; + } + + private IEnumerable GetObjectAdapter(object data) + { + var properties = data.GetType().GetProperties(); + + foreach (var property in properties) + { + yield return new Entry() + { + Key = property.Name, + Value = property.GetValue(data) + }; + } + } + + private IEnumerable GetArrayAdapter(ICollection collection) + { + int i = 0; + foreach (var item in collection) + { + yield return new Entry() + { + Key = i.ToString(), + Value = item, + }; + i++; + } + } + + private IEnumerable GetDictionaryAdapter(IDictionary collection) + { + foreach (DictionaryEntry item in collection) + { + yield return new Entry() + { + Key = item.Key.ToString(), + Value = item.Value, + }; + } + } + } +} \ No newline at end of file diff --git a/Retailcrm/Request.cs b/Retailcrm/Request.cs new file mode 100644 index 0000000..406b401 --- /dev/null +++ b/Retailcrm/Request.cs @@ -0,0 +1,99 @@ +namespace Retailcrm +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Net; + using System.Text; + + public class Request + { + public const string MethodGet = "GET"; + public const string MethodPost = "POST"; + private const string UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; + private const string ContentType = "application/x-www-form-urlencoded"; + + private readonly string _url; + private readonly Dictionary _defaultParameters; + + public Request(string apiUrl, Dictionary parameters = null) + { + if (apiUrl.IndexOf("https://", StringComparison.Ordinal) == -1) + { + throw new ArgumentException("API schema requires HTTPS protocol"); + } + + _url = apiUrl; + _defaultParameters = parameters; + } + + public Response MakeRequest(string path, string method, Dictionary parameters = null) + { + string[] allowedMethods = { MethodGet, MethodPost }; + + if (allowedMethods.Contains(method) == false) + { + throw new ArgumentException($"Method {method} is not valid. Allowed HTTP methods are {string.Join(", ", allowedMethods)}"); + } + + if (parameters == null) + { + parameters = new Dictionary(); + } + + parameters = _defaultParameters.Union(parameters).ToDictionary(k => k.Key, v => v.Value); + path = _url + path; + + string httpQuery = QueryStringBuilder.BuildQueryString(parameters); + + if (method.Equals(MethodGet) && parameters.Count > 0) + { + path += "?" + httpQuery; + } + + Exception exception = null; + + HttpWebRequest request = (HttpWebRequest)WebRequest.Create(path); + request.Method = method; + + if (method.Equals(MethodPost)) + { + UTF8Encoding encoding = new UTF8Encoding(); + byte[] bytes = encoding.GetBytes(httpQuery); + request.ContentLength = bytes.Length; + request.ContentType = ContentType; + request.UserAgent = UserAgent; + + Stream post = request.GetRequestStream(); + post.Write(bytes, 0, bytes.Length); + post.Flush(); + post.Close(); + } + + HttpWebResponse response; + + try + { + response = (HttpWebResponse)request.GetResponse(); + } + catch (WebException webException) + { + response = (HttpWebResponse)webException.Response; + exception = webException; + } + + if (request == null || response == null) + { + throw new WebException(exception.ToString(), exception); + } + + // ReSharper disable once AssignNullToNotNullAttribute + StreamReader reader = new StreamReader(response.GetResponseStream()); + string responseBody = reader.ReadToEnd(); + int statusCode = (int)response.StatusCode; + + return new Response(statusCode, responseBody); + } + } +} \ No newline at end of file diff --git a/Retailcrm/Response.cs b/Retailcrm/Response.cs new file mode 100644 index 0000000..3e3a4ae --- /dev/null +++ b/Retailcrm/Response.cs @@ -0,0 +1,48 @@ +namespace Retailcrm +{ + using System; + using System.Collections.Generic; + + public class Response + { + private readonly int _statusCode; + private readonly string _rawResponse; + private readonly Dictionary _responseData; + + public Response(int statusCode, string responseBody = null) + { + _statusCode = statusCode; + + if (string.IsNullOrEmpty(responseBody)) + { + throw new ArgumentException("Response body is empty"); + } + + _rawResponse = responseBody; + + var jsSerializer = new System.Web.Script.Serialization.JavaScriptSerializer(); + _responseData = (Dictionary)jsSerializer.DeserializeObject(responseBody); + } + + public int GetStatusCode() + { + return _statusCode; + } + + public Dictionary GetResponse() + { + return _responseData; + } + + public string GetRawResponse() + { + return _rawResponse; + } + + public bool IsSuccessfull() + { + return _statusCode < 400; + } + + } +} \ No newline at end of file diff --git a/Retailcrm/Retailcrm.csproj b/Retailcrm/Retailcrm.csproj new file mode 100644 index 0000000..3026f21 --- /dev/null +++ b/Retailcrm/Retailcrm.csproj @@ -0,0 +1,57 @@ + + + + + Debug + AnyCPU + {9C378EF4-9F9B-4214-A9AA-1FC3C44EDB41} + Library + Properties + Retailcrm + Retailcrm + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Retailcrm/Versions/AbstractClient.cs b/Retailcrm/Versions/AbstractClient.cs new file mode 100644 index 0000000..910d82a --- /dev/null +++ b/Retailcrm/Versions/AbstractClient.cs @@ -0,0 +1,61 @@ +namespace Retailcrm +{ + using System; + using System.Collections.Generic; + using System.Linq; + + public class AbstractClient + { + private string _siteCode; + + /// + /// Return current site + /// + /// string + public string GetSite() + { + return _siteCode; + } + + /// + /// Return current site + /// + public void SetSite(string site) + { + _siteCode = site; + } + + /// + /// Check ID parameter + /// + /// + protected static void CheckIdParameter(string by) + { + string[] allowedForBy = { "externalId", "id" }; + if (allowedForBy.Contains(by) == false) + { + throw new ArgumentException($"Value {by} for parameter `by` is not valid. Allowed values are {string.Join(", ", allowedForBy)}"); + } + } + + /// + /// Fill params by site value + /// + /// + /// + /// Dictionary + protected Dictionary FillSite(string site, Dictionary param) + { + if (site.Length > 1) + { + param.Add("site", site); + } + else if (_siteCode.Length > 1) + { + param.Add("site", _siteCode); + } + + return param; + } + } +} diff --git a/Retailcrm/Versions/V3/Client.cs b/Retailcrm/Versions/V3/Client.cs new file mode 100644 index 0000000..7692a9e --- /dev/null +++ b/Retailcrm/Versions/V3/Client.cs @@ -0,0 +1,123 @@ +namespace Retailcrm.Versions.V3 +{ + using System; + using System.Collections.Generic; + using System.Web.Script.Serialization; + + public class Client : AbstractClient + { + private readonly Request _request; + private string _siteCode; + + public Client(string url, string key, string version, string site = "") + { + if ("/" != url.Substring(url.Length - 1, 1)) + { + url += "/"; + } + + url += "api/" + version; + + _request = new Request(url, new Dictionary { { "apiKey", key } }); + _siteCode = site; + } + + public Response OrdersCreate(Dictionary order, string site = "") + { + if (order.Count < 1) + { + throw new ArgumentException("Parameter `order` must contains a data"); + } + + return _request.MakeRequest( + "/orders/create", + Request.MethodPost, + FillSite( + site, + new Dictionary + { + { "order", new JavaScriptSerializer().Serialize(order) } + } + ) + ); + } + + public Response OrdersUpdate(Dictionary order, string by = "externalId", string site = "") + { + if (order.Count < 1) + { + throw new ArgumentException("Parameter `order` must contains a data"); + } + + if (!order.ContainsKey("id") && !order.ContainsKey("externalId")) + { + throw new ArgumentException("Parameter `order` must contains an id or externalId"); + } + + CheckIdParameter(by); + + string uid = by == "externalId" ? order["externalId"].ToString() : order["id"].ToString(); + + return _request.MakeRequest( + $"/orders/{uid}/edit", + Request.MethodPost, + FillSite( + site, + new Dictionary + { + { "by", by }, + { "order", new JavaScriptSerializer().Serialize(order) } + } + ) + ); + } + + public Response OrdersGet(string id, string by = "externalId", string site = "") + { + AbstractClient.CheckIdParameter(by); + + return _request.MakeRequest( + $"/orders/{id}", + Request.MethodGet, + FillSite( + site, + new Dictionary() { + { "by", by } + } + ) + ); + } + + public Response OrdersList(Dictionary filter = null, int page = 0, int limit = 0) + { + Dictionary parameters = new Dictionary(); + + if (filter != null && filter.Count > 0) + { + parameters.Add("filter", filter); + } + if (page > 0) + { + parameters.Add("page", page); + } + if (limit > 0) + { + parameters.Add("limit", limit); + } + + return _request.MakeRequest("/orders", Request.MethodGet, parameters); + } + + public Response OrdersFixExternalIds(Dictionary[] ids) + { + return _request.MakeRequest( + "/orders/fix-external-ids", + Request.MethodPost, + new Dictionary + { + { "orders", new JavaScriptSerializer().Serialize(ids) } + } + ); + } + } +} diff --git a/RetailcrmUnitTest/App.config.dist b/RetailcrmUnitTest/App.config.dist new file mode 100644 index 0000000..07cb110 --- /dev/null +++ b/RetailcrmUnitTest/App.config.dist @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/RetailcrmUnitTest/Properties/AssemblyInfo.cs b/RetailcrmUnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..7117b97 --- /dev/null +++ b/RetailcrmUnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("RetailcrmUnitTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("RetailcrmUnitTest")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("4069c2bc-c277-48a3-96ae-da5934a75f4d")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RetailcrmUnitTest/RetailcrmUnitTest.csproj b/RetailcrmUnitTest/RetailcrmUnitTest.csproj new file mode 100644 index 0000000..99eae47 --- /dev/null +++ b/RetailcrmUnitTest/RetailcrmUnitTest.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {4069C2BC-C277-48A3-96AE-DA5934A75F4D} + Library + Properties + RetailcrmUnitTest + RetailcrmUnitTest + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\packages\MSTest.TestFramework.1.1.11\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + ..\Retailcrm\bin\Debug\Retailcrm.dll + + + + + + + + + + + + + + + + + + + + + + Данный проект ссылается на пакеты NuGet, отсутствующие на этом компьютере. Используйте восстановление пакетов NuGet, чтобы скачать их. Дополнительную информацию см. по адресу: http://go.microsoft.com/fwlink/?LinkID=322105. Отсутствует следующий файл: {0}. + + + + + + \ No newline at end of file diff --git a/RetailcrmUnitTest/V3/Orders.cs b/RetailcrmUnitTest/V3/Orders.cs new file mode 100644 index 0000000..8750897 --- /dev/null +++ b/RetailcrmUnitTest/V3/Orders.cs @@ -0,0 +1,120 @@ +namespace RetailcrmUnitTest.V3 +{ + using System; + using System.Diagnostics; + using System.Collections.Generic; + using System.Collections.Specialized; + using System.Configuration; + using Microsoft.VisualStudio.TestTools.UnitTesting; + using Retailcrm; + using Retailcrm.Versions.V3; + + [TestClass] + public class Orders + { + private readonly Client _client; + + public Orders() + { + NameValueCollection appSettings = ConfigurationManager.AppSettings; + + _client = new Client( + appSettings["apiUrl"], + appSettings["apiKey"], + "v3" + ); + } + + [TestMethod] + public void OrdersCreateReadUpdate() + { + Dictionary order = new Dictionary(); + + long epochTicks = new DateTime(1970, 1, 1).Ticks; + long unixTime = ((DateTime.UtcNow.Ticks - epochTicks) / TimeSpan.TicksPerSecond); + + order.Add("number", unixTime); + order.Add("createdAt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + order.Add("lastName", "Doe"); + order.Add("firstName", "John"); + order.Add("email", "john@example.com"); + order.Add("phone", "+79999999999"); + + Debug.WriteLine("Running order create, get & update"); + + Response createResponse = _client.OrdersCreate(order); + Assert.IsTrue(createResponse.IsSuccessfull()); + Assert.IsInstanceOfType(createResponse, typeof(Response)); + Assert.IsTrue(createResponse.GetResponse().ContainsKey("id")); + + string id = createResponse.GetResponse()["id"].ToString(); + + Response getResponse = _client.OrdersGet(id, "id"); + Assert.IsTrue(getResponse.IsSuccessfull()); + Assert.IsInstanceOfType(getResponse, typeof(Response)); + Assert.IsTrue(getResponse.GetResponse().ContainsKey("order")); + + Dictionary update = new Dictionary + { + {"id", id}, + {"status", "cancel-other"} + }; + + Response updateResponse = _client.OrdersUpdate(update, "id"); + Assert.IsTrue(updateResponse.IsSuccessfull()); + Assert.IsInstanceOfType(updateResponse, typeof(Response)); + Assert.IsTrue(updateResponse.GetStatusCode() == 200); + } + + [TestMethod] + public void OrdersFixExternalId() + { + Dictionary order = new Dictionary(); + + long epochTicks = new DateTime(1970, 1, 1).Ticks; + long unixTime = ((DateTime.UtcNow.Ticks - epochTicks) / TimeSpan.TicksPerSecond); + + order.Add("number", unixTime); + order.Add("createdAt", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")); + order.Add("lastName", "Doe"); + order.Add("firstName", "John"); + order.Add("email", "john@example.com"); + order.Add("phone", "+79999999999"); + + Debug.WriteLine("Running orders fix external ids"); + Response createResponse = _client.OrdersCreate(order); + + string id = createResponse.GetResponse()["id"].ToString(); + string externalId = $"{unixTime}ID"; + + Dictionary[] fix = + { + new Dictionary + { + { "id", id }, + { "externalId", externalId } + } + }; + + Assert.IsTrue(createResponse.IsSuccessfull()); + Assert.IsInstanceOfType(createResponse, typeof(Response)); + Assert.IsTrue(createResponse.GetResponse().ContainsKey("id")); + + Response fixResponse = _client.OrdersFixExternalIds(fix); + Assert.IsTrue(fixResponse.IsSuccessfull()); + Assert.IsInstanceOfType(fixResponse, typeof(Response)); + } + + [TestMethod] + public void OrdersList() + { + Debug.WriteLine("Running orders list"); + Response response = _client.OrdersList(); + + Assert.IsTrue(response.IsSuccessfull()); + Assert.IsTrue(response.GetStatusCode() == 200 || response.GetStatusCode() == 201); + Assert.IsInstanceOfType(response, typeof(Response)); + Assert.IsTrue(response.GetResponse().ContainsKey("orders")); + } + } +} diff --git a/RetailcrmUnitTest/packages.config b/RetailcrmUnitTest/packages.config new file mode 100644 index 0000000..a21677b --- /dev/null +++ b/RetailcrmUnitTest/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file