-
-
Notifications
You must be signed in to change notification settings - Fork 146
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added new [Document] attribute in the Marten HTTP support
- Loading branch information
1 parent
488fb78
commit b18f69f
Showing
11 changed files
with
480 additions
and
91 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
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
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,71 @@ | ||
using System.Reflection; | ||
using JasperFx.CodeGeneration.Frames; | ||
using JasperFx.CodeGeneration.Model; | ||
using JasperFx.Core; | ||
using Lamar; | ||
using Marten; | ||
|
||
namespace Wolverine.Http.Marten; | ||
|
||
/// <summary> | ||
/// Marks a parameter to an HTTP endpoint as being loaded as a Marten | ||
/// document identified by a route argument. If the route argument | ||
/// is not specified, this would look for either "typeNameId" or "id" | ||
/// </summary> | ||
[AttributeUsage(AttributeTargets.Parameter)] | ||
public class DocumentAttribute : HttpChainParameterAttribute | ||
{ | ||
public string? RouteArgumentName { get; } | ||
|
||
public DocumentAttribute() | ||
{ | ||
} | ||
|
||
public DocumentAttribute(string routeArgumentName) | ||
{ | ||
RouteArgumentName = routeArgumentName; | ||
} | ||
|
||
public override Variable Modify(HttpChain chain, ParameterInfo parameter, IContainer container) | ||
{ | ||
var store = container.GetInstance<IDocumentStore>(); | ||
var documentType = parameter.ParameterType; | ||
var mapping = store.Options.FindOrResolveDocumentType(documentType); | ||
var idType = mapping.IdType; | ||
|
||
var argument = FindRouteVariable(idType, documentType, chain); | ||
|
||
var loader = typeof(IQuerySession).GetMethods() | ||
.FirstOrDefault(x => x.Name == nameof(IDocumentSession.LoadAsync) && x.GetParameters()[0].ParameterType == idType); | ||
|
||
var load = new MethodCall(typeof(IDocumentSession), loader.MakeGenericMethod(documentType)); | ||
load.Arguments[0] = argument; | ||
|
||
chain.Middleware.Add(load); | ||
|
||
return load.ReturnVariable; | ||
} | ||
|
||
public Variable? FindRouteVariable(Type idType, Type documentType, HttpChain chain) | ||
{ | ||
if (RouteArgumentName.IsNotEmpty()) | ||
{ | ||
if (chain.FindRouteVariable(idType, RouteArgumentName, out var variable)) | ||
{ | ||
return variable; | ||
} | ||
} | ||
|
||
if (chain.FindRouteVariable(idType, $"{documentType.Name.ToCamelCase()}Id", out var v2)) | ||
{ | ||
return v2; | ||
} | ||
|
||
if (chain.FindRouteVariable(idType, "id", out var v3)) | ||
{ | ||
return v3; | ||
} | ||
|
||
return null; | ||
} | ||
} |
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,53 @@ | ||
using System.Reflection; | ||
using JasperFx.CodeGeneration.Frames; | ||
using JasperFx.CodeGeneration.Model; | ||
using JasperFx.Core.Reflection; | ||
using Marten.Events; | ||
using Wolverine.Marten; | ||
|
||
namespace Wolverine.Http.Marten; | ||
|
||
internal class LoadAggregateFrame<T> : MethodCall where T : class | ||
{ | ||
private readonly AggregateAttribute _att; | ||
|
||
public LoadAggregateFrame(AggregateAttribute att) : base(typeof(IEventStore), FindMethod(att)) | ||
{ | ||
_att = att; | ||
CommentText = "Loading Marten aggregate"; | ||
} | ||
|
||
public override IEnumerable<Variable> FindVariables(IMethodVariables chain) | ||
{ | ||
Arguments[0] = _att.IdVariable; | ||
if (_att.LoadStyle == ConcurrencyStyle.Optimistic && _att.VersionVariable != null) | ||
{ | ||
Arguments[1] = _att.VersionVariable; | ||
} | ||
|
||
foreach (var variable in base.FindVariables(chain)) yield return variable; | ||
} | ||
|
||
internal static MethodInfo FindMethod(AggregateAttribute att) | ||
{ | ||
var isGuidIdentified = att.IdVariable.VariableType == typeof(Guid); | ||
|
||
if (att.LoadStyle == ConcurrencyStyle.Exclusive) | ||
{ | ||
return isGuidIdentified | ||
? ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForExclusiveWriting<T>(Guid.Empty, default))! | ||
: ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForExclusiveWriting<T>(string.Empty, default))!; | ||
} | ||
|
||
if (att.VersionVariable == null) | ||
{ | ||
return isGuidIdentified | ||
? ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForWriting<T>(Guid.Empty, default))! | ||
: ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForWriting<T>(string.Empty, default))!; | ||
} | ||
|
||
return isGuidIdentified | ||
? ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForWriting<T>(Guid.Empty, long.MaxValue, default))! | ||
: ReflectionHelper.GetMethod<IEventStore>(x => x.FetchForWriting<T>(string.Empty, long.MaxValue, default))!; | ||
} | ||
} |
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,34 @@ | ||
using System.Reflection; | ||
using JasperFx.CodeGeneration; | ||
using JasperFx.CodeGeneration.Frames; | ||
using JasperFx.CodeGeneration.Model; | ||
using JasperFx.Core.Reflection; | ||
|
||
namespace Wolverine.Http.Marten; | ||
|
||
internal class MemberAccessFrame : SyncFrame | ||
{ | ||
private readonly Type _targetType; | ||
private readonly MemberInfo _member; | ||
private Variable _parent; | ||
public Variable Variable { get; } | ||
|
||
public MemberAccessFrame(Type targetType, MemberInfo member, string name) | ||
{ | ||
_targetType = targetType; | ||
_member = member; | ||
Variable = new Variable(member.GetMemberType(), name, this); | ||
} | ||
|
||
public override void GenerateCode(GeneratedMethod method, ISourceWriter writer) | ||
{ | ||
writer.Write($"var {Variable.Usage} = {_parent.Usage}.{_member.Name};"); | ||
Next?.GenerateCode(method, writer); | ||
} | ||
|
||
public override IEnumerable<Variable> FindVariables(IMethodVariables chain) | ||
{ | ||
_parent = chain.FindVariable(_targetType); | ||
yield return _parent; | ||
} | ||
} |
71 changes: 71 additions & 0 deletions
71
src/Http/Wolverine.Http.Tests/Marten/document_attribute_usage.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,71 @@ | ||
using Alba; | ||
using Shouldly; | ||
using WolverineWebApi.Marten; | ||
|
||
namespace Wolverine.Http.Tests.Marten; | ||
|
||
public class document_attribute_usage : IntegrationContext | ||
{ | ||
public document_attribute_usage(AppFixture fixture) : base(fixture) | ||
{ | ||
} | ||
|
||
[Fact] | ||
public async Task returns_404_on_id_miss() | ||
{ | ||
await Scenario(x => | ||
{ | ||
x.Get.Url("/invoices/" + Guid.NewGuid()); | ||
x.StatusCodeShouldBe(404); | ||
}); | ||
} | ||
|
||
[Fact] | ||
public async Task default_to_id_route() | ||
{ | ||
var invoice = new Invoice(); | ||
using var session = Store.LightweightSession(); | ||
session.Store(invoice); | ||
await session.SaveChangesAsync(); | ||
|
||
|
||
var invoice2 = await Host.GetAsJson<Invoice>("/invoices/" + invoice.Id); | ||
invoice2.ShouldNotBeNull(); | ||
} | ||
|
||
[Fact] | ||
public async Task try_to_use_document_name_id_naming_convention() | ||
{ | ||
var invoice = new Invoice(); | ||
using var session = Store.LightweightSession(); | ||
session.Store(invoice); | ||
await session.SaveChangesAsync(); | ||
|
||
await Host.Scenario(x => | ||
{ | ||
x.Post.Url($"/invoices/{invoice.Id}/pay"); | ||
x.StatusCodeShouldBe(204); | ||
}); | ||
|
||
var loaded = await session.LoadAsync<Invoice>(invoice.Id); | ||
loaded.Paid.ShouldBeTrue(); | ||
} | ||
|
||
[Fact] | ||
public async Task use_explicit_path_argument() | ||
{ | ||
var invoice = new Invoice(); | ||
using var session = Store.LightweightSession(); | ||
session.Store(invoice); | ||
await session.SaveChangesAsync(); | ||
|
||
await Host.Scenario(x => | ||
{ | ||
x.Post.Url($"/invoices/{invoice.Id}/approve"); | ||
x.StatusCodeShouldBe(204); | ||
}); | ||
|
||
var loaded = await session.LoadAsync<Invoice>(invoice.Id); | ||
loaded.Approved.ShouldBeTrue(); | ||
} | ||
} |
63 changes: 63 additions & 0 deletions
63
src/Http/WolverineWebApi/Internal/Generated/WolverineHandlers/GET_invoices_id.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,63 @@ | ||
// <auto-generated/> | ||
#pragma warning disable | ||
using Microsoft.AspNetCore.Routing; | ||
using System; | ||
using System.Linq; | ||
using Wolverine.Http; | ||
using Wolverine.Marten.Publishing; | ||
using Wolverine.Runtime; | ||
|
||
namespace Internal.Generated.WolverineHandlers | ||
{ | ||
// START: GET_invoices_id | ||
public class GET_invoices_id : Wolverine.Http.HttpHandler | ||
{ | ||
private readonly Wolverine.Http.WolverineHttpOptions _wolverineHttpOptions; | ||
private readonly Wolverine.Runtime.IWolverineRuntime _wolverineRuntime; | ||
private readonly Wolverine.Marten.Publishing.OutboxedSessionFactory _outboxedSessionFactory; | ||
|
||
public GET_invoices_id(Wolverine.Http.WolverineHttpOptions wolverineHttpOptions, Wolverine.Runtime.IWolverineRuntime wolverineRuntime, Wolverine.Marten.Publishing.OutboxedSessionFactory outboxedSessionFactory) : base(wolverineHttpOptions) | ||
{ | ||
_wolverineHttpOptions = wolverineHttpOptions; | ||
_wolverineRuntime = wolverineRuntime; | ||
_outboxedSessionFactory = outboxedSessionFactory; | ||
} | ||
|
||
|
||
|
||
public override async System.Threading.Tasks.Task Handle(Microsoft.AspNetCore.Http.HttpContext httpContext) | ||
{ | ||
if (!System.Guid.TryParse((string)httpContext.GetRouteValue("id"), out var id)) | ||
{ | ||
httpContext.Response.StatusCode = 404; | ||
return; | ||
} | ||
|
||
|
||
var messageContext = new Wolverine.Runtime.MessageContext(_wolverineRuntime); | ||
// Building the Marten session | ||
await using var documentSession = _outboxedSessionFactory.OpenSession(messageContext); | ||
var invoice = await documentSession.LoadAsync<WolverineWebApi.Marten.Invoice>(id, httpContext.RequestAborted).ConfigureAwait(false); | ||
|
||
// The actual HTTP request handler execution | ||
var invoice_response = WolverineWebApi.Marten.InvoicesEndpoint.Get(invoice); | ||
|
||
|
||
// Commit any outstanding Marten changes | ||
await documentSession.SaveChangesAsync(httpContext.RequestAborted).ConfigureAwait(false); | ||
|
||
|
||
// Have to flush outgoing messages just in case Marten did nothing because of https://github.com/JasperFx/wolverine/issues/536 | ||
await messageContext.FlushOutgoingMessagesAsync().ConfigureAwait(false); | ||
|
||
// Writing the response body to JSON because this was the first 'return variable' in the method signature | ||
await WriteJsonAsync(httpContext, invoice_response); | ||
} | ||
|
||
} | ||
|
||
// END: GET_invoices_id | ||
|
||
|
||
} | ||
|
Oops, something went wrong.