Skip to content

Commit

Permalink
Fix builder bind (#69)
Browse files Browse the repository at this point in the history
* Fix builder bind
  • Loading branch information
dbrattli authored Feb 25, 2021
1 parent 9a31d47 commit 60b7415
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 53 deletions.
23 changes: 7 additions & 16 deletions src/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,29 +31,20 @@ type RequestBuilder () =
/// Binds value of 'TValue for let! All handlers runs in same context within the builder.
member _.Bind
(
source: HttpHandler<'TSource, 'TNext>,
fn: 'TNext -> HttpHandler<'TNext, 'TResult>
source: HttpHandler<'TSource, 'TValue>,
fn: 'TValue -> HttpHandler<'TSource, 'TResult>
): HttpHandler<'TSource, 'TResult> =

let subscribe (next: IHttpNext<'TResult>) =
let (next: IHttpNext<'TNext>) =
{ new IHttpNext<'TNext> with
let next =
{ new IHttpNext<'TValue> with
member _.NextAsync(ctx, ?content) =
task {
let obv =
{ new IHttpNext<'TResult> with
member _.NextAsync(ctx', content) = next.NextAsync(ctx, ?content = content)
member _.ErrorAsync(ctx, exn) = next.ErrorAsync(ctx, exn)
}

match content with
| Some content ->
let res = fn content

return!
res.Subscribe(obv)
|> (fun obv -> obv.NextAsync(ctx, content = content))
| None -> return! obv.NextAsync(ctx)
let bound: HttpHandler<'TSource, 'TResult> = fn content
return! bound.Subscribe(next).NextAsync(ctx)
| None -> return! next.NextAsync(ctx)
}

member _.ErrorAsync(ctx, exn) = next.ErrorAsync(ctx, exn)
Expand Down
29 changes: 14 additions & 15 deletions src/Context.fs
Original file line number Diff line number Diff line change
Expand Up @@ -267,21 +267,21 @@ module Context =
}

/// Merge the list of context objects. Used by the sequential and concurrent HTTP handlers.
let merge (context: List<Context>): Context =
let merge (ctxs: List<Context>): Context =
let ctxs =
match ctxs with
| [] -> [ defaultContext ]
| _ -> ctxs

// Use the max status code.
let statusCode =
let codes =
context
|> List.map (fun ctx -> ctx.Response.StatusCode)

if codes.IsEmpty then
HttpStatusCode.NotFound
else
List.max codes
ctxs
|> List.map (fun ctx -> ctx.Response.StatusCode)
|> List.max

// Concat the reason phrases (if they are different)
let reasonPhrase =
context
ctxs
|> List.map (fun ctx -> ctx.Response.ReasonPhrase)
|> List.distinct
|> String.concat ", "
Expand All @@ -297,18 +297,17 @@ module Context =

// Merge headers
let headers =
context
ctxs
|> List.map (fun ctx -> ctx.Response.Headers)
|> List.fold
(fun state hdr -> merge state hdr (fun k (a, b) -> if a = b then a else Seq.append a b))
Map.empty

{
Request =
context
|> List.tryHead
|> Option.map (fun ctx -> ctx.Request)
|> Option.defaultValue defaultRequest
ctxs
|> Seq.map (fun ctx -> ctx.Request)
|> Seq.head
Response =
{
Headers = headers
Expand Down
10 changes: 5 additions & 5 deletions test/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let ``Simple unit handler in builder is Ok`` () =
let! result =
let a =
req {
let! value = unit 42
let! value = singleton 42
return value
}

Expand All @@ -47,10 +47,10 @@ let ``Simple return from unit handler in builder is Ok`` () =
// Arrange
let ctx = Context.defaultContext

let a = unit 42 |> runAsync ctx
let a = singleton 42 |> runAsync ctx

// Act
let! result = req { return! unit 42 } |> runUnsafeAsync ctx
let! result = req { return! singleton 42 } |> runUnsafeAsync ctx

// Assert
test <@ result = 42 @>
Expand All @@ -65,8 +65,8 @@ let ``Multiple handlers in builder is Ok`` () =
// Act
let request =
req {
let! a = unit 10
let! b = unit 20
let! a = singleton 10
let! b = singleton 20
return! add a b
}

Expand Down
6 changes: 3 additions & 3 deletions test/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,16 @@ type HttpMessageHandlerStub (NextAsync: Func<HttpRequestMessage, CancellationTok
): Task<HttpResponseMessage> =
task { return! NextAsync.Invoke(request, cancellationToken) }

let unit<'TSource, 'TResult> (value: 'TResult): HttpHandler<'TSource, 'TResult> =
let singleton<'TSource, 'TResult> (value: 'TResult): HttpHandler<'TSource, 'TResult> =
HttpHandler
<| fun next ->
{ new IHttpNext<'TSource> with
member _.NextAsync(ctx, ?content) = next.NextAsync(ctx, value)
member _.NextAsync(ctx, _) = next.NextAsync(ctx, value)
member _.ErrorAsync(ctx, exn) = next.ErrorAsync(ctx, exn)
}


let add (a: int) (b: int) = unit (a + b)
let add (a: int) (b: int) = singleton (a + b)


exception TestException of code: int * message: string with
Expand Down
28 changes: 14 additions & 14 deletions test/Handler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ let ``Simple unit handler is Ok`` () =
let ctx = Context.defaultContext

// Act
let! content = unit 42 >=> unit 43 |> runUnsafeAsync ctx
let! content = singleton 42 >=> singleton 43 |> runUnsafeAsync ctx

// Assert
test <@ content = 43 @>
Expand Down Expand Up @@ -50,7 +50,7 @@ let ``Simple error then ok is Error`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = error "failed" >=> unit 42
let req = error "failed" >=> singleton 42

// Act
let! result = req |> runAsync ctx
Expand All @@ -68,7 +68,7 @@ let ``Simple ok then error is Error`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = unit 42 >=> error "failed"
let req = singleton 42 >=> error "failed"

// Act
let! result = req |> runAsync ctx
Expand All @@ -87,7 +87,7 @@ let ``Catching ok is Ok`` () =
let errorHandler = badRequestHandler 420

let req =
unit 42
singleton 42
>=> map (fun a -> a * 10)
>=> catch errorHandler

Expand All @@ -105,7 +105,7 @@ let ``Catching errors is Ok`` () =
let ctx = Context.defaultContext
let errorHandler = badRequestHandler 420

let req = unit 42 >=> error "failed" >=> catch errorHandler
let req = singleton 42 >=> error "failed" >=> catch errorHandler

// Act
let! content = req |> runUnsafeAsync ctx
Expand All @@ -121,7 +121,7 @@ let ``Not catching errors is Error`` () =
let ctx = Context.defaultContext
let errorHandler = badRequestHandler 420

let req = unit 42 >=> catch errorHandler >=> error "failed"
let req = singleton 42 >=> catch errorHandler >=> error "failed"

// Act
let! result = req |> runAsync ctx
Expand All @@ -137,7 +137,7 @@ let ``Sequential handlers is Ok`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = sequential [ unit 1; unit 2; unit 3; unit 4; unit 5 ]
let req = sequential [ singleton 1; singleton 2; singleton 3; singleton 4; singleton 5 ]

// Act
let! content = req |> runUnsafeAsync ctx
Expand All @@ -151,7 +151,7 @@ let ``Sequential handlers with an Error is Error`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = sequential [ unit 1; unit 2; error "fail"; unit 4; unit 5 ]
let req = sequential [ singleton 1; singleton 2; error "fail"; singleton 4; singleton 5 ]

// Act
let! result = req |> runAsync ctx
Expand All @@ -169,7 +169,7 @@ let ``Concurrent handlers is Ok`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = concurrent [ unit 1; unit 2; unit 3; unit 4; unit 5 ]
let req = concurrent [ singleton 1; singleton 2; singleton 3; singleton 4; singleton 5 ]

// Act
let! result = req |> runAsync ctx
Expand All @@ -187,7 +187,7 @@ let ``Concurrent handlers with an Error is Error`` () =
task {
// Arrange
let ctx = Context.defaultContext
let req = concurrent [ unit 1; unit 2; error "fail"; unit 4; unit 5 ]
let req = concurrent [ singleton 1; singleton 2; error "fail"; singleton 4; singleton 5 ]

// Act
let! result = req |> runAsync ctx
Expand All @@ -207,7 +207,7 @@ let ``Chunked handlers is Ok`` (PositiveInt chunkSize) (PositiveInt maxConcurren
let ctx = Context.defaultContext

let req =
chunk<unit, int, int> chunkSize maxConcurrency unit [ 1; 2; 3; 4; 5 ]
chunk<unit, int, int> chunkSize maxConcurrency singleton [ 1; 2; 3; 4; 5 ]

// Act
let! result = req |> runUnsafeAsync ctx
Expand All @@ -220,7 +220,7 @@ let ``Choose handlers is Ok`` =
// Arrange
let ctx = Context.defaultContext

let req = choose [ error "1"; unit 2; error "3"; unit 4 ]
let req = choose [ error "1"; singleton 2; error "3"; singleton 4 ]

// Act
let! result = req |> runUnsafeAsync ctx
Expand Down Expand Up @@ -258,7 +258,7 @@ let ``Request with token renewer without token gives error`` () =
let renewer _ = err |> Error |> Task.FromResult
let ctx = Context.defaultContext

let req = withTokenRenewer renewer >=> unit 42
let req = withTokenRenewer renewer >=> singleton 42

// Act
let! result = req |> runAsync ctx
Expand All @@ -276,7 +276,7 @@ let ``Request with token renewer throws exception gives error`` () =
let renewer _ = failwith "failing" |> Task.FromResult
let ctx = Context.defaultContext

let req = withTokenRenewer renewer >=> unit 42
let req = withTokenRenewer renewer >=> singleton 42

// Act
let! result = req |> runAsync ctx
Expand Down

0 comments on commit 60b7415

Please sign in to comment.