diff --git a/.gitignore b/.gitignore index 839b78456..d31be9e13 100644 --- a/.gitignore +++ b/.gitignore @@ -189,4 +189,6 @@ FakesAssemblies/ # Ignore config because api keys are stored here (copy example configs to these files) web.config app.config +settings.json Nexmo.Api.Test.Unit/private.key +project.lock.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ee11aa0e..b51a40e18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,27 @@ - +# 2.0.0 (2016-10-24) + +* Dependency marking for netstandard1.6 + +# 2.0.0-rc2 (2016-10-22) + +* Fix JWT generation (key import fail) on OSX/Linux + +# 2.0.0-rc1 (2016-10-16) + +* NumberInsight basic + standard support +* NumberVerify control call +* JWT token generation +* Application API support +* Application-based call API support +* .NET Standard 1.6 support +* __[BREAKING]__ Moved configuration from app.config to settings.json +* __[BREAKING]__ Nexmo.Api.Voice static class has been deprecated - you must move to the new Voice calls inside the new Nexmo.Api.Voice namespace. See [the Nexmo docs](https://docs.nexmo.com/voice/voice-api) for details. + +JWT notes: + +* When registering a new application, make sure you save the private key. This library does not (currently) take care of this for you. +* Make sure your saved private key is ASCII (not UTF-8, no BOM) - http://stackoverflow.com/questions/1068650/using-awk-to-remove-the-byte-order-mark + # 1.0.0 (2016-03-19) * Initial release with nuget package \ No newline at end of file diff --git a/NetFrameworkSamples.sln b/NetFrameworkSamples.sln new file mode 100644 index 000000000..345ad08ff --- /dev/null +++ b/NetFrameworkSamples.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Nexmo.Web.Sample", "Nexmo.Web.Sample\Nexmo.Web.Sample.csproj", "{3B32C093-EBED-4D60-B2FC-046F8FCE11D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + Release452|Any CPU = Release452|Any CPU + Release46|Any CPU = Release46|Any CPU + Release461|Any CPU = Release461|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release|Any CPU.Build.0 = Release|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release452|Any CPU.ActiveCfg = Release452|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release452|Any CPU.Build.0 = Release452|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release46|Any CPU.ActiveCfg = Release46|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release46|Any CPU.Build.0 = Release46|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release461|Any CPU.ActiveCfg = Release461|Any CPU + {3B32C093-EBED-4D60-B2FC-046F8FCE11D9}.Release461|Any CPU.Build.0 = Release461|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/Nexmo.Api.Test.Integration/AccountTest.cs b/Nexmo.Api.Test.Integration/AccountTest.cs index 726dec1b5..d20a58f14 100644 --- a/Nexmo.Api.Test.Integration/AccountTest.cs +++ b/Nexmo.Api.Test.Integration/AccountTest.cs @@ -1,5 +1,4 @@ -using System.Configuration; -using System.Linq; +using System.Linq; using NUnit.Framework; namespace Nexmo.Api.Test.Integration @@ -44,7 +43,7 @@ public void should_get_numbers() { var response = Account.GetNumbers(); Assert.AreEqual(1, response.count); - Assert.AreEqual(ConfigurationManager.AppSettings["nexmo_number"], response.numbers[0].msisdn); + Assert.AreEqual(Configuration.Instance.Settings["nexmo_number"], response.numbers[0].msisdn); } } } \ No newline at end of file diff --git a/Nexmo.Api.Test.Integration/CallTest.cs b/Nexmo.Api.Test.Integration/CallTest.cs index c75b1d237..2a3018ec3 100644 --- a/Nexmo.Api.Test.Integration/CallTest.cs +++ b/Nexmo.Api.Test.Integration/CallTest.cs @@ -1,5 +1,4 @@ -using System.Configuration; -using Nexmo.Api.Voice; +using Nexmo.Api.Voice; using NUnit.Framework; namespace Nexmo.Api.Test.Integration @@ -17,13 +16,13 @@ public void should_call() new Call.Endpoint { type = "phone", - number = ConfigurationManager.AppSettings["test_number"] + number = Configuration.Instance.Settings["test_number"] } }, from = new Call.Endpoint { type = "phone", - number = ConfigurationManager.AppSettings["nexmo_number"] + number = Configuration.Instance.Settings["nexmo_number"] }, answer_url = new[] { @@ -69,13 +68,13 @@ public void should_call_then_stream_then_end_stream() new Call.Endpoint { type = "phone", - number = ConfigurationManager.AppSettings["test_number"] + number = Configuration.Instance.Settings["test_number"] } }, from = new Call.Endpoint { type = "phone", - number = ConfigurationManager.AppSettings["nexmo_number"] + number = Configuration.Instance.Settings["nexmo_number"] }, answer_url = new[] { diff --git a/Nexmo.Api.Test.Integration/Nexmo.Api.Test.Integration.csproj b/Nexmo.Api.Test.Integration/Nexmo.Api.Test.Integration.csproj index 8eff2426e..deab146c7 100644 --- a/Nexmo.Api.Test.Integration/Nexmo.Api.Test.Integration.csproj +++ b/Nexmo.Api.Test.Integration/Nexmo.Api.Test.Integration.csproj @@ -51,13 +51,59 @@ MinimumRecommendedRules.ruleset + + ..\packages\jose-jwt.2.0.1\lib\net40\jose-jwt.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.1.0.0\lib\netstandard1.1\Microsoft.Extensions.Configuration.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.Abstractions.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.FileExtensions.1.0.0\lib\net451\Microsoft.Extensions.Configuration.FileExtensions.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.Json.1.0.0\lib\net451\Microsoft.Extensions.Configuration.Json.dll + True + + + ..\packages\Microsoft.Extensions.FileProviders.Abstractions.1.0.0\lib\netstandard1.0\Microsoft.Extensions.FileProviders.Abstractions.dll + True + + + ..\packages\Microsoft.Extensions.FileProviders.Physical.1.0.0\lib\net451\Microsoft.Extensions.FileProviders.Physical.dll + True + + + ..\packages\Microsoft.Extensions.FileSystemGlobbing.1.0.0\lib\net451\Microsoft.Extensions.FileSystemGlobbing.dll + True + + + ..\packages\Microsoft.Extensions.Primitives.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + True + + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\Nexmo.Csharp.Client.2.0.0\lib\net452\Nexmo.Api.dll + True + ..\packages\NUnit.3.2.0\lib\net45\nunit.framework.dll True - + + + @@ -77,17 +123,14 @@ - - App.config + + settings.json + + + settings.json.example - - - {EE7DA727-2AE3-4C76-809A-56B7433004FE} - Nexmo.Api - - - \ No newline at end of file diff --git a/Nexmo.Api/Nexmo.Api.nuspec b/Nexmo.Api/Nexmo.Api.nuspec index c89b4cca8..40740482c 100644 --- a/Nexmo.Api/Nexmo.Api.nuspec +++ b/Nexmo.Api/Nexmo.Api.nuspec @@ -2,27 +2,54 @@ Nexmo.Csharp.Client - 1.0.0 + 2.0.0 Nexmo API Client Nexmo Nexmo - https://github.com/Nexmo/csharp-client/blob/master/LICENSE.md - https://github.com/Nexmo/csharp-client + https://github.com/Nexmo/nexmo-dotnet/blob/master/LICENSE.md + https://github.com/Nexmo/nexmo-dotnet https://dashboard.nexmo.com/apple-touch-icon.png false - Official C#/.NET wrapper for the nexmo API - Initial release. + Official C#/.NET wrapper for the Nexmo API + + 2.0.0: + * Dependency marking for netstandard1.6 + RC2: + * Fix JWT generation (key import fail) on OSX/Linux + RC1: + * NumberInsight basic + standard support + * NumberVerify control call + * JWT token generation + * Application API support + * Application-based call API support + * .NET Standard 1.6 support + * [BREAKING] Moved configuration from app.config to settings.json + * [BREAKING] Nexmo.Api.Voice static class has been deprecated - you must move to the new Voice calls inside the new Nexmo.Api.Voice namespace. See [the Nexmo docs](https://docs.nexmo.com/voice/voice-api) for details. + © Nexmo 2016 SMS voice telephony phone nexmo - + + + + + + + + + + + + - - - + + + + + \ No newline at end of file diff --git a/Nexmo.Api/Nexmo.Api.xproj b/Nexmo.Api/Nexmo.Api.xproj new file mode 100644 index 000000000..a86db5f5c --- /dev/null +++ b/Nexmo.Api/Nexmo.Api.xproj @@ -0,0 +1,22 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 41ef17c9-8d2e-4c7b-b2ee-3bd1a4cc1a1b + Nexmo.Api + .\obj + .\bin\ + + + 2.0 + + + + + + + \ No newline at end of file diff --git a/Nexmo.Api/PemParse.cs b/Nexmo.Api/PemParse.cs index 45e5ffaa0..5995dde7d 100644 --- a/Nexmo.Api/PemParse.cs +++ b/Nexmo.Api/PemParse.cs @@ -51,6 +51,7 @@ THE SOFTWARE. using System; using System.IO; +using System.Runtime.InteropServices; using System.Security.Cryptography; using System.Text; @@ -61,7 +62,7 @@ public class PemParse private const string pemprivheader = "-----BEGIN RSA PRIVATE KEY-----"; private const string pemprivfooter = "-----END RSA PRIVATE KEY-----"; - public static RSACryptoServiceProvider DecodePEMKey(string pemstr) + public static RSA DecodePEMKey(string pemstr) { if (!pemstr.StartsWith(pemprivheader) || !pemstr.EndsWith(pemprivfooter)) return null; var pemprivatekey = DecodeOpenSSLPrivateKey(pemstr); @@ -145,93 +146,120 @@ private static byte[] DecodeOpenSSLPrivateKey(string instr) //////////} } - public static RSACryptoServiceProvider DecodeRSAPrivateKey(byte[] privkey) + public static RSA DecodeRSAPrivateKey(byte[] privkey) { byte[] MODULUS, E, D, P, Q, DP, DQ, IQ; // --------- Set up stream to decode the asn.1 encoded RSA private key ------ - var mem = new MemoryStream(privkey); - var binr = new BinaryReader(mem); //wrap Memory Stream with BinaryReader for easy reading - byte bt = 0; - ushort twobytes = 0; - var elems = 0; - try + using (var mem = new MemoryStream(privkey)) + using (var binr = new BinaryReader(mem)) //wrap Memory Stream with BinaryReader for easy reading { - twobytes = binr.ReadUInt16(); - if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) - binr.ReadByte(); //advance 1 byte - else if (twobytes == 0x8230) - binr.ReadInt16(); //advance 2 bytes - else - return null; - - twobytes = binr.ReadUInt16(); - if (twobytes != 0x0102) //version number - return null; - bt = binr.ReadByte(); - if (bt != 0x00) + byte bt = 0; + ushort twobytes = 0; + var elems = 0; + try + { + twobytes = binr.ReadUInt16(); + if (twobytes == 0x8130) //data read as little endian order (actual data order for Sequence is 30 81) + binr.ReadByte(); //advance 1 byte + else if (twobytes == 0x8230) + binr.ReadInt16(); //advance 2 bytes + else + return null; + + twobytes = binr.ReadUInt16(); + if (twobytes != 0x0102) //version number + return null; + bt = binr.ReadByte(); + if (bt != 0x00) + return null; + + + //------ all private key components are Integer sequences ---- + elems = GetIntegerSize(binr); + MODULUS = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + E = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + D = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + P = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + Q = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DP = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + DQ = binr.ReadBytes(elems); + + elems = GetIntegerSize(binr); + IQ = binr.ReadBytes(elems); + + //Console.WriteLine("showing components .."); + //if (verbose) + //{ + // showBytes("\nModulus", MODULUS); + // showBytes("\nExponent", E); + // showBytes("\nD", D); + // showBytes("\nP", P); + // showBytes("\nQ", Q); + // showBytes("\nDP", DP); + // showBytes("\nDQ", DQ); + // showBytes("\nIQ", IQ); + //} + + // ------- create RSACryptoServiceProvider instance and initialize with public key ----- +#if NETSTANDARD1_6 + RSA RSA; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) || + RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + RSA = new RSAOpenSsl(); + } + else + { + RSA = new RSACng(); + } + var RSAparams = new RSAParameters + { + Modulus = MODULUS, + Exponent = E, + D = D, + P = P, + Q = Q, + DP = DP, + DQ = DQ, + InverseQ = IQ + }; + RSA.ImportParameters(RSAparams); + return RSA; +#else + var RSA = new RSACryptoServiceProvider(); + var RSAparams = new RSAParameters + { + Modulus = MODULUS, + Exponent = E, + D = D, + P = P, + Q = Q, + DP = DP, + DQ = DQ, + InverseQ = IQ + }; + RSA.ImportParameters(RSAparams); + return RSA; +#endif + } + catch (Exception) + { + // TODO: log this! return null; - - - //------ all private key components are Integer sequences ---- - elems = GetIntegerSize(binr); - MODULUS = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - E = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - D = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - P = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - Q = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DP = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - DQ = binr.ReadBytes(elems); - - elems = GetIntegerSize(binr); - IQ = binr.ReadBytes(elems); - - //Console.WriteLine("showing components .."); - //if (verbose) - //{ - // showBytes("\nModulus", MODULUS); - // showBytes("\nExponent", E); - // showBytes("\nD", D); - // showBytes("\nP", P); - // showBytes("\nQ", Q); - // showBytes("\nDP", DP); - // showBytes("\nDQ", DQ); - // showBytes("\nIQ", IQ); - //} - - // ------- create RSACryptoServiceProvider instance and initialize with public key ----- - var RSA = new RSACryptoServiceProvider(); - var RSAparams = new RSAParameters(); - RSAparams.Modulus = MODULUS; - RSAparams.Exponent = E; - RSAparams.D = D; - RSAparams.P = P; - RSAparams.Q = Q; - RSAparams.DP = DP; - RSAparams.DQ = DQ; - RSAparams.InverseQ = IQ; - RSA.ImportParameters(RSAparams); - return RSA; - } - catch (Exception) - { - return null; - } - finally - { - binr.Close(); + } } } diff --git a/Nexmo.Api/Properties/AssemblyInfo.cs b/Nexmo.Api/Properties/AssemblyInfo.cs index 41fd72c6e..4d9b61b32 100644 --- a/Nexmo.Api/Properties/AssemblyInfo.cs +++ b/Nexmo.Api/Properties/AssemblyInfo.cs @@ -34,5 +34,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/Nexmo.Api/Request/ApiRequest.cs b/Nexmo.Api/Request/ApiRequest.cs index a75645f02..dc2d48193 100644 --- a/Nexmo.Api/Request/ApiRequest.cs +++ b/Nexmo.Api/Request/ApiRequest.cs @@ -1,44 +1,36 @@ using System; using System.Collections.Generic; -using System.Configuration; using System.IO; +using System.Linq; using System.Net; +using System.Reflection; using System.Text; -using System.Web; using Newtonsoft.Json; +using System.Net.Http; namespace Nexmo.Api.Request { internal static class ApiRequest - { - private static IHttpWebRequestFactory _webRequestFactory = new NetWebRequestFactory(); - public static IHttpWebRequestFactory WebRequestFactory - { - get - { - return _webRequestFactory; - } - set { _webRequestFactory = value; } - } - + { internal static Dictionary GetParameters(object parameters) { + var paramType = parameters.GetType().GetTypeInfo(); var apiParams = new Dictionary(); - foreach (var property in parameters.GetType().GetProperties()) + foreach (var property in paramType.GetProperties()) { string jsonPropertyName = null; - if (property.GetCustomAttributes(typeof(JsonPropertyAttribute), false).Length > 0) + if (property.GetCustomAttributes(typeof(JsonPropertyAttribute), false).Any()) { jsonPropertyName = - ((JsonPropertyAttribute)property.GetCustomAttributes(typeof(JsonPropertyAttribute), false)[0]) + ((JsonPropertyAttribute)property.GetCustomAttributes(typeof(JsonPropertyAttribute), false).First()) .PropertyName; } - if (null == parameters.GetType().GetProperty(property.Name).GetValue(parameters, null)) continue; + if (null == paramType.GetProperty(property.Name).GetValue(parameters, null)) continue; apiParams.Add(string.IsNullOrEmpty(jsonPropertyName) ? property.Name : jsonPropertyName, - parameters.GetType().GetProperty(property.Name).GetValue(parameters, null).ToString()); + paramType.GetProperty(property.Name).GetValue(parameters, null).ToString()); } return apiParams; } @@ -50,11 +42,11 @@ public static Uri GetBaseUriFor(Type component, string url = null) || typeof(Application) == component || typeof(Voice.Call) == component) { - baseUri = new Uri(ConfigurationManager.AppSettings["Nexmo.Url.Api"]); + baseUri = new Uri(Configuration.Instance.Settings["Nexmo.Url.Api"]); } else { - baseUri = new Uri(ConfigurationManager.AppSettings["Nexmo.Url.Rest"]); + baseUri = new Uri(Configuration.Instance.Settings["Nexmo.Url.Rest"]); } return string.IsNullOrEmpty(url) ? baseUri : new Uri(baseUri, url); } @@ -62,11 +54,11 @@ public static Uri GetBaseUriFor(Type component, string url = null) public static string DoRequest(Uri uri, Dictionary parameters) { var sb = new StringBuilder(); - parameters.Add("api_key", ConfigurationManager.AppSettings["Nexmo.api_key"]); - parameters.Add("api_secret", ConfigurationManager.AppSettings["Nexmo.api_secret"]); + parameters.Add("api_key", Configuration.Instance.Settings["Nexmo.api_key"]); + parameters.Add("api_secret", Configuration.Instance.Settings["Nexmo.api_secret"]); foreach (var key in parameters.Keys) { - sb.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(parameters[key])); + sb.AppendFormat("{0}={1}&", WebUtility.UrlEncode(key), WebUtility.UrlEncode(parameters[key])); } return DoRequest(new Uri(uri, "?" + sb)); @@ -76,12 +68,12 @@ public static StringBuilder GetQueryStringBuilderFor(object parameters) { var apiParams = GetParameters(parameters); - apiParams.Add("api_key", ConfigurationManager.AppSettings["Nexmo.api_key"]); - apiParams.Add("api_secret", ConfigurationManager.AppSettings["Nexmo.api_secret"]); + apiParams.Add("api_key", Configuration.Instance.Settings["Nexmo.api_key"]); + apiParams.Add("api_secret", Configuration.Instance.Settings["Nexmo.api_secret"]); var sb = new StringBuilder(); foreach (var key in apiParams.Keys) { - sb.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(apiParams[key])); + sb.AppendFormat("{0}={1}&", WebUtility.UrlEncode(key), WebUtility.UrlEncode(apiParams[key])); } return sb; } @@ -95,62 +87,71 @@ public static string DoRequest(Uri uri, object parameters) public static string DoRequest(Uri uri) { - var req = _webRequestFactory.CreateHttp(uri); - var resp = req.GetResponse(); - string json; - using (var sr = new StreamReader(resp.GetResponseStream())) - { - json = sr.ReadToEnd(); - } + var req = new HttpRequestMessage + { + RequestUri = uri, + Method = HttpMethod.Get, + }; + + var sendTask = Configuration.Instance.Client.SendAsync(req); + sendTask.Wait(); + var readTask = sendTask.Result.Content.ReadAsStreamAsync(); + readTask.Wait(); + string json; + using (var sr = new StreamReader(readTask.Result)) + { + json = sr.ReadToEnd(); + } return json; } - public static NexmoResponse DoRequest(string method, Uri uri, Dictionary parameters) + private static NexmoResponse DoRequest(string method, Uri uri, Dictionary parameters) { var sb = new StringBuilder(); // if parameters is null, assume that key and secret have been taken care of if (null != parameters) { - parameters.Add("api_key", ConfigurationManager.AppSettings["Nexmo.api_key"]); - parameters.Add("api_secret", ConfigurationManager.AppSettings["Nexmo.api_secret"]); + parameters.Add("api_key", Configuration.Instance.Settings["Nexmo.api_key"]); + parameters.Add("api_secret", Configuration.Instance.Settings["Nexmo.api_secret"]); foreach (var key in parameters.Keys) { - sb.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(parameters[key])); + sb.AppendFormat("{0}={1}&", WebUtility.UrlEncode(key), WebUtility.UrlEncode(parameters[key])); } } - var req = _webRequestFactory.CreateHttp(uri); - - req.Method = method; - var data = Encoding.ASCII.GetBytes(sb.ToString()); + var req = new HttpRequestMessage + { + RequestUri = uri, + Method = new HttpMethod(method), + }; - req.ContentType = "application/x-www-form-urlencoded"; - req.ContentLength = data.Length; - var requestStream = req.GetRequestStream(); - requestStream.Write(data, 0, data.Length); - requestStream.Close(); + var data = Encoding.ASCII.GetBytes(sb.ToString()); + req.Content = new ByteArrayContent(data); + req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/x-www-form-urlencoded"); - try - { - var resp = req.GetResponse(); - string json; - using (var sr = new StreamReader(resp.GetResponseStream())) - { - json = sr.ReadToEnd(); - } + var sendTask = Configuration.Instance.Client.SendAsync(req); + sendTask.Wait(); + + if (!sendTask.Result.IsSuccessStatusCode) + { return new NexmoResponse { - Status = resp.GetResponseStatusCode(), - JsonResponse = json - }; - } - catch (WebException ex) - { - return new NexmoResponse - { - Status = ((HttpWebResponse)ex.Response).StatusCode - }; - } + Status = sendTask.Result.StatusCode + }; + } + + string json; + var readTask = sendTask.Result.Content.ReadAsStreamAsync(); + readTask.Wait(); + using (var sr = new StreamReader(readTask.Result)) + { + json = sr.ReadToEnd(); + } + return new NexmoResponse + { + Status = sendTask.Result.StatusCode, + JsonResponse = json + }; } public static NexmoResponse DoPostRequest(Uri uri, object parameters) @@ -158,16 +159,6 @@ public static NexmoResponse DoPostRequest(Uri uri, object parameters) var apiParams = GetParameters(parameters); return DoPostRequest(uri, apiParams); } - public static NexmoResponse DoPutRequest(Uri uri, object parameters) - { - var apiParams = GetParameters(parameters); - return DoPutRequest(uri, apiParams); - } - public static NexmoResponse DoDeleteRequest(Uri uri, object parameters) - { - var apiParams = GetParameters(parameters); - return DoDeleteRequest(uri, apiParams); - } public static NexmoResponse DoPostRequest(Uri uri, Dictionary parameters) => DoRequest("POST", uri, parameters); public static NexmoResponse DoPutRequest(Uri uri, Dictionary parameters) => DoRequest("PUT", uri, parameters); diff --git a/Nexmo.Api/Request/IHttpWebRequest.cs b/Nexmo.Api/Request/IHttpWebRequest.cs deleted file mode 100644 index cfdc57d30..000000000 --- a/Nexmo.Api/Request/IHttpWebRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.IO; - -namespace Nexmo.Api.Request -{ - public interface IHttpWebRequest - { - string Method { get; set; } - string ContentType { get; set; } - long ContentLength { get; set; } - IWebResponse GetResponse(); - Stream GetRequestStream(); - void SetBearerToken(string token); - } -} \ No newline at end of file diff --git a/Nexmo.Api/Request/IHttpWebRequestFactory.cs b/Nexmo.Api/Request/IHttpWebRequestFactory.cs deleted file mode 100644 index 9e260abe1..000000000 --- a/Nexmo.Api/Request/IHttpWebRequestFactory.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System; - -namespace Nexmo.Api.Request -{ - public interface IHttpWebRequestFactory - { - IHttpWebRequest CreateHttp(Uri uri); - } -} diff --git a/Nexmo.Api/Request/IWebResponse.cs b/Nexmo.Api/Request/IWebResponse.cs deleted file mode 100644 index 69ba54aba..000000000 --- a/Nexmo.Api/Request/IWebResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.IO; -using System.Net; - -namespace Nexmo.Api.Request -{ - public interface IWebResponse - { - Stream GetResponseStream(); - HttpStatusCode GetResponseStatusCode(); - } -} \ No newline at end of file diff --git a/Nexmo.Api/Request/NetWebRequestFactory.cs b/Nexmo.Api/Request/NetWebRequestFactory.cs deleted file mode 100644 index 520beacb9..000000000 --- a/Nexmo.Api/Request/NetWebRequestFactory.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Net; - -namespace Nexmo.Api.Request -{ - public class NetWebRequestFactory : IHttpWebRequestFactory - { - public IHttpWebRequest CreateHttp(Uri uri) - { - return new WebRequestAdapter((HttpWebRequest) WebRequest.Create(uri)); - } - } -} \ No newline at end of file diff --git a/Nexmo.Api/Request/VersionedApiRequest.cs b/Nexmo.Api/Request/VersionedApiRequest.cs index 14def087d..68dbeb639 100644 --- a/Nexmo.Api/Request/VersionedApiRequest.cs +++ b/Nexmo.Api/Request/VersionedApiRequest.cs @@ -1,25 +1,14 @@ using System; -using System.Configuration; using System.IO; using System.Net; using System.Text; -using System.Web; using Newtonsoft.Json; +using System.Net.Http; namespace Nexmo.Api.Request { internal static class VersionedApiRequest { - private static IHttpWebRequestFactory _webRequestFactory = new NetWebRequestFactory(); - public static IHttpWebRequestFactory WebRequestFactory - { - get - { - return _webRequestFactory; - } - set { _webRequestFactory = value; } - } - private static StringBuilder GetQueryStringBuilderFor(object parameters) { var apiParams = ApiRequest.GetParameters(parameters); @@ -27,20 +16,32 @@ private static StringBuilder GetQueryStringBuilderFor(object parameters) var sb = new StringBuilder(); foreach (var key in apiParams.Keys) { - sb.AppendFormat("{0}={1}&", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(apiParams[key])); + sb.AppendFormat("{0}={1}&", WebUtility.UrlEncode(key), WebUtility.UrlEncode(apiParams[key])); } return sb; } private static string DoRequest(Uri uri) { - var req = _webRequestFactory.CreateHttp(uri); + var req = new HttpRequestMessage + { + RequestUri = uri, + Method = HttpMethod.Get, + }; // attempt bearer token auth - req.SetBearerToken(Jwt.CreateToken(ConfigurationManager.AppSettings["Nexmo.Application.Id"], ConfigurationManager.AppSettings["Nexmo.Application.Key"])); + req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", + Jwt.CreateToken(Configuration.Instance.Settings["Nexmo.Application.Id"], Configuration.Instance.Settings["Nexmo.Application.Key"])); + + var sendTask = Configuration.Instance.Client.SendAsync(req); + sendTask.Wait(); + + //if (!sendTask.Result.IsSuccessStatusCode) + // throw new Exception("Error while retrieving resource."); - var resp = req.GetResponse(); string json; - using (var sr = new StreamReader(resp.GetResponseStream())) + var readTask = sendTask.Result.Content.ReadAsStreamAsync(); + readTask.Wait(); + using (var sr = new StreamReader(readTask.Result)) { json = sr.ReadToEnd(); } @@ -49,39 +50,42 @@ private static string DoRequest(Uri uri) public static NexmoResponse DoRequest(string method, Uri uri, object payload) { - var req = _webRequestFactory.CreateHttp(uri); + var req = new HttpRequestMessage + { + RequestUri = uri, + Method = new HttpMethod(method), + }; // attempt bearer token auth - req.SetBearerToken(Jwt.CreateToken(ConfigurationManager.AppSettings["Nexmo.Application.Id"], ConfigurationManager.AppSettings["Nexmo.Application.Key"])); + req.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", + Jwt.CreateToken(Configuration.Instance.Settings["Nexmo.Application.Id"], Configuration.Instance.Settings["Nexmo.Application.Key"])); - req.Method = method; - req.ContentType = "application/json"; var data = Encoding.ASCII.GetBytes(JsonConvert.SerializeObject(payload)); - req.ContentLength = data.Length; - var requestStream = req.GetRequestStream(); - requestStream.Write(data, 0, data.Length); - requestStream.Close(); + req.Content = new ByteArrayContent(data); + req.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"); - try + var sendTask = Configuration.Instance.Client.SendAsync(req); + sendTask.Wait(); + + if (!sendTask.Result.IsSuccessStatusCode) { - var resp = req.GetResponse(); - string json; - using (var sr = new StreamReader(resp.GetResponseStream())) - { - json = sr.ReadToEnd(); - } return new NexmoResponse { - Status = resp.GetResponseStatusCode(), - JsonResponse = json + Status = sendTask.Result.StatusCode }; } - catch (WebException ex) + + string json; + var readTask = sendTask.Result.Content.ReadAsStreamAsync(); + readTask.Wait(); + using (var sr = new StreamReader(readTask.Result)) { - return new NexmoResponse - { - Status = ((HttpWebResponse)ex.Response).StatusCode - }; + json = sr.ReadToEnd(); } + return new NexmoResponse + { + Status = sendTask.Result.StatusCode, + JsonResponse = json + }; } public static string DoRequest(Uri uri, object parameters) diff --git a/Nexmo.Api/Request/WebRequestAdapter.cs b/Nexmo.Api/Request/WebRequestAdapter.cs deleted file mode 100644 index 49946031b..000000000 --- a/Nexmo.Api/Request/WebRequestAdapter.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.IO; -using System.Net; - -namespace Nexmo.Api.Request -{ - public class WebRequestAdapter : IHttpWebRequest - { - private readonly WebRequest _request; - - public WebRequestAdapter(WebRequest request) - { - _request = request; - } - - public string Method - { - get { return _request.Method; } - set { _request.Method = value; } - } - public string ContentType - { - get { return _request.ContentType; } - set { _request.ContentType = value; } - } - public long ContentLength - { - get { return _request.ContentLength; } - set { _request.ContentLength = value; } - } - - public IWebResponse GetResponse() - { - return new WebResponseAdapter(_request.GetResponse()); - } - - public Stream GetRequestStream() - { - return _request.GetRequestStream(); - } - - public void SetBearerToken(string token) - { - _request.Headers.Add("Authorization", $"Bearer {token}"); - } - } -} \ No newline at end of file diff --git a/Nexmo.Api/Request/WebResponseAdapter.cs b/Nexmo.Api/Request/WebResponseAdapter.cs deleted file mode 100644 index 31339231b..000000000 --- a/Nexmo.Api/Request/WebResponseAdapter.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.IO; -using System.Net; - -namespace Nexmo.Api.Request -{ - public class WebResponseAdapter : IWebResponse - { - private readonly WebResponse _response; - - public WebResponseAdapter(WebResponse response) - { - _response = response; - } - - public Stream GetResponseStream() - { - return _response.GetResponseStream(); - } - - public HttpStatusCode GetResponseStatusCode() - { - return ((HttpWebResponse) _response).StatusCode; - } - } -} \ No newline at end of file diff --git a/Nexmo.Api/SMS.cs b/Nexmo.Api/SMS.cs index c1121dbc6..30fe38fd8 100644 --- a/Nexmo.Api/SMS.cs +++ b/Nexmo.Api/SMS.cs @@ -190,7 +190,7 @@ public static SMSResponse Send(SMSRequest request) { if (string.IsNullOrEmpty(request.from)) { - request.from = System.Configuration.ConfigurationManager.AppSettings["Nexmo.sender_id"]; + request.from = Configuration.Instance.Settings["Nexmo.sender_id"]; } var jsonstring = ApiRequest.DoRequest(ApiRequest.GetBaseUriFor(typeof(SMSResponse), "/sms/json"), request); diff --git a/Nexmo.Api/ShortCode.cs b/Nexmo.Api/ShortCode.cs index 76928c0f8..c291e7429 100644 --- a/Nexmo.Api/ShortCode.cs +++ b/Nexmo.Api/ShortCode.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Web; using Newtonsoft.Json; using Nexmo.Api.Request; @@ -43,7 +42,7 @@ public static SMS.SMSResponse RequestAlert(AlertRequest request, Dictionary - - - - \ No newline at end of file diff --git a/Nexmo.Api/project.json b/Nexmo.Api/project.json new file mode 100644 index 000000000..e810b5bdf --- /dev/null +++ b/Nexmo.Api/project.json @@ -0,0 +1,32 @@ +{ + "version": "1.0.0-*", + + "dependencies": { + "Microsoft.Extensions.Configuration": "1.0.*", + "Microsoft.Extensions.Configuration.Json": "1.0.*", + "System.Net.Http": "4.*", + "System.Runtime.Extensions": "4.*", + + "Newtonsoft.Json": "9.0.*", + "jose-jwt": "2.0.*" + }, + + "frameworks": { + "net452": { + }, + "net46": { + }, + "net461": { + }, + "net462": { + }, + "netstandard1.6": { + "dependencies": { + "NETStandard.Library": "1.6.0", + "System.Security.Cryptography.Algorithms": "4.2.0", + "System.Security.Cryptography.OpenSsl": "4.0.0", + "System.Security.Cryptography.Cng": "4.2.0" + } + } + } +} \ No newline at end of file diff --git a/Nexmo.Samples.Coverage/Nexmo.Samples.Coverage.xproj b/Nexmo.Samples.Coverage/Nexmo.Samples.Coverage.xproj new file mode 100644 index 000000000..11063449c --- /dev/null +++ b/Nexmo.Samples.Coverage/Nexmo.Samples.Coverage.xproj @@ -0,0 +1,19 @@ + + + + 14.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 797bd6e3-6749-4b10-81bc-2587f635b94a + Nexmo.Samples.Coverage + .\obj + .\bin\ + v4.5.2 + + + 2.0 + + + \ No newline at end of file diff --git a/Nexmo.Samples.Coverage/Program.cs b/Nexmo.Samples.Coverage/Program.cs new file mode 100644 index 000000000..39e21cd2e --- /dev/null +++ b/Nexmo.Samples.Coverage/Program.cs @@ -0,0 +1,98 @@ +using System; +using System.Linq; +using Nexmo.Api; +using Nexmo.Api.Voice; + +namespace Nexmo.Samples.Coverage +{ + public class Program + { + public static void Main(string[] args) + { + ApiRequestCoverageTest(); + + //RemoveUnusedApplications(); + } + + /// + /// This is a quick sanity check with 90%+ code coverage of the ApiRequest classes + /// + private static void ApiRequestCoverageTest() + { + Console.WriteLine("Account.GetBalance() = {0}", Account.GetBalance()); + Console.WriteLine("Account.GetNumbers() = {0}", Account.GetNumbers().count); + + ///// + + var niResp = NumberInsight.Request(new NumberInsight.NumberInsightRequest + { + Number = Configuration.Instance.Settings["test_number"], + Callback = "https://abcdefg.ngrok.io/ni/" + }); + Console.WriteLine("NumberInsight.Request() = {0}", niResp.status); + + ///// + + var appRequest = new ApplicationRequest + { + name = "coveragetest", + type = "voice", + answer_url = "https://abcdefg.ngrok.io/api/voice", + event_url = "https://abcdefg.ngrok.io/api/voice", + }; + + var appResp = Application.Create(appRequest); + Console.WriteLine("Application.Create() = {0}", appResp.id); + + var appList = Application.List(); + Console.WriteLine("Application.List() = {0}", appList.Count); + var filteredAppList = Application.List(10, 0, appResp.id); + Console.WriteLine("Application.List(10, 0, appResp.id) = {0}", filteredAppList.Count); + appRequest.id = appResp.id; + appRequest.name = "updcoveragetest"; + var appUpdateResp = Application.Update(appRequest); + Console.WriteLine("Application.Update(appRequest) = {0}", appUpdateResp.name); + var isDeleted = Application.Delete(appUpdateResp.id); + Console.WriteLine("Application.Delete(id) = {0}", isDeleted); + + ///// + + var results = Call.Do(new Call.CallCommand + { + to = new[] + { + new Call.Endpoint + { + type = "phone", + number = Configuration.Instance.Settings["test_number"] + } + }, + @from = new Call.Endpoint + { + type = "phone", + number = Configuration.Instance.Settings["nexmo_number"] + }, + answer_url = new[] + { + "https://nexmo-community.github.io/ncco-examples/first_call_talk.json" + } + }); + Console.WriteLine("Call.Do() = {0}", results.status); + var callListResults = Call.List(); + Console.WriteLine("Call.List() = {0}", callListResults.count); + } + + private static void RemoveUnusedApplications() + { + var appList = Application.List(); + Console.WriteLine("BEFORE: Application.List() = {0}", appList.Count); + foreach (var app in appList.Where(a => a.id != Configuration.Instance.Settings["Nexmo.Application.Id"])) + { + Console.WriteLine(app.id); + Application.Delete(app.id); + } + Console.WriteLine("AFTER: Application.List() = {0}", Application.List().Count); + } + } +} + diff --git a/Nexmo.Samples.Coverage/Properties/AssemblyInfo.cs b/Nexmo.Samples.Coverage/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..122e642b0 --- /dev/null +++ b/Nexmo.Samples.Coverage/Properties/AssemblyInfo.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Nexmo")] +[assembly: AssemblyProduct("Nexmo.Samples.Coverage")] +[assembly: AssemblyTrademark("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("797bd6e3-6749-4b10-81bc-2587f635b94a")] diff --git a/Nexmo.Samples.Coverage/project.json b/Nexmo.Samples.Coverage/project.json new file mode 100644 index 000000000..395acbece --- /dev/null +++ b/Nexmo.Samples.Coverage/project.json @@ -0,0 +1,28 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true + }, + + "dependencies": { + "Microsoft.NETCore.App": { + //"type": "platform", + "version": "1.0.1" + }, + "Nexmo.Api": "2.0.*" + }, + + "frameworks": { + "netcoreapp1.0": { + "imports": "dnxcore50", + "dependencies": { + "NETStandard.Library": "1.6.0" + } + } + }, + "runtimes": { + "win7-x64": {}, + "osx": {}, + "linux": {} + } +} diff --git a/Nexmo.Samples.Voice.FirstTTS/App.config.example b/Nexmo.Samples.Voice.FirstTTS/App.config.example deleted file mode 100644 index 3c944d626..000000000 --- a/Nexmo.Samples.Voice.FirstTTS/App.config.example +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.csproj b/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.csproj deleted file mode 100644 index 3d2cb42e9..000000000 --- a/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.csproj +++ /dev/null @@ -1,68 +0,0 @@ - - - - - Debug - AnyCPU - {C515B039-AC1F-44F2-9703-B61B4A1DE449} - Exe - Properties - Nexmo.Samples.Voice.FirstTTS - Nexmo.Samples.Voice.FirstTTS - v4.5.2 - 512 - true - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - {EE7DA727-2AE3-4C76-809A-56B7433004FE} - Nexmo.Api - - - - - \ No newline at end of file diff --git a/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.xproj b/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.xproj new file mode 100644 index 000000000..3004e3721 --- /dev/null +++ b/Nexmo.Samples.Voice.FirstTTS/Nexmo.Samples.Voice.FirstTTS.xproj @@ -0,0 +1,19 @@ + + + + 14.0.25420 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + + + + 9668e570-8dc4-4cb2-9058-7508223cab49 + Nexmo.Samples.Voice.FirstTTS + .\obj + .\bin\ + + + + 2.0 + + + \ No newline at end of file diff --git a/Nexmo.Samples.Voice.FirstTTS/Program.cs b/Nexmo.Samples.Voice.FirstTTS/Program.cs index 8cbc011f2..c77ed6e2f 100644 --- a/Nexmo.Samples.Voice.FirstTTS/Program.cs +++ b/Nexmo.Samples.Voice.FirstTTS/Program.cs @@ -1,6 +1,6 @@ using System; -using System.Configuration; using System.Net; +using Nexmo.Api; using Nexmo.Api.Voice; namespace Nexmo.Samples.Voice.FirstTTS @@ -9,11 +9,15 @@ public class Program { private static void Main(string[] args) { + // https://github.com/dotnet/corefx/issues/4476 + // https://github.com/dotnet/corefx/issues/7623 +#if net452 ServicePointManager.ServerCertificateValidationCallback += (sender, certificate, chain, sslPolicyErrors) => true; +#endif // Set up some variables - var applicationId = ConfigurationManager.AppSettings["Nexmo.Application.Id"]; - const string phoneNumberToCall = "15555551212"; + var applicationId = Configuration.Instance.Settings["Nexmo.Application.Id"]; + var phoneNumberToCall = Configuration.Instance.Settings["test_number"]; // Make a TTS Call to a phone number @@ -34,7 +38,7 @@ private static void Main(string[] args) from = new Call.Endpoint { type = "phone", - number = "15554443333" + number = Configuration.Instance.Settings["nexmo_number"] }, answer_url = new[] { diff --git a/Nexmo.Samples.Voice.FirstTTS/Properties/AssemblyInfo.cs b/Nexmo.Samples.Voice.FirstTTS/Properties/AssemblyInfo.cs index ed0f7b950..85ebed0a8 100644 --- a/Nexmo.Samples.Voice.FirstTTS/Properties/AssemblyInfo.cs +++ b/Nexmo.Samples.Voice.FirstTTS/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("2.0.0.0")] +[assembly: AssemblyFileVersion("2.0.0.0")] diff --git a/Nexmo.Samples.Voice.FirstTTS/project.json b/Nexmo.Samples.Voice.FirstTTS/project.json new file mode 100644 index 000000000..c2940d194 --- /dev/null +++ b/Nexmo.Samples.Voice.FirstTTS/project.json @@ -0,0 +1,29 @@ +{ + "version": "1.0.0-*", + "buildOptions": { + "emitEntryPoint": true + }, + "dependencies": { + "Nexmo.Api": "2.0.*" + }, + + "frameworks": { + "net452": { + }, + "netcoreapp1.0": { + "imports": "dnxcore50", + "dependencies": { + //"NETStandard.Library": "1.6.0", + "Microsoft.NETCore.App": { + //"type": "platform", + "version": "1.0.*" + } + } + } + }, + "runtimes": { + "win7-x64": {}, + "osx": {}, + "linux": {} + } +} \ No newline at end of file diff --git a/Nexmo.Samples.Voice.FirstTTS/settings.json.example b/Nexmo.Samples.Voice.FirstTTS/settings.json.example new file mode 100644 index 000000000..06e7e819a --- /dev/null +++ b/Nexmo.Samples.Voice.FirstTTS/settings.json.example @@ -0,0 +1,9 @@ +{ + "Nexmo.Url.Rest": "https://rest.nexmo.com", + "Nexmo.Url.Api": "https://api.nexmo.com", + "Nexmo.Application.Id": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Nexmo.Application.Key": "c:\\path\\to\\your\\application\\private.key", + + "nexmo_number": "15555551212", + "test_number": "15557771212" +} \ No newline at end of file diff --git a/Nexmo.Web.Sample/Nexmo.Web.Sample.csproj b/Nexmo.Web.Sample/Nexmo.Web.Sample.csproj index 69d383c56..1a6f1aca8 100644 --- a/Nexmo.Web.Sample/Nexmo.Web.Sample.csproj +++ b/Nexmo.Web.Sample/Nexmo.Web.Sample.csproj @@ -61,6 +61,10 @@ MinimumRecommendedRules.ruleset + + ..\packages\jose-jwt.2.0.2\lib\net40\jose-jwt.dll + True + ..\packages\Microsoft.AspNet.Identity.Core.2.2.1\lib\net45\Microsoft.AspNet.Identity.Core.dll True @@ -70,6 +74,38 @@ True + + ..\packages\Microsoft.Extensions.Configuration.1.0.0\lib\netstandard1.1\Microsoft.Extensions.Configuration.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.Abstractions.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Configuration.Abstractions.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.FileExtensions.1.0.0\lib\net451\Microsoft.Extensions.Configuration.FileExtensions.dll + True + + + ..\packages\Microsoft.Extensions.Configuration.Json.1.0.0\lib\net451\Microsoft.Extensions.Configuration.Json.dll + True + + + ..\packages\Microsoft.Extensions.FileProviders.Abstractions.1.0.0\lib\netstandard1.0\Microsoft.Extensions.FileProviders.Abstractions.dll + True + + + ..\packages\Microsoft.Extensions.FileProviders.Physical.1.0.0\lib\net451\Microsoft.Extensions.FileProviders.Physical.dll + True + + + ..\packages\Microsoft.Extensions.FileSystemGlobbing.1.0.0\lib\net451\Microsoft.Extensions.FileSystemGlobbing.dll + True + + + ..\packages\Microsoft.Extensions.Primitives.1.0.0\lib\netstandard1.0\Microsoft.Extensions.Primitives.dll + True + False ..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll @@ -114,11 +150,16 @@ False ..\packages\Microsoft.Owin.Security.Twitter.3.0.1\lib\net45\Microsoft.Owin.Security.Twitter.dll - - ..\packages\Newtonsoft.Json.8.0.3\lib\net45\Newtonsoft.Json.dll + + ..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll + True + + + ..\packages\Nexmo.Csharp.Client.2.0.0\lib\net452\Nexmo.Api.dll True + @@ -227,14 +268,12 @@ + + Always + + - - - {ee7da727-2ae3-4c76-809a-56b7433004fe} - Nexmo.Api - - 10.0 $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) diff --git a/Nexmo.Web.Sample/Views/Web.config b/Nexmo.Web.Sample/Views/Web.config index 7d28e8e55..342caa3e2 100644 --- a/Nexmo.Web.Sample/Views/Web.config +++ b/Nexmo.Web.Sample/Views/Web.config @@ -9,7 +9,7 @@ - + diff --git a/Nexmo.Web.Sample/Web.config.example b/Nexmo.Web.Sample/Web.config.example index 55658d3e9..e877a0ce6 100644 --- a/Nexmo.Web.Sample/Web.config.example +++ b/Nexmo.Web.Sample/Web.config.example @@ -17,6 +17,7 @@ + @@ -41,44 +42,24 @@ - - + + - - + + - - + + - - + + - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/Nexmo.Web.Sample/packages.config b/Nexmo.Web.Sample/packages.config index 04bcb6932..ebc05d735 100644 --- a/Nexmo.Web.Sample/packages.config +++ b/Nexmo.Web.Sample/packages.config @@ -1,5 +1,6 @@  + @@ -11,6 +12,14 @@ + + + + + + + + @@ -23,6 +32,17 @@ - + + + + + + + + + + + + \ No newline at end of file diff --git a/Nexmo.Web.Sample/settings.json.example b/Nexmo.Web.Sample/settings.json.example new file mode 100644 index 000000000..780103ef0 --- /dev/null +++ b/Nexmo.Web.Sample/settings.json.example @@ -0,0 +1,12 @@ +{ + "Nexmo.Url.Rest": "https://rest.nexmo.com", + "Nexmo.Url.Api": "https://api.nexmo.com", + "Nexmo.api_key": "deadbeef", + "Nexmo.api_secret": "deadbeef", + + "Nexmo.Application.Id": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Nexmo.Application.Key": "c:\\path\\to\\your\\application\\private.key", + + "nexmo_number": "15555551212", + "test_number": "15557771212" +} \ No newline at end of file diff --git a/README.md b/README.md index da71c16ab..00535907d 100644 --- a/README.md +++ b/README.md @@ -25,20 +25,24 @@ To install the C# client library using NuGet: Alternatively: * Download or build (see developer instructions) the Nexmo.Api.dll. -* If you have downloaded a release, ensure you are referencing the -Newtonsoft.Json.dll dependency by either including it with your project's -NuGet dependencies or manually referencing it. +* If you have downloaded a release, ensure you are referencing the required dependencies by +either including them with your project's NuGet dependencies or manually referencing them. * Reference the assembly in your code. Configuration: -------------- -* Provide your API key, secret, and nexmo URLs in appSettings: +* Provide the nexmo URLs, API key, secret, and application credentials (for JWT) in ```settings.json```: -```XML - - - - +```json +{ + "Nexmo.Url.Rest": "https://rest.nexmo.com", + "Nexmo.Url.Api": "https://api.nexmo.com", + "Nexmo.api_key": "deadbeef", + "Nexmo.api_secret": "deadbeef", + + "Nexmo.Application.Id": "ffffffff-ffff-ffff-ffff-ffffffffffff", + "Nexmo.Application.Key": "c:\\path\\to\\your\\application\\private.key" +} ``` Examples @@ -48,7 +52,7 @@ The following examples show how to: * [Receive a message](#receiving-a-message) * [Initiate a call](#initiating-a-call) -### Sending A Message +### Sending a Message Use [Nexmo's SMS API][doc_sms] to send a SMS message. @@ -75,13 +79,30 @@ public ActionResult Get([FromUri]SMS.SMSDeliveryReceipt response) ### Initiating a Call Use [Nexmo's Call API][doc_voice] to initiate a voice call. + +__NOTE:__ You must have a valid Application ID and key in order to make voice calls! Use ```Nexmo.Api.Application``` to register. See the [Application API][doc_app] documentation for details. + ```C# -var result = Voice.Call(new Voice.CallCommand +using Nexmo.Api.Voice; + +Call.Do(new Call.CallCommand { - to = "17775551212", - answer_url = "https://abcdefgh.ngrok.io/content/voiceDemo.xml", - status_url = "https://abcdefgh.ngrok.io/api/voice", - from = "15555551212", + to = new[] + { + new Call.Endpoint { + type = "phone", + number = "15555551212" + } + }, + from = new Call.Endpoint + { + type = "phone", + number = "15557772424" + }, + answer_url = new[] + { + "https://nexmo-community.github.io/ncco-examples/first_call_talk.json" + } }); ``` @@ -130,16 +151,30 @@ API Coverage * [X] Event Based Alerts * [X] Sending Alerts * [X] Campaign Subscription Management -* Voice - * [X] Outbound Calls - * [X] Inbound Call - * [X] Text-To-Speech Call - * [X] Text-To-Speech Prompt +* Application + * [X] Create + * [X] List + * [X] Update + * [X] Delete +* Call + * [X] Outbound + * [X] Get + * [X] List + * [X] Edit + * [X] TTS + * [X] Stream + * [X] DTMF Contributing ------------ -We are currently targeting the 4.5.2 - 4.6.1 frameworks and using Visual Studio 2015 Update 1. +Targeted frameworks: + +* 4.5.2 +* 4.6, 4.6.1, 4.6.2 +* .NET Standard 1.6 + +Visual Studio 2015 is required (Community should be fine). Update 3 is recommended. 1. Get latest code either by cloning the repository or downloading a snapshot of the source. 2. Open "Nexmo.Api.sln" @@ -147,6 +182,14 @@ We are currently targeting the 4.5.2 - 4.6.1 frameworks and using Visual Studio Pull requests are welcome! +Thanks +------ + +Special thanks to our contributors: + +* [jdpearce](https://github.com/jdpearce) +* [jonferreira](https://github.com/jonferreira) + License ------- @@ -155,5 +198,6 @@ This library is released under the [MIT License][license] [create_account]: https://docs.nexmo.com/tools/dashboard#setting-up-your-nexmo-account [signup]: https://dashboard.nexmo.com/sign-up?utm_source=DEV_REL&utm_medium=github&utm_campaign=csharp-client-library [doc_sms]: https://docs.nexmo.com/api-ref/sms-api?utm_source=DEV_REL&utm_medium=github&utm_campaign=csharp-client-library -[doc_voice]: https://docs.nexmo.com/voice/call?utm_source=DEV_REL&utm_medium=github&utm_campaign=csharp-client-library +[doc_voice]: https://docs.nexmo.com/voice/voice-api?utm_source=DEV_REL&utm_medium=github&utm_campaign=csharp-client-library +[doc_app]: https://docs.nexmo.com/tools/application-api?utm_source=DEV_REL&utm_medium=github&utm_campaign=csharp-client-library [license]: LICENSE.md \ No newline at end of file