Skip to content

Commit

Permalink
Use configuration for Video and Meetings, refactor Configuration (#349)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tr00d authored Feb 3, 2023
1 parent d8de61a commit 4bd1dd3
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 61 deletions.
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ Or
"Vonage.UserAgent": "myApp/1.0",
"Vonage.Url.Rest": "https://rest.nexmo.com",
"Vonage.Url.Api": "https://api.nexmo.com",
"Vonage.Meetings.Url.Api": "https://api-eu.vonage.com",
"Vonage.Video.Url.Api": "https://video.api.vonage.com",
"Vonage_key": "VONAGE-API-KEY",
"Vonage_secret": "VONAGE-API-SECRET",
"Vonage.Application.Id": "ffffffff-ffff-ffff-ffff-ffffffffffff",
Expand All @@ -120,27 +122,32 @@ Or
Or

* Access the Configuration instance and set the appropriate key in your code for example:

```cshap
Configuration.Instance.Settings["appSettings:Vonage.Url.Api"] = "https://www.example.com/api";
Configuration.Instance.Settings["appSettings:Vonage.Url.Rest"] = "https://www.example.com/rest";
Configuration.Instance.Settings["appSettings:Vonage.Meetings.Url.Api"] = "https://www.meetings.example.com/api";
Configuration.Instance.Settings["appSettings:Vonage.Video.Url.Rest"] = "https://www.video.example.com/rest";
```

> NOTE: Private Key is the literal key - not a path to the file containing the key
### Configuration Reference

Key | Description
----|------------
Vonage_key | Your API key from the [dashboard](https://dashboard.nexmo.com/settings)
Vonage_secret | Your API secret from the [dashboard](https://dashboard.nexmo.com/settings)
Vonage.Application.Id | Your application ID
Vonage.Application.Key | Your application's private key
Vonage.security_secret | Optional. This is the signing secret that's used for [signing SMS](https://developer.nexmo.com/concepts/guides/signing-messages)
Vonage.signing_method | Optional. This is the method used for signing SMS messages
Vonage.Url.Rest | Optional. Vonage REST API base URL. Defaults to https://rest.nexmo.com
Vonage.Url.Api | Optional. Vonage API base URL. Defaults to https://api.nexmo.com
Vonage.RequestsPerSecond | Optional. Throttle to specified requests per second.
Vonage.UserAgent | Optional. Your app-specific usage identifier in the format of `name/version`. Example: `"myApp/1.0"`
Key | Description
--------------------------|----------------------------------------------------------------------------------------------------------------------------------
Vonage_key | Your API key from the [dashboard](https://dashboard.nexmo.com/settings)
Vonage_secret | Your API secret from the [dashboard](https://dashboard.nexmo.com/settings)
Vonage.Application.Id | Your application ID
Vonage.Application.Key | Your application's private key
Vonage.security_secret | Optional. This is the signing secret that's used for [signing SMS](https://developer.nexmo.com/concepts/guides/signing-messages)
Vonage.signing_method | Optional. This is the method used for signing SMS messages
Vonage.Url.Rest | Optional. Vonage REST API base URL. Defaults to https://rest.nexmo.com
Vonage.Url.Api | Optional. Vonage API base URL. Defaults to https://api.nexmo.com
Vonage.Meetings.Url.Api | Optional. Vonage API base URL for Meetings. Defaults to https://api-eu.vonage.com
Vonage.Video.Url.Api | Optional. Vonage API base URL for Video. Defaults to https://video.api.vonage.com
Vonage.RequestsPerSecond | Optional. Throttle to specified requests per second.
Vonage.UserAgent | Optional. Your app-specific usage identifier in the format of `name/version`. Example: `"myApp/1.0"`

### Logging

Expand Down
6 changes: 2 additions & 4 deletions Vonage.Server/Video/VideoClient.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Net.Http;
using System.Net.Http;
using Vonage.Request;
using Vonage.Server.Video.Archives;
using Vonage.Server.Video.Moderation;
Expand All @@ -11,7 +10,6 @@ namespace Vonage.Server.Video;
/// <inheritdoc />
public class VideoClient : IVideoClient
{
private const string ApiUrl = "https://video.api.vonage.com";
private Credentials credentials;

/// <inheritdoc />
Expand Down Expand Up @@ -61,7 +59,7 @@ private static HttpClient InitializeHttpClient()
{
var client = new HttpClient(new HttpClientHandler())
{
BaseAddress = new Uri(ApiUrl),
BaseAddress = Configuration.Instance.VideoApiUrl,
};
client.DefaultRequestHeaders.Add("Accept", "application/json");
return client;
Expand Down
123 changes: 80 additions & 43 deletions Vonage/Configuration.cs
Original file line number Diff line number Diff line change
@@ -1,53 +1,62 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Vonage.Request;
using System;
using System;
using System.Collections.Generic;
using System.Net.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Vonage.Common.Monads;
using Vonage.Logger;
using Vonage.Request;

namespace Vonage
{
/// <summary>
/// Represents the SDK Configuration.
/// </summary>
public sealed class Configuration
{
private const string LoggerCategory = "Vonage.Configuration";

// Explicit static constructor to tell C# compiler
// not to mark type as beforefieldinit

private static Maybe<double> RequestsPerSecond =>
double.TryParse(Instance.Settings["appSettings:Vonage.RequestsPerSecond"], out var requestsPerSecond)
? requestsPerSecond
: Maybe<double>.None;

static Configuration()
{
}

private Configuration()
{
var logger = Logger.LogProvider.GetLogger(LoggerCategory);
var logger = LogProvider.GetLogger(LoggerCategory);
var builder = new ConfigurationBuilder()
.AddInMemoryCollection(new Dictionary<string, string>
{
{ "appSettings:Vonage.Url.Rest", "https://rest.nexmo.com"},
{ "appSettings:Vonage.Url.Api", "https://api.nexmo.com"},
{ "appSettings:Vonage.EnsureSuccessStatusCode", "false" }
})
.AddJsonFile("settings.json", true, true)
.AddJsonFile("appsettings.json", true, true)
;

Settings = builder.Build();
.AddInMemoryCollection(new Dictionary<string, string>
{
{"appSettings:Vonage.Url.Rest", "https://rest.nexmo.com"},
{"appSettings:Vonage.Url.Api", "https://api.nexmo.com"},
{"appSettings:Vonage.Meetings.Url.Api", "https://api-eu.vonage.com"},
{"appSettings:Vonage.Video.Url.Api", "https://video.api.vonage.com"},
{"appSettings:Vonage.EnsureSuccessStatusCode", "false"},
})
.AddJsonFile("settings.json", true, true)
.AddJsonFile("appsettings.json", true, true)
;
this.Settings = builder.Build();

// verify we have a minimum amount of configuration

var authCapabilities = new List<string>();

if (!string.IsNullOrWhiteSpace(Settings["appSettings:Vonage_key"]) &&
!string.IsNullOrWhiteSpace(Settings["appSettings:Vonage_secret"]))
if (!string.IsNullOrWhiteSpace(this.Settings["appSettings:Vonage_key"]) &&
!string.IsNullOrWhiteSpace(this.Settings["appSettings:Vonage_secret"]))
{
authCapabilities.Add("Key/Secret");
}
if (!string.IsNullOrWhiteSpace(Settings["appSettings:Vonage.security_secret"]))

if (!string.IsNullOrWhiteSpace(this.Settings["appSettings:Vonage.security_secret"]))
{
authCapabilities.Add("Security/Signing");
}
if (!string.IsNullOrWhiteSpace(Settings["appSettings:Vonage.Application.Id"]) &&
!string.IsNullOrWhiteSpace(Settings["appSettings:Vonage.Application.Key"]))

if (!string.IsNullOrWhiteSpace(this.Settings["appSettings:Vonage.Application.Id"]) &&
!string.IsNullOrWhiteSpace(this.Settings["appSettings:Vonage.Application.Key"]))
{
authCapabilities.Add("Application");
}
Expand All @@ -62,27 +71,55 @@ private Configuration()
}
}

public static Configuration Instance { get; } = new Configuration();
/// <summary>
/// Retrieves a configured HttpClient.
/// </summary>
public HttpClient Client =>
RequestsPerSecond
.Map(BuildSemaphore)
.Map(this.GetThrottlingMessageHandler)
.Match(some => new HttpClient(some), this.BuildDefaultClient);

public IConfiguration Settings { get; }

/// <summary>
/// Exposes an HttpMessageHandler.
/// </summary>
public HttpMessageHandler ClientHandler { get; set; }

private HttpClient _client;
public HttpClient Client
/// <summary>
/// Retrieves the unique instance (Singleton).
/// </summary>
public static Configuration Instance { get; } = new();

/// <summary>
/// Retrieves the Meetings Api Url.
/// </summary>
public Uri MeetingsApiUrl => new(this.Settings["appSettings:Vonage.Meetings.Url.Api"] ?? string.Empty);

/// <summary>
/// Exposes the configuration's content.
/// </summary>
public IConfiguration Settings { get; }

/// <summary>
/// Retrieves the Video Api Url.
/// </summary>
public Uri VideoApiUrl => new(this.Settings["appSettings:Vonage.Video.Url.Api"] ?? string.Empty);

private HttpClient BuildDefaultClient() =>
this.ClientHandler == null
? new HttpClient()
: new HttpClient(this.ClientHandler);

private static TimeSpanSemaphore BuildSemaphore(double requestsPerSecond)
{
get
{
var reqPerSec = Instance.Settings["appSettings:Vonage.RequestsPerSecond"];
if (string.IsNullOrEmpty(reqPerSec))
return _client ?? (_client = ClientHandler == null ? new HttpClient(): new HttpClient(ClientHandler));

var delay = 1 / double.Parse(reqPerSec);
var execTimeSpanSemaphore = new TimeSpanSemaphore(1, TimeSpan.FromSeconds(delay));
// TODO: this messes up the unit test mock if throttle config is set
var handler = ClientHandler != null ? new ThrottlingMessageHandler(execTimeSpanSemaphore, ClientHandler) : new ThrottlingMessageHandler(execTimeSpanSemaphore);
return _client ?? (_client = new HttpClient(handler));
}
var delay = 1 / requestsPerSecond;
var execTimeSpanSemaphore = new TimeSpanSemaphore(1, TimeSpan.FromSeconds(delay));
return execTimeSpanSemaphore;
}

private ThrottlingMessageHandler GetThrottlingMessageHandler(TimeSpanSemaphore semaphore) =>
this.ClientHandler != null
? new ThrottlingMessageHandler(semaphore, this.ClientHandler)
: new ThrottlingMessageHandler(semaphore);
}
}
3 changes: 1 addition & 2 deletions Vonage/VonageClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ namespace Vonage
/// </summary>
public class VonageClient
{
private const string MeetingsApiUrl = "https://api-eu.vonage.com";
private Credentials credentials;

public IAccountClient AccountClient { get; private set; }
Expand Down Expand Up @@ -82,7 +81,7 @@ private static HttpClient InitializeHttpClient()
{
var client = new HttpClient(new HttpClientHandler())
{
BaseAddress = new Uri(MeetingsApiUrl),
BaseAddress = Configuration.Instance.MeetingsApiUrl,
};
client.DefaultRequestHeaders.Add("Accept", "application/json");
return client;
Expand Down

0 comments on commit 4bd1dd3

Please sign in to comment.