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
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,13 @@
<PackageVersion Include="Grpc.StatusProto" Version="2.76.0" />
<PackageVersion Include="Grpc.Tools" Version="2.76.0" />
<PackageVersion Include="HtmlTags" Version="9.0.0" />
<PackageVersion Include="JasperFx" Version="2.2.3" />
<PackageVersion Include="JasperFx.Events" Version="2.2.3" />
<PackageVersion Include="JasperFx.Events.SourceGenerator" Version="2.2.3" />
<PackageVersion Include="JasperFx" Version="2.2.4" />
<PackageVersion Include="JasperFx.Events" Version="2.2.4" />
<PackageVersion Include="JasperFx.Events.SourceGenerator" Version="2.2.4" />
<!-- RuntimeCompiler is on its own 5.x line (the Roslyn compiler package) — not the 2.1.x
family; it stays at 5.0.0. -->
<PackageVersion Include="JasperFx.RuntimeCompiler" Version="5.0.0" />
<PackageVersion Include="JasperFx.SourceGenerator" Version="2.2.3" />
<PackageVersion Include="JasperFx.SourceGenerator" Version="2.2.4" />
<PackageVersion Include="Marten" Version="9.2.0" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.1.3" />
<PackageVersion Include="Polecat" Version="4.2.1" />
Expand Down
15 changes: 15 additions & 0 deletions src/Http/Wolverine.Http/CodeGen/JsonHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using JasperFx.Core.Reflection;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Wolverine.Configuration;
using Wolverine.Runtime;

namespace Wolverine.Http.CodeGen;
Expand Down Expand Up @@ -40,6 +41,20 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)

Next?.GenerateCode(method, writer);
}

public override void GenerateFSharpCode(GeneratedMethod method, ISourceWriter writer)
{
writer.WriteComment("Reading the request body via JSON deserialization");

// ReadJsonAsync is an inherited *instance* method on HttpHandler (qualified with the member's
// `this` self identifier, jasperfx#393) returning (body, continuation). F# has no early
// `return`, so the abort guard renders the rest of the chain inside its `else` branch.
writer.Write(
$"let! ({Variable.Usage}, jsonContinue) = this.{nameof(HttpHandler.ReadJsonAsync)}<{Variable.VariableType.FSharpName()}>(httpContext)");

var condition = $"jsonContinue = {typeof(HandlerContinuation).FSharpName()}.{nameof(HandlerContinuation.Stop)}";
FSharpEmitHelpers.WriteAbortGuard(writer, method, condition, Next);
}
}

internal class JsonBodyParameterStrategy : IParameterStrategy
Expand Down
12 changes: 12 additions & 0 deletions src/Http/Wolverine.Http/CodeGen/WriteJsonFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,16 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
writer.Write($"await {nameof(HttpHandler.WriteJsonAsync)}(httpContext, {_resourceVariable.Usage});");
Next?.GenerateCode(method, writer);
}

public override void GenerateFSharpCode(GeneratedMethod method, ISourceWriter writer)
{
writer.WriteComment("Writing the response body to JSON because this was the first 'return variable' in the method signature");

// WriteJsonAsync is an inherited *instance* method on HttpHandler, so it must be qualified with
// the generated member's `this` self identifier (jasperfx#393).
var call = $"this.{nameof(HttpHandler.WriteJsonAsync)}(httpContext, {_resourceVariable.Usage})";
writer.Write(method.AsyncMode == AsyncMode.AsyncTask ? $"do! {call}" : call);

Next?.GenerateFSharpCode(method, writer);
}
}
13 changes: 8 additions & 5 deletions src/Testing/Wolverine.Core.FSharpFixture/Generated.fs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type CheckThingHandler649476295(loggerForMessage: Microsoft.Extensions.Logging.I
inherit Wolverine.Runtime.Handlers.MessageHandler()
let _loggerForMessage = loggerForMessage

override _.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
override this.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
// The actual message body
let checkThing = context.Envelope.Message :?> Wolverine.Core.FSharpContracts.CheckThing

Expand All @@ -32,13 +32,14 @@ type CheckThingHandler649476295(loggerForMessage: Microsoft.Extensions.Logging.I
// The actual message execution
checkThingHandler.Handle(checkThing)

this.RecordCauseAndEffect(context, context.Runtime.Observer)
System.Threading.Tasks.Task.CompletedTask

type CreateNameHandler1923366998(loggerForMessage: Microsoft.Extensions.Logging.ILogger<Wolverine.Core.FSharpContracts.CreateName>) =
inherit Wolverine.Runtime.Handlers.MessageHandler()
let _loggerForMessage = loggerForMessage

override _.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
override this.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
task {
// The actual message body
let createName = context.Envelope.Message :?> Wolverine.Core.FSharpContracts.CreateName
Expand All @@ -64,11 +65,12 @@ type CreateNameHandler1923366998(loggerForMessage: Microsoft.Extensions.Logging.
// Outgoing, cascaded message
do! context.EnqueueCascadingAsync(outgoing1)

this.RecordCauseAndEffect(context, context.Runtime.Observer)
}

type GateHandler1696712162() =
inherit Wolverine.Runtime.Handlers.MessageHandler()
override _.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
override this.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
// The actual message body
let gate = context.Envelope.Message :?> Wolverine.Core.FSharpContracts.Gate

Expand All @@ -86,13 +88,14 @@ type GateHandler1696712162() =
// The actual message execution
gateHandler.Handle(gate)

this.RecordCauseAndEffect(context, context.Runtime.Observer)
System.Threading.Tasks.Task.CompletedTask

type IncrementCountHandler540640831(inMemorySagaPersistor: Wolverine.Persistence.Sagas.InMemorySagaPersistor) =
inherit Wolverine.Runtime.Handlers.MessageHandler()
let _inMemorySagaPersistor = inMemorySagaPersistor

override _.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
override this.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
// The actual message body
let incrementCount = context.Envelope.Message :?> Wolverine.Core.FSharpContracts.IncrementCount

Expand Down Expand Up @@ -126,7 +129,7 @@ type StartCountHandler1561563330(inMemorySagaPersistor: Wolverine.Persistence.Sa
inherit Wolverine.Runtime.Handlers.MessageHandler()
let _inMemorySagaPersistor = inMemorySagaPersistor

override _.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
override this.HandleAsync(context: Wolverine.Runtime.MessageContext, cancellation: System.Threading.CancellationToken) : System.Threading.Tasks.Task =
// The actual message body
let startCount = context.Envelope.Message :?> Wolverine.Core.FSharpContracts.StartCount

Expand Down
4 changes: 4 additions & 0 deletions src/Testing/Wolverine.Core.FSharpTests/FSharpCodegenSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ public static string GenerateCode()

// Inserts ApplyExecutionDiagnosticTagsFrame at the head of every chain.
opts.Tracking.HandlerExecutionDiagnosticsEnabled = true;

// Inserts RecordMessageCausationFrame after the handler call (a `this.`-qualified
// inherited instance call, now resolvable per JasperFx 2.2.4 / jasperfx#393).
opts.Tracking.EnableMessageCausationTracking = true;
})
.Build();

Expand Down
22 changes: 21 additions & 1 deletion src/Testing/Wolverine.Http.FSharpFixture/Generated.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ type GET_fsharp_hello(wolverineHttpOptions: Wolverine.Http.WolverineHttpOptions)
inherit Wolverine.Http.HttpHandler(wolverineHttpOptions)
let _wolverineHttpOptions = wolverineHttpOptions

override _.Handle(httpContext: Microsoft.AspNetCore.Http.HttpContext) : System.Threading.Tasks.Task =
override this.Handle(httpContext: Microsoft.AspNetCore.Http.HttpContext) : System.Threading.Tasks.Task =
task {
let thingEndpoints = Wolverine.Http.FSharpContracts.ThingEndpoints()

Expand All @@ -23,3 +23,23 @@ type GET_fsharp_hello(wolverineHttpOptions: Wolverine.Http.WolverineHttpOptions)
do! Wolverine.Http.HttpHandler.WriteString(httpContext, result_of_Hello)
}

type POST_fsharp_things(wolverineHttpOptions: Wolverine.Http.WolverineHttpOptions) =
inherit Wolverine.Http.HttpHandler(wolverineHttpOptions)
let _wolverineHttpOptions = wolverineHttpOptions

override this.Handle(httpContext: Microsoft.AspNetCore.Http.HttpContext) : System.Threading.Tasks.Task =
task {
// Reading the request body via JSON deserialization
let! (command, jsonContinue) = this.ReadJsonAsync<Wolverine.Http.FSharpContracts.CreateThing>(httpContext)
if jsonContinue = Wolverine.HandlerContinuation.Stop then
()
else
let thingEndpoints = Wolverine.Http.FSharpContracts.ThingEndpoints()

// The actual HTTP request handler execution
let thingCreated_response = thingEndpoints.Create(command)

// Writing the response body to JSON because this was the first 'return variable' in the method signature
do! this.WriteJsonAsync(httpContext, thingCreated_response)
}

Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,13 @@ public static string GenerateCode()
var container = new ServiceContainer(registry, registry.BuildServiceProvider());
var httpGraph = new HttpGraph(new WolverineOptions { ApplicationAssembly = typeof(ThingEndpoints).Assembly }, container);

// Phase C increment 1 renders the static-response GET endpoint. The JSON POST endpoint
// (ReadJsonBody / WriteJsonFrame) calls *instance* HttpHandler methods unqualified, which F#
// cannot resolve from a `member _.Handle` body — blocked on the JasperFx F# self-identifier
// gap (tracked upstream; same gap as RecordMessageCausationFrame). Add it back once JasperFx
// emits a named self for generated members.
// GET (static string response) + POST (JSON body bind + JSON response). The JSON path calls
// the inherited instance HttpHandler methods ReadJsonAsync/WriteJsonAsync, now qualified with
// the generated member's `this` self identifier (JasperFx 2.2.4 / jasperfx#393).
var chains = new[]
{
HttpChain.ChainFor<ThingEndpoints>(x => x.Hello(), httpGraph)
HttpChain.ChainFor<ThingEndpoints>(x => x.Hello(), httpGraph),
HttpChain.ChainFor<ThingEndpoints>(x => x.Create(null!), httpGraph)
};

var generatedAssembly = httpGraph.StartAssembly(httpGraph.Rules);
Expand Down
10 changes: 10 additions & 0 deletions src/Wolverine/Runtime/RecordMessageCausationFrame.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,14 @@ public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)

Next?.GenerateCode(method, writer);
}

public override void GenerateFSharpCode(GeneratedMethod method, ISourceWriter writer)
{
// RecordCauseAndEffect is an inherited instance method on the generated MessageHandler subclass,
// so it must be qualified with the member's `this` self identifier (jasperfx#393).
writer.Write(
$"this.{nameof(MessageHandler.RecordCauseAndEffect)}({_context!.Usage}, {_context!.Usage}.Runtime.Observer)");

Next?.GenerateFSharpCode(method, writer);
}
}
Loading