diff --git a/README.md b/README.md index ca3d6ca..0848ae3 100644 --- a/README.md +++ b/README.md @@ -30,16 +30,71 @@ Constructor arguments are: ``` csharp +ApiClient api; string url, key; -Dictionary orderTypes = new Dictionary(); +try +{ + api = new ApiClient(url, key); +} +catch (WebException e) +{ + System.Console.WriteLine(e.ToString()); +} -RestApi api = new RestApi(url, key); -try { - orderTypes = api.orderTypesList(); -} catch (ApiException e) { - Console.WriteLine(ex.Message); -} catch (CurlException e) { - Console.WriteLine(ex.Message); +Dictionary tmpOrder = new Dictionary(){ + {"number", "example"}, + {"externalId", "example"}, + {"createdAt", DateTime.Now.ToString("Y-m-d H:i:s")}, + {"discount", 50}, + {"phone", "89263832233"}, + {"email", "vshirokov@gmail.com"}, + {"customerComment", "example"}, + {"customFields", new Dictionary(){ + {"reciever_phone", "example"}, + {"reciever_name", "example"}, + {"ext_number", "example"} + } + }, + {"contragentType", "individual"}, + {"orderType", "eshop-individual"}, + {"orderMethod", "app"}, + {"customerId", "555"}, + {"managerId", 8}, + {"items", new Dictionary(){ + {"0", new Dictionary(){ + {"initialPrice", 100}, + {"quantity", 1}, + {"productId", 55}, + {"productName", "example"} + } + } + } + }, + {"delivery", new Dictionary(){ + {"code", "courier"}, + {"date", DateTime.Now.ToString("Y-m-d")}, + {"address", new Dictionary(){ + {"text", "exampleing"} + } + } + } + } + }; + +ApiResponse response = null; +try +{ + response = api.ordersEdit(order); +} +catch (WebException e) +{ + System.Console.WriteLine(e.ToString()); +} + +if (response.isSuccessful() && 201 == response["statusCosde"]) { + System.Console.WriteLine("Заказ успешно создан. ID заказа в retailCRM = " + response["id"]); +} else { + System.Console.WriteLine("Ошибка создания заказа: [Статус HTTP-ответа " + response["statusCosde"] + "] " + response["errorMsg"]); } ``` \ No newline at end of file diff --git a/RetailCrm.sln b/RetailCrm.sln deleted file mode 100644 index 9386f3f..0000000 --- a/RetailCrm.sln +++ /dev/null @@ -1,20 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RetailCrm", "IntaroCrm\RetailCrm.csproj", "{1C407E40-0B79-4593-AC79-03BA8DD76DD1}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {1C407E40-0B79-4593-AC79-03BA8DD76DD1}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/RetailCrm/ApiClient.cs b/RetailCrm/ApiClient.cs new file mode 100644 index 0000000..0e2ad76 --- /dev/null +++ b/RetailCrm/ApiClient.cs @@ -0,0 +1,755 @@ +using Newtonsoft.Json; +using RetailCrm.Http; +using RetailCrm.Response; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +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("Y-m-d H:i:s")); + } + if (endDate != null) + { + parameters.Add("endDate", endDate.Value.ToString("Y-m-d H:i:s")); + } + 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 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); + } + + /// + /// 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) } + } + ); + } + + /// + /// 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/ApiException.cs b/RetailCrm/ApiException.cs deleted file mode 100644 index 3bcf0f5..0000000 --- a/RetailCrm/ApiException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace RetailCrm -{ - public class ApiException : Exception - { - public ApiException() - { - } - - public ApiException(string message) - : base(message) - { - } - - public ApiException(string message, Exception inner) - : base(message, inner) - { - } - } -} diff --git a/RetailCrm/CurlException.cs b/RetailCrm/CurlException.cs deleted file mode 100644 index fff3862..0000000 --- a/RetailCrm/CurlException.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; - -namespace RetailCrm -{ - public class CurlException : Exception - { - public CurlException() - { - } - - public CurlException(string message) - : base(message) - { - } - - public CurlException(string message, Exception inner) - : base(message, inner) - { - } - } -} diff --git a/RetailCrm/Exceptions/InvalidJsonException.cs b/RetailCrm/Exceptions/InvalidJsonException.cs new file mode 100644 index 0000000..12f19d0 --- /dev/null +++ b/RetailCrm/Exceptions/InvalidJsonException.cs @@ -0,0 +1,21 @@ +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 new file mode 100644 index 0000000..f464251 --- /dev/null +++ b/RetailCrm/Extra/Tools.cs @@ -0,0 +1,94 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +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) + { + System.Console.WriteLine(kvp.Key.ToString()); + System.Console.WriteLine(kvp.Value.ToString()); + System.Console.ReadLine(); + object valueObj = kvp.Value; + string value = String.Empty; + + if (valueObj.GetType() == typeof(JArray)) + { + string tmpValue = JsonConvert.SerializeObject(((JArray)valueObj).ToArray()); + value = tmpValue.Replace("[", "{"); + value = value.Replace("]", "}"); + } + else + { + value = valueObj.ToString(); + } + System.Console.WriteLine(value); + System.Console.ReadLine(); + if (value != "") + { + if (valueObj.GetType() == typeof(JObject) || valueObj.GetType() == typeof(JArray)) + { + valueObj = jsonObjectToDictionary((Dictionary)JsonConvert.DeserializeObject>(value)); + } + result.Add(kvp.Key.ToString(), valueObj); + } + } + return result; + } + } +} diff --git a/RetailCrm/Http/Client.cs b/RetailCrm/Http/Client.cs new file mode 100644 index 0000000..9cb9810 --- /dev/null +++ b/RetailCrm/Http/Client.cs @@ -0,0 +1,111 @@ +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/Properties/AssemblyInfo.cs b/RetailCrm/Properties/AssemblyInfo.cs deleted file mode 100644 index f84f137..0000000 --- a/RetailCrm/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// Управление общими сведениями о сборке осуществляется с помощью -// набора атрибутов. Измените значения этих атрибутов, чтобы изменить сведения, -// связанные со сборкой. -[assembly: AssemblyTitle("IntaroCrm")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("IntaroCrm")] -[assembly: AssemblyCopyright("Copyright © 2014")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Параметр ComVisible со значением FALSE делает типы в сборке невидимыми -// для COM-компонентов. Если требуется обратиться к типу в этой сборке через -// COM, задайте атрибуту ComVisible значение TRUE для этого типа. -[assembly: ComVisible(false)] - -// Следующий GUID служит для идентификации библиотеки типов, если этот проект будет видимым для COM -[assembly: Guid("9a3f29a5-d878-430c-b7ee-8548d4d03f43")] - -// Сведения о версии сборки состоят из следующих четырех значений: -// -// Основной номер версии -// Дополнительный номер версии -// Номер построения -// Редакция -// -// Можно задать все значения или принять номер построения и номер редакции по умолчанию, -// используя "*", как показано ниже: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/RetailCrm/Response/ApiResponse.cs b/RetailCrm/Response/ApiResponse.cs new file mode 100644 index 0000000..6344f18 --- /dev/null +++ b/RetailCrm/Response/ApiResponse.cs @@ -0,0 +1,94 @@ +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/RestApi.cs b/RetailCrm/RestApi.cs deleted file mode 100644 index 092c632..0000000 --- a/RetailCrm/RestApi.cs +++ /dev/null @@ -1,708 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Threading.Tasks; -using Newtonsoft.Json.Linq; -using Newtonsoft.Json; -using System.Collections; - -namespace RetailCrm -{ - public class RestApi - { - protected string apiUrl; - protected string apiKey; - protected string apiVersion = "3"; - protected DateTime generatedAt; - protected Dictionary parameters; - - private 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 string contentType = "application/x-www-form-urlencoded"; - - /// адрес CRM - /// ключ для работы с api - public RestApi(string crmUrl, string crmKey) - { - apiUrl = crmUrl + "/api/v" + apiVersion + "/"; - apiKey = crmKey; - parameters = new Dictionary(); - parameters.Add("apiKey", apiKey); - } - - /// - /// Получение заказа по id - /// - /// идентификатор заказа - /// поиск заказа по id или externalId - /// информация о заказе - public Dictionary orderGet(int id, string by) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/" + id.ToString(); - if (by.Equals("externalId")) - { - parameters.Add("by", by); - } - result = request(url, "GET"); - - return result; - } - - /// - /// Создание заказа - /// - /// информация о заказе - /// - public Dictionary orderCreate(Dictionary order) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/create"; - string dataJson = JsonConvert.SerializeObject(order); - parameters.Add("order", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Изменение заказа - /// - /// информация о заказе - /// - public Dictionary orderEdit(Dictionary order) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/" + order["externalId"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(order); - parameters.Add("order", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Пакетная загрузка заказов - /// - /// массив заказов - /// - public Dictionary orderUpload(Dictionary orders) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/upload"; - string dataJson = JsonConvert.SerializeObject(orders); - parameters.Add("orders", dataJson); - result = request(url, "POST"); - - if (result.ContainsKey("uploadedOrders") && result != null) - { - return getDictionary(result["uploadedOrders"]); - } - - return result; - } - - /// - /// Обновление externalId у заказов с переданными id - /// - /// массив, содержащий id и externalId заказа - /// - public Dictionary orderFixExternalIds(Dictionary orders) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/fix-external-ids"; - string dataJson = JsonConvert.SerializeObject(orders); - parameters.Add("orders", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение последних измененных заказов - /// - /// начальная дата выборки - /// конечная дата - /// ограничение на размер выборки - /// сдвиг - /// массив заказов - public Dictionary orderHistory(DateTime startDate, DateTime endDate, int limit, int offset) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "orders/history"; - parameters.Add("startDate", startDate.ToString()); - parameters.Add("startDate", endDate.ToString()); - parameters.Add("limit", limit.ToString()); - parameters.Add("offset", offset.ToString()); - result = request(url, "GET"); - - return result; - } - - /// - /// Получение клиента по id - /// - /// идентификатор - /// поиск заказа по id или externalId - /// информация о клиенте - public Dictionary customerGet(string id, string by) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/" + id; - if (by.Equals("externalId")) - { - parameters.Add("by", by); - } - result = request(url, "GET"); - - return result; - } - - /// - /// Получение списка клиентов в соответсвии с запросом - /// - /// телефон - /// почтовый адрес - /// фио пользователя - /// ограничение на размер выборки - /// сдвиг - /// массив клиентов - public Dictionary customers(string phone, string email, string fio, int limit, int offset) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers"; - parameters.Add("phone", phone); - parameters.Add("email", email); - parameters.Add("fio", fio); - parameters.Add("limit", limit.ToString()); - parameters.Add("offset", offset.ToString()); - result = request(url, "GET"); - - return result; - } - - /// - /// Создание клиента - /// - /// информация о клиенте - /// - public Dictionary customerCreate(Dictionary customer) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/create"; - string dataJson = JsonConvert.SerializeObject(customer); - parameters.Add("customer", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Редактирование клиента - /// - /// информация о клиенте - /// - public Dictionary customerEdit(Dictionary customer) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/" + customer["externalId"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(customer); - parameters.Add("customer", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Пакетная загрузка клиентов - /// - /// массив клиентов - /// - public Dictionary customerUpload(Dictionary customers) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/upload"; - string dataJson = JsonConvert.SerializeObject(customers); - parameters.Add("customers", dataJson); - result = request(url, "POST"); - - if (result.ContainsKey("uploaded") && result != null) - { - return getDictionary(result["uploaded"]); - } - - return result; - } - - /// - /// Обновление externalId у клиентов с переданными id - /// - /// массив, содержащий id и externalId заказа - /// - public Dictionary customerFixExternalIds(Dictionary customers) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/fix-external-ids"; - string dataJson = JsonConvert.SerializeObject(customers); - parameters.Add("customers", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка заказов клиента - /// - /// идентификатор клиента - /// начальная дата выборки - /// конечная дата - /// ограничение на размер выборки - /// сдвиг - /// поиск заказа по id или externalId - /// массив заказов - public Dictionary customerOrdersList(string id, DateTime startDate, DateTime endDate, int limit, int offset, string by) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "customers/" + id + "/orders"; - if (by.Equals("externalId")) - { - parameters.Add("by", by); - } - parameters.Add("startDate", startDate.ToString()); - parameters.Add("endDate", endDate.ToString()); - parameters.Add("limit", limit.ToString()); - parameters.Add("offset", offset.ToString()); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка типов доставки - /// - /// массив типов доставки - public Dictionary deliveryTypesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/delivery-types"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование типа доставки - /// - /// информация о типе доставки - /// - public Dictionary deliveryTypeEdit(Dictionary deliveryType) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/delivery-types/" + deliveryType["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(deliveryType); - parameters.Add("deliveryType", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка служб доставки - /// - /// массив служб доставки - public Dictionary deliveryServicesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/delivery-services"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование службы доставки - /// - /// информация о службе доставки - /// - public Dictionary deliveryServiceEdit(Dictionary deliveryService) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/delivery-services/" + deliveryService["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(deliveryService); - parameters.Add("deliveryService", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка типов оплаты - /// - /// массив типов оплаты - public Dictionary paymentTypesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/payment-types"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование типа оплаты - /// - /// информация о типе оплаты - /// - public Dictionary paymentTypesEdit(Dictionary paymentType) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/payment-types/" + paymentType["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(paymentType); - parameters.Add("paymentType", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка статусов оплаты - /// - /// массив статусов оплаты - public Dictionary paymentStatusesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/payment-statuses"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование статуса оплаты - /// - /// информация о статусе оплаты - /// - public Dictionary paymentStatusesEdit(Dictionary paymentStatus) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/payment-statuses/" + paymentStatus["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(paymentStatus); - parameters.Add("paymentStatus", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка типов заказа - /// - /// массив типов заказа - public Dictionary orderTypesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/order-types"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование типа заказа - /// - /// информация о типе заказа - /// - public Dictionary orderTypesEdit(Dictionary orderType) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/order-types/" + orderType["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(orderType); - parameters.Add("orderType", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка способов оформления заказа - /// - /// массив способов оформления заказа - public Dictionary orderMethodsList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/order-methods"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование способа оформления заказа - /// - /// информация о способе оформления заказа - /// - public Dictionary orderMethodsEdit(Dictionary orderMethod) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/order-methods/" + orderMethod["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(orderMethod); - parameters.Add("orderMethod", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка статусов заказа - /// - /// массив статусов заказа - public Dictionary orderStatusesList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/statuses"; - result = request(url, "GET"); - - return result; - } - - /// - /// Редактирование статуса заказа - /// - /// информация о статусе заказа - /// - public Dictionary orderStatusEdit(Dictionary status) - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/statuses/" + status["code"].ToString() + "/edit"; - string dataJson = JsonConvert.SerializeObject(status); - parameters.Add("status", dataJson); - result = request(url, "POST"); - - return result; - } - - /// - /// Получение списка групп статусов заказа - /// - /// массив групп статусов заказа - public Dictionary orderStatusGroupsList() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "reference/status-groups"; - result = request(url, "GET"); - - return result; - } - - /// - /// Обновление статистики - /// - /// статус выполненного обновления - public Dictionary statisticUpdate() - { - Dictionary result = new Dictionary(); - string url = apiUrl + "statistic/update"; - result = request(url, "GET"); - - return result; - } - - /// дата генерации - public DateTime getGeneratedAt() - { - return generatedAt; - } - - /// - /// - /// - protected Dictionary request(string url, string method) - { - Dictionary data = new Dictionary(); - string urlParameters = httpBuildQuery(parameters); - - if (method.Equals("GET") && urlParameters.Length > 0) - { - url += "?" + urlParameters; - } - - Exception exception = null; - - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - - request.Method = method; - - if (method.Equals("POST")) - { - UTF8Encoding encoding = new UTF8Encoding(); - byte[] postBytes = encoding.GetBytes(urlParameters); - - request.ContentType = contentType; - request.ContentLength = postBytes.Length; - request.UserAgent = userAgent; - - Stream postStream = request.GetRequestStream(); - postStream.Write(postBytes, 0, postBytes.Length); - postStream.Flush(); - postStream.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 CurlException(exception.ToString(), exception); - } - - int statusCode = (int)response.StatusCode; - - Stream dataStream = response.GetResponseStream(); - - StreamReader reader = new StreamReader(dataStream); - string serverResponse = reader.ReadToEnd(); - - parameters.Clear(); - parameters.Add("apiKey", apiKey); - - data = jsonDecode(serverResponse); - - if (data.ContainsKey("generatedAt")) - { - generatedAt = DateTime.ParseExact(data["generatedAt"].ToString(), "Y-m-d H:i:s", - System.Globalization.CultureInfo.InvariantCulture); - data.Remove("generatedAt"); - } - - if (statusCode >= 400 || (data.ContainsKey("success") && !(bool)data["success"])) - { - throw new ApiException(getErrorMessage(data)); - } - - data.Remove("success"); - - if (data.Count == 0) - { - return null; - } - - return data; - } - - /// - /// - protected string getErrorMessage(Dictionary data) - { - string error = ""; - - if (data.ContainsKey("message")) - { - error = data["message"].ToString(); - } - else if (data.ContainsKey("0")) - { - Dictionary sub = getDictionary(data["0"]); - if (sub.ContainsKey("message")) - { - error = sub["message"].ToString(); - } - - } - else if (data.ContainsKey("errorMsg")) - { - error = data["errorMsg"].ToString(); - } - else if (data.ContainsKey("error")) - { - Dictionary sub = getDictionary(data["error"]); - if (sub.ContainsKey("message")) - { - error = sub["message"].ToString(); - } - } - - if (data.ContainsKey("errors")) - { - Dictionary sub = getDictionary(data["errors"]); - foreach (KeyValuePair kvp in data) - { - error += ". " + kvp.Value.ToString(); - } - } - - return error; - } - - /// - /// - public Dictionary getDictionary(object data) - { - Dictionary result = new Dictionary(); - IDictionary idict = (IDictionary)data; - - foreach (object key in idict.Keys) - { - result.Add(key.ToString(), idict[key]); - } - return result; - } - - /// - /// - protected Dictionary jsonDecode(string json) - { - Dictionary data = new Dictionary(); - data = JsonConvert.DeserializeObject>(json); - data = jsonToDictionary(data); - - return data; - } - - /// - /// - protected static Dictionary jsonToDictionary(Dictionary data) - { - Dictionary result = new Dictionary(); - foreach (KeyValuePair kvp in data) - { - string key = kvp.Key; - object value = kvp.Value; - - if (value.GetType() == typeof(JObject)) - { - Dictionary valueJson = JsonConvert.DeserializeObject>(value.ToString()); - value = jsonToDictionary(valueJson); - } - result.Add(key, value); - } - return result; - } - - /// - /// - protected string httpBuildQuery(Dictionary data) - { - string queryString = null; - foreach (KeyValuePair kvp in data) - { - queryString += kvp.Key + "=" + kvp.Value + "&"; - } - - if (queryString.Length > 0) - { - queryString = queryString.Substring(0, queryString.Length - 1); - } - - return queryString; - } - } -} diff --git a/RetailCrm/RetailCrm.csproj b/RetailCrm/RetailCrm.csproj deleted file mode 100644 index 739db1f..0000000 --- a/RetailCrm/RetailCrm.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Debug - AnyCPU - {1C407E40-0B79-4593-AC79-03BA8DD76DD1} - Library - Properties - RetailCrm - IntaroCrm - v4.5 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - true - - - intaro.pfx - - - - ..\packages\Newtonsoft.Json.6.0.6\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/RetailCrm/packages.config b/RetailCrm/packages.config deleted file mode 100644 index 592edee..0000000 --- a/RetailCrm/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/lib/RetailCrm.dll b/lib/RetailCrm.dll index 72950af..147cbaa 100644 Binary files a/lib/RetailCrm.dll and b/lib/RetailCrm.dll differ