Skip to content

Commit

Permalink
Add upgrade info (#73)
Browse files Browse the repository at this point in the history
* Add upgrade info

* Format code and add fantomas to CI

* Do not create new manifest

* Add github example

* Use Ply

* Use Fstoolkit.Errorhandling

Co-authored-by: Ivan Polomani <[email protected]>

* Update examples/github/GitHub.fsproj

Co-authored-by: Ivan Polomani <[email protected]>

* Update examples/github/GitHub.fsproj

Co-authored-by: Ivan Polomani <[email protected]>

Co-authored-by: Ivan Polomani <[email protected]>
  • Loading branch information
dbrattli and polomani authored Mar 1, 2021
1 parent 020ec1a commit bb6c123
Show file tree
Hide file tree
Showing 41 changed files with 521 additions and 302 deletions.
18 changes: 18 additions & 0 deletions .config/dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"version": 1,
"isRoot": true,
"tools": {
"paket": {
"version": "5.257.0",
"commands": [
"paket"
]
},
"fantomas-tool": {
"version": "4.4.0",
"commands": [
"fantomas"
]
}
}
}
51 changes: 25 additions & 26 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,35 @@ name: Build and Test

on:
push:
branches: [ master ]
branches: [master]
pull_request:
branches: [ master ]
branches: [master]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.100
- name: Setup dotnet manifest
run: dotnet new tool-manifest
- name: Setup paket
run: dotnet tool install Paket
- name: Install dependencies
run: dotnet restore
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: ./test.sh
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.lcov
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
- uses: actions/checkout@v2
- name: Setup .NET Core
uses: actions/setup-dotnet@v1
with:
dotnet-version: 5.0.101
- name: Setup dotnet tools
run: dotnet tool restore
- name: Install dependencies
run: dotnet restore
- name: Check formatting
run: dotnet fantomas . -r --check
- name: Build
run: dotnet build --configuration Release --no-restore
- name: Test
run: ./test.sh
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
token: ${{ secrets.CODECOV_TOKEN }}
file: ./coverage.lcov
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
obj
bin
.vs
.config
BenchmarkDotNet.Artifacts
benchmark/bin
benchmark/obj
Expand Down
56 changes: 51 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ Or [directly in Visual Studio](https://docs.microsoft.com/en-us/nuget/quickstart
open System.Net.Http
open System.Text.Json
open FSharp.Control.Tasks.V2.ContextInsensitive
open FSharp.Control.Tasks
open Oryx
open Oryx.SystemTextJson.ResponseReader
Expand Down Expand Up @@ -270,10 +270,10 @@ val withError:
-> IHttpNext<HttpContent>
```

It's also possible to catch errors using the `catch` handler _before_ e.g `fetch`. The function takes an `errorHandler`
that is given the returned error and produces a new `next` continuation that may then decide to return `Ok` instead of
`Error`. This is very helpful when a failed request not necessarily means an error, e.g if you need to check if an
object with a given id exists at the server.
It's also possible to catch errors using the `catch` handler _after_ e.g `fetch`. The function takes an `errorHandler`
that is given the returned error and produces a new `HttpHandler` that may then decide to transform the error and
continue processing, or fail with an error. This is very helpful when a failed request not necessarily means an error,
e.g if you need to check if an object with a given id exists at the server.

```fs
val catch:
Expand Down Expand Up @@ -604,6 +604,52 @@ type Context<'T> =
}
```

## Upgrade from Oryx v2 to v3

Oryx v3 is mostly backwards compatible with v2. Your chains of operators will for most part look and work exactly the
same. There are however some notable changes:

- The `retry` operator has been deprecated for now. Use [Polly](https://github.com/App-vNext/Polly) instead.
- The `catch` operator needs to run __after__ the error producing operator e.g `fetch` (not before). This is because
Oryx v3 pushes results "down" instead of returning them "up" the chain of operators. The good thing with this change
is that a handler can now continue processing the rest of the pipeline after catching an error. This was not possible
in v2 / v1 where the `catch` operator had to abort processing and produce a result.
- Http handlers take 2 generic types instead of 4. E.g `fetch<'TSource, 'TNext, 'TResult, 'TError>` now becomes
`fetch<'TSource, 'TNext>` and the last two types can simply be removed from your code.
- `ResponseError` is gone. You need to sub-class an exception instead. This means that the `'TError' type is also gone from the handlers.
- Custom context builders do not need any changes
- Custom HTTP handlers must be refactored. Instead of returning a result (Ok/Error) the handler needs to push down the
result either using the Ok path `next.NextAsync()` or fail with an error `next.ErrorAsync()`. This is very similar to
e.g Reactive Extensions (Rx) `OnNext` / `OnError`. E.g:

```fs
let withResource (resource: string) (next: NextFunc<_,_>) (context: HttpContext) =
next { context with Request = { context.Request with Items = context.Request.Items.Add(PlaceHolder.Resource, String resource) } }
```

Needs to be refactored to:

```fs
let withResource (resource: string): HttpHandler<'TSource> =
HttpHandler <| fun next ->
{ new IHttpNext<'TSource> with
member _.NextAsync(ctx, ?content) =
next.NextAsync(
{ ctx with
Request =
{ ctx.Request with
Items = ctx.Request.Items.Add(PlaceHolder.Resource, String resource)
}
},
?content = content
)
member _.ErrorAsync(ctx, exn) = next.ErrorAsync(ctx, exn)
}
```

It's a bit more verbose, but the inner part of the code is exactly the same.

## Upgrade from Oryx v1 to v2

The context is now initiated with a content `'T` of `unit`. E.g your custom HTTP handlers that is used before `fetch`
Expand Down
16 changes: 16 additions & 0 deletions examples/github/GitHub.fsproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Oryx" Version="3.0.0-beta-001" />
<PackageReference Include="Oryx.ThothJsonNet" Version="3.0.0-beta-001" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Ply" Version="0.3.1" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
</Project>
32 changes: 32 additions & 0 deletions examples/github/Program.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
open Oryx
open System.Net.Http
open Oryx.ThothJsonNet.ResponseReader
open Thoth.Json.Net
open FSharp.Control.Tasks.ContextInsensitive

[<EntryPoint>]
let main argv =
use client = new HttpClient()

let context =
Context.defaultContext
|> Context.withHttpClient client

let response =
GET
>=> withUrl "https://api.github.com/repos/cognitedata/oryx/releases/latest"
>=> fetch
>=> json (Decode.field "tag_name" Decode.string)

let _ =
(task {
let! tag = (response |> runAsync context)

printfn "%A" tag

return 0
})
.GetAwaiter()
.GetResult()

0 // return an integer exit code
2 changes: 0 additions & 2 deletions examples/paket.references

This file was deleted.

6 changes: 3 additions & 3 deletions examples/Program.fs → examples/wikisearch/Program.fs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
open System.Net.Http
open System.Text.Json

open FSharp.Control.Tasks.V2.ContextInsensitive
open FSharp.Control.Tasks

open Oryx
open Oryx.ThothJsonNet.ResponseReader
Expand All @@ -13,10 +13,10 @@ type WikiSearchHit =

type WikiSearchHits = WikiSearchHits of WikiSearchHit list

let wikiDataItemDecoder: Decoder<WikiSearchHit> =
let wikiDataItemDecoder : Decoder<WikiSearchHit> =
Decode.oneOf [ Decode.string |> Decode.map SearchTerm; Decode.list Decode.string |> Decode.map SearchHits ]

let wikiDataItemsDecoders: Decoder<WikiSearchHits> =
let wikiDataItemsDecoders : Decoder<WikiSearchHits> =
Decode.list wikiDataItemDecoder
|> Decode.map WikiSearchHits

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
<TargetFramework>netcoreapp3.1</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\Oryx.fsproj" />
<ProjectReference Include="..\extensions\Oryx.ThothJsonNet\Oryx.ThothJsonNet.fsproj" />
<ProjectReference Include="..\..\src\Oryx.fsproj" />
<ProjectReference Include="..\..\extensions\Oryx.ThothJsonNet\Oryx.ThothJsonNet.fsproj" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<Import Project="..\.paket\Paket.Restore.targets" />
<Import Project="..\..\.paket\Paket.Restore.targets" />
</Project>
4 changes: 4 additions & 0 deletions examples/wikisearch/paket.references
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
group Examples

Thoth.Json.Net
Ply
8 changes: 4 additions & 4 deletions extensions/Oryx.NewtonsoftJson/JsonPushStreamContent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ open System.Net.Http.Headers
open System.Text
open System.Threading.Tasks

open FSharp.Control.Tasks.V2.ContextInsensitive
open FSharp.Control.Tasks

open Newtonsoft.Json
open Newtonsoft.Json.Linq

/// HttpContent implementation to push a JsonValue directly to the output stream.
type JsonPushStreamContent (content: JToken) =
inherit HttpContent()
inherit HttpContent ()
let _content = content
do base.Headers.ContentType <- MediaTypeHeaderValue "application/json"

override this.SerializeToStreamAsync(stream: Stream, context: TransportContext): Task =
override this.SerializeToStreamAsync(stream: Stream, context: TransportContext) : Task =
task {
use sw = new StreamWriter(stream, UTF8Encoding(false), 1024, true)
use jtw = new JsonTextWriter(sw, Formatting = Formatting.None)
Expand All @@ -27,6 +27,6 @@ type JsonPushStreamContent (content: JToken) =
}
:> _

override this.TryComputeLength(length: byref<int64>): bool =
override this.TryComputeLength(length: byref<int64>) : bool =
length <- -1L
false
2 changes: 1 addition & 1 deletion extensions/Oryx.NewtonsoftJson/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ group Newtonsoft

FSharp.Core
Newtonsoft.Json
Taskbuilder.fs
Ply
Oryx
6 changes: 3 additions & 3 deletions extensions/Oryx.Protobuf/ProtobufPushStreamContent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ open System.Threading.Tasks
open Google.Protobuf

type ProtobufPushStreamContent (content: IMessage) =
inherit HttpContent()
inherit HttpContent ()
let _content = content
do base.Headers.ContentType <- MediaTypeHeaderValue "application/protobuf"

override this.SerializeToStreamAsync(stream: Stream, context: TransportContext): Task =
override this.SerializeToStreamAsync(stream: Stream, context: TransportContext) : Task =
content.WriteTo stream |> Task.FromResult :> _

override this.TryComputeLength(length: byref<int64>): bool =
override this.TryComputeLength(length: byref<int64>) : bool =
length <- -1L
false
2 changes: 1 addition & 1 deletion extensions/Oryx.Protobuf/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ group Google

FSharp.Core
Google.Protobuf
Taskbuilder.fs
Ply
Oryx
10 changes: 5 additions & 5 deletions extensions/Oryx.SystemTextJson/JsonPushStreamContent.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ open System.Net
open System.Net.Http.Headers
open System.Threading.Tasks

open FSharp.Control.Tasks.V2.ContextInsensitive
open FSharp.Control.Tasks

open System.Text.Json

/// HttpContent implementation to push content directly to the output stream.
type JsonPushStreamContent<'T> (content: 'T, options: JsonSerializerOptions) =
inherit HttpContent()
inherit HttpContent ()
let _content = content
let _options = options
do base.Headers.ContentType <- MediaTypeHeaderValue "application/json"

new(content: 'T) =
new (content: 'T) =
let options = JsonSerializerOptions()
new JsonPushStreamContent<'T>(content, options)

override this.SerializeToStreamAsync(stream: Stream, context: TransportContext): Task =
override this.SerializeToStreamAsync(stream: Stream, context: TransportContext) : Task =
task {
do! JsonSerializer.SerializeAsync<'T>(stream, _content, _options)
do! stream.FlushAsync()
}
:> _

override this.TryComputeLength(length: byref<int64>): bool =
override this.TryComputeLength(length: byref<int64>) : bool =
length <- -1L
false

Expand Down
2 changes: 1 addition & 1 deletion extensions/Oryx.SystemTextJson/ResponseReader.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module ResponseReader =
/// </summary>
/// <param name="options">JSON serializer options to use. </param>
/// <returns>Decoded context.</returns>
let json<'TResult> (options: JsonSerializerOptions): HttpHandler<HttpContent, 'TResult> =
let json<'TResult> (options: JsonSerializerOptions) : HttpHandler<HttpContent, 'TResult> =
let parser stream =
(JsonSerializer.DeserializeAsync<'TResult>(stream, options))
.AsTask()
Expand Down
2 changes: 1 addition & 1 deletion extensions/Oryx.SystemTextJson/paket.references
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ group Json

FSharp.Core
System.Text.Json
Taskbuilder.fs
Ply
Oryx
2 changes: 1 addition & 1 deletion extensions/Oryx.ThothJsonNet/Decode.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ open System
open System.IO

open Thoth.Json.Net
open FSharp.Control.Tasks.V2.ContextInsensitive
open FSharp.Control.Tasks
open Newtonsoft.Json

[<AutoOpen>]
Expand Down
Loading

0 comments on commit bb6c123

Please sign in to comment.