Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

reafactor: adapt methods visibility #543

Merged
merged 3 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 33 additions & 3 deletions Vonage/Cryptography/SmsSignatureGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
using System.Security.Cryptography;
using System.Text;
using Vonage.Request;

namespace Vonage.Cryptography;

Expand All @@ -14,7 +13,7 @@ public static string GenerateSignature(string query, string securitySecret, Meth
query += securitySecret;
var hashgen = MD5.Create();
var hash = hashgen.ComputeHash(Encoding.UTF8.GetBytes(query));
return ByteArrayToHexHelper.ByteArrayToHex(hash).ToLower();
return ByteArrayToHex(hash).ToLower();
}

var securityBytes = Encoding.UTF8.GetBytes(securitySecret);
Expand All @@ -37,7 +36,7 @@ public static string GenerateSignature(string query, string securitySecret, Meth
}

var hmac = hmacGen.ComputeHash(input);
var sig = ByteArrayToHexHelper.ByteArrayToHex(hmac).ToUpper();
var sig = ByteArrayToHex(hmac).ToUpper();
return sig;
}

Expand All @@ -49,4 +48,35 @@ public enum Method
sha256,
sha512
}

///// There is no built-in byte[] => hex string, so here's an implementation
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa/24343727#24343727
/// We're not going to going with the unchecked version. Seems overkill for now.
internal static readonly uint[] _lookup32 = CreateLookup32();

internal static uint[] CreateLookup32()
{
var result = new uint[256];
for (var i = 0; i < 256; i++)
{
var s = i.ToString("X2");
result[i] = s[0] + ((uint) s[1] << 16);
}

return result;
}

internal static string ByteArrayToHex(byte[] bytes)
{
var lookup32 = _lookup32;
var result = new char[bytes.Length * 2];
for (var i = 0; i < bytes.Length; i++)
{
var val = lookup32[bytes[i]];
result[2 * i] = (char) val;
result[2 * i + 1] = (char) (val >> 16);
}

return new string(result);
}
}
124 changes: 63 additions & 61 deletions Vonage/Request/ApiRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Epoch.net;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Vonage.Common;
Expand All @@ -17,17 +16,18 @@
using Vonage.Cryptography;
using Vonage.Logger;
using Vonage.Serialization;
using ITimeProvider = Vonage.Common.ITimeProvider;

namespace Vonage.Request;

internal record VonageResponse(string JsonResponse);

