Skip to content

Commit

Permalink
Anonymous obj, static functions, multiple methods
Browse files Browse the repository at this point in the history
Methods can now return anonymous objects and a type will be generated for the openapi spec. It's not used during execution of the request.
Request method functions can now be static.
Classes can now have multiple http methods without causing a compile error.
  • Loading branch information
smithgeek committed Oct 25, 2024
1 parent aa6c1cf commit 008578f
Show file tree
Hide file tree
Showing 7 changed files with 372 additions and 58 deletions.
81 changes: 80 additions & 1 deletion Benchmarks/VoyagerBench/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
.AddAuthorization();
builder.Services.AddHttpContextAccessor();
builder.Services.AddVoyager();
builder.Services.AddVoyager2();

var app = builder.Build();
app.UseAuthorization();
Expand Down Expand Up @@ -53,6 +52,15 @@ public int Get()
}
}

[VoyagerEndpoint("/static")]
public class StaticEndpoint
{
public static IResult Get(Service service)
{
return TypedResults.Ok(new { test = true });
}
}

[VoyagerEndpoint("/benchmark/ok/{id}")]
public class Endpoint : IConfigurableEndpoint
{
Expand All @@ -75,11 +83,82 @@ public Response Post(Request req, ILogger<Program> logger)
}
}

[VoyagerEndpoint("/anonymous")]
public class AnonymousEndpoint
{
public IResult Get(Body request)
{
if (request.Test != null)
{
return TypedResults.Ok(new
{
something = "here"
});
}
return TypedResults.Ok(new
{
result = request.Test
});
}

public class Body
{
public string? Test { get; init; }
}
}

namespace Duplicate
{
[VoyagerEndpoint("/duplicate/anonymous")]
public class AnonymousEndpoint
{
public IResult Get(Body request)
{
if (request.Test != null)
{
return TypedResults.Ok(new
{
something = "here"
});
}
return TypedResults.Ok(new
{
result = request.Test
});
}

public class Body
{
public string? Test { get; init; }
public int Value { get; init; }
}
}
}

[VoyagerEndpoint("/multipleInjections")]
public class MultipleInjections
{
public IResult Get(Service service)
{
return TypedResults.Ok();
}

public IResult Post(Service service)
{
return TypedResults.Ok();
}
}

public class Response
{
public int Id { get; set; }
public string? Name { get; set; }
public int Age { get; set; }
public string? PhoneNumber { get; set; }
}

public class Service
{

}
}
170 changes: 149 additions & 21 deletions Benchmarks/VoyagerBench/TestingGrounds.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,25 @@
using Voyager;
using Voyager.ModelBinding;

namespace Voyager.Generated.VoyagerBenchGen2
namespace Voyager.Generated.VoyagerBench_VoyagerSourceGen
{
internal class EndpointMapper : Voyager.IVoyagerMapping
{
public void MapEndpoints(WebApplication app)
{
var modelBinder = app.Services.GetService<IModelBinder>() ?? new ModelBinder();
var jsonOptions = app.Services.GetRequiredService<IOptions<JsonOptions>>().Value.SerializerOptions;
var stringProvider = app.Services.GetService<Voyager.ModelBinding.IStringValuesProvider>() ?? new Voyager.ModelBinding.StringValuesProvider();
var inst_VoyagerApi_NoRequestEndpoint = app.Services.GetRequiredService<VoyagerApi.NoRequestEndpoint>();
var instRequestValidator = new RequestValidator();
var inst_VoyagerApi_StaticEndpoint = app.Services.GetRequiredService<VoyagerApi.StaticEndpoint>();
var inst_VoyagerApi_Endpoint = app.Services.GetRequiredService<VoyagerApi.Endpoint>();
app.MapGet("/norequest", (HttpContext context) =>
var instVoyagerApi_EndpointPostValidator = new VoyagerApi_EndpointPostValidator();
var inst_VoyagerApi_AnonymousEndpoint = app.Services.GetRequiredService<VoyagerApi.AnonymousEndpoint>();
var inst_VoyagerApi_Duplicate_AnonymousEndpoint = app.Services.GetRequiredService<VoyagerApi.Duplicate.AnonymousEndpoint>();
var inst_VoyagerApi_MultipleInjections = app.Services.GetRequiredService<VoyagerApi.MultipleInjections>();
app.MapGet("/norequest", (Microsoft.AspNetCore.Http.HttpContext context) =>
{
return TypedResults.Ok(inst_VoyagerApi_NoRequestEndpoint.Get());
return Microsoft.AspNetCore.Http.TypedResults.Ok(inst_VoyagerApi_NoRequestEndpoint.Get());
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
Expand All @@ -29,62 +34,185 @@ public void MapEndpoints(WebApplication app)
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
VoyagerApi.Endpoint.Configure(app.MapPost("/benchmark2/ok/{id}", async (HttpContext context, [Microsoft.AspNetCore.Mvc.FromRouteAttribute(Name = "id")] int id, string? firstName, string? lastName, int age, System.Collections.Generic.IEnumerable<string>? phoneNumbers) =>
app.MapGet("/static", (Microsoft.AspNetCore.Http.HttpContext context) =>
{
var body = await JsonSerializer.DeserializeAsync<ManualRequestBody>(context.Request.Body, jsonOptions);
return (Microsoft.AspNetCore.Http.IResult)(VoyagerApi.StaticEndpoint.Get(context.RequestServices.GetRequiredService<VoyagerApi.Service>()));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, typeof(VoyagerApi_StaticEndpointGetResponse0));
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
VoyagerApi.Endpoint.Configure(app.MapPost("/benchmark/ok/{id}", async (Microsoft.AspNetCore.Http.HttpContext context, [Microsoft.AspNetCore.Mvc.FromRouteAttribute(Name = "id")] int id) =>
{
var body = await JsonSerializer.DeserializeAsync<VoyagerApi_EndpointPostRequest>(context.Request.Body, jsonOptions);
var request = new VoyagerApi.Request
{
Id = id,
FirstName = body?.FirstName ?? default,
LastName = body?.LastName ?? default,
Age = body?.Age ?? default,
PhoneNumbers = body?.PhoneNumbers ?? default,
FirstName = body?.FirstName ?? null,
LastName = body?.LastName ?? null,
Age = body?.Age ?? default!,
PhoneNumbers = body?.PhoneNumbers ?? null,
};
var validationResult = await instRequestValidator.ValidateAsync(request);
var validationResult = await instVoyagerApi_EndpointPostValidator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return Results.ValidationProblem(validationResult.ToDictionary());
return Microsoft.AspNetCore.Http.Results.ValidationProblem(validationResult.ToDictionary());
}
return TypedResults.Ok(inst_VoyagerApi_Endpoint.Post(request, context.RequestServices.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VoyagerApi.Program>>()));
return Microsoft.AspNetCore.Http.TypedResults.Ok(inst_VoyagerApi_Endpoint.Post(request, context.RequestServices.GetRequiredService<Microsoft.Extensions.Logging.ILogger<VoyagerApi.Program>>()));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddParameter("id", Microsoft.OpenApi.Models.ParameterLocation.Path, typeof(int), false);
builder.AddBody(typeof(ManualRequestBody));
builder.AddBody(typeof(VoyagerApi_EndpointPostRequest));
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(200, typeof(VoyagerApi.Response));
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)()));
app.MapGet("/anonymous", async (Microsoft.AspNetCore.Http.HttpContext context) =>
{
var body = await JsonSerializer.DeserializeAsync<VoyagerApi_AnonymousEndpointGetRequest>(context.Request.Body, jsonOptions);
var request = new VoyagerApi.AnonymousEndpoint.Body
{
Test = body?.Test ?? null,
};
return (Microsoft.AspNetCore.Http.IResult)(inst_VoyagerApi_AnonymousEndpoint.Get(request));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddBody(typeof(VoyagerApi_AnonymousEndpointGetRequest));
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, typeof(VoyagerApi_AnonymousEndpointGetResponse0));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, typeof(VoyagerApi_AnonymousEndpointGetResponse1));
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
app.MapGet("/duplicate/anonymous", async (Microsoft.AspNetCore.Http.HttpContext context) =>
{
var body = await JsonSerializer.DeserializeAsync<VoyagerApi_Duplicate_AnonymousEndpointGetRequest>(context.Request.Body, jsonOptions);
var request = new VoyagerApi.Duplicate.AnonymousEndpoint.Body
{
Test = body?.Test ?? null,
Value = body?.Value ?? default!,
};
return (Microsoft.AspNetCore.Http.IResult)(inst_VoyagerApi_Duplicate_AnonymousEndpoint.Get(request));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddBody(typeof(VoyagerApi_Duplicate_AnonymousEndpointGetRequest));
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, typeof(VoyagerApi_Duplicate_AnonymousEndpointGetResponse0));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, typeof(VoyagerApi_Duplicate_AnonymousEndpointGetResponse1));
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
app.MapGet("/multipleInjections", (Microsoft.AspNetCore.Http.HttpContext context) =>
{
return (Microsoft.AspNetCore.Http.IResult)(inst_VoyagerApi_MultipleInjections.Get(context.RequestServices.GetRequiredService<VoyagerApi.Service>()));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, null);
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
app.MapPost("/multipleInjections", (Microsoft.AspNetCore.Http.HttpContext context) =>
{
return (Microsoft.AspNetCore.Http.IResult)(inst_VoyagerApi_MultipleInjections.Post(context.RequestServices.GetRequiredService<VoyagerApi.Service>()));
}
).WithMetadata(new Func<Voyager.OpenApi.VoyagerOpenApiMetadata>(() =>
{
var builder = Voyager.OpenApi.OperationBuilderFactory.Create(app.Services, new());
builder.AddResponse(400, typeof(Microsoft.AspNetCore.Http.HttpValidationProblemDetails));
builder.AddResponse(Microsoft.AspNetCore.Http.TypedResults.Ok().StatusCode, null);
return new Voyager.OpenApi.VoyagerOpenApiMetadata { Operation = builder.Build() };
}
)());
}

private class ManualRequestBody
#pragma warning disable CS8618
private class VoyagerApi_StaticEndpointGetResponse0
{
public bool test { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_EndpointPostRequest
{
public string? FirstName { get; set; }
public string? LastName { get; set; }
public int Age { get; set; }
public System.Collections.Generic.IEnumerable<string>? PhoneNumbers { get; set; }
}
public class RequestValidator : AbstractValidator<VoyagerApi.Request>
#pragma warning restore CS8618
public class VoyagerApi_EndpointPostValidator : AbstractValidator<VoyagerApi.Request>
{
public RequestValidator()
public VoyagerApi_EndpointPostValidator()
{
VoyagerApi.Request.Validate(this);
}
}
#pragma warning disable CS8618
private class VoyagerApi_AnonymousEndpointGetRequest
{
public string? Test { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_AnonymousEndpointGetResponse0
{
public string something { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_AnonymousEndpointGetResponse1
{
public string? result { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_Duplicate_AnonymousEndpointGetRequest
{
public string? Test { get; set; }
public int Value { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_Duplicate_AnonymousEndpointGetResponse0
{
public string something { get; set; }
}
#pragma warning restore CS8618
#pragma warning disable CS8618
private class VoyagerApi_Duplicate_AnonymousEndpointGetResponse1
{
public string? result { get; set; }
}
#pragma warning restore CS8618
}
}

namespace Microsoft.Extensions.DependencyInjection
{
internal static class VoyagerEndpoints2
internal static class VoyagerEndpoints
{
internal static void AddVoyager2(this IServiceCollection services)
internal static void AddVoyager(this IServiceCollection services)
{
services.AddTransient<VoyagerApi.NoRequestEndpoint>();
services.AddTransient<VoyagerApi.Endpoint>();
services.AddTransient<IVoyagerMapping, Voyager.Generated.VoyagerBenchGen2.EndpointMapper>();
services.AddTransient<VoyagerApi.AnonymousEndpoint>();
services.AddTransient<VoyagerApi.Duplicate.AnonymousEndpoint>();
services.AddTransient<VoyagerApi.MultipleInjections>();
services.AddTransient<IVoyagerMapping, Voyager.Generated.VoyagerBench_VoyagerSourceGen.EndpointMapper>();
}
}
}
1 change: 1 addition & 0 deletions Samples/MinimalApi/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
config.AddVoyager();
config.SupportNonNullableReferenceTypes();
});
builder.Services.AddSingleton<Service>();

var app = builder.Build();

Expand Down
4 changes: 4 additions & 0 deletions Samples/MinimalApi/Service.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public class Service
{

}
1 change: 0 additions & 1 deletion Samples/MinimalApi/Test.cs

This file was deleted.

Loading

0 comments on commit 008578f

Please sign in to comment.