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

feat: add multi-tenancy builder #135

Merged
merged 1 commit into from
Nov 19, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,8 @@
<PackageTags>$(PackageCommonTags);multi-tenancy</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (c) Raphael Strotz. All rights reserved.

namespace Ayaka.MultiTenancy.DependencyInjection;

using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Provides functionality to configure multi-tenancy services.
/// </summary>
public interface IMultiTenancyBuilder
{
/// <summary>
/// Gets the <see cref="IServiceCollection"/> where multi-tenancy services are configured.
/// </summary>
IServiceCollection Services { get; }
}
2 changes: 2 additions & 0 deletions src/Ayaka.MultiTenancy.Abstractions/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#nullable enable
Ayaka.MultiTenancy.DependencyInjection.IMultiTenancyBuilder
Ayaka.MultiTenancy.DependencyInjection.IMultiTenancyBuilder.Services.get -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
Ayaka.MultiTenancy.ITenantContextAccessor
Ayaka.MultiTenancy.ITenantContextAccessor.TenantContext.get -> Ayaka.MultiTenancy.TenantContext?
Ayaka.MultiTenancy.ITenantContextAccessor.TenantContext.set -> void
Expand Down
8 changes: 4 additions & 4 deletions src/Ayaka.MultiTenancy/Ayaka.MultiTenancy.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
<TargetFramework>$(DotNetTargetFramework)</TargetFramework>
</PropertyGroup>

<ItemGroup>
<InternalsVisibleTo Include="Ayaka.MultiTenancy.Tests" />
</ItemGroup>

<PropertyGroup>
<PackageDescription>Provides functionality for creating multi-tenanted applications. $(PackageDescriptionAppendix)</PackageDescription>
<PackageTags>$(PackageCommonTags);multi-tenancy</PackageTags>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Ayaka.MultiTenancy.Abstractions\Ayaka.MultiTenancy.Abstractions.csproj" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Copyright (c) Raphael Strotz. All rights reserved.

namespace Ayaka.MultiTenancy.DependencyInjection;

using Microsoft.Extensions.DependencyInjection;

/// <summary>
/// Allows configuration of multi-tenancy services.
/// </summary>
internal sealed class MultiTenancyBuilder : IMultiTenancyBuilder
{
/// <summary>
/// Initializes a new instance of the <see cref="MultiTenancyBuilder"/> class.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection" /> to add services to.</param>
public MultiTenancyBuilder(IServiceCollection services)
{
Services = services;
}

/// <inheritdoc />
public IServiceCollection Services { get; }
}
1 change: 1 addition & 0 deletions src/Ayaka.MultiTenancy/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ Ayaka.MultiTenancy.AsyncLocalTenantContextAccessor.AsyncLocalTenantContextAccess
Ayaka.MultiTenancy.AsyncLocalTenantContextAccessor.TenantContext.get -> Ayaka.MultiTenancy.TenantContext?
Ayaka.MultiTenancy.AsyncLocalTenantContextAccessor.TenantContext.set -> void
Ayaka.MultiTenancy.ServiceCollectionExtensions
static Ayaka.MultiTenancy.ServiceCollectionExtensions.AddMultiTenancy(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Ayaka.MultiTenancy.DependencyInjection.IMultiTenancyBuilder!
static Ayaka.MultiTenancy.ServiceCollectionExtensions.AddTenantContextAccessor(this Microsoft.Extensions.DependencyInjection.IServiceCollection! services) -> Microsoft.Extensions.DependencyInjection.IServiceCollection!
24 changes: 24 additions & 0 deletions src/Ayaka.MultiTenancy/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Ayaka.MultiTenancy;

using Ayaka.MultiTenancy.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

Expand All @@ -10,6 +11,22 @@ namespace Ayaka.MultiTenancy;
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Adds multi-tenancy services to the specified <see cref="IServiceCollection"/>.
/// </summary>
/// <remarks>
/// In order to fine-tune the multi-tenancy configuration, use the <see cref="IMultiTenancyBuilder"/> returned by
/// this method.
/// </remarks>
/// <param name="services">The <see cref="IServiceCollection"/> to add the service to.</param>
/// <returns>A <see cref="IMultiTenancyBuilder"/> that can be used to further configure multi-tenancy.</returns>
public static IMultiTenancyBuilder AddMultiTenancy(this IServiceCollection services)
{
ConfigureDefaultServices(services);

return new MultiTenancyBuilder(services);
}

/// <summary>
/// Adds the default implementation for the <see cref="ITenantContextAccessor"/> service.
/// </summary>
Expand All @@ -20,4 +37,11 @@ public static IServiceCollection AddTenantContextAccessor(this IServiceCollectio
services.TryAddSingleton<ITenantContextAccessor, AsyncLocalTenantContextAccessor>();
return services;
}

[SuppressMessage("Style", "IDE0058:Expression value is never used")]
private static void ConfigureDefaultServices(this IServiceCollection services)
{
// The heart of multi-tenancy
services.AddTenantContextAccessor();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,57 @@

namespace Ayaka.MultiTenancy.Tests;

using Ayaka.MultiTenancy.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;

public sealed class ServiceCollectionExtensionsTest
{
public sealed class AddMultiTenancy
{
[Fact]
public void Does_return_instance_of_MultiTenancyBuilder()
{
var services = new ServiceCollection();

var builder = services.AddMultiTenancy();

builder.Should().NotBeNull();
builder.Should().BeOfType<MultiTenancyBuilder>();
}

[Fact]
public void Does_use_specified_service_collection()
{
var services = new ServiceCollection();

var builder = services.AddMultiTenancy();

builder.Services.Should().BeSameAs(services);
}

[Fact]
public void Does_add_default_services()
{
var services = new ServiceCollection();

services.AddMultiTenancy();

var accessor = services.FirstOrDefault(x => x.ServiceType == typeof(ITenantContextAccessor));
accessor.Should().NotBeNull("ITenantContextAccessor should be registered");
}

[Fact]
public void Does_not_add_default_services_twice()
{
var services = new ServiceCollection();

services.AddMultiTenancy();
services.AddMultiTenancy();

services.Count(x => x.ServiceType == typeof(ITenantContextAccessor)).Should().Be(1);
}
}

public sealed class AddTenantContextAccessor
{
[Fact]
Expand Down