internal partial class ApiRequest
{
private readonly Maybe<Credentials> credentials;
private readonly ILogger logger;
private readonly string userAgent;
private readonly Maybe<Configuration> configuration;
private readonly ITimeProvider timeProvider;
private readonly Maybe<Configuration> configuration;
private readonly Maybe<Credentials> credentials;
private readonly string userAgent;

private ApiRequest()
{
Expand All @@ -48,54 +48,6 @@ private ApiRequest(Credentials credentials, Configuration configuration, ITimePr
this.timeProvider = provider;
}

internal static ApiRequest Build(Credentials credentials, Configuration configuration, ITimeProvider provider) => new ApiRequest(credentials, configuration, provider);

private Configuration GetConfiguration() => this.configuration.IfNone(Configuration.Instance);

public async Task<VonageResponse> DoDeleteRequestWithUrlContentAsync(Uri uri,
Dictionary<string, string> parameters, AuthType authType = AuthType.Query) =>
await this.DoRequestWithUrlContentAsync(HttpMethod.Delete, uri, parameters, authType);

public async Task<HttpResponseMessage> DoGetRequestWithJwtAsync(Uri uri)
{
var req = this.BuildMessage(uri, HttpMethod.Get);
req.Headers.Authorization = this.BuildBearerAuth();
this.logger.LogDebug("GET {Uri}", uri);
var result = await this.GetConfiguration().Client.SendAsync(req);
try
{
result.EnsureSuccessStatusCode();
return result;
}
catch (HttpRequestException ex)
{
this.logger.LogError("FAIL: {StatusCode}", result.StatusCode);
throw new VonageHttpRequestException(ex) { HttpStatusCode = result.StatusCode };
}
}

public async Task<T> DoGetRequestWithQueryParametersAsync<T>(Uri uri, AuthType authType,
object parameters = null)
{
parameters ??= new Dictionary<string, string>();
var sb = this.GetQueryStringBuilderFor(parameters, authType);
var requestUri = new Uri(uri + (sb.Length != 0 ? "?" + sb : ""));
return await this.SendGetRequestAsync<T>(requestUri, authType);
}

public async Task<T> DoPostRequestUrlContentFromObjectAsync<T>(Uri uri, object parameters,
bool withCredentials = true) =>
await this.DoPostRequestWithUrlContentAsync<T>(uri, GetParameters(parameters), withCredentials);

public Task<T> DoRequestWithJsonContentAsync<T>(HttpMethod method, Uri uri, object payload,
AuthType authType) =>
this.DoRequestWithJsonContentAsync(method, uri, payload, authType,
value => JsonConvert.SerializeObject(value, VonageSerialization.SerializerSettings),
JsonConvert.DeserializeObject<T>);

public static Uri GetBaseUri(UriType uriType, string url = null) =>
string.IsNullOrEmpty(url) ? BuildBaseUri(uriType) : new Uri(BuildBaseUri(uriType), url);

private static Uri BuildBaseUri(UriType uriType) =>
uriType switch
{
Expand Down Expand Up @@ -131,7 +83,10 @@ private HttpRequestMessage BuildMessage(Uri uri, HttpMethod method)

private StringBuilder BuildQueryString(IDictionary<string, string> parameters, bool withCredentials = true)
{
var method = this.credentials.Map(value => value.Method).IfNone(Enum.TryParse(this.GetConfiguration().SigningMethod, out SmsSignatureGenerator.Method output) ? output : SmsSignatureGenerator.Method.md5hash);
var method = this.credentials.Map(value => value.Method).IfNone(
Enum.TryParse(this.GetConfiguration().SigningMethod, out SmsSignatureGenerator.Method output)
? output
: SmsSignatureGenerator.Method.md5hash);
var sb = new StringBuilder();
var signatureSb = new StringBuilder();
if (withCredentials)
Expand Down Expand Up @@ -226,6 +181,8 @@ private string GetApplicationId() => this.credentials.Bind(value => value.Applic
private string GetApplicationKey() => this.credentials.Bind(value => value.ApplicationKey ?? Maybe<string>.None)
.IfNone(this.GetConfiguration().ApplicationKey);

private Configuration GetConfiguration() => this.configuration.IfNone(Configuration.Instance);

private static Dictionary<string, string> GetParameters(object parameters)
{
var json = JsonConvert.SerializeObject(parameters, VonageSerialization.SerializerSettings);
Expand All @@ -251,9 +208,11 @@ private StringBuilder GetQueryStringBuilderFor(object parameters, AuthType type,
return sb;
}

private string GetSecuritySecret() => this.credentials.Bind(value => value.SecuritySecret ?? Maybe<string>.None).IfNone(this.GetConfiguration().SecuritySecret);
private string GetSecuritySecret() => this.credentials.Bind(value => value.SecuritySecret ?? Maybe<string>.None)
.IfNone(this.GetConfiguration().SecuritySecret);

private string GetUserAgent() => this.credentials.Bind(value => value.AppUserAgent ?? Maybe<string>.None).IfNone(this.GetConfiguration().UserAgent);
private string GetUserAgent() => this.credentials.Bind(value => value.AppUserAgent ?? Maybe<string>.None)
.IfNone(this.GetConfiguration().UserAgent);

private async Task<T> SendGetRequestAsync<T>(Uri uri, AuthType authType)
{
Expand Down Expand Up @@ -291,11 +250,7 @@ private async Task<VonageResponse> SendHttpRequestAsync(HttpRequestMessage req)
{
this.logger.LogDebug("{Json}", json);
response.EnsureSuccessStatusCode();
return new VonageResponse
{
Status = response.StatusCode,
JsonResponse = json,
};
return new VonageResponse(json);
}
catch (HttpRequestException exception)
{
Expand All @@ -308,6 +263,53 @@ private async Task<VonageResponse> SendHttpRequestAsync(HttpRequestMessage req)
}
}

internal async Task<VonageResponse> DoDeleteRequestWithUrlContentAsync(Uri uri,
Dictionary<string, string> parameters, AuthType authType = AuthType.Query) =>
await this.DoRequestWithUrlContentAsync(HttpMethod.Delete, uri, parameters, authType);

internal async Task<HttpResponseMessage> DoGetRequestWithJwtAsync(Uri uri)
{
var req = this.BuildMessage(uri, HttpMethod.Get);
req.Headers.Authorization = this.BuildBearerAuth();
this.logger.LogDebug("GET {Uri}", uri);
var result = await this.GetConfiguration().Client.SendAsync(req);
try
{
result.EnsureSuccessStatusCode();
return result;
}
catch (HttpRequestException ex)
{
this.logger.LogError("FAIL: {StatusCode}", result.StatusCode);
throw new VonageHttpRequestException(ex) { HttpStatusCode = result.StatusCode };
}
}

internal async Task<T> DoGetRequestWithQueryParametersAsync<T>(Uri uri, AuthType authType,
object parameters = null)
{
parameters ??= new Dictionary<string, string>();
var sb = this.GetQueryStringBuilderFor(parameters, authType);
var requestUri = new Uri(uri + (sb.Length != 0 ? "?" + sb : ""));
return await this.SendGetRequestAsync<T>(requestUri, authType);
}

internal async Task<T> DoPostRequestUrlContentFromObjectAsync<T>(Uri uri, object parameters,
bool withCredentials = true) =>
await this.DoPostRequestWithUrlContentAsync<T>(uri, GetParameters(parameters), withCredentials);

internal Task<T> DoRequestWithJsonContentAsync<T>(HttpMethod method, Uri uri, object payload,
AuthType authType) =>
this.DoRequestWithJsonContentAsync(method, uri, payload, authType,
value => JsonConvert.SerializeObject(value, VonageSerialization.SerializerSettings),
JsonConvert.DeserializeObject<T>);

internal static Uri GetBaseUri(UriType uriType, string url = null) =>
string.IsNullOrEmpty(url) ? BuildBaseUri(uriType) : new Uri(BuildBaseUri(uriType), url);

internal static ApiRequest Build(Credentials credentials, Configuration configuration, ITimeProvider provider) =>
new(credentials, configuration, provider);

/// <summary>
/// Type of the Uri.
/// </summary>
Expand Down
80 changes: 17 additions & 63 deletions Vonage/Request/ApiRequestSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,11 @@
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Vonage.Common.Exceptions;

namespace Vonage.Request;

internal partial class ApiRequest
{
/// <summary>
/// Sends an HTTP DELETE
/// </summary>
/// <param name="uri"></param>
/// <param name="parameters"></param>
/// <returns></returns>
/// <exception cref="VonageHttpRequestException">thrown if an error is encountered when talking to the API</exception>
public VonageResponse DoDeleteRequestWithUrlContent(Uri uri, Dictionary<string, string> parameters,
AuthType authType = AuthType.Query) =>
ExecuteAsyncOperation(() => this.DoRequestWithUrlContentAsync(HttpMethod.Delete, uri, parameters, authType));

/// <summary>
/// Sends a GET request to the Vonage API using a JWT and returns the full HTTP resonse message
/// this is primarily for pulling a raw stream off an API call -e.g. a recording
/// </summary>
/// <param name="uri"></param>
/// <param name="creds"></param>
/// <returns>HttpResponseMessage</returns>
/// <exception cref="VonageHttpRequestException">thrown if an error is encountered when talking to the API</exception>
public HttpResponseMessage DoGetRequestWithJwt(Uri uri) =>
ExecuteAsyncOperation(() => this.DoGetRequestWithJwtAsync(uri));

/// <summary>
/// SendAsync a GET request to the versioned Vonage API.
/// Do not include credentials in the parameters object. If you need to override credentials, use the optional
/// Credentials parameter.
/// </summary>
/// <param name="uri">The URI to GET</param>
/// <param name="parameters">Parameters required by the endpoint (do not include credentials)</param>
/// <param name="credentials">(Optional) Overridden credentials for only this request</param>
/// <exception cref="VonageHttpRequestException">Thrown if the API encounters a non-zero result</exception>
public T DoGetRequestWithQueryParameters<T>(Uri uri, AuthType authType, object parameters = null) =>
ExecuteAsyncOperation(() => this.DoGetRequestWithQueryParametersAsync<T>(uri, authType, parameters));

/// <summary>
/// Sends a Post request to the specified endpoint with the given parameters
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="uri"></param>
/// <param name="parameters"></param>
/// <param name="creds"></param>
/// <param name="withCredentials">Indicates whether credentials should be included in Query string.</param>
/// <returns></returns>
/// <exception cref="VonageHttpRequestException">thrown if an error is encountered when talking to the API</exception>
public T DoPostRequestUrlContentFromObject<T>(Uri uri, object parameters,
bool withCredentials = true) =>
ExecuteAsyncOperation(() => this.DoPostRequestUrlContentFromObjectAsync<T>(uri, parameters, withCredentials));

/// <summary>
/// SendAsync a request to the versioned Vonage API.
/// Do not include credentials in the parameters object. If you need to override credentials, use the optional
/// Credentials parameter.
/// </summary>
/// <param name="method">HTTP method (POST, PUT, DELETE, etc)</param>
/// <param name="uri">The URI to communicate with</param>
/// <param name="payload">Parameters required by the endpoint (do not include credentials)</param>
/// <param name="authType">Authorization type used on the API</param>
/// <param name="creds">(Optional) Overridden credentials for only this request</param>
/// <exception cref="VonageHttpRequestException">thrown if an error is encountered when talking to the API</exception>
public T DoRequestWithJsonContent<T>(HttpMethod method, Uri uri, object payload, AuthType authType) =>
ExecuteAsyncOperation(() => this.DoRequestWithJsonContentAsync<T>(method, uri, payload, authType));

private static T ExecuteAsyncOperation<T>(Func<Task<T>> operation)
{
try
Expand All @@ -82,4 +19,21 @@ private static T ExecuteAsyncOperation<T>(Func<Task<T>> operation)
throw exception.InnerExceptions.First();
}
}

internal VonageResponse DoDeleteRequestWithUrlContent(Uri uri, Dictionary<string, string> parameters,
AuthType authType = AuthType.Query) =>
ExecuteAsyncOperation(() => this.DoRequestWithUrlContentAsync(HttpMethod.Delete, uri, parameters, authType));

internal HttpResponseMessage DoGetRequestWithJwt(Uri uri) =>
ExecuteAsyncOperation(() => this.DoGetRequestWithJwtAsync(uri));

internal T DoGetRequestWithQueryParameters<T>(Uri uri, AuthType authType, object parameters = null) =>
ExecuteAsyncOperation(() => this.DoGetRequestWithQueryParametersAsync<T>(uri, authType, parameters));

internal T DoPostRequestUrlContentFromObject<T>(Uri uri, object parameters,
bool withCredentials = true) =>
ExecuteAsyncOperation(() => this.DoPostRequestUrlContentFromObjectAsync<T>(uri, parameters, withCredentials));

internal T DoRequestWithJsonContent<T>(HttpMethod method, Uri uri, object payload, AuthType authType) =>
ExecuteAsyncOperation(() => this.DoRequestWithJsonContentAsync<T>(method, uri, payload, authType));
}
34 changes: 1 addition & 33 deletions Vonage/Request/ByteArrayToHexHelper.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1 @@
namespace Vonage.Request;

internal static class ByteArrayToHexHelper
{
///// There is no built-in byte[] => hex string, so here's an implementation
/// http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa/24343727#24343727
/// We're not going to going with the unchecked version. Seems overkill for now.
internal static readonly uint[] _lookup32 = CreateLookup32();

internal static uint[] CreateLookup32()
{
var result = new uint[256];
for (var i = 0; i < 256; i++)
{
var s = i.ToString("X2");
result[i] = s[0] + ((uint)s[1] << 16);
}
return result;
}

internal static string ByteArrayToHex(byte[] bytes)
{
var lookup32 = _lookup32;
var result = new char[bytes.Length * 2];
for (var i = 0; i < bytes.Length; i++)
{
var val = lookup32[bytes[i]];
result[2 * i] = (char)val;
result[2 * i + 1] = (char)(val >> 16);
}
return new string(result);
}
}
namespace Vonage.Request;
9 changes: 0 additions & 9 deletions Vonage/Request/VonageResponse.cs

This file was deleted.

Loading