Skip to content

Commit

Permalink
Lifecycle Hooks (#1)
Browse files Browse the repository at this point in the history
* Spike out an idea for the WebApplicationBuilder API.

* Call onRequest on every request.

* Get rid of intermediate Build() step.

* Implement lifecycle hooks.

* Refactor builder a little.
  • Loading branch information
craigjbass authored Nov 2, 2019
1 parent 8b17b3d commit 35f589a
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 100 deletions.
60 changes: 54 additions & 6 deletions Frank.EndToEndTests/FrankTcpConnectionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class FrankTcpConnectionTest
private IWebApplication _webApplication;
private IRestResponse _response;
private int _port;
private bool _expectExceptionInTeardown = false;

private void MakeGetRequest(string path)
{
Expand Down Expand Up @@ -50,25 +51,38 @@ public void SetUp()
{
_port = 8019;
_builder = Server.Configure();
_expectExceptionInTeardown = false;
}

[TearDown]
public void TearDown()
{
StopFrank();
var thrown = false;
try
{
StopFrank();
}
catch (Exception e)
{
if (!_expectExceptionInTeardown) throw e;

thrown = true;
}

if (_expectExceptionInTeardown && !thrown)
{
throw new Exception("Expected an exception, but one was not thrown.");
}
}

private void StartFrank()
{
_builder.ListenOn(_port);
_webApplication = _builder.Build();
_webApplication.Start();
_webApplication = _builder.StartListeningOn(_port);
}

private void StartFrankWithRoutes(Action<IRouteConfigurer> action)
{
_builder.WithRoutes(action);
_builder.ListenOn(_port);
_builder.OnRequest(action);
StartFrank();
}

Expand Down Expand Up @@ -194,5 +208,39 @@ public void CanDeserializeIncomingPostRequest()
processedRequest?.Body.Should().Be("\"This is the body!!\"");
processedRequest?.Headers["x-api-key"].Should().Be("1234supersecure");
}

[Test]
public void CanExecuteBeforeAndAfterHandlers()
{
var customContext = new LifecycleHooksSpy();
_builder
.Before(() =>
{
customContext.Before();
return customContext;
})
.After(context => { context.After(); })
.OnRequest(((route, context) =>
{
context.Request();
route.Get("/").To(() =>
{
context.RouteHandler();
throw new Exception();
});
}));

StartFrank();

var response = new RestClient("http://127.0.0.1:8019/").Execute(
new RestRequest("/", Method.GET)
);

customContext._list.Should().ContainInOrder(
"before", "request", "route-handler", "after"
);

_expectExceptionInTeardown = true;
}
}
}
67 changes: 45 additions & 22 deletions Frank.EndToEndTests/FrankTestHarnessConnectionTest.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using System;
using System.Text;
using FluentAssertions;
using Frank.API.WebDevelopers;
using Frank.API.WebDevelopers.DTO;
using NUnit.Framework;
using static Frank.API.WebDevelopers.DTO.ResponseBuilders;

namespace Frank.EndToEndTests
{
Expand All @@ -11,9 +13,7 @@ public class FrankTestHarnessConnectionTest
[Test]
public void CanMakeTestRequestAndRespondWith404()
{
ITestWebApplication webApplication = Server.Configure().ForTesting().Build();

webApplication.Start();
ITestWebApplication webApplication = Server.Configure().StartTesting();

var response = webApplication.Execute(new Request());

Expand All @@ -25,39 +25,62 @@ public void CanRouteToARoute()
{
ITestWebApplication webApplication = Server
.Configure()
.WithRoutes(
c => c.Get("/").To(request => ResponseBuilders.Ok().WithBody(new { a = 123 }))
).ForTesting()
.Build();

webApplication.Start();
.OnRequest(
c => c.Get("/").To(request => Ok().WithBody(new {a = 123}))
).StartTesting();

var response = webApplication.Execute(new Request());

webApplication.Stop();

response.Status.Should().Be(200);
Encoding.UTF8.GetString(response.Body).Should().Be("{\"a\":123}");
}

[Test]
public void CanDoThing()
public void ItCallsOnRequestOncePerRequest()
{
var i = 0;
ITestWebApplication webApplication = Server
.Configure()
.WithRoutes(
c => c.Get("/").To(request => ResponseBuilders.Ok().WithBody(new { a = 123 }))
).ForTesting()
.Build();
.OnRequest(
_ => i++
).StartTesting();

webApplication.Start();

var response = webApplication.Execute(new Request());
webApplication.Execute(new Request());
webApplication.Execute(new Request());

webApplication.Stop();

response.Status.Should().Be(200);
Encoding.UTF8.GetString(response.Body).Should().Be("{\"a\":123}");
i.Should().Be(2);
}

[Test]
public void CanExecuteBeforeAndAfterHandlers()
{
var customContext = new LifecycleHooksSpy();
var webApplication = Server.Configure()
.Before(() =>
{
customContext.Before();
return customContext;
})
.After(context => { context.After(); })
.OnRequest(((configurer, context) =>
{
context.Request();
configurer.Get("/").To(() =>
{
context.RouteHandler();
throw new Exception();
});
}))
.StartTesting();

webApplication.Execute(new Request());

customContext._list.Should().ContainInOrder(
"before", "request", "route-handler", "after"
);
}
}
}
29 changes: 29 additions & 0 deletions Frank.EndToEndTests/LifecycleHooksSpy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using System.Collections.Generic;

namespace Frank.EndToEndTests
{
class LifecycleHooksSpy
{
public List<string> _list = new List<string>();

public void Before()
{
_list.Add("before");
}

public void After()
{
_list.Add("after");
}

public void Request()
{
_list.Add("request");
}

public void RouteHandler()
{
_list.Add("route-handler");
}
}
}
1 change: 1 addition & 0 deletions Frank/API/WebDevelopers/IWebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Frank.API.WebDevelopers
{
public interface IWebApplication
{
//todo: this is an internally used method only.
void Start();
void Stop();
}
Expand Down
15 changes: 10 additions & 5 deletions Frank/API/WebDevelopers/IWebApplicationBuilder.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
using System;
using Frank.Internals;

namespace Frank.API.WebDevelopers
{
public interface IWebApplicationBuilder
{
IWebApplicationBuilder WithRoutes(Action<IRouteConfigurer> action);
IWebApplicationBuilder ListenOn(int port);
IWebApplication Build();
ITestWebApplicationBuilder ForTesting();
IWebApplicationBuilder OnRequest(Action<IRouteConfigurer> action);
IWebApplicationBuilderWithBefore<T> Before<T>(Func<T> onBefore);
IWebApplication StartListeningOn(int port);
ITestWebApplication StartTesting();
}

public interface IWebApplicationBuilderWithBefore<T>
{
IWebApplicationBuilderWithBefore<T> After(Action<T> onAfter);
IWebApplicationBuilder OnRequest(Action<IRouteConfigurer, T> action);
}
}
1 change: 1 addition & 0 deletions Frank/Frank.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<Authors>[email protected]</Authors>
<RepositoryUrl>https://github.com/craigjbass/Frank</RepositoryUrl>
<LangVersion>8</LangVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
Expand Down
27 changes: 20 additions & 7 deletions Frank/Internals/WebApplication.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,10 @@ namespace Frank.Internals
internal class WebApplication : IWebApplication
{
private readonly IServer _server;
private readonly IRequestRouter _requestRouter;
private readonly List<Exception> _exceptions = new List<Exception>();
private readonly Action<IRouteConfigurer, object> _onRequest;
private readonly Func<object> _onBefore;
private readonly Action<object> _onAfter;

internal interface IRequestRouter
{
Expand All @@ -23,10 +25,16 @@ Request request
);
}

public WebApplication(IServer server, IRequestRouter requestRouter)
public WebApplication(
IServer server,
Action<IRouteConfigurer, object> onRequest,
Func<object> onBefore,
Action<object> onAfter)
{
_server = server;
_requestRouter = requestRouter;
_onRequest = onRequest;
_onBefore = onBefore;
_onAfter = onAfter;
}

public void Start()
Expand All @@ -37,11 +45,15 @@ public void Start()

private void ProcessRequest(Request request, IResponseBuffer responseBuffer)
{
var context = _onBefore();
var router = new RequestRouter();
_onRequest(router, context);

try
{
if (_requestRouter.CanRoute(request.Path))
if (router.CanRoute(request.Path))
{
var actualResponse = _requestRouter.Route(request);
var actualResponse = router.Route(request);
responseBuffer.SetContentsOfBufferTo(actualResponse);
}
else
Expand All @@ -63,19 +75,20 @@ private void ProcessRequest(Request request, IResponseBuffer responseBuffer)
finally
{
responseBuffer.Flush();
_onAfter(context);
}
}

public void Stop()
{
_server.Stop();

if (_exceptions.Any())
{
var exception = new Exception();
exception.Data.Add("Exceptions", _exceptions);
throw exception;
}

_server.Stop();
}
}
}
Loading

0 comments on commit 35f589a

Please sign in to comment.