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

Add EventHub client extension methodS #7034

Merged
merged 3 commits into from
Jul 29, 2019
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
19 changes: 13 additions & 6 deletions sdk/core/Azure.Core.Extensions/src/AzureClientFactoryBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Azure.Core.Extensions
{
public sealed class AzureClientFactoryBuilder : IAzureClientFactoryBuilderWithConfiguration<IConfiguration>, IAzureClientsBuilderWithCredential
public sealed class AzureClientFactoryBuilder : IAzureClientFactoryBuilderWithConfiguration<IConfiguration>, IAzureClientFactoryBuilderWithCredential
{
private readonly IServiceCollection _serviceCollection;

Expand All @@ -25,15 +25,22 @@ internal AzureClientFactoryBuilder(IServiceCollection serviceCollection)

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilder.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory)
{
return ((IAzureClientsBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>((options, _) => clientFactory(options));
return ((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>((options, _) => clientFactory(options));
}

IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithConfiguration<IConfiguration>.RegisterClientFactory<TClient, TOptions>(IConfiguration configuration)
{
return ((IAzureClientsBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>(
var credentialsFromConfig = ClientFactory.CreateCredential(configuration);
var clientBuilder =((IAzureClientFactoryBuilderWithCredential)this).RegisterClientFactory<TClient, TOptions>(
(options, credentials) => (TClient)ClientFactory.CreateClient(typeof(TClient), typeof(TOptions), options, configuration, credentials))
.ConfigureOptions(configuration)
.WithCredential(ClientFactory.CreateCredential(configuration));
.ConfigureOptions(configuration);

if (credentialsFromConfig != null)
{
clientBuilder.WithCredential(credentialsFromConfig);
}

return clientBuilder;
}

public AzureClientFactoryBuilder ConfigureDefaults(Action<ClientOptions> configureOptions)
Expand Down Expand Up @@ -63,7 +70,7 @@ public AzureClientFactoryBuilder ConfigureDefaults(IConfiguration configuration)
return this;
}

IAzureClientBuilder<TClient, TOptions> IAzureClientsBuilderWithCredential.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory)
IAzureClientBuilder<TClient, TOptions> IAzureClientFactoryBuilderWithCredential.RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory)
{
var clientRegistration = new ClientRegistration<TClient, TOptions>(DefaultClientName, clientFactory);
_serviceCollection.AddSingleton(clientRegistration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Azure.Core.Extensions
{
internal class DefaultClientOptionsSetup<T> : IConfigureNamedOptions<T> where T : ClientOptions
internal class DefaultClientOptionsSetup<T> : IConfigureNamedOptions<T> where T : class
{
private readonly IOptions<AzureClientsGlobalOptions> _defaultOptions;
private readonly IServiceProvider _serviceProvider;
Expand All @@ -20,9 +20,12 @@ public DefaultClientOptionsSetup(IOptions<AzureClientsGlobalOptions> defaultOpti

public void Configure(T options)
{
foreach (var globalConfigureOption in _defaultOptions.Value.ConfigureOptionDelegates)
if (options is ClientOptions clientOptions)
{
globalConfigureOption(options, _serviceProvider);
foreach (var globalConfigureOption in _defaultOptions.Value.ConfigureOptionDelegates)
{
globalConfigureOption(clientOptions, _serviceProvider);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public static IAzureClientBuilder<TestClientWithCredentials, TestClientOptions>
}

public static IAzureClientBuilder<TestClientWithCredentials, TestClientOptions> AddTestClientWithCredentials<TBuilder>(this TBuilder builder, Uri uri)
where TBuilder: IAzureClientsBuilderWithCredential
where TBuilder: IAzureClientFactoryBuilderWithCredential
{
return builder.RegisterClientFactory<TestClientWithCredentials, TestClientOptions>((options, cred) => new TestClientWithCredentials(uri, cred, options));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ namespace Azure.Core.Extensions
{
public interface IAzureClientFactoryBuilder
{
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : ClientOptions;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TClient> clientFactory) where TOptions : class;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we consider making the options always have a default constructor and using that constraint, or is that unimportant down the line?

Copy link
Contributor Author

@pakrym pakrym Jul 29, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, they almost never have a default constructor. Instead, the guidelines prescribe having a constructor with ServiceVersion parameter defaulted to latest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes. Forgot about that due to the deviation in the Event Hubs service design.

}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core.Pipeline;

namespace Azure.Core.Extensions
{

public interface IAzureClientFactoryBuilderWithConfiguration<in TConfiguration>: IAzureClientFactoryBuilder
{
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(TConfiguration configuration) where TOptions : ClientOptions;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(TConfiguration configuration) where TOptions : class;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@
// Licensed under the MIT License.

using System;
using Azure.Core.Pipeline;

namespace Azure.Core.Extensions
{
public interface IAzureClientsBuilderWithCredential
public interface IAzureClientFactoryBuilderWithCredential
{
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory) where TOptions : ClientOptions;
IAzureClientBuilder<TClient, TOptions> RegisterClientFactory<TClient, TOptions>(Func<TOptions, TokenCredential, TClient> clientFactory) where TOptions: class;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using Azure.Core.Extensions;
using Azure.Messaging.EventHubs;

namespace Azure.ApplicationModel.Configuration
{
/// <summary>
/// Extension methods to add <see cref="EventHubClient"/> client to clients builder
/// </summary>
public static class AzureClientBuilderExtensions
{
/// <summary>
/// Registers a <see cref="EventHubClient"/> instance with the provided <paramref name="connectionString"/>
/// </summary>
public static IAzureClientBuilder<EventHubClient, EventHubClientOptions> AddEventHubClient<TBuilder>(this TBuilder builder, string connectionString)
where TBuilder: IAzureClientFactoryBuilder
{
return builder.RegisterClientFactory<EventHubClient, EventHubClientOptions>(options => new EventHubClient(connectionString, options));
}

/// <summary>
/// Registers a <see cref="EventHubClient"/> instance with the provided <paramref name="connectionString"/> and <paramref name="eventHubPath"/>
/// </summary>
public static IAzureClientBuilder<EventHubClient, EventHubClientOptions> AddEventHubClient<TBuilder>(this TBuilder builder, string connectionString, string eventHubPath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I may not have context enough to be reading this entirely correctly, but I believe this limits scenarios to:

  • Here's a connection string (with path embedded) and a set of options
  • Here's a connection string and an event hub path, but you have to use default options (I suspect this will be the majority case)

If that is the case, I'd suggest that we also allow:

  • Here's a connection string, event hub path, and set of options

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IAzureClientBuilder return type has a ConfigureOptions(options => {}) method on it.

So to add a client and configure its options:

builder.AddEventHubClient("connectionString", "path").ConfigureOptions(options => {} );

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Works for me!

where TBuilder: IAzureClientFactoryBuilder
{
return builder.RegisterClientFactory<EventHubClient, EventHubClientOptions>(options => new EventHubClient(connectionString, eventHubPath, options));
}

/// <summary>
/// Registers a <see cref="EventHubClient"/> instance with the provided <paramref name="host"/> and <paramref name="eventHubPath"/>
/// </summary>
public static IAzureClientBuilder<EventHubClient, EventHubClientOptions> AddEventHubClientWithHost<TBuilder>(this TBuilder builder, string host, string eventHubPath)
where TBuilder: IAzureClientFactoryBuilderWithCredential
{
return builder.RegisterClientFactory<EventHubClient, EventHubClientOptions>((options, token) => new EventHubClient(host, eventHubPath, token, options));
}

/// <summary>
/// Registers a <see cref="EventHubClient"/> instance with connection options loaded from the provided <paramref name="configuration"/> instance.
/// </summary>
public static IAzureClientBuilder<EventHubClient, EventHubClientOptions> AddEventHubClient<TBuilder, TConfiguration>(this TBuilder builder, TConfiguration configuration)
where TBuilder: IAzureClientFactoryBuilderWithConfiguration<TConfiguration>
{
return builder.RegisterClientFactory<EventHubClient, EventHubClientOptions>(configuration);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ public static class AzureClientBuilderExtensions
/// Registers a <see cref="SecretClient"/> instance with the provided <paramref name="vaultUri"/>
/// </summary>
public static IAzureClientBuilder<SecretClient, SecretClientOptions> AddSecretClient<TBuilder>(this TBuilder builder, Uri vaultUri)
where TBuilder: IAzureClientsBuilderWithCredential
where TBuilder: IAzureClientFactoryBuilderWithCredential
{
return builder.RegisterClientFactory<SecretClient, SecretClientOptions>((options, cred) => new SecretClient(vaultUri, cred, options));
}
Expand Down