Skip to content

Commit

Permalink
Adds more core handles (#89)
Browse files Browse the repository at this point in the history
* Adds more core handles

- never
- complete
- ask
- await
- update

* Fix failing test
  • Loading branch information
dbrattli authored May 25, 2021
1 parent 6318ab0 commit 1f523e8
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 8 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ are like lego bricks and may be composed into more complex HTTP handlers. The HT
- `concurrent` - Runs a sequence of HTTP handlers concurrently.
- `fail`- Fails the pipeline and pushes an exception downstream.
- `fetch` - Fetches from remote using the current context
- `forget` - Handler that forgets (ignores) the content and outputs unit.
- `skip` - Handler that skips (ignores) the content and outputs unit.
- `get` - Retrieves the content (for use in `req` builder)
- `log` - Log information about the given request.
- `map` - Map the content of the HTTP handler.
Expand Down
4 changes: 2 additions & 2 deletions src/HttpHandler/HttpHandler.fs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ module HttpHandler =
/// Validate content using a predicate function.
let validate<'TSource> = Core.validate<HttpContext, 'TSource>

/// Handler that forgets (ignores) the content and outputs unit.
let forget<'TSource> = Core.forget<HttpContext, 'TSource>
/// Handler that skips (ignores) the content and outputs unit.
let skip<'TSource> = Core.skip<HttpContext, 'TSource>

/// Retrieves the content.
let get<'TSource> () = map<'TSource, 'TSource> id
Expand Down
2 changes: 1 addition & 1 deletion src/Middleware/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type MiddlewareBuilder () =
: IAsyncMiddleware<'TContext, 'TSource, 'TResult> =
req

member _.Delay(fn) = fn ()
member _.Delay(fn) : IAsyncMiddleware<'TContext, 'TSource, 'TResult> = fn ()

member _.Combine(source: IAsyncMiddleware<'TContext, 'T1, 'T2>, other: IAsyncMiddleware<'TContext, 'T2, 'T3>) =
source >=> other
Expand Down
56 changes: 53 additions & 3 deletions src/Middleware/Core.fs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,20 @@ type IAsyncMiddleware<'TContext, 'TSource, 'TResult> =
type IAsyncMiddleware<'TContext, 'TSource> = IAsyncMiddleware<'TContext, 'TSource, 'TSource>

module Core =
/// Returns a middleware whose elements are the result of invoking the async transform function on each
/// element of the source.
let transform<'TContext, 'TSource, 'TResult>
(transform: ('TContext * 'TResult -> Task<unit>) -> 'TContext -> 'TSource -> Task<unit>)
: IAsyncMiddleware<'TContext, 'TSource, 'TResult> =
let subscribeAsync (next: IAsyncNext<'TContext, 'TResult>) =
{ new IAsyncNext<'TContext, 'TSource> with
member __.OnNextAsync(ctx, x) = transform next.OnNextAsync ctx x
member __.OnErrorAsync(ctx, err) = next.OnErrorAsync(ctx, err)
member __.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) }

{ new IAsyncMiddleware<'TContext, 'TSource, 'TResult> with
member __.Subscribe o = subscribeAsync o }

/// A next continuation for observing the final result.
let finish (tcs: TaskCompletionSource<'TResult>) =
{ new IAsyncNext<'TContext, 'TResult> with
Expand Down Expand Up @@ -216,15 +230,33 @@ module Core =
// Collect results
>=> map (Seq.ofList >> Seq.collect (Seq.collect id))

/// Handler that forgets (ignores) the content and outputs unit.
let forget<'TContext, 'TSource> =
/// Handler that skips (ignores) the content and outputs unit.
let skip<'TContext, 'TSource> =
{ new IAsyncMiddleware<'TContext, 'TSource, unit> with
member _.Subscribe(next) =
{ new IAsyncNext<'TContext, 'TSource> with
member _.OnNextAsync(ctx, _) = next.OnNextAsync(ctx, content = ())
member _.OnErrorAsync(ctx, error) = next.OnErrorAsync(ctx, error)
member _.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

/// Never produces a result.
let never =
{ new IAsyncMiddleware<'TContext, 'TSource, 'TResult> with
member _.Subscribe(next) =
{ new IAsyncNext<'TContext, 'TSource> with
member _.OnNextAsync(ctx, _) = Task.FromResult()
member __.OnErrorAsync(ctx, error) = next.OnErrorAsync(ctx, error)
member __.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

/// Completes the current request.
let complete =
{ new IAsyncMiddleware<'TContext, 'TSource, 'TResult> with
member _.Subscribe(next) =
{ new IAsyncNext<'TContext, 'TSource> with
member _.OnNextAsync(ctx, _) = next.OnCompletedAsync(ctx)
member __.OnErrorAsync(ctx, error) = next.OnErrorAsync(ctx, error)
member __.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

/// Filter content using a predicate function.
let filter<'TContext, 'TSource> (predicate: 'TSource -> bool) : IAsyncMiddleware<'TContext, 'TSource> =
{ new IAsyncMiddleware<'TContext, 'TSource, 'TSource> with
Expand Down Expand Up @@ -254,7 +286,25 @@ module Core =
member _.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

/// Retrieves the content.
let get<'TContext, 'TSource> () = map<'TContext, 'TSource, 'TSource> id
let await<'TContext, 'TSource> () = map<'TContext, 'TSource, 'TSource> id

/// Returns the current environment.
let ask =
{ new IAsyncMiddleware<'TContext, 'TSource, 'TContext> with
member _.Subscribe(next) =
{ new IAsyncNext<'TContext, 'TSource> with
member _.OnNextAsync(ctx, _) = next.OnNextAsync(ctx, ctx)
member __.OnErrorAsync(ctx, error) = next.OnErrorAsync(ctx, error)
member __.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

/// Update (asks) the context.
let update (update: 'TContext -> 'TContext) =
{ new IAsyncMiddleware<'TContext, 'TSource, 'TSource> with
override _.Subscribe(next) =
{ new IAsyncNext<'TContext, 'TSource> with
member _.OnNextAsync(ctx, content) = next.OnNextAsync(update ctx, content)
member __.OnErrorAsync(ctx, error) = next.OnErrorAsync(ctx, error)
member __.OnCompletedAsync(ctx) = next.OnCompletedAsync(ctx) } }

[<AutoOpen>]
module Extensions =
Expand Down
2 changes: 1 addition & 1 deletion test/Builder.fs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ let ``Simple return from unit handler in builder is Ok`` () =
// Arrange
let ctx = HttpContext.defaultContext

let a = singleton 42 >=> forget |> runAsync ctx
let a = singleton 42 >=> skip |> runAsync ctx

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

0 comments on commit 1f523e8

Please sign in to comment.