Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
brianhdk committed Sep 29, 2023
2 parents 13e19e8 + b7deced commit 21c6641
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 15 deletions.
44 changes: 43 additions & 1 deletion src/Relewise.Client.Extensions.Tests/FromConfigurationTester.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,48 @@ public void ReadFromConfiguration_SpecificSectionWithNamedClientsWithOtherServer
Assert.AreEqual("https://stage02-api.relewise.com/", namedClientTracker.ServerUrl);
}

[Test]
public void ApiKeySetOnClientButNeverGlobally()
{
var serviceCollection = new ServiceCollection()
.AddRelewise(options => options.ReadFromConfiguration(BuildConfiguration(), "ApiKeySetOnClientButNeverGlobally"));

ServiceProvider provider = serviceCollection.BuildServiceProvider();
var tracker = provider.GetService<ITracker>();
IRelewiseClientFactory factory = provider.GetRequiredService<IRelewiseClientFactory>();
RelewiseClientOptions trackerOptions = factory.GetOptions<ITracker>();

Assert.IsNotNull(tracker);
Assert.AreEqual(Guid.Parse("6D9361AA-A23D-4BF2-A818-5ABA792E2102"), trackerOptions.DatasetId);
Assert.AreEqual("r4FqfMqtiZjJmoN", trackerOptions.ApiKey);
}

[Test]
public void ReadFromConfiguration_OnlySetOnClient()
{
var serviceCollection = new ServiceCollection()
.AddRelewise(options => options.ReadFromConfiguration(BuildConfiguration(), "OnlySetOnTracker"));

ServiceProvider provider = serviceCollection.BuildServiceProvider();
var tracker = provider.GetService<ITracker>();
IRelewiseClientFactory factory = provider.GetRequiredService<IRelewiseClientFactory>();
RelewiseClientOptions trackerOptions = factory.GetOptions<ITracker>();

Assert.IsNotNull(tracker);
Assert.AreEqual(Guid.Parse("B57CB490-1556-4F06-AA26-96451533A9B8"), trackerOptions.DatasetId);
Assert.AreEqual("61ce444b6e7c4f", trackerOptions.ApiKey);
}

[Test]
public void ReadFromConfiguration_OnlySetOnWrongClient()
{
var serviceCollection = new ServiceCollection()
.AddRelewise(options => options.ReadFromConfiguration(BuildConfiguration(), "OnlySetOnTracker"));

ServiceProvider provider = serviceCollection.BuildServiceProvider();
Assert.Throws<InvalidOperationException>(() => provider.GetService<ISearcher>());
}

private static void FromConfigAssertion(IServiceCollection serviceCollection)
{
ServiceProvider provider = serviceCollection.BuildServiceProvider();
Expand All @@ -103,7 +145,7 @@ private static void FromConfigAssertion(IServiceCollection serviceCollection)

Assert.AreEqual(Guid.Parse("6D9361AA-A23D-4BF2-A818-5ABA792E2102"), tracker.DatasetId);
Assert.AreEqual("https://api.relewise.com", tracker.ServerUrl);
Assert.AreEqual(TimeSpan.FromSeconds(3), tracker.Timeout);
Assert.AreEqual(TimeSpan.FromSeconds(10), tracker.Timeout);
}

private static IConfiguration BuildConfiguration()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,54 @@ public void AddDatasetIdAndApiKey()
Assert.AreEqual(TimeSpan.FromSeconds(5), tracker.Timeout);
}

[Test]
public void SetDatasetIdGloballyButApiKeyOnlyOnSpecificClient()
{
var serviceCollection = new ServiceCollection();

var datasetId = Guid.NewGuid();
var searcherApiKey = "r4FqfMqtiZjJmoN";

serviceCollection.AddRelewise(options =>
{
options.DatasetId = datasetId;
options.Searcher.ApiKey = searcherApiKey;
});

ServiceProvider provider = serviceCollection.BuildServiceProvider();

ISearcher searcher = null;
Assert.DoesNotThrow(() => searcher = provider.GetService<ISearcher>());
Assert.IsNotNull(searcher);

IRelewiseClientFactory factory = provider.GetRequiredService<IRelewiseClientFactory>();
RelewiseClientOptions options = factory.GetOptions<ISearcher>();

Assert.AreEqual(searcherApiKey, options.ApiKey);
}

[Test]
public void SetDatasetIdGloballyButApiKeyOnlyOnNamedClient()
{
var serviceCollection = new ServiceCollection();

var datasetId = Guid.NewGuid();
var integrationApiKey = "r4FqfMqtiZjJmoN";

serviceCollection.AddRelewise(options =>
{
options.DatasetId = datasetId;
options.Named.Add("Integration", integration => integration.ApiKey = integrationApiKey);
});

ServiceProvider provider = serviceCollection.BuildServiceProvider();

IRelewiseClientFactory factory = provider.GetRequiredService<IRelewiseClientFactory>();
RelewiseClientOptions options = factory.GetOptions<ITracker>("Integration");

Assert.AreEqual(integrationApiKey, options.ApiKey);
}

[Test]
public void AddDatasetServerUrl()
{
Expand Down
22 changes: 22 additions & 0 deletions src/Relewise.Client.Extensions.Tests/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -144,5 +144,27 @@
}
}
}
},
"ApiKeySetOnClientButNeverGlobally": {
"DatasetId": "6D9361AA-A23D-4BF2-A818-5ABA792E2102",
"Timeout": "00:00:03",
"Tracker": {
"Timeout": "00:00:10",
"ApiKey": "r4FqfMqtiZjJmoN"
}
},
"OnlySetOnTracker": {
"Timeout": "00:00:03",
"Tracker": {
"Timeout": "00:00:10",
"DatasetId": "B57CB490-1556-4F06-AA26-96451533A9B8",
"ApiKey": "61ce444b6e7c4f"
},
"Recommender": {
"Timeout": "00:00:05"
},
"Searcher": {
"Timeout": "00:00:10"
}
}
}
24 changes: 12 additions & 12 deletions src/Relewise.Client.Extensions/Builders.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,12 @@ public void Add(string name, Action<RelewiseClientsOptionsBuilder> options, bool

private class JsonConfiguration : ClientJsonConfiguration
{
public ClientJsonConfiguration? Tracker { get; }
public ClientJsonConfiguration? Recommender { get; }
public ClientJsonConfiguration? Searcher { get; }
public ClientJsonConfiguration? DataAccessor { get; }
public ClientJsonConfiguration? SearchAdministrator { get; }
public ClientJsonConfiguration? Analyzer { get; }
public ClientJsonConfiguration? Tracker { get; set; }
public ClientJsonConfiguration? Recommender { get; set; }
public ClientJsonConfiguration? Searcher { get; set; }
public ClientJsonConfiguration? DataAccessor { get; set; }
public ClientJsonConfiguration? SearchAdministrator { get; set; }
public ClientJsonConfiguration? Analyzer { get; set; }

public Dictionary<string, RelewiseClientsOptionsBuilder>? Named { get; set; }

Expand Down Expand Up @@ -254,16 +254,16 @@ public class RelewiseClientOptionsBuilder
Guid datasetId = DatasetId.GetValueOrDefault(parentOptions?.DatasetId ?? Guid.Empty);

if (datasetId.Equals(Guid.Empty))
throw new ArgumentOutOfRangeException($"Value for '{nameof(DatasetId)} cannot be an empty Guid. The correct value can be found using https://my.relewise.com.");
throw new ArgumentOutOfRangeException($"Value for '{nameof(DatasetId)}' cannot be an empty Guid. The correct value can be found using https://my.relewise.com.");

string? apiKey = ApiKey ?? parentOptions?.ApiKey;
TimeSpan timeout = Timeout.GetValueOrDefault(parentOptions?.Timeout ?? TimeSpan.FromSeconds(5));

if (apiKey is null || string.IsNullOrWhiteSpace(apiKey)) // compiler is not happy about only having the string.IsNullOrWhiteSpace-check
throw new ArgumentException($@"Value for '{nameof(ApiKey)} cannot be null or empty. The correct value can be found using https://my.relewise.com.", nameof(ApiKey));
Uri? serverUrl = ServerUrl ?? parentOptions?.ServerUrl;

TimeSpan timeout = Timeout.GetValueOrDefault(parentOptions?.Timeout ?? TimeSpan.FromSeconds(5));
string? apiKey = ApiKey ?? parentOptions?.ApiKeyWhenReadAsParent;

var serverUrl = ServerUrl ?? parentOptions?.ServerUrl;
if (apiKey == null)
return new RelewiseClientOptions.WithoutApiKey(datasetId, timeout, serverUrl); // ApiKey has not been configured (which is okay)

return new RelewiseClientOptions(datasetId, apiKey, timeout, serverUrl);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ private static void TryAdd<TInterface, TClass>(
{
throw new InvalidOperationException($@"No options were given to create a non-named client for {typeof(TInterface).Name}.
To configure this client, use the 'services.AddRelewise(options => {{ ... }});'-method in your startup code.");
}

if (clientOptions is RelewiseClientOptions.WithoutApiKey)
{
throw new InvalidOperationException($@"Non-named client for {typeof(TInterface).Name} is missing an ApiKey - either directly set on client or read from global options.
To configure this client, use the 'services.AddRelewise(options => {{ ... }});'-method in your startup code.");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<PackageProjectUrl>https://github.com/Relewise/relewise-sdk-csharp-extensions</PackageProjectUrl>
<RepositoryUrl>https://github.com/Relewise/relewise-sdk-csharp-extensions</RepositoryUrl>
<PackageId>Relewise.Client.Extensions</PackageId>
<PackageVersion>1.4.1</PackageVersion>
<PackageVersion>1.4.2</PackageVersion>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<Authors>Relewise</Authors>
Expand Down
5 changes: 4 additions & 1 deletion src/Relewise.Client.Extensions/RelewiseClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ public RelewiseClientFactory(IServiceProvider provider)
throw new InvalidOperationException($"Options for {typeof(T).Name} is missing required information. {ex.Message}", ex);
}

if (options != null)
if (options != null && options is not RelewiseClientOptions.WithoutApiKey)
_options.Add(GenerateClientLookupKey<T>(), options);

return options;
Expand All @@ -127,6 +127,9 @@ private void AddNamedClient<TInterface, TImplementation>(
ex);
}

if (options is RelewiseClientOptions.WithoutApiKey)
return; // No ApiKey was added ... Skip adding named client.

if (options != null)
{
TImplementation client = create(
Expand Down
15 changes: 15 additions & 0 deletions src/Relewise.Client.Extensions/RelewiseClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,19 @@ private static bool IsInvalidUrl(Uri serverUrl)
{
return !serverUrl.IsAbsoluteUri || !serverUrl.IsWellFormedOriginalString();
}

/// <summary>
/// Defines the internal property to read the optional ApiKey, when reading from parent.
/// </summary>
protected internal virtual string? ApiKeyWhenReadAsParent => ApiKey;

internal class WithoutApiKey : RelewiseClientOptions
{
public WithoutApiKey(Guid datasetId, TimeSpan timeout, Uri? serverUrl = null)
: base(datasetId, "dummy-api-key", timeout, serverUrl)
{
}

protected internal override string? ApiKeyWhenReadAsParent => null;
}
}

0 comments on commit 21c6641

Please sign in to comment.