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

Expand eventhub resource with possibility to add consumer groups #282

Merged
merged 11 commits into from
Feb 6, 2024
8 changes: 7 additions & 1 deletion source/TestCommon/documents/eventhubresourceprovider.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ The `EventHubResourceProvider` and its related types, support us with the follow
- The `EventHubResourceProvider` is the fluent API builder root. It automatically tracks and cleanup any resources created, when it is disposed.
- The `EventHubResourceBuilder` type encapsulate the creation of an event hub in an existing Azure Event Hub namespace.
- The `EventHubResource` type support lazy creation of a producer client.
- The `EventHubConsumerGroupBuilder` type to encapsulate the creation of `ConsumerGroup`'s and adding to event hubs during creation of these.

> For usage, see `EventHubResourceProviderTests` or [Aggregations](https://github.com/Energinet-DataHub/geh-aggregations) repository/domain.

Expand All @@ -20,6 +21,10 @@ The fluent API chain always starts with a `BuildEventHub()` operation, and ends

The `CreateAsync()` operation returns the resource type `EventHubResource`. This type give us access to the full resource name created, and a producer client configured to send events to the created resource.

### `AddConsumerGroup()`

When building an EventHub its possible to add consumer groups.

### `Do()` and `SetEnvironmentVariableTo` extensions

The `EventHubBuilder` type support the `Do()` operation which allows us to register *post actions*. Each post action will be called just after the event hub has been created, with the properties of the event hub.
Expand All @@ -42,9 +47,10 @@ var resourceProvider = new EventHubResourceProvider(
Example 1 - creating an event hub:

```csharp
// Create an event hub prefixed with the name "eventhub".
// Create an event hub prefixed with the name "eventhub" and a consumergroup with name "consumer_group" (without optional user metadata).
var eventHubResource = await resourceProvider
.BuildEventHub("eventhub")
.AddConsumerGroup("consumer_group")
.CreateAsync();

// We can access the full event hub name...
Expand Down
4 changes: 4 additions & 0 deletions source/TestCommon/documents/release-notes/release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# TestCommon Release notes

## Version 4.6.0

- Extended `EventHubResource` with possibility to add consumer groups

## Version 4.5.0

- Added property `ApplicationInsightsConnectionString` to `IntegrationTestConfiguration` to support use of connection string when using Application Insights.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
using Energinet.DataHub.Core.TestCommon.AutoFixture.Extensions;
using Energinet.DataHub.Core.TestCommon.Diagnostics;
using FluentAssertions;
using FluentAssertions.Execution;
using Microsoft.Azure.Management.EventHub.Models;
using Xunit;

Expand Down Expand Up @@ -174,6 +175,51 @@ public async Task When_SetEnvironmentVariable_Then_EnvironmentVariableContainsAc
var actualEnvironmentValue = Environment.GetEnvironmentVariable(environmentVariable);
actualEnvironmentValue.Should().Be(actualName);
}

[Theory]
[InlineData("some user metadata")]
[InlineData(null)]
public async Task When_AddConsumerGroup_Then_CreatedEventHubHasConsumerGroup(string userMetadata)
{
// Arrange
var consumerGroupName = "consumer_group_name";

// Act
var actualResource = await Sut
.BuildEventHub(NamePrefix)
.AddConsumerGroup(consumerGroupName, userMetadata)
.CreateAsync();

// Assert
using var response = await ResourceProviderFixture.ManagementClient.ConsumerGroups.GetWithHttpMessagesAsync(
actualResource.ResourceGroup,
actualResource.EventHubNamespace,
actualResource.Name,
consumerGroupName);

using var assertionScope = new AssertionScope();
response.Body.Name.Should().Be(consumerGroupName);
response.Body.UserMetadata.Should().Be(userMetadata);
}

[Fact]
public async Task When_SetEnvironmentVariableToConsumerGroupName_Then_EnvironmentVariableContainsActualName()
{
// Arrange
const string environmentVariable = "env_consumer_group_name";
const string consumerGroupName = "consumer_group_name";

// Act
var actualResource = await Sut
.BuildEventHub(NamePrefix)
.AddConsumerGroup(consumerGroupName)
.SetEnvironmentVariableToConsumerGroupName(environmentVariable)
.CreateAsync();

// Assert
var actualEnvironmentValue = Environment.GetEnvironmentVariable(environmentVariable);
actualEnvironmentValue.Should().Be(consumerGroupName);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://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 System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.Management.EventHub.Models;

namespace Energinet.DataHub.Core.FunctionApp.TestCommon.EventHub.ResourceProvider;

public class EventHubConsumerGroupBuilder : IEventHubResourceBuilder
{
internal EventHubConsumerGroupBuilder(EventHubResourceBuilder eventHubResourceBuilder, string consumerGroupName, string? userMetadata = default)
{
EventHubResourceBuilder = eventHubResourceBuilder;
ConsumerGroupName = consumerGroupName;
UserMetadata = userMetadata;

PostActions = new List<Action<ConsumerGroup>>();
}

internal string ConsumerGroupName { get; }

internal string? UserMetadata { get; }

internal IList<Action<ConsumerGroup>> PostActions { get; }

private EventHubResourceBuilder EventHubResourceBuilder { get; }

/// <summary>
/// Add an action that will be called after the consumer group has been created.
/// </summary>
/// <param name="postAction">Action to call with consumer group name and metadata when it has been created.</param>
/// <returns>Consumer group builder.</returns>
public EventHubConsumerGroupBuilder Do(Action<ConsumerGroup> postAction)
{
PostActions.Add(postAction);

return this;
}

/// <inheritdoc/>
public EventHubConsumerGroupBuilder AddConsumerGroup(string consumerGroupName, string? userMetaData = default)
{
return EventHubResourceBuilder.AddConsumerGroup(consumerGroupName, userMetaData);
}

/// <inheritdoc/>
public Task<EventHubResource> CreateAsync()
{
return EventHubResourceBuilder.CreateAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://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 System;

namespace Energinet.DataHub.Core.FunctionApp.TestCommon.EventHub.ResourceProvider;

public static class EventHubConsumerGroupBuilderExtensions
{
public static EventHubConsumerGroupBuilder SetEnvironmentVariableToConsumerGroupName(this EventHubConsumerGroupBuilder builder, string variable)
{
builder.Do(consumerGroup => Environment.SetEnvironmentVariable(variable, consumerGroup.Name));

return builder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
// limitations under the License.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Azure.Messaging.EventHubs.Producer;
using Microsoft.Azure.Management.EventHub;
Expand All @@ -24,13 +26,17 @@ public class EventHubResource : IAsyncDisposable
{
private readonly Eventhub _properties;
private readonly Lazy<EventHubProducerClient> _lazyProducerClient;
private readonly IList<ConsumerGroup> _consumerGroups;

internal EventHubResource(EventHubResourceProvider resourceProvider, Eventhub properties)
{
ResourceProvider = resourceProvider;

_properties = properties;
_lazyProducerClient = new Lazy<EventHubProducerClient>(CreateProducerClient);
_consumerGroups = new List<ConsumerGroup>();

ConsumerGroups = new ReadOnlyCollection<ConsumerGroup>(_consumerGroups);
}

public string ResourceGroup => ResourceProvider.ResourceManagementSettings.ResourceGroup;
Expand All @@ -41,6 +47,8 @@ internal EventHubResource(EventHubResourceProvider resourceProvider, Eventhub pr

public EventHubProducerClient ProducerClient => _lazyProducerClient.Value;

public IReadOnlyCollection<ConsumerGroup>? ConsumerGroups { get; }

public bool IsDisposed { get; private set; }

private EventHubResourceProvider ResourceProvider { get; }
Expand All @@ -52,6 +60,11 @@ await DisposeAsyncCore()
GC.SuppressFinalize(this);
}

internal void AddConsumerGroup(ConsumerGroup consumerGroup)
{
_consumerGroups.Add(consumerGroup);
}

private EventHubProducerClient CreateProducerClient()
{
return new EventHubProducerClient(ResourceProvider.ConnectionString, Name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ namespace Energinet.DataHub.Core.FunctionApp.TestCommon.EventHub.ResourceProvide
/// <summary>
/// Fluent API for creating an event hub resource.
/// </summary>
public class EventHubResourceBuilder
public class EventHubResourceBuilder : IEventHubResourceBuilder
{
internal EventHubResourceBuilder(EventHubResourceProvider resourceProvider, string eventHubName, Eventhub createEventHubOptions)
{
ResourceProvider = resourceProvider;
EventHubName = eventHubName;
CreateEventHubOptions = createEventHubOptions;
ConsumerGroupBuilders = new Dictionary<string, EventHubConsumerGroupBuilder>();

PostActions = new List<Action<Eventhub>>();
}
Expand All @@ -40,6 +41,8 @@ internal EventHubResourceBuilder(EventHubResourceProvider resourceProvider, stri

private Eventhub CreateEventHubOptions { get; }

private IDictionary<string, EventHubConsumerGroupBuilder> ConsumerGroupBuilders { get; }

private IList<Action<Eventhub>> PostActions { get; }

/// <summary>
Expand All @@ -59,6 +62,23 @@ public EventHubResourceBuilder Do(Action<Eventhub> postAction)
/// </summary>
/// <returns>Instance with information about the created event hub.</returns>
public async Task<EventHubResource> CreateAsync()
{
var eventhubResource = await CreateEventhubResourceAsync().ConfigureAwait(false);

await CreateConsumerGroupsAsync(eventhubResource).ConfigureAwait(false);

return eventhubResource;
}

/// <inheritdoc/>
public EventHubConsumerGroupBuilder AddConsumerGroup(string consumerGroupName, string? userMetaData = default)
{
var consumerGroupBuilder = new EventHubConsumerGroupBuilder(this, consumerGroupName, userMetaData);
ConsumerGroupBuilders.Add(consumerGroupName, consumerGroupBuilder);
return consumerGroupBuilder;
}

private async Task<EventHubResource> CreateEventhubResourceAsync()
{
ResourceProvider.TestLogger.WriteLine($"Creating event hub '{EventHubName}'");

Expand All @@ -83,5 +103,32 @@ public async Task<EventHubResource> CreateAsync()

return eventHubResource;
}

private async Task CreateConsumerGroupsAsync(EventHubResource eventHubResource)
{
var managementClient = await ResourceProvider.LazyManagementClient
.ConfigureAwait(false);

foreach (var consumerGroupBuilderPair in ConsumerGroupBuilders)
{
var consumerGroup = consumerGroupBuilderPair.Value;

var createdConsumerGroup = await managementClient.ConsumerGroups
.CreateOrUpdateAsync(
ResourceProvider.ResourceManagementSettings.ResourceGroup,
ResourceProvider.EventHubNamespace,
eventHubResource.Name,
consumerGroup.ConsumerGroupName,
consumerGroup.UserMetadata)
.ConfigureAwait(false);

eventHubResource.AddConsumerGroup(createdConsumerGroup);

foreach (var postAction in consumerGroupBuilderPair.Value.PostActions)
{
postAction(createdConsumerGroup);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2020 Energinet DataHub A/S
//
// Licensed under the Apache License, Version 2.0 (the "License2");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://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 System.Threading.Tasks;

namespace Energinet.DataHub.Core.FunctionApp.TestCommon.EventHub.ResourceProvider;

/// <summary>
/// Part of fluent API for creating an Event Hub with consumer groups.
/// </summary>
public interface IEventHubResourceBuilder
{
/// <summary>
/// Add a Consumer Group to the Event Hub being created.
/// </summary>
/// <param name="consumerGroupName">Name of consumer group to add.</param>
/// <param name="userMetaData">Metadata for consumer group to add (optional).</param>
/// <returns>EventHub consumer group builder.</returns>
EventHubConsumerGroupBuilder AddConsumerGroup(string consumerGroupName, string? userMetaData = default);

/// <summary>
/// Create event hub according to configured builder.
/// </summary>
/// <returns>Instance with information about the created event hub.</returns>
Task<EventHubResource> CreateAsync();
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ limitations under the License.

<PropertyGroup>
<PackageId>Energinet.DataHub.Core.FunctionApp.TestCommon</PackageId>
<PackageVersion>4.5.0$(VersionSuffix)</PackageVersion>
<PackageVersion>4.6.0$(VersionSuffix)</PackageVersion>
<Title>FunctionApp TestCommon library</Title>
<Company>Energinet-DataHub</Company>
<Authors>Energinet-DataHub</Authors>
Expand Down
2 changes: 1 addition & 1 deletion source/TestCommon/source/TestCommon/TestCommon.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ limitations under the License.

<PropertyGroup>
<PackageId>Energinet.DataHub.Core.TestCommon</PackageId>
<PackageVersion>4.5.0$(VersionSuffix)</PackageVersion>
<PackageVersion>4.6.0$(VersionSuffix)</PackageVersion>
<Title>TestCommon library</Title>
<Company>Energinet-DataHub</Company>
<Authors>Energinet-DataHub</Authors>
Expand Down
Loading