-
Notifications
You must be signed in to change notification settings - Fork 358
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Added IServiceCollection extension methods for PublisherClient …
…and SubscriberClient
- Loading branch information
Showing
3 changed files
with
283 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
185 changes: 185 additions & 0 deletions
185
apis/Google.Cloud.PubSub.V1/Google.Cloud.PubSub.V1.Tests/ServiceCollectionExtensionsTest.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"). | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using Google.Apis.Auth.OAuth2; | ||
using Grpc.Auth; | ||
using Microsoft.Extensions.DependencyInjection; | ||
using System; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Xunit; | ||
|
||
namespace Google.Cloud.PubSub.V1.Tests; | ||
|
||
public class ServiceCollectionExtensionsTest | ||
{ | ||
[Fact] | ||
public void AddPublisherClient_WithTopicName() | ||
{ | ||
// Arrange. | ||
TopicName topicName = TopicName.FromProjectTopic("projectId", "topicId"); | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
// As Application Default Credentials are not available for unit tests, we need to pass a fake credential. | ||
serviceCollection.AddSingleton(new FakeCredential().ToChannelCredentials()); | ||
|
||
// Act. | ||
serviceCollection.AddPublisherClient(topicName); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
var client = provider.GetService<PublisherClient>(); | ||
|
||
// Assert. | ||
Assert.NotNull(client); | ||
Assert.Equal(topicName, client.TopicName); | ||
} | ||
|
||
[Fact] | ||
public void AddPublisherClient_WithAction() | ||
{ | ||
// Arrange. | ||
TopicName topicName = TopicName.FromProjectTopic("projectId", "topicId"); | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
// As Application Default Credentials are not available for unit tests, we need to pass a fake credential. | ||
serviceCollection.AddSingleton(new FakeCredential().ToChannelCredentials()); | ||
|
||
// Act. | ||
serviceCollection.AddPublisherClient(builder => builder.TopicName = topicName); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
var client = provider.GetService<PublisherClient>(); | ||
|
||
// Assert. | ||
Assert.NotNull(client); | ||
Assert.Equal(topicName, client.TopicName); | ||
} | ||
|
||
[Fact] | ||
public void AddPublisherClient_WithAction_NoTopicName() | ||
{ | ||
// Arrange. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
serviceCollection.AddSingleton(new FakeCredential().ToChannelCredentials()); | ||
|
||
// Act. | ||
serviceCollection.AddPublisherClient(builder => { }); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
|
||
// Assert. | ||
// When TopicName isn't specified, it results in an InvalidOperationException. | ||
Assert.Throws<InvalidOperationException>(provider.GetRequiredService<PublisherClient>); | ||
} | ||
|
||
[Fact] | ||
public void AddPublisherClient_WithNullTopicName() | ||
{ | ||
// Arrange. | ||
// Passing null topicName to AddPublisherClient should throw ArgumentNullException. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
|
||
// Act and Assert. | ||
Assert.Throws<ArgumentNullException>(() => serviceCollection.AddPublisherClient(topicName: null)); | ||
} | ||
|
||
[Fact] | ||
public void AddPublisherClient_WithNullAction() | ||
{ | ||
// Arrange. | ||
// Passing null action to AddPublisherClient should throw ArgumentNullException. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
|
||
// Act and assert. | ||
Assert.Throws<ArgumentNullException>(() => serviceCollection.AddPublisherClient(action: null)); | ||
} | ||
|
||
[Fact] | ||
public void AddSubscriberClient_WithTopicName() | ||
{ | ||
// Arrange. | ||
SubscriptionName subscriptionName = SubscriptionName.FromProjectSubscription("projectId", "subscriptionId"); | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
// As Application Default Credentials are not available for unit tests, we need to pass a fake credential. | ||
serviceCollection.AddSingleton(new FakeCredential().ToChannelCredentials()); | ||
|
||
// Act. | ||
serviceCollection.AddSubscriberClient(subscriptionName); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
var client = provider.GetService<SubscriberClient>(); | ||
|
||
// Assert. | ||
Assert.NotNull(client); | ||
Assert.Equal(subscriptionName, client.SubscriptionName); | ||
} | ||
|
||
[Fact] | ||
public void AddSubscriberClient_WithAction() | ||
{ | ||
// Arrange. | ||
SubscriptionName subscriptionName = SubscriptionName.FromProjectSubscription("projectId", "subscriptionId"); | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
// As Application Default Credentials are not available for unit tests, we need to pass a fake credential. | ||
serviceCollection.AddSingleton(new FakeCredential().ToChannelCredentials()); | ||
|
||
// Act. | ||
serviceCollection.AddSubscriberClient(builder => builder.SubscriptionName = subscriptionName); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
var client = provider.GetService<SubscriberClient>(); | ||
|
||
// Assert. | ||
Assert.NotNull(client); | ||
Assert.Equal(subscriptionName, client.SubscriptionName); | ||
} | ||
|
||
[Fact] | ||
public void AddSubscriberClient_WithAction_NoSubscriptionName() | ||
{ | ||
// Arrange. | ||
// When SubscriptionName isn't specified in the builder, it should result in an InvalidOperationException. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
|
||
//Act. | ||
serviceCollection.AddSubscriberClient(builder => { }); | ||
var provider = serviceCollection.BuildServiceProvider(); | ||
|
||
// Assert. | ||
Assert.Throws<InvalidOperationException>(provider.GetRequiredService<SubscriberClient>); | ||
} | ||
|
||
[Fact] | ||
public void AddSubscriberClient_WithNullSubscriptionName() | ||
{ | ||
// Arrange. | ||
// Passing null subscriptionName to AddSubscriberClient should throw ArgumentNullException. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
|
||
// Act and assert. | ||
Assert.Throws<ArgumentNullException>(() => serviceCollection.AddSubscriberClient(subscriptionName: null)); | ||
} | ||
|
||
[Fact] | ||
public void AddSubscriberClient_WithNullAction() | ||
{ | ||
// Arrange. | ||
// Passing null action to AddSubscriberClient should throw ArgumentNullException. | ||
IServiceCollection serviceCollection = new ServiceCollection(); | ||
|
||
// Act and assert. | ||
Assert.Throws<ArgumentNullException>(() => serviceCollection.AddSubscriberClient(action: null)); | ||
} | ||
|
||
// Fake credential used just to ensure that we don't fetch the application default credentials | ||
// (which may not be available for unit tests). | ||
private class FakeCredential : ITokenAccess | ||
{ | ||
public Task<string> GetAccessTokenForRequestAsync(string authUri = null, CancellationToken cancellationToken = default) => | ||
throw new NotImplementedException(); | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
apis/Google.Cloud.PubSub.V1/Google.Cloud.PubSub.V1/ServiceCollectionExtensions.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
// Copyright 2023 Google LLC | ||
// | ||
// Licensed under the Apache License, Version 2.0 (the "License"). | ||
// you may not use this file except in compliance with the License. | ||
// You may obtain a copy of the License at | ||
// | ||
// https://www.apache.org/licenses/LICENSE-2.0 | ||
// | ||
// Unless required by applicable law or agreed to in writing, software | ||
// distributed under the License is distributed on an "AS IS" BASIS, | ||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
using Google.Api.Gax; | ||
using Google.Cloud.PubSub.V1; | ||
using System; | ||
|
||
namespace Microsoft.Extensions.DependencyInjection; | ||
|
||
/// <summary> | ||
/// Provides extension methods to configure dependency injection with PubSub. | ||
/// </summary> | ||
public static partial class ServiceCollectionExtensions | ||
{ | ||
/// <summary> | ||
/// Adds a singleton <see cref="PublisherClient"/> to the <see cref="IServiceCollection"/> as customized by the <paramref name="action"/>. | ||
/// </summary> | ||
/// <param name="services"> | ||
/// The <see cref="IServiceCollection"/> to add the singleton client to. | ||
/// </param> | ||
/// <param name="action"> | ||
/// An action delegate to invoke on the <see cref="PublisherClientBuilder"/> for configuring the <see cref="PublisherClient"/>. This is invoked before <paramref name="services"/> are used. | ||
/// Must not be null and at-least <see cref="PublisherClientBuilder.TopicName"/> must be set. | ||
/// </param> | ||
/// <returns>The updated <see cref="IServiceCollection"/>, for method chaining.</returns> | ||
public static IServiceCollection AddPublisherClient(this IServiceCollection services, Action<PublisherClientBuilder> action) | ||
{ | ||
GaxPreconditions.CheckNotNull(action, nameof(action)); | ||
return services.AddSingleton(provider => | ||
{ | ||
var builder = new PublisherClientBuilder(); | ||
action.Invoke(builder); | ||
return builder.Build(provider); | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a singleton <see cref="PublisherClient"/> associated with the specified <see cref="TopicName"/>, using default settings to the <see cref="IServiceCollection"/>. | ||
/// </summary> | ||
/// <param name="services"> | ||
/// The <see cref="IServiceCollection"/> to add the singleton client to. | ||
/// </param> | ||
/// <param name="topicName">The <see cref="TopicName"/> to publish messages to. Must not be null.</param> | ||
/// <returns>The updated <see cref="IServiceCollection"/>, for method chaining.</returns> | ||
public static IServiceCollection AddPublisherClient(this IServiceCollection services, TopicName topicName) | ||
{ | ||
GaxPreconditions.CheckNotNull(topicName, nameof(topicName)); | ||
return services.AddPublisherClient(new Action<PublisherClientBuilder>(builder => builder.TopicName = topicName)); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a singleton <see cref="SubscriberClient"/> to the <see cref="IServiceCollection"/> as customized by the <paramref name="action"/>. | ||
/// </summary> | ||
/// <param name="services"> | ||
/// The <see cref="IServiceCollection"/> to add the singleton client to. | ||
/// </param> | ||
/// <param name="action"> | ||
/// An action to invoke on the <see cref="SubscriberClientBuilder"/> for configuring the <see cref="SubscriberClient"/>. This is invoked before <paramref name="services"/> are used. | ||
/// Must not be null and at-least <see cref="SubscriberClientBuilder.SubscriptionName"/> must be set. | ||
/// </param> | ||
/// <returns>The updated <see cref="IServiceCollection"/>, for method chaining.</returns> | ||
public static IServiceCollection AddSubscriberClient(this IServiceCollection services, Action<SubscriberClientBuilder> action) | ||
{ | ||
GaxPreconditions.CheckNotNull(action, nameof(action)); | ||
return services.AddSingleton(provider => | ||
{ | ||
var builder = new SubscriberClientBuilder(); | ||
action.Invoke(builder); | ||
return builder.Build(provider); | ||
}); | ||
} | ||
|
||
/// <summary> | ||
/// Adds a singleton <see cref="SubscriberClient"/> associated with the specified <see cref="SubscriptionName"/>, using default settings to the <see cref="IServiceCollection"/>. | ||
/// </summary> | ||
/// <param name="services"> | ||
/// The <see cref="IServiceCollection"/> to add the singleton client to. | ||
/// </param> | ||
/// <param name="subscriptionName">The <see cref="SubscriptionName"/> to receive messages from. Must not be null.</param> | ||
/// <returns>The updated <see cref="IServiceCollection"/>, for method chaining.</returns> | ||
public static IServiceCollection AddSubscriberClient(this IServiceCollection services, SubscriptionName subscriptionName) | ||
{ | ||
GaxPreconditions.CheckNotNull(subscriptionName, nameof(subscriptionName)); | ||
return services.AddSubscriberClient(new Action<SubscriberClientBuilder>(builder => builder.SubscriptionName = subscriptionName)); | ||
} | ||
} |