Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move code from markdown to snippets #1540

Merged
merged 8 commits into from
Sep 4, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .config/dotnet-tools.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
"commands": [
"dotnet-cake"
]
},
"markdownsnippets.tool": {
"version": "25.1.0",
"commands": [
"mdsnippets"
]
}
}
}
}
99 changes: 99 additions & 0 deletions .github/workflows/on-push-do-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: on-push-do-docs

on:
push:
branches: [main]
paths: [ "src/Snippets/**" ]
workflow_dispatch:

permissions:
contents: read

jobs:
update-docs:
name: update-docs
runs-on: ubuntu-latest

permissions:
contents: write

martincostello marked this conversation as resolved.
Show resolved Hide resolved
steps:
- name: Checkout code
uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0

- name: Setup .NET SDK
uses: actions/setup-dotnet@3447fd6a9f9e57506b15f895c5b76d3b197dc7c2 # v3.2.0

- name: Update documentation
id: update-docs
shell: pwsh
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
run: |
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"

dotnet tool restore
dotnet mdsnippets

$GitStatus = (git status --porcelain)
if ([string]::IsNullOrEmpty($GitStatus)) {
Write-Host "No changes to commit."
exit 0
}

$TimeStamp = Get-Date -Format "yyyy-MM-dd-HH-mm"
$BranchName = "docs/update-docs-$TimeStamp"
echo "branchName=$BranchName" >> $env:GITHUB_OUTPUT
martincostello marked this conversation as resolved.
Show resolved Hide resolved

$GitEmail = "138034000+polly-updater-bot[bot]@users.noreply.github.com"
$GitUser = "polly-updater-bot[bot]"

git config user.email $GitEmail | Out-Null
git config user.name $GitUser | Out-Null
git remote set-url "${{ github.server_url }}/${{ github.repository }}.git" | Out-Null
git fetch origin | Out-Null
git rev-parse --verify --quiet ("remotes/origin/" + $BranchName) | Out-Null

if ($LASTEXITCODE -eq 0) {
Write-Host "Branch $BranchName already exists."
exit 0
}

git checkout -b $BranchName
git add .
git commit -m "Update the code-snippets in the documentation"
git push -u origin $BranchName
"updated-docs=true" >> $env:GITHUB_OUTPUT

- name: Generate GitHub application token
if: steps.update-docs.outputs.updated-docs == 'true'
id: generate-application-token
uses: peter-murray/workflow-application-token-action@8e1ba3bf1619726336414f1014e37f17fbadf1db # v2.1.0
with:
application_id: ${{ secrets.POLLY_UPDATER_BOT_APP_ID }}
application_private_key: ${{ secrets.POLLY_UPDATER_BOT_KEY }}
permissions: "contents:write, pull_requests:write"

- name: Create pull request
if: steps.update-docs.outputs.updated-docs == 'true'
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
with:
github-token: ${{ steps.generate-application-token.outputs.token }}
script: |
const { repo, owner } = context.repo;
const workflowUrl = `${{ github.server_url }}/${owner}/${repo}/actions/runs/${process.env.GITHUB_RUN_ID}`;
const branchName = "${{ steps.update-docs.outputs.branchName }}";
const result = await github.rest.pulls.create({
title: 'Update the code-snippets in the documentation',
owner,
repo,
head: branchName,
base: 'main',
body: [
'This PR updates the code-snippets in the documentation.',
'',
`This pull request was generated by [GitHub Actions](${workflowUrl}).`
].join('\n')
});
2 changes: 2 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
<PackageVersion Include="NSubstitute" Version="5.0.0" />
<PackageVersion Include="Polly" Version="$(PollyVersion)" />
<PackageVersion Include="Polly.Core" Version="$(PollyVersion)" />
<PackageVersion Include="Polly.RateLimiting" Version="$(PollyVersion)" />
<PackageVersion Include="Polly.Extensions" Version="$(PollyVersion)" />
<PackageVersion Include="Polly.Testing" Version="$(PollyVersion)" />
<PackageVersion Include="Polly.Contrib.WaitAndRetry" Version="1.1.1" />
<PackageVersion Include="ReportGenerator" Version="5.1.24" />
<PackageVersion Include="SonarAnalyzer.CSharp" Version="9.8.0.76515" />
Expand Down
7 changes: 7 additions & 0 deletions Polly.sln
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Polly.Testing", "src\Polly.
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Polly.Testing.Tests", "test\Polly.Testing.Tests\Polly.Testing.Tests.csproj", "{D333B5CE-982D-4C11-BDAF-4217AA02306E}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Snippets", "src\Snippets\Snippets.csproj", "{D812B941-79B0-4E1E-BB70-4FAE345B5234}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -114,6 +116,10 @@ Global
{D333B5CE-982D-4C11-BDAF-4217AA02306E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D333B5CE-982D-4C11-BDAF-4217AA02306E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D333B5CE-982D-4C11-BDAF-4217AA02306E}.Release|Any CPU.Build.0 = Release|Any CPU
{D812B941-79B0-4E1E-BB70-4FAE345B5234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D812B941-79B0-4E1E-BB70-4FAE345B5234}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D812B941-79B0-4E1E-BB70-4FAE345B5234}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D812B941-79B0-4E1E-BB70-4FAE345B5234}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -133,6 +139,7 @@ Global
{C04DEE61-C1EA-4028-B457-CDBD304B8ED9} = {A6CC41B9-E0B9-44F8-916B-3E4A78DA3BFB}
{9AD2D6AD-56E4-49D6-B6F1-EE975D5760B9} = {B7BF406B-B06F-4025-83E6-7219C53196A6}
{D333B5CE-982D-4C11-BDAF-4217AA02306E} = {A6CC41B9-E0B9-44F8-916B-3E4A78DA3BFB}
{D812B941-79B0-4E1E-BB70-4FAE345B5234} = {B7BF406B-B06F-4025-83E6-7219C53196A6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {2E5D54CD-770A-4345-B585-1848FC2EA6F4}
Expand Down
2 changes: 1 addition & 1 deletion bench/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Benchmarks
# Benchmarks

To run the benchmarks, use the `benchmarks.ps1` script in this repository:

Expand Down
11 changes: 11 additions & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -233,13 +233,24 @@ Task("__CreateNuGetPackages")
}
});

Task("__ValidateDocs")
.Does(() =>
{
var result = StartProcess("dotnet", "mdsnippets --validate-content");
if (result != 0)
{
throw new InvalidOperationException($"Failed to validate the documentation snippets. Are the links correct?");
}
});

martintmk marked this conversation as resolved.
Show resolved Hide resolved
//////////////////////////////////////////////////////////////////////
// BUILD TASKS
//////////////////////////////////////////////////////////////////////

Task("Build")
.IsDependentOn("__Clean")
.IsDependentOn("__RestoreNuGetPackages")
.IsDependentOn("__ValidateDocs")
.IsDependentOn("__BuildSolutions")
.IsDependentOn("__RunTests")
.IsDependentOn("__RunMutationTests")
Expand Down
7 changes: 7 additions & 0 deletions mdsnippets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/SimonCropp/MarkdownSnippets/main/schema.json",
"ExcludeDirectories": [ "test", "artifacts", "bench", "eng" ],
"ExcludeSnippetDirectories": [ "src/Polly", "src/Polly.Core", "src/Polly.RateLimiting", "src/Polly.Testing", "src/Polly.Extensions", "artifacts", "test" ],
"OmitSnippetLinks": true,
"Convention": "InPlaceOverwrite"
}
85 changes: 39 additions & 46 deletions src/Polly.Core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,31 +71,26 @@ The resilience pipeline may consist of one or more individual resilience strateg

Here's an example of a non-reactive strategy that executes a user-provided callback:

```csharp
<!-- snippet: my-custom-strategy -->
```cs
internal class MyCustomStrategy : ResilienceStrategy
{
private readonly TimeProvider _timeProvider;

public MyCustomStrategy(TimeProvider timeProvider)
{
_timeProvider = timeProvider;
}

protected override async ValueTask<T> ExecuteCore<T, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<T>>> callback,
ResilienceContext context,
protected override async ValueTask<Outcome<TResult>> ExecuteCore<TResult, TState>(
Func<ResilienceContext, TState, ValueTask<Outcome<TResult>>> callback,
ResilienceContext context,
TState state)
{
// Perform actions before execution

var outcome = await callback(context, state).ContinueOnCapturedContext(context.ContinueOnCapturedContext);
var outcome = await callback(context, state).ConfigureAwait(context.ContinueOnCapturedContext);

// Perform actions after execution

return outcome;
}
}
```
<!-- endSnippet -->

### About Synchronous and Asynchronous Executions

Expand All @@ -113,37 +108,51 @@ To construct a resilience pipeline, chain various extensions on the `ResilienceP

### Creating a non-generic pipeline

```csharp
var pipeline = new ResiliencePipelineBuilder()
<!-- snippet: create-generic-pipeline -->
```cs
ResiliencePipeline<string> pipeline = new ResiliencePipelineBuilder<string>()
.AddRetry(new())
.AddCircuitBreaker(new())
.AddTimeout(new TimeoutStrategyOptions() { ... })
.AddTimeout(TimeSpan.FromSeconds(1))
.Build();
```
<!-- endSnippet -->

### Creating a generic pipeline

```csharp
var pipeline = new ResiliencePipelineBuilder<string>()
<!-- snippet: create-non-generic-pipeline -->
```cs
ResiliencePipeline<string> pipeline = new ResiliencePipelineBuilder<string>()
.AddRetry(new())
.AddCircuitBreaker(new())
.AddTimeout(new TimeoutStrategyOptions() { ... })
.AddTimeout(TimeSpan.FromSeconds(1))
.Build();
```
<!-- endSnippet -->

## Extensibility

Extending the resilience functionality is straightforward. You can create extensions for `ResiliencePipelineBuilder` by leveraging the `AddStrategy` extension methods. If you aim to design a resilience strategy that is compatible with both generic and non-generic builders, consider using `ResiliencePipelineBuilderBase` as your target class.

Here's an example:

```csharp
<!-- snippet: add-my-custom-strategy -->
```cs
public static TBuilder AddMyCustomStrategy<TBuilder>(this TBuilder builder, MyCustomStrategyOptions options)
where TBuilder : ResiliencePipelineBuilderBase
{
return builder.AddStrategy(context => new MyCustomStrategy(), options);
}

public class MyCustomStrategyOptions : ResilienceStrategyOptions
{
public MyCustomStrategyOptions()
{
Name = "MyCustomStrategy";
}
}
```
<!-- endSnippet -->

To gain insights into implementing custom resilience strategies, you can explore the following Polly strategy examples:

Expand Down Expand Up @@ -185,7 +194,8 @@ These delegates accept either `Args` or `Args<TResult>` arguments, which encapsu

For non-reactive strategies, the `Args` structure might resemble:

```csharp
<!-- snippet: on-timeout-args -->
```cs
public readonly struct OnTimeoutArguments
{
public OnTimeoutArguments(ResilienceContext context, TimeSpan timeout)
Expand All @@ -195,39 +205,23 @@ public readonly struct OnTimeoutArguments
}

public ResilienceContext Context { get; } // Include the Context property
public TimeSpan Timeout { get; } // Additional event-related properties
}
```

For reactive strategies, `Args<TResult>` could look like:

```csharp
public readonly struct OnRetryArguments<TResult>
{
public OnRetryArguments(ResilienceContext context, Outcome<TResult> outcome, int attemptNumber)
{
Context = context;
Outcome = outcome;
AttemptNumber = attemptNumber;
}

public ResilienceContext Context { get; } // Include the Context property
public Outcome<TResult> Outcome { get; } // Includes the outcome associated with the event
public int AttemptNumber { get; }
public TimeSpan Timeout { get; } // Additional event-related properties
}
```
<!-- endSnippet -->

### Example: Usage of Delegates

Below are some examples illustrating the usage of these delegates:

```csharp
<!-- snippet: delegate-usage -->
```cs
new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{

// Non-Generic predicate for multiple result types
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True(),
{ Result: string result } when result == "Failure" => PredicateResult.True(),
Expand All @@ -236,27 +230,26 @@ new ResiliencePipelineBuilder()
},
})
.Build();
```

```csharp
new ResiliencePipelineBuilder<string>()
.AddRetry(new RetryStrategyOptions<string>
{
// Generic predicate for a single result type
ShouldHandle = args => args switch
ShouldHandle = args => args.Outcome switch
{
{ Exception: InvalidOperationException } => PredicateResult.True(),
{ Result: result } when result == "Failure" => PredicateResult.True(),
{ Result: { } result } when result == "Failure" => PredicateResult.True(),
_ => PredicateResult.False()
},
})
.Build();
```
<!-- endSnippet -->

## Telemetry

Each resilience strategy can generate telemetry data through the [`ResiliencePipelineTelemetry`](Telemetry/ResiliencePipelineTelemetry.cs) API. Polly encapsulates event details as [`TelemetryEventArguments`](Telemetry/TelemetryEventArguments.cs) and emits them via `TelemetryListener`.

To leverage this telemetry data, users should assign a `TelemetryListener` instance to `ResiliencePipelineBuilder.TelemetryListener` and then consume the `TelemetryEventArguments`.

For common scenarios, it is expected that users would make use of `Polly.Extensions`. This extension enables telemetry configuration through the `ResiliencePipelineBuilder.ConfigureTelemetry(...)` method, which processes `TelemetryEventArguments` to generate logs and metrics.
For common scenarios, it is expected that users would make use of `Polly.Extensions`. This extension enables telemetry configuration through the `ResiliencePipelineBuilder.ConfigureTelemetry(...)` method, which processes `TelemetryEventArguments` to generate logs and metrics.
Loading