From 1f523e8caff8b643d04052b33f03d47ee58ff837 Mon Sep 17 00:00:00 2001 From: Dag Brattli Date: Tue, 25 May 2021 11:13:51 +0200 Subject: [PATCH] Adds more core handles (#89) * Adds more core handles - never - complete - ask - await - update * Fix failing test --- README.md | 2 +- src/HttpHandler/HttpHandler.fs | 4 +-- src/Middleware/Builder.fs | 2 +- src/Middleware/Core.fs | 56 ++++++++++++++++++++++++++++++++-- test/Builder.fs | 2 +- 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 8d1f543..4173dba 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/HttpHandler/HttpHandler.fs b/src/HttpHandler/HttpHandler.fs index faf733f..b1e88e7 100644 --- a/src/HttpHandler/HttpHandler.fs +++ b/src/HttpHandler/HttpHandler.fs @@ -86,8 +86,8 @@ module HttpHandler = /// Validate content using a predicate function. let validate<'TSource> = Core.validate - /// Handler that forgets (ignores) the content and outputs unit. - let forget<'TSource> = Core.forget + /// Handler that skips (ignores) the content and outputs unit. + let skip<'TSource> = Core.skip /// Retrieves the content. let get<'TSource> () = map<'TSource, 'TSource> id diff --git a/src/Middleware/Builder.fs b/src/Middleware/Builder.fs index 759799e..ebf8ab2 100644 --- a/src/Middleware/Builder.fs +++ b/src/Middleware/Builder.fs @@ -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 diff --git a/src/Middleware/Core.fs b/src/Middleware/Core.fs index ec87264..39049b1 100644 --- a/src/Middleware/Core.fs +++ b/src/Middleware/Core.fs @@ -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) -> 'TContext -> 'TSource -> Task) + : 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 @@ -216,8 +230,8 @@ 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 @@ -225,6 +239,24 @@ module Core = 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 @@ -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) } } [] module Extensions = diff --git a/test/Builder.fs b/test/Builder.fs index 1ca07a0..16b0471 100644 --- a/test/Builder.fs +++ b/test/Builder.fs @@ -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