Skip to content

Commit

Permalink
Use MarkdownSnippets for docs (#320)
Browse files Browse the repository at this point in the history
* show usage of merging snippets into docs

* Docs changes

* Update Examples.cs

* Docs changes

* Update .github/workflows/on-push-do-docs.yml

Co-authored-by: Martin Costello <martin@martincostello.com>

* Update mdsnippets.json

Co-authored-by: Martin Costello <martin@martincostello.com>

* Update tests/HttpClientInterception.Tests/JustEat.HttpClientInterception.Tests.csproj

Co-authored-by: Martin Costello <martin@martincostello.com>

* Update .github/workflows/on-push-do-docs.yml

Co-authored-by: Martin Costello <martin@martincostello.com>

* Update tests/HttpClientInterception.Tests/Examples.cs

Co-authored-by: Martin Costello <martin@martincostello.com>

* Update tests/HttpClientInterception.Tests/Examples.cs

Co-authored-by: Martin Costello <martin@martincostello.com>

* Docs changes

* Update Examples.cs

* Docs changes

* Update on-push-do-docs.yml

* remove regions

Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
  • Loading branch information
4 people authored May 15, 2021
1 parent 967f883 commit f84b6e1
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 27 deletions.
24 changes: 24 additions & 0 deletions .github/workflows/on-push-do-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: on-push-do-docs
on:
push:
jobs:
docs:
runs-on: windows-latest
steps:
- name: Setup .NET Core SDK
uses: actions/setup-dotnet@v1
- uses: actions/checkout@v2
- name: Run MarkdownSnippets
run: |
dotnet tool install --global MarkdownSnippets.Tool
mdsnippets ${GITHUB_WORKSPACE}
shell: bash
- name: Push changes
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git commit -m "Docs changes" -a || echo "nothing to commit"
remote="https://${GITHUB_ACTOR}:${{secrets.GITHUB_TOKEN}}@github.com/${GITHUB_REPOSITORY}.git"
branch="${GITHUB_REF:11}"
git push "${remote}" ${branch} || echo "nothing to push"
shell: bash
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.12.1" />
<PackageVersion Include="coverlet.msbuild" Version="3.0.3" />
<PackageVersion Include="MarkdownSnippets.MsBuild" Version="23.1.2" />
<PackageVersion Include="MartinCostello.Logging.XUnit" Version="0.1.1" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="5.0.6" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="1.0.0" />
Expand Down
57 changes: 36 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,24 +34,31 @@ dotnet add package JustEat.HttpClientInterception

Below is a minimal example of intercepting an HTTP GET request to an API for a JSON resource to return a custom response using the fluent API:

```csharp
// using JustEat.HttpClientInterception;
<!-- snippet: minimal-example -->
<a id='snippet-minimal-example'></a>
```cs
// Arrange
var options = new HttpClientInterceptorOptions();
var builder = new HttpRequestInterceptionBuilder();

var builder = new HttpRequestInterceptionBuilder()
builder
.Requests()
.ForGet()
.ForHttps()
.ForHost("public.je-apis.com")
.ForPath("terms")
.Responds()
.WithJsonContent(new { Id = 1, Link = "https://www.just-eat.co.uk/privacy-policy" })
.RegisterWith(options);

var client = options.CreateHttpClient();
using var client = options.CreateHttpClient();

// The value of json will be "{\"Id\":1,\"Link\":\"https://www.just-eat.co.uk/privacy-policy\"}"
var json = await client.GetStringAsync("http://public.je-apis.com/terms");
// Act
// The value of json will be: {"Id":1, "Link":"https://www.just-eat.co.uk/privacy-policy"}
string json = await client.GetStringAsync("https://public.je-apis.com/terms");
```
<sup><a href='/tests/HttpClientInterception.Tests/Examples.cs#L50-L72' title='Snippet source file'>snippet source</a> | <a href='#snippet-minimal-example' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

`HttpRequestInterceptionBuilder` objects are mutable, so properties can be freely changed once a particular setup has been registered with an instance of `HttpClientInterceptorOptions` as the state is captured at the point of registration. This allows multiple responses and paths to be configured from a single `HttpRequestInterceptionBuilder` instance where multiple registrations against a common hostname.

Expand All @@ -67,6 +74,8 @@ Below is an example bundle file, which can return content in formats such as a s

The full JSON schema for HTTP bundle files can be found [here](https://raw.githubusercontent.com/justeat/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json "JSON Schema for HTTP request interception bundles for use with JustEat.HttpClientInterception.").

<!-- snippet: sample-bundle.json -->
<a id='snippet-sample-bundle.json'></a>
```json
{
"$schema": "https://raw.githubusercontent.com/justeat/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json",
Expand All @@ -92,6 +101,8 @@ The full JSON schema for HTTP bundle files can be found [here](https://raw.githu
]
}
```
<sup><a href='/tests/HttpClientInterception.Tests/Bundles/sample-bundle.json#L1-L23' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample-bundle.json' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

###### Code

Expand All @@ -115,9 +126,9 @@ Further examples of using HTTP bundles can be found in the [tests](https://githu

Below is a minimal example of intercepting a request to inject an HTTP fault:

```csharp
// using JustEat.HttpClientInterception;
<!-- snippet: fault-injection -->
<a id='snippet-fault-injection'></a>
```cs
var options = new HttpClientInterceptorOptions();

var builder = new HttpRequestInterceptionBuilder()
Expand All @@ -129,19 +140,26 @@ var builder = new HttpRequestInterceptionBuilder()
var client = options.CreateHttpClient();

// Throws an HttpRequestException
await client.GetStringAsync("http://public.je-apis.com");
await Assert.ThrowsAsync<HttpRequestException>(
() => client.GetStringAsync("http://public.je-apis.com"));
```
<sup><a href='/tests/HttpClientInterception.Tests/Examples.cs#L29-L44' title='Snippet source file'>snippet source</a> | <a href='#snippet-fault-injection' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

#### Registering Request Interception When Using IHttpClientFactory

If you are using [`IHttpClientFactory`](https://docs.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests "Use IHttpClientFactory to implement resilient HTTP requests") to register `HttpClient` for Dependency Injection in a .NET Core 2.1 application (or later), you can implement a custom [`IHttpMessageHandlerBuilderFilter`](https://docs.microsoft.com/en-us/dotnet/api/microsoft.extensions.http.ihttpmessagehandlerbuilderfilter "IHttpMessageHandlerBuilderFilter Interface") to register during test setup, which makes an instance of `HttpClientInterceptorOptions` available to inject an HTTP handler.

A working example of this approach can be found in the [sample application](https://github.com/justeat/httpclient-interception/blob/main/samples/README.md "Sample application that uses JustEat.HttpClientInterception").

```csharp
using Microsoft.Extensions.Http;

public class InterceptionFilter : IHttpMessageHandlerBuilderFilter
<!-- snippet: interception-filter -->
<a id='snippet-interception-filter'></a>
```cs
/// <summary>
/// A class that registers an intercepting HTTP message handler at the end of
/// the message handler pipeline when an <see cref="HttpClient"/> is created.
/// </summary>
private sealed class InterceptionFilter : IHttpMessageHandlerBuilderFilter
{
private readonly HttpClientInterceptorOptions _options;

Expand All @@ -150,6 +168,7 @@ public class InterceptionFilter : IHttpMessageHandlerBuilderFilter
_options = options;
}

/// <inheritdoc/>
public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuilder> next)
{
return (builder) =>
Expand All @@ -163,12 +182,8 @@ public class InterceptionFilter : IHttpMessageHandlerBuilderFilter
}
}
```

```csharp
var options = new HttpClientInterceptorOptions();
services.AddSingleton<IHttpMessageHandlerBuilderFilter, InterceptionFilter>(
(_) => new InterceptionFilter(options));
```
<sup><a href='/samples/SampleApp.Tests/HttpServerFixture.cs#L54-L83' title='Snippet source file'>snippet source</a> | <a href='#snippet-interception-filter' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

#### Setting Up HttpClient for Dependency Injection Manually

Expand Down
3 changes: 3 additions & 0 deletions mdsnippets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"Convention": "InPlaceOverwrite"
}
4 changes: 4 additions & 0 deletions samples/SampleApp.Tests/HttpServerFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ protected override void ConfigureWebHost(IWebHostBuilder builder)
builder.ConfigureLogging((p) => p.AddXUnit(this));
}

// begin-snippet: interception-filter

/// <summary>
/// A class that registers an intercepting HTTP message handler at the end of
/// the message handler pipeline when an <see cref="HttpClient"/> is created.
Expand All @@ -78,5 +80,7 @@ public Action<HttpMessageHandlerBuilder> Configure(Action<HttpMessageHandlerBuil
};
}
}

// end-snippet
}
}
2 changes: 1 addition & 1 deletion samples/SampleApp.Tests/SampleApp.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<NoWarn>$(NoWarn);CA1056;CA1062;CA1707;CA1711;CA2007;SA1600</NoWarn>
<NoWarn>$(NoWarn);CA1056;CA1062;CA1707;CA1711;CA2007;SA1124;SA1600</NoWarn>
<TargetFrameworks>net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
Expand Down
23 changes: 23 additions & 0 deletions tests/HttpClientInterception.Tests/Bundles/sample-bundle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"$schema": "https://raw.githubusercontent.com/justeat/httpclient-interception/main/src/HttpClientInterception/Bundles/http-request-bundle-schema.json",
"id": "my-bundle",
"comment": "A bundle of HTTP requests",
"items": [
{
"id": "home",
"comment": "Returns the home page",
"uri": "https://www.just-eat.co.uk",
"contentString": "<html><head><title>Just Eat</title></head></html>"
},
{
"id": "terms",
"comment": "Returns the Ts & Cs",
"uri": "https://public.je-apis.com/terms",
"contentFormat": "json",
"contentJson": {
"Id": 1,
"Link": "https://www.just-eat.co.uk/privacy-policy"
}
}
]
}
38 changes: 35 additions & 3 deletions tests/HttpClientInterception.Tests/Examples.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,22 +23,54 @@ namespace JustEat.HttpClientInterception
/// </summary>
public static class Examples
{
[Fact]
public static async Task Fault_Injection()
{
// begin-snippet: fault-injection
var options = new HttpClientInterceptorOptions();

var builder = new HttpRequestInterceptionBuilder()
.Requests()
.ForHost("public.je-apis.com")
.WithStatus(HttpStatusCode.InternalServerError)
.RegisterWith(options);

var client = options.CreateHttpClient();

// Throws an HttpRequestException
await Assert.ThrowsAsync<HttpRequestException>(
() => client.GetStringAsync("http://public.je-apis.com"));

// end-snippet
}

[Fact]
public static async Task Intercept_Http_Get_For_Json_Object()
{
// begin-snippet: minimal-example

// Arrange
var options = new HttpClientInterceptorOptions();
var builder = new HttpRequestInterceptionBuilder();

builder.Requests().ForGet().ForHttps().ForHost("public.je-apis.com").ForPath("terms")
.Responds().WithJsonContent(new { Id = 1, Link = "https://www.just-eat.co.uk/privacy-policy" })
.RegisterWith(options);
builder
.Requests()
.ForGet()
.ForHttps()
.ForHost("public.je-apis.com")
.ForPath("terms")
.Responds()
.WithJsonContent(new { Id = 1, Link = "https://www.just-eat.co.uk/privacy-policy" })
.RegisterWith(options);

using var client = options.CreateHttpClient();

// Act
// The value of json will be: {"Id":1, "Link":"https://www.just-eat.co.uk/privacy-policy"}
string json = await client.GetStringAsync("https://public.je-apis.com/terms");

// end-snippet

// Assert
var content = JObject.Parse(json);
content.Value<int>("Id").ShouldBe(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Tests for JustEat.HttpClientInterception</Description>
<NoWarn>$(NoWarn);CA1303;CA1600;CA1707;CA1812;CA2000;CA2007;SA1600</NoWarn>
<NoWarn>$(NoWarn);CA1303;CA1600;CA1707;CA1812;CA2000;CA2007;SA1124;SA1600;SA1123</NoWarn>
<RootNamespace>JustEat.HttpClientInterception</RootNamespace>
<Summary>Tests for JustEat.HttpClientInterception</Summary>
<TargetFrameworks>net5.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<Content Include="example-bundle.json;xunit.runner.json;Bundles\*.json" CopyToOutputDirectory="PreserveNewest" />
<None Remove="Bundles\*.json" />
<Content Include="..\..\src\HttpClientInterception\Bundles\http-request-bundle-schema.json" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
Expand All @@ -16,6 +17,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="Moq" />
<PackageReference Include="MarkdownSnippets.MsBuild" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="Newtonsoft.Json.Schema" />
<PackageReference Include="Polly" />
Expand Down

0 comments on commit f84b6e1

Please sign in to comment.