Skip to content

Commit

Permalink
Using 204 as the status code when there's no resource. Closes GH-303
Browse files Browse the repository at this point in the history
  • Loading branch information
Jeremy D. Miller authored and Jeremy D. Miller committed Apr 19, 2023
1 parent 42c3361 commit 1f64c2a
Show file tree
Hide file tree
Showing 36 changed files with 118 additions and 526 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public async Task do_not_blow_up()
var (tracked, result) = await TrackedHttpCall(x =>
{
x.Post.Json(new TelegramUpdated("foo")).ToUrl("/convert-book");
x.StatusCodeShouldBe(204);
});

tracked.Executed.SingleMessage<TelegramUpdated>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ public async Task concrete_type_decorated_with_NotBody_is_sourced_by_container()
var recorder = Host.Services.GetRequiredService<Recorder>();
recorder.Actions.Clear();

await Scenario(x => { x.Post.Url("/notbody"); });
await Scenario(x =>
{
x.Post.Url("/notbody");
});

recorder.Actions.Single().ShouldBe("Called AttributesEndpoints.Post()");
}
Expand Down
12 changes: 10 additions & 2 deletions src/Http/Wolverine.Http.Tests/using_efcore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ public async Task using_db_context_without_outbox()

var command = new CreateItemCommand { Name = "Isaiah Pacheco" };

await Scenario(x => { x.Post.Json(command).ToUrl("/ef/create"); });
await Scenario(x =>
{
x.Post.Json(command).ToUrl("/ef/create");
x.StatusCodeShouldBe(204);
});

using var nested = Host.Services.As<IContainer>().GetNestedContainer();
var context = nested.GetInstance<ItemsDbContext>();
Expand All @@ -40,7 +44,11 @@ public async Task using_db_context_with_outbox()

var command = new CreateItemCommand { Name = "Jerick McKinnon" };

var (tracked, _) = await TrackedHttpCall(x => { x.Post.Json(command).ToUrl("/ef/publish"); });
var (tracked, _) = await TrackedHttpCall(x =>
{
x.Post.Json(command).ToUrl("/ef/publish");
x.StatusCodeShouldBe(204);
});

using var nested = Host.Services.As<IContainer>().GetNestedContainer();
var context = nested.GetInstance<ItemsDbContext>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ protected async Task assertExecutesWithNoErrors(string url)
await Scenario(x =>
{
x.Get.Url(url);
x.StatusCodeShouldBeOk();
x.StatusCodeShouldBe(204);
});
}

Expand Down Expand Up @@ -47,6 +47,7 @@ public async Task use_claims_principal()
{
x.ConfigureHttpContext(c => c.User = principal);
x.Get.Url("/http/principal");
x.StatusCodeShouldBe(204);
});

HttpContextEndpoints.User.ShouldBeSameAs(principal);
Expand Down
6 changes: 5 additions & 1 deletion src/Http/Wolverine.Http.Tests/using_marten.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@ public async Task use_marten_document_session_with_outbox()
{
var input = new Data { Id = Guid.NewGuid(), Name = "Jaylen Watson" };

var (tracked, _) = await TrackedHttpCall(x => { x.Post.Json(input).ToUrl("/publish/marten/message"); });
var (tracked, _) = await TrackedHttpCall(x =>
{
x.Post.Json(input).ToUrl("/publish/marten/message");
x.StatusCodeShouldBe(204);
});

var published = tracked.Sent.SingleMessage<Data>();
published.Id.ShouldBe(input.Id);
Expand Down
7 changes: 7 additions & 0 deletions src/Http/Wolverine.Http/HttpChain.EndpointBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing;
using Wolverine.Http.Resources;

namespace Wolverine.Http;

Expand Down Expand Up @@ -47,6 +48,12 @@ public RouteEndpoint BuildEndpoint()
applier.Apply(builder, Method.Method);
}

if (ResourceType == null)
{
builder.RemoveStatusCodeResponse(200);
builder.Metadata.Add(new ProducesResponseTypeMetadata{StatusCode = 204, Type = null});
}

Endpoint = (RouteEndpoint?)builder.Build();

return Endpoint;
Expand Down
1 change: 1 addition & 0 deletions src/Http/Wolverine.Http/HttpGraph.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public partial class HttpGraph : EndpointDataSource, ICodeFileCollection, IChang

private readonly List<IResourceWriterPolicy> _writerPolicies = new()
{
new EmptyBody204Policy(),
new StatusCodePolicy(),
new ResultWriterPolicy(),
new StringResourceWriterPolicy(),
Expand Down
10 changes: 2 additions & 8 deletions src/Http/Wolverine.Http/IHttpAware.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Metadata;
using Wolverine.Http.Resources;

namespace Wolverine.Http;

Expand Down Expand Up @@ -62,7 +63,7 @@ public static void PopulateMetadata(MethodInfo method, EndpointBuilder builder)
builder.RemoveStatusCodeResponse(200);

var create = new MethodCall(method.DeclaringType, method).Creates.FirstOrDefault()?.VariableType;
var metadata = new Metadata { Type = create, StatusCode = 201 };
var metadata = new ProducesResponseTypeMetadata { Type = create, StatusCode = 201 };
builder.Metadata.Add(metadata);
}

Expand All @@ -73,11 +74,4 @@ void IHttpAware.Apply(HttpContext context)
context.Response.Headers.Location = Url();
context.Response.StatusCode = 201;
}

internal class Metadata : IProducesResponseTypeMetadata
{
public Type? Type { get; init; }
public int StatusCode { get; init; }
public IEnumerable<string> ContentTypes => new string[] { "application/json" };
}
}
38 changes: 38 additions & 0 deletions src/Http/Wolverine.Http/Resources/EmptyBody204Policy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using JasperFx.CodeGeneration;
using JasperFx.CodeGeneration.Frames;
using JasperFx.CodeGeneration.Model;
using Microsoft.AspNetCore.Http;

namespace Wolverine.Http.Resources;

internal class EmptyBody204Policy : IResourceWriterPolicy
{
public bool TryApply(HttpChain chain)
{
if (chain.ResourceType == null || chain.ResourceType == typeof(void))
{
chain.Postprocessors.Insert(0, new WriteEmptyBodyStatusCode());
return true;
}

return false;
}
}

internal class WriteEmptyBodyStatusCode : SyncFrame
{
private Variable? _context;

public override void GenerateCode(GeneratedMethod method, ISourceWriter writer)
{
writer.WriteComment("Wolverine automatically sets the status code to 204 for empty responses");
writer.Write($"{_context!.Usage}.{nameof(HttpContext.Response)}.{nameof(HttpResponse.StatusCode)} = 204;");
Next?.GenerateCode(method, writer);
}

public override IEnumerable<Variable> FindVariables(IMethodVariables chain)
{
_context = chain.FindVariable(typeof(HttpContext));
yield return _context;
}
}
10 changes: 10 additions & 0 deletions src/Http/Wolverine.Http/Resources/ProducesResponseTypeMetadata.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Http.Metadata;

namespace Wolverine.Http.Resources;

internal class ProducesResponseTypeMetadata : IProducesResponseTypeMetadata
{
public Type? Type { get; init; }
public int StatusCode { get; init; }
public IEnumerable<string> ContentTypes => new string[] { "application/json" };
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit 1f64c2a

Please sign in to comment.