Skip to content
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 @@ -4,13 +4,16 @@
using JasperFx;
using JasperFx.CodeGeneration.Frames;
using JasperFx.CodeGeneration.Model;
using JasperFx.Core;
using Marten;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NSubstitute;
using Shouldly;
using Wolverine.Attributes;
using Wolverine.ComplianceTests;
using Wolverine.Configuration;
using Wolverine.Marten;

Expand Down Expand Up @@ -43,6 +46,18 @@ private async Task<IAlbaHost> buildHost(ServiceProviderSource providerSource, Ac

opts.Services.AddScoped<IThing>(s => new BigThing());

opts.IncludeType(typeof(CSP5User));
opts.CodeGeneration.AlwaysUseServiceLocationFor<IFlag>();

opts.Services.AddScoped<IGateway, Gateway>();
opts.Services.AddScoped<IFlag>(x =>
{
var context = x.GetRequiredService<ColorContext>();
return context.Color.EqualsIgnoreCase("red") ? new RedFlag() : new GreenFlag();
});

opts.Services.AddSingleton(new ColorContext("Red"));

configure(opts);
});

Expand Down Expand Up @@ -196,6 +211,47 @@ public async Task can_use_service_locations_with_handler(ServiceLocationPolicy p


}

[Theory]
[InlineData(ServiceLocationPolicy.AllowedButWarn, ServiceProviderSource.IsolatedAndScoped)]
[InlineData(ServiceLocationPolicy.AlwaysAllowed, ServiceProviderSource.IsolatedAndScoped)]
[InlineData(ServiceLocationPolicy.AllowedButWarn, ServiceProviderSource.FromHttpContextRequestServices)]
[InlineData(ServiceLocationPolicy.AlwaysAllowed, ServiceProviderSource.FromHttpContextRequestServices)]
public async Task always_use_service_location_does_not_count_toward_validation(ServiceLocationPolicy policy, ServiceProviderSource source)
{
await using var host = await buildHost(source, opts =>
{
opts.ServiceLocationPolicy = policy;
});

CSP5User.Flag = null;

await host.InvokeAsync(new CSP5());
CSP5User.Flag.ShouldBeOfType<RedFlag>();
}

[Theory]
[InlineData(ServiceLocationPolicy.AllowedButWarn, ServiceProviderSource.IsolatedAndScoped)]
[InlineData(ServiceLocationPolicy.AlwaysAllowed, ServiceProviderSource.IsolatedAndScoped)]
[InlineData(ServiceLocationPolicy.AllowedButWarn, ServiceProviderSource.FromHttpContextRequestServices)]
[InlineData(ServiceLocationPolicy.AlwaysAllowed, ServiceProviderSource.FromHttpContextRequestServices)]
public async Task always_use_service_location_does_not_count_toward_validation_in_http_endpoint(ServiceLocationPolicy policy, ServiceProviderSource source)
{
await using var host = await buildHost(source, opts =>
{
opts.ServiceLocationPolicy = policy;
});

CSP5User.Flag = null;

await host.Scenario(x =>
{
x.Post.Json(new CSP5()).ToUrl("/csp5");
x.StatusCodeShouldBe(204);
});

CSP5User.Flag.ShouldBeOfType<RedFlag>();
}
}

public interface IWidget;
Expand Down Expand Up @@ -261,4 +317,28 @@ public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Except
{
Messages.Add(formatter(state, exception));
}
}
}

public record CSP5;

public static class CSP5User
{
public static IFlag? Flag { get; set; }

[WolverinePost("/csp5")]
public static void Handle(CSP5 message, IFlag flag, IGateway gateway)
{
Flag = flag;
}
}



public interface IFlag;
public record ColorContext(string Color);

public record RedFlag : IFlag;
public record GreenFlag : IFlag;

public interface IGateway;
public class Gateway : IGateway;
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public async Task InitializeAsync()
opts.Services.AddMartenStore<IThingStore>(m =>
{
m.Connection(thingsConnectionString);
}).IntegrateWithWolverine(masterDatabaseConnectionString: Servers.PostgresConnectionString);
}).IntegrateWithWolverine(x => x.MainConnectionString = Servers.PostgresConnectionString);

opts.Services.AddResourceSetupOnStartup();
}).StartAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ public async Task InitializeAsync()
tenancy.AddSingleTenantDatabase(tenant3ConnectionString, "tenant3");
});
m.DatabaseSchemaName = "things";
}).IntegrateWithWolverine(masterDatabaseConnectionString: Servers.PostgresConnectionString);
}).IntegrateWithWolverine(x => x.MainConnectionString = Servers.PostgresConnectionString);

opts.Services.AddResourceSetupOnStartup();
}).StartAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,13 @@ public async Task do_not_override_when_the_schema_name_is_explicitly_set()
{
m.Connection(Servers.PostgresConnectionString);
m.DatabaseSchemaName = "players";
}).IntegrateWithWolverine(schemaName:"different");
}).IntegrateWithWolverine(x => x.SchemaName = "different");

opts.Services.AddMartenStore<IThingStore>(m =>
{
m.Connection(Servers.PostgresConnectionString);
m.DatabaseSchemaName = "things";
}).IntegrateWithWolverine(schemaName:"different");
}).IntegrateWithWolverine(x => x.SchemaName = "different");

opts.Services.AddResourceSetupOnStartup();
}).StartAsync();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,40 +27,60 @@

namespace Wolverine.Marten;

public static class AncillaryWolverineOptionsMartenExtensions
public class AncillaryMartenIntegration
{
/// <summary>
/// Integrate Marten with Wolverine's persistent outbox and add Marten-specific middleware
/// to Wolverine
/// Optionally move the Wolverine envelope storage to a separate schema.
/// The recommendation would be to either leave this null, or use the same
/// schema name as the main Marten store
/// </summary>
/// <param name="expression"></param>
/// <param name="schemaName">Optionally move the Wolverine envelope storage to a separate schema</param>
/// <param name="masterDataSource">
public string? SchemaName { get; set; }

/// <summary>
/// In the case of Marten using a database per tenant, you may wish to
/// explicitly determine the master database for Wolverine where Wolverine will store node and envelope information.
/// This does not have to be one of the tenant databases
/// Wolverine will try to use the master database from the Marten configuration when possible
/// </param>
/// <param name="masterDatabaseConnectionString">
/// </summary>
public string? MainConnectionString { get; set; }

/// <summary>
/// In the case of Marten using a database per tenant, you may wish to
/// explicitly determine the master database for Wolverine where Wolverine will store node and envelope information.
/// This does not have to be one of the tenant databases
/// Wolverine will try to use the master database from the Marten configuration when possible
/// </param>
/// <param name="autoCreate">Optionally override whether to automatically create message database schema objects. Defaults to <see cref="StoreOptions.AutoCreateSchemaObjects"/>.</param>
/// <returns></returns>
public static MartenServiceCollectionExtensions.MartenStoreExpression<T> IntegrateWithWolverine<T>(
this MartenServiceCollectionExtensions.MartenStoreExpression<T> expression,
string? schemaName = null,
string? masterDatabaseConnectionString = null,
NpgsqlDataSource? masterDataSource = null,
AutoCreate? autoCreate = null) where T : class, IDocumentStore
/// </summary>
public NpgsqlDataSource? MainDataSource { get; set; }

/// <summary>
/// Optionally override whether to automatically create message database schema objects. Defaults to <see cref="StoreOptions.AutoCreateSchemaObjects"/>.
/// </summary>
public AutoCreate? AutoCreate { get; set; }

internal void AssertValidity()
{
if (schemaName.IsNotEmpty() && schemaName != schemaName.ToLowerInvariant())
if (SchemaName.IsNotEmpty() && SchemaName != SchemaName.ToLowerInvariant())
{
throw new ArgumentOutOfRangeException(nameof(schemaName),
throw new ArgumentOutOfRangeException(nameof(SchemaName),
"The schema name must be in all lower case characters");
}
}
}

public static class AncillaryWolverineOptionsMartenExtensions
{
/// <summary>
/// Integrate Marten with Wolverine's persistent outbox and add Marten-specific middleware
/// to Wolverine
/// </summary>
/// <param name="configure">Optional configuration of ancillary Marten integration</param>
public static MartenServiceCollectionExtensions.MartenStoreExpression<T> IntegrateWithWolverine<T>(
this MartenServiceCollectionExtensions.MartenStoreExpression<T> expression,
Action<AncillaryMartenIntegration>? configure = null) where T : class, IDocumentStore
{
var integration = new AncillaryMartenIntegration();
configure?.Invoke(integration);
integration.AssertValidity();

expression.Services.AddSingleton<IConfigureMarten<T>, MartenOverrides<T>>();

Expand All @@ -71,15 +91,15 @@ public static MartenServiceCollectionExtensions.MartenStoreExpression<T> Integra
var runtime = s.GetRequiredService<IWolverineRuntime>();
var logger = s.GetRequiredService<ILogger<PostgresqlMessageStore>>();

schemaName ??= runtime.Options.Durability.MessageStorageSchemaName ?? store.Options.DatabaseSchemaName;
integration.SchemaName ??= runtime.Options.Durability.MessageStorageSchemaName ?? store.Options.DatabaseSchemaName;

// TODO -- hacky. Need a way to expose this in Marten
if (store.Tenancy.GetType().Name == "DefaultTenancy")
{
return BuildSinglePostgresqlMessageStore<T>(schemaName, autoCreate, store, runtime, logger);
return BuildSinglePostgresqlMessageStore<T>(integration.SchemaName, integration.AutoCreate, store, runtime, logger);
}

return BuildMultiTenantedMessageDatabase<T>(schemaName, autoCreate, masterDatabaseConnectionString, masterDataSource, store, runtime);
return BuildMultiTenantedMessageDatabase<T>(integration.SchemaName, integration.AutoCreate, integration.MainConnectionString, integration.MainDataSource, store, runtime);
});

expression.Services.AddType(typeof(IDatabaseSource), typeof(MessageDatabaseDiscovery),
Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/Wolverine.Marten/Wolverine.Marten.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<ProjectReference Include="..\Wolverine.Postgresql\Wolverine.Postgresql.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Marten" Version="8.12.0" />
<PackageReference Include="Marten" Version="8.13.0" />
</ItemGroup>
<Import Project="../../../Analysis.Build.props" />
</Project>
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
using JasperFx.CodeGeneration;
using JasperFx.Core;
using Microsoft.Extensions.DependencyInjection;
using Wolverine.Attributes;
using Wolverine.ComplianceTests;
using Xunit;
using Xunit.Abstractions;
Expand Down Expand Up @@ -33,6 +35,29 @@ public async Task IServiceProvider_as_method_parameter()
{
await Execute(new CSP4());
}

[Fact]
public async Task using_service_location_with_one_service()
{
IfWolverineIsConfiguredAs(opts =>
{
opts.IncludeType(typeof(CSP5User));
opts.CodeGeneration.AlwaysUseServiceLocationFor<IFlag>();

opts.Services.AddScoped<IGateway, Gateway>();
opts.Services.AddScoped<IFlag>(x =>
{
var context = x.GetRequiredService<ColorContext>();
return context.Color.EqualsIgnoreCase("red") ? new RedFlag() : new GreenFlag();
});

opts.Services.AddSingleton(new ColorContext("Red"));
});

await Execute(new CSP5());

CSP5User.Flag.ShouldBeOfType<RedFlag>();
}
}

public class CSP3;
Expand Down Expand Up @@ -60,4 +85,27 @@ public void Handle(CSP4 message, IServiceProvider container)
{
container.ShouldNotBeNull();
}
}
}

public record CSP5;

// Just need this to be explicit
[WolverineIgnore]
public static class CSP5User
{
public static IFlag? Flag { get; set; }

public static void Handle(CSP5 message, IFlag flag, IGateway gateway)
{
Flag = flag;
}
}

public interface IFlag;
public record ColorContext(string Color);

public record RedFlag : IFlag;
public record GreenFlag : IFlag;

public interface IGateway;
public class Gateway : IGateway;
2 changes: 1 addition & 1 deletion src/Wolverine/Wolverine.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<PackageId>WolverineFx</PackageId>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="JasperFx" Version="1.8.1" />
<PackageReference Include="JasperFx" Version="1.9.0" />
<PackageReference Include="JasperFx.RuntimeCompiler" Version="4.0.0" />
<PackageReference Include="NewId" Version="4.0.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
Loading