diff --git a/WolverineWebApiFSharp/Library.fs b/WolverineWebApiFSharp/Library.fs index ce67e9d0d..e76c0a018 100644 --- a/WolverineWebApiFSharp/Library.fs +++ b/WolverineWebApiFSharp/Library.fs @@ -6,5 +6,8 @@ open Wolverine.Http [] let myHandler() = task { - () - } \ No newline at end of file + () + } + +[] +let isAuthenticated () = true \ No newline at end of file diff --git a/src/Http/Wolverine.Http.Tests/end_to_end.cs b/src/Http/Wolverine.Http.Tests/end_to_end.cs index fb15f2d2a..eafe3016c 100644 --- a/src/Http/Wolverine.Http.Tests/end_to_end.cs +++ b/src/Http/Wolverine.Http.Tests/end_to_end.cs @@ -35,6 +35,32 @@ public async Task post_returning_fsharp_taskunit() }); } + [Fact] + public async Task get_fsharp_bool_endpoint() + { + var body = await Scenario(x => + { + x.Get.Url("/fsharp/is-authenticated"); + x.StatusCodeShouldBeOk(); + }); + + var result = body.ReadAsJson(); + result.ShouldBeTrue(); + } + + [Fact] + public async Task get_csharp_static_bool_endpoint() + { + var body = await Scenario(x => + { + x.Get.Url("/simple/bool"); + x.StatusCodeShouldBeOk(); + }); + + var result = body.ReadAsJson(); + result.ShouldBeTrue(); + } + [Fact] public async Task retrieve_json_data() { diff --git a/src/Http/Wolverine.Http.Tests/endpoint_discovery_and_construction.cs b/src/Http/Wolverine.Http.Tests/endpoint_discovery_and_construction.cs index 8ec3a3c75..a5398a594 100644 --- a/src/Http/Wolverine.Http.Tests/endpoint_discovery_and_construction.cs +++ b/src/Http/Wolverine.Http.Tests/endpoint_discovery_and_construction.cs @@ -22,6 +22,20 @@ public void discover_endpoint_returning_fsharp_taskunit() chain.ShouldNotBeNull(); } + [Fact] + public void discover_fsharp_bool_endpoint() + { + var chain = HttpChains.ChainFor("GET", "/fsharp/is-authenticated"); + chain.ShouldNotBeNull(); + } + + [Fact] + public void discover_csharp_static_bool_endpoint() + { + var chain = HttpChains.ChainFor("GET", "/simple/bool"); + chain.ShouldNotBeNull(); + } + [Fact] public void read_order_from_attribute() { diff --git a/src/Http/Wolverine.Http/HttpChain.Codegen.cs b/src/Http/Wolverine.Http/HttpChain.Codegen.cs index 5942eb6ee..00cfd4d37 100644 --- a/src/Http/Wolverine.Http/HttpChain.Codegen.cs +++ b/src/Http/Wolverine.Http/HttpChain.Codegen.cs @@ -84,15 +84,11 @@ bool ICodeFile.AttachTypesSynchronously(GenerationRules rules, Assembly assembly string containingNamespace) { Debug.WriteLine(_generatedType?.SourceCode); - - _handlerType = assembly.ExportedTypes.FirstOrDefault(x => x.Name == _fileName); - if (_handlerType == null) - { - return false; - } + _handlerType = assembly.ExportedTypes.FirstOrDefault(x => x.Name == _fileName) + ?? assembly.GetTypes().FirstOrDefault(x => x.Name == _fileName); - return true; + return _handlerType != null; } string ICodeFile.FileName => _fileName!; diff --git a/src/Http/Wolverine.Http/HttpChain.EndpointBuilder.cs b/src/Http/Wolverine.Http/HttpChain.EndpointBuilder.cs index 179e5be14..884cecf06 100644 --- a/src/Http/Wolverine.Http/HttpChain.EndpointBuilder.cs +++ b/src/Http/Wolverine.Http/HttpChain.EndpointBuilder.cs @@ -46,43 +46,42 @@ private bool tryApplyAsEndpointMetadataProvider(Type? type, RouteEndpointBuilder return false; } + private HttpHandler buildHandler() + { + this.InitializeSynchronously(_parent.Rules, _parent, _parent.Container.Services); + + if (_handlerType == null) + { + throw new InvalidOperationException( + $"Failed to resolve the generated handler type for endpoint {_fileName} " + + $"({string.Join(", ", _httpMethods)} {RoutePattern?.RawText}) " + + $"on handler type {Method.HandlerType.FullNameInCode()}. " + + $"The generated source code was:\n{_generatedType?.SourceCode}"); + } + + return (HttpHandler)_parent.Container.QuickBuild(_handlerType); + } + public RouteEndpoint BuildEndpoint(RouteWarmup warmup) { if (Endpoint != null) return Endpoint; - + RequestDelegate? requestDelegate = null; if (_parent.Rules.TypeLoadMode == TypeLoadMode.Static && !DynamicCodeBuilder.WithinCodegenCommand) { - this.InitializeSynchronously(_parent.Rules, _parent, _parent.Container.Services); - var handler = (HttpHandler)_parent.Container.QuickBuild(_handlerType); + var handler = buildHandler(); requestDelegate = handler.Handle; } else { if (warmup == RouteWarmup.Eager && !DynamicCodeBuilder.WithinCodegenCommand) { - this.InitializeSynchronously(_parent.Rules, _parent, _parent.Container.Services); - var handler = (HttpHandler)_parent.Container.QuickBuild(_handlerType); + var handler = buildHandler(); requestDelegate = c => handler.Handle(c); } else { - var handler = new Lazy(() => - { - this.InitializeSynchronously(_parent.Rules, _parent, _parent.Container.Services); - - try - { - return (HttpHandler)_parent.Container.QuickBuild(_handlerType); - } - catch (Exception e) - { - throw new InvalidOperationException( - "Wolverine may be having trouble with concurrent access to the same route at startup. Set the WolverineHttpOptions.Warmup = Eager to work around this problem", - e); - } - }); - + var handler = new Lazy(buildHandler); requestDelegate = c => handler.Value.Handle(c); } } diff --git a/src/Http/WolverineWebApi/FakeEndpoint.cs b/src/Http/WolverineWebApi/FakeEndpoint.cs index 0c520a05b..0d453b483 100644 --- a/src/Http/WolverineWebApi/FakeEndpoint.cs +++ b/src/Http/WolverineWebApi/FakeEndpoint.cs @@ -76,4 +76,10 @@ public string ReadEnumArgument(Direction direction) } } +public static class NoDependencyEndpoints +{ + [WolverineGet("/simple/bool")] + public static bool GetBool() => true; +} + public class BigResponse; \ No newline at end of file