Skip to content

Commit

Permalink
feat: allow to override api urls with SinchOptions.cs (#34)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dovchik authored Jan 22, 2024
1 parent 0c4da8d commit e0476a9
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 56 deletions.
7 changes: 1 addition & 6 deletions src/Sinch/Auth/OAuth.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,17 +22,12 @@ internal class OAuth : ISinchAuth

public string Scheme { get; } = AuthSchemes.Bearer;

public OAuth(string keyId, string keySecret, HttpClient httpClient, ILoggerAdapter<OAuth> logger)
public OAuth(string keyId, string keySecret, HttpClient httpClient, ILoggerAdapter<OAuth> logger, Uri baseAddress)
{
_keyId = keyId;
_keySecret = keySecret;
_httpClient = httpClient;
_logger = logger;
_baseAddress = new Uri("https://auth.sinch.com");
}

internal OAuth(Uri baseAddress, HttpClient httpClient) : this("", "", httpClient, null)
{
_baseAddress = baseAddress;
}

Expand Down
56 changes: 21 additions & 35 deletions src/Sinch/SinchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,11 +123,12 @@ public class SinchClient : ISinchClient
private const string SmsApiUrlTemplate = "https://zt.{0}.sms.api.sinch.com";
private const string ConversationApiUrlTemplate = "https://{0}.conversation.api.sinch.com/";
private const string VoiceApiUrlTemplate = "https://{0}.api.sinch.com/";
private const string AuthApiUrl = "https://auth.sinch.com";

private readonly LoggerFactory _loggerFactory;
private readonly HttpClient _httpClient;
private readonly Uri _verificationBaseAddress;
private readonly Uri _voiceBaseAddress;

private readonly ApiUrlOverrides _apiUrlOverrides;

/// <summary>
/// Initialize a new <see cref="SinchClient"/>
Expand Down Expand Up @@ -165,59 +166,44 @@ public SinchClient(string keyId, string keySecret, string projectId,
var logger = _loggerFactory?.Create<SinchClient>();
logger?.LogInformation("Initializing SinchClient...");

_apiUrlOverrides = optionsObj?.ApiUrlOverrides;

ISinchAuth auth =
new OAuth(keyId, keySecret, _httpClient, _loggerFactory?.Create<OAuth>());
new OAuth(keyId, keySecret, _httpClient, _loggerFactory?.Create<OAuth>(),
new Uri(_apiUrlOverrides?.AuthUrl ?? AuthApiUrl));
Auth = auth;
var httpCamelCase = new Http(auth, _httpClient, _loggerFactory?.Create<Http>(),
JsonNamingPolicy.CamelCase);
var httpSnakeCase = new Http(auth, _httpClient, _loggerFactory?.Create<Http>(),
SnakeCaseNamingPolicy.Instance);

Numbers = new Numbers.Numbers(projectId, new Uri(NumbersApiUrl),
Numbers = new Numbers.Numbers(projectId, new Uri(_apiUrlOverrides?.NumbersUrl ?? NumbersApiUrl),
_loggerFactory, httpCamelCase);
Sms = new Sms(projectId, GetSmsBaseAddress(optionsObj.SmsHostingRegion), _loggerFactory,
Sms = new Sms(projectId, GetSmsBaseAddress(optionsObj.SmsHostingRegion, _apiUrlOverrides?.SmsUrl),
_loggerFactory,
httpSnakeCase);
Conversation = new Conversation.Conversation(projectId,
new Uri(string.Format(ConversationApiUrlTemplate, optionsObj.ConversationRegion.Value)),
new Uri(_apiUrlOverrides?.ConversationUrl ??
string.Format(ConversationApiUrlTemplate, optionsObj.ConversationRegion.Value)),
_loggerFactory, httpSnakeCase);

Auth = auth;

logger?.LogInformation("SinchClient initialized.");
}

private static Uri GetSmsBaseAddress(SmsHostingRegion smsHostingRegion)
private static Uri GetSmsBaseAddress(SmsHostingRegion smsHostingRegion, string smsUrlOverride)
{
if (smsUrlOverride != null)
{
return new Uri(smsUrlOverride);
}

// General SMS rest api uses service_plan_id to performs calls
// But SDK is based on single-account model which uses project_id
// Thus, baseAddress for sms api is using a special endpoint where service_plan_id is replaced with projectId
// for each provided endpoint
return new Uri(string.Format(SmsApiUrlTemplate, smsHostingRegion.Value.ToLowerInvariant()));
}

/// <summary>
/// For E2E tests only. Here you can override base addresses.
/// </summary>
/// <param name="projectId"></param>
/// <param name="authUri"></param>
/// <param name="numbersBaseAddress"></param>
/// <param name="smsBaseAddress"></param>
/// <param name="verificationBaseAddress"></param>
/// <param name="voiceBaseAddress"></param>
internal SinchClient(string projectId, Uri authUri, Uri numbersBaseAddress, Uri smsBaseAddress,
Uri verificationBaseAddress, Uri voiceBaseAddress)
{
_httpClient = new HttpClient();
Auth = new OAuth(authUri, _httpClient);
var httpCamelCase = new Http(Auth, _httpClient, null,
JsonNamingPolicy.CamelCase);
var httpSnakeCase = new Http(Auth, _httpClient, null,
SnakeCaseNamingPolicy.Instance);
Numbers = new Numbers.Numbers(projectId, numbersBaseAddress, null, httpCamelCase);
Sms = new Sms(projectId, smsBaseAddress, null, httpSnakeCase);
_verificationBaseAddress = verificationBaseAddress;
_voiceBaseAddress = voiceBaseAddress;
}

/// <inheritdoc/>
public ISinchNumbers Numbers { get; }

Expand Down Expand Up @@ -256,7 +242,7 @@ public ISinchVerificationClient Verification(string appKey, string appSecret,
}

var http = new Http(auth, _httpClient, _loggerFactory?.Create<Http>(), JsonNamingPolicy.CamelCase);
return new SinchVerificationClient(_verificationBaseAddress ?? new Uri(VerificationApiUrl),
return new SinchVerificationClient(new Uri(_apiUrlOverrides?.VerificationUrl ?? VerificationApiUrl),
_loggerFactory, http);
}

Expand Down Expand Up @@ -289,7 +275,7 @@ public ISinchVoiceClient Voice(string appKey, string appSecret,

var http = new Http(auth, _httpClient, _loggerFactory?.Create<Http>(), JsonNamingPolicy.CamelCase);
return new SinchVoiceClient(
_voiceBaseAddress ?? new Uri(string.Format(VoiceApiUrlTemplate, callingRegion.Value)),
new Uri(_apiUrlOverrides?.VoiceUrl ?? string.Format(VoiceApiUrlTemplate, callingRegion.Value)),
_loggerFactory, http);
}
}
Expand Down
39 changes: 39 additions & 0 deletions src/Sinch/SinchOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,44 @@ public sealed class SinchOptions
/// Defaults to "us"
/// </summary>
public ConversationRegion ConversationRegion { get; set; } = ConversationRegion.Us;

/// <inheritdoc cref="ApiUrlOverrides"/>
public ApiUrlOverrides ApiUrlOverrides { get; set; }
}

/// <summary>
/// If you want to set your own url for proxy or testing, you can do it here for each API endpoint.
/// </summary>
public sealed class ApiUrlOverrides
{
/// <summary>
/// Overrides SMS api base url
/// </summary>
public string SmsUrl { get; init; }

/// <summary>
/// Overrides Conversation api base url
/// </summary>
public string ConversationUrl { get; init; }

/// <summary>
/// Overrides Voice api base url
/// </summary>
public string VoiceUrl { get; init; }

/// <summary>
/// Overrides Verification api base url
/// </summary>
public string VerificationUrl { get; init; }

/// <summary>
/// Overrides Auth api base url
/// </summary>
public string AuthUrl { get; init; }

/// <summary>
/// Overrides Numbers api base url
/// </summary>
public string NumbersUrl { get; init; }
}
}
2 changes: 1 addition & 1 deletion tests/Sinch.Tests/AuthTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public AuthTests()
var httpClient = new HttpClient(_messageHandlerMock);
const string mockKeyId = "mock_key_id";
const string mockKeySecret = "mock_key_secret";
_auth = new OAuth(mockKeyId, mockKeySecret, httpClient, _logger);
_auth = new OAuth(mockKeyId, mockKeySecret, httpClient, _logger, new Uri("https://auth.sinch.com/"));
var basicAuthHeaderValue = Convert.ToBase64String(Encoding.ASCII.GetBytes($"{mockKeyId}:{mockKeySecret}"));
_mockedRequest = _messageHandlerMock.When(HttpMethod.Post, "https://auth.sinch.com/oauth2/token")
.WithFormData(new[]
Expand Down
40 changes: 26 additions & 14 deletions tests/Sinch.Tests/e2e/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,32 @@ public class TestBase
protected TestBase()
{
Env.Load();
SinchClientMockStudio = new Sinch.SinchClient(ProjectId, new Uri("http://localhost:8001"),
new Uri("http://localhost:8000"),
new Uri("http://localhost:8002"), null, null);
var authUri = new Uri($"http://localhost:{Environment.GetEnvironmentVariable("MOCK_AUTH_PORT")}");
var smsUri = new Uri($"http://localhost:{Environment.GetEnvironmentVariable("MOCK_SMS_PORT")}");
// TODO: use mock server endpoints
var numbersUri = new Uri($"http://localhost:{Environment.GetEnvironmentVariable("MOCK_NUMBERS_PORT")}");
var verificationBaseUri =
new Uri($"http://localhost:{Environment.GetEnvironmentVariable("MOCK_VERIFICATION_PORT")}");
var voiceBaseUri =
new Uri($"http://localhost:{Environment.GetEnvironmentVariable("MOCK_VOICE_PORT")}");
SinchClientMockServer = new Sinch.SinchClient(ProjectId, authUri,
new Uri("http://localhost:8000"),
smsUri, verificationBaseUri, voiceBaseUri);
SinchClientMockStudio = new SinchClient("key_id", "key_secret", ProjectId,
options =>
{
options.ApiUrlOverrides = new ApiUrlOverrides()
{
AuthUrl = "http://localhost:8001",
NumbersUrl = "http://localhost:8000",
SmsUrl = "http://localhost:8002"
};
});

SinchClientMockServer = new SinchClient("key_id", "key_secret", ProjectId, options =>
{
options.ApiUrlOverrides = new ApiUrlOverrides()
{
AuthUrl = GetTestUrl("MOCK_AUTH_PORT"),
SmsUrl = GetTestUrl("MOCK_SMS_PORT"),
ConversationUrl = GetTestUrl("MOCK_CONVERSATION_PORT"),
NumbersUrl = GetTestUrl("MOCK_NUMBERS_PORT"),
VoiceUrl = GetTestUrl("MOCK_VOICE_PORT"),
VerificationUrl = GetTestUrl("MOCK_VERIFICATION_PORT"),
};
});
}

private string GetTestUrl(string portEnvVar) =>
$"http://localhost:{Environment.GetEnvironmentVariable(portEnvVar)}";
}
}

0 comments on commit e0476a9

Please sign in to comment.