From bf7eca63dfe8a28a308deab7c46b06d87ab0adfe Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 07:44:54 +0200 Subject: [PATCH 1/6] ci: sonar.login is deprecated - switching to sonar.token --- .github/workflows/post-integration.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/post-integration.yml b/.github/workflows/post-integration.yml index 7b64f57..fb5299b 100644 --- a/.github/workflows/post-integration.yml +++ b/.github/workflows/post-integration.yml @@ -62,9 +62,9 @@ jobs: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} shell: pwsh run: | - dotnet sonarscanner begin /k:"atc-rest-client" /o:"atc-net" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" + dotnet sonarscanner begin /k:"atc-rest-client" /o:"atc-net" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" dotnet build -c Release /p:UseSourceLink=true --no-restore - dotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" + dotnet sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" - name: ⏩ Merge to stable-branch run: | From 185c8035970043fceb77c5527211ae2abf68138a Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 08:31:44 +0200 Subject: [PATCH 2/6] ci: upgrade workflow actions and dotnet version --- .github/workflows/post-integration.yml | 8 ++++---- .github/workflows/pre-integration.yml | 16 ++++++++-------- .github/workflows/release.yml | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/post-integration.yml b/.github/workflows/post-integration.yml index fb5299b..e3f470f 100644 --- a/.github/workflows/post-integration.yml +++ b/.github/workflows/post-integration.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 🛒 Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.PAT_WORKFLOWS }} @@ -30,10 +30,10 @@ jobs: with: setAllVars: true - - name: ⚙️ Setup dotnet 5.0.x - uses: actions/setup-dotnet@v1 + - name: ⚙️ Setup dotnet 8.0.x + uses: actions/setup-dotnet@v4 with: - dotnet-version: '5.0.x' + dotnet-version: '8.0.x' - name: ⚙️ Set up JDK 17 uses: actions/setup-java@v3 diff --git a/.github/workflows/pre-integration.yml b/.github/workflows/pre-integration.yml index 1036719..ff34062 100644 --- a/.github/workflows/pre-integration.yml +++ b/.github/workflows/pre-integration.yml @@ -15,14 +15,14 @@ jobs: runs-on: ${{ matrix.os }} steps: - name: 🛒 Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: ⚙️ Setup dotnet 5.0.x - uses: actions/setup-dotnet@v1 + - name: ⚙️ Setup dotnet 8.0.x + uses: actions/setup-dotnet@v4 with: - dotnet-version: '5.0.x' + dotnet-version: '8.0.x' - name: 🧹 Clean run: dotnet clean -c Release && dotnet nuget locals all --clear @@ -39,14 +39,14 @@ jobs: - dotnet5-build steps: - name: 🛒 Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: ⚙️ Setup dotnet 5.0.x - uses: actions/setup-dotnet@v1 + - name: ⚙️ Setup dotnet 8.0.x + uses: actions/setup-dotnet@v4 with: - dotnet-version: '5.0.x' + dotnet-version: '8.0.x' - name: 🔁 Restore packages run: dotnet restore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f00e4a..49aebcd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 🛒 Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 token: ${{ secrets.PAT_WORKFLOWS }} @@ -27,10 +27,10 @@ jobs: with: setAllVars: true - - name: ⚙️ Setup dotnet 5.0.x - uses: actions/setup-dotnet@v1 + - name: ⚙️ Setup dotnet 8.0.x + uses: actions/setup-dotnet@v4 with: - dotnet-version: '5.0.x' + dotnet-version: '8.0.x' - name: 🧹 Clean run: dotnet clean -c Release && dotnet nuget locals all --clear From 8864a4112e23a78a00f8b217e04abf829ef3066a Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 08:32:43 +0200 Subject: [PATCH 3/6] feat: upgrading to dotnet 8 and cleanup superfluous package references in test project --- .editorconfig | 68 +++++++++++++++++-- Directory.Build.props | 19 +++--- src/.editorconfig | 13 ++-- src/Directory.Build.props | 5 +- test/.editorconfig | 20 ++++-- .../Atc.Rest.Client.Tests.csproj | 24 +++---- test/Directory.Build.props | 14 +++- 7 files changed, 118 insertions(+), 45 deletions(-) diff --git a/.editorconfig b/.editorconfig index d402d2f..ec6e506 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1,8 @@ # ATC coding rules - https://github.com/atc-net/atc-coding-rules -# Version: 1.0.4 -# Updated: 20-12-2020 +# Version: 1.0.0 +# Updated: 25-09-2023 # Location: Root +# Distribution: DotNet8 # Inspired by: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options ########################################## @@ -58,6 +59,12 @@ end_of_line = crlf # Bash Files [*.sh] end_of_line = lf +indent_size = 2 + +# Powershell +[*.ps1] +end_of_line = lf +indent_size = 2 # Makefiles [Makefile] @@ -80,6 +87,7 @@ dotnet_separate_import_directive_groups = false # .NET Code Style Settings # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#net-code-style-settings [*.{cs,csx,cake}] + # "this." and "Me." qualifiers # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#this-and-me dotnet_style_qualification_for_field = false @@ -139,12 +147,17 @@ dotnet_style_operator_placement_when_wrapping = end_of_line # C# Code Style Settings # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#c-code-style-settings [*.{cs,csx,cake}] + # Implicit and explicit types # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#implicit-and-explicit-types csharp_style_var_for_built_in_types = true # IDE0007 and IDE0008 csharp_style_var_when_type_is_apparent = true # IDE0007 and IDE0008 csharp_style_var_elsewhere = true # IDE0007 and IDE0008 +# Namespace declaration preferences +# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0160-ide0161?view=vs-2022 +csharp_style_namespace_declarations = file_scoped # IDE0160 and IDE0161 + # Expression-bodied members # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#expression-bodied-members csharp_style_expression_bodied_constructors = when_on_single_line # IDE0021 @@ -188,8 +201,8 @@ csharp_style_unused_value_assignment_preference = discard_variable # Index and range preferences # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#index-and-range-preferences -csharp_style_prefer_index_operator = true # IDE0056 -csharp_style_prefer_range_operator = true # IDE0057 +csharp_style_prefer_index_operator = true:suggestion # IDE0056 +csharp_style_prefer_range_operator = true:suggestion # IDE0057 # Miscellaneous preferences # https://docs.microsoft.com/visualstudio/ide/editorconfig-language-conventions#miscellaneous-preferences @@ -252,6 +265,7 @@ csharp_space_between_square_brackets = false # https://docs.microsoft.com/visualstudio/ide/editorconfig-formatting-conventions#wrap-options csharp_preserve_single_line_statements = false csharp_preserve_single_line_blocks = true +csharp_style_namespace_declarations = file_scoped:suggestion ########################################## # .NET Naming Conventions @@ -433,15 +447,35 @@ dotnet_naming_rule.parameters_rule.severity = warning # Meziantou # https://www.meziantou.net/enforcing-asynchronous-code-good-practices-using-a-roslyn-analyzer.htm +dotnet_diagnostic.MA0003.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0003.md +dotnet_diagnostic.MA0004.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0004.md +dotnet_diagnostic.MA0006.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0006.md +dotnet_diagnostic.MA0011.severity = none # Duplicate of CA1305 dotnet_diagnostic.MA0016.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0016.md dotnet_diagnostic.MA0025.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0025.md dotnet_diagnostic.MA0026.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0026.md +dotnet_diagnostic.MA0028.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0028.md +dotnet_diagnostic.MA0038.severity = none # Duplicate of CA1822 +dotnet_diagnostic.MA0048.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0048.md # Microsoft - Code Analysis # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +dotnet_diagnostic.CA1014.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1014.md +dotnet_diagnostic.CA1068.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1068.md +dotnet_diagnostic.CA1305.severity = error +dotnet_diagnostic.CA1707.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1707.md +dotnet_diagnostic.CA1812.severity = none +dotnet_diagnostic.CA1822.severity = suggestion +dotnet_diagnostic.CA2007.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA2007.md +dotnet_diagnostic.IDE0005.severity = warning # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/IDE0005.md +dotnet_diagnostic.IDE0058.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/IDE0058.md + + +# Microsoft - Compiler Errors +# https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/ +dotnet_diagnostic.CS4014.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCompilerErrors/CS4014.md -dotnet_diagnostic.CA1014.severity = none # SecurityCodeScan # https://security-code-scan.github.io/ @@ -451,23 +485,43 @@ dotnet_diagnostic.CA1014.severity = none # https://github.com/DotNetAnalyzers/StyleCopAnalyzers dotnet_diagnostic.SA1009.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1009.md dotnet_diagnostic.SA1101.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1101.md +dotnet_diagnostic.SA1122.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1122.md +dotnet_diagnostic.SA1133.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1133.md dotnet_diagnostic.SA1200.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1200.md +dotnet_diagnostic.SA1201.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1201.md +dotnet_diagnostic.SA1202.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1202.md +dotnet_diagnostic.SA1204.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1204.md dotnet_diagnostic.SA1413.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1413.md dotnet_diagnostic.SA1600.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1600.md +dotnet_diagnostic.SA1601.severity = none dotnet_diagnostic.SA1602.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1602.md dotnet_diagnostic.SA1604.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1604.md +dotnet_diagnostic.SA1623.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1623.md +dotnet_diagnostic.SA1629.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1629.md dotnet_diagnostic.SA1633.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1633.md +dotnet_diagnostic.SA1649.severity = error # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1649.md # SonarAnalyzer.CSharp # https://rules.sonarsource.com/csharp dotnet_diagnostic.S1135.severity = suggestion # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/SonarAnalyzerCSharp/S1135.md +dotnet_diagnostic.S2629.severity = none # Don't use string interpolation in logging message templates. +dotnet_diagnostic.S6602.severity = none # "Find" method should be used instead of the "FirstOrDefault" +dotnet_diagnostic.S6603.severity = none # The collection-specific "TrueForAll" method should be used instead of the "All" +dotnet_diagnostic.S6605.severity = none # Collection-specific "Exists" method should be used instead of the "Any" + + +########################################## +# Custom - File Extension Settings +########################################## ########################################## # Custom - Code Analyzers Rules ########################################## -dotnet_diagnostic.CA1014.severity = none # CA1014: Mark assemblies with CLSCompliant +dotnet_diagnostic.CA1014.severity = none # +dotnet_diagnostic.CA1859.severity = none # -dotnet_diagnostic.SA1202.severity = none # SA1202: Elements should be ordered by access +dotnet_diagnostic.SA1010.severity = none # +dotnet_diagnostic.SA1202.severity = none # \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index 59265d8..1ae26a3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,9 +16,11 @@ enable - 9.0 + 12.0 + enable + net8.0 true - 1573,1591,1712 + 1573,1591,1712,CA1014,NU5104 full @@ -39,13 +41,12 @@ - - - - - - - + + + + + + \ No newline at end of file diff --git a/src/.editorconfig b/src/.editorconfig index 5fadd27..970bed4 100644 --- a/src/.editorconfig +++ b/src/.editorconfig @@ -1,7 +1,8 @@ # ATC coding rules - https://github.com/atc-net/atc-coding-rules -# Version: 1.0.2 -# Updated: 10-12-2020 -# Location: Src +# Version: 1.0.0 +# Updated: 25-09-2023 +# Location: src +# Distribution: DotNet8 # Inspired by: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options ########################################## @@ -25,6 +26,10 @@ # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +# Microsoft - Compiler Errors +# https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/ + + # SecurityCodeScan # https://security-code-scan.github.io/ @@ -39,4 +44,4 @@ ########################################## # Custom - Code Analyzers Rules -########################################## +########################################## \ No newline at end of file diff --git a/src/Directory.Build.props b/src/Directory.Build.props index fd3051e..f516744 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -22,7 +22,6 @@ Library - net5.0 true @@ -54,8 +53,8 @@ - - + + diff --git a/test/.editorconfig b/test/.editorconfig index eb860d8..92667f9 100644 --- a/test/.editorconfig +++ b/test/.editorconfig @@ -1,7 +1,8 @@ # ATC coding rules - https://github.com/atc-net/atc-coding-rules -# Version: 1.0.3 -# Updated: 20-12-2020 -# Location: Test +# Version: 1.0.0 +# Updated: 25-09-2023 +# Location: test +# Distribution: DotNet8 # Inspired by: https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/code-style-rule-options ########################################## @@ -19,11 +20,20 @@ # Meziantou # https://www.meziantou.net/enforcing-asynchronous-code-good-practices-using-a-roslyn-analyzer.htm +dotnet_diagnostic.MA0004.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0004.md +dotnet_diagnostic.MA0016.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/Meziantou/MA0016.md # Microsoft - Code Analysis # https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/ +dotnet_diagnostic.CA1068.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1068.md +dotnet_diagnostic.CA1602.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1602.md dotnet_diagnostic.CA1707.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA1707.md +dotnet_diagnostic.CA2007.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/MicrosoftCodeAnalysis/CA2007.md + + +# Microsoft - Compiler Errors +# https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/compiler-messages/ # SecurityCodeScan @@ -32,6 +42,7 @@ dotnet_diagnostic.CA1707.severity = none # https://github.com/atc-net # StyleCop # https://github.com/DotNetAnalyzers/StyleCopAnalyzers +dotnet_diagnostic.SA1122.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1122.md dotnet_diagnostic.SA1133.severity = none # https://github.com/atc-net/atc-coding-rules/blob/main/documentation/CodeAnalyzersRules/StyleCop/SA1133.md @@ -44,6 +55,7 @@ dotnet_diagnostic.SA1133.severity = none # https://github.com/atc-net ########################################## dotnet_diagnostic.CA1062.severity = none # Validate arguments of public methods dotnet_diagnostic.CA1806.severity = none # Do not ignore method results +dotnet_diagnostic.CA1861.severity = none # dotnet_diagnostic.CA2007.severity = none # Consider calling ConfigureAwait on the awaited task -dotnet_diagnostic.MA0076.severity = none # Do not use implicit culture-sensitive ToString in interpolated +dotnet_diagnostic.MA0076.severity = none # Do not use implicit culture-sensitive ToString in interpolated \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/Atc.Rest.Client.Tests.csproj b/test/Atc.Rest.Client.Tests/Atc.Rest.Client.Tests.csproj index 8170d1f..b9fc20f 100644 --- a/test/Atc.Rest.Client.Tests/Atc.Rest.Client.Tests.csproj +++ b/test/Atc.Rest.Client.Tests/Atc.Rest.Client.Tests.csproj @@ -1,29 +1,21 @@ - net5.0 + net8.0 false - - - - annotations - 9.0 + true - - - - - - - - all + + + runtime; build; native; contentfiles; analyzers; buildtransitive - - all + + runtime; build; native; contentfiles; analyzers; buildtransitive + all diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 73044b8..020fd5c 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -10,8 +10,18 @@ annotations - - + + + + + + + + + + + + \ No newline at end of file From 74ab9fc5675a369dec98b694b8cbadc536c280f5 Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 08:32:58 +0200 Subject: [PATCH 4/6] feat: update atc-coding-rules updater configuration file and script --- atc-coding-rules-updater.json | 21 +++++++++++++++++---- atc-coding-rules-updater.ps1 | 5 +++-- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/atc-coding-rules-updater.json b/atc-coding-rules-updater.json index 134b239..65e3cb3 100644 --- a/atc-coding-rules-updater.json +++ b/atc-coding-rules-updater.json @@ -1,6 +1,19 @@ { - "Mappings": { - "Src": { "Paths": [ "src" ] }, - "Test": { "Paths": [ "test" ] } - } + "projectTarget": "DotNet8", + "useLatestMinorNugetVersion": true, + "useTemporarySuppressions": false, + "temporarySuppressionAsExcel": false, + "analyzerProviderCollectingMode": "LocalCache", + "mappings": { + "src": { + "paths": [ + "src" + ] + }, + "test": { + "paths": [ + "test" + ] + } + } } \ No newline at end of file diff --git a/atc-coding-rules-updater.ps1 b/atc-coding-rules-updater.ps1 index 3153285..3d555a8 100644 --- a/atc-coding-rules-updater.ps1 +++ b/atc-coding-rules-updater.ps1 @@ -6,6 +6,7 @@ $currentPath = Get-Location Write-Host "Running atc-coding-rules-updater to fetch updated rulesets and configurations" atc-coding-rules-updater ` - -r $currentPath ` + run ` + -p $currentPath ` --optionsPath $currentPath'\atc-coding-rules-updater.json' ` - -v true \ No newline at end of file + --verbose \ No newline at end of file From 821a40b2809102917017ef83b82dcb595d3a9d47 Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 08:33:28 +0200 Subject: [PATCH 5/6] feat: upgrade nuget packages in main library --- src/Atc.Rest.Client/Atc.Rest.Client.csproj | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Atc.Rest.Client/Atc.Rest.Client.csproj b/src/Atc.Rest.Client/Atc.Rest.Client.csproj index ead481b..1217429 100644 --- a/src/Atc.Rest.Client/Atc.Rest.Client.csproj +++ b/src/Atc.Rest.Client/Atc.Rest.Client.csproj @@ -9,14 +9,14 @@ - - - - + + + + - + all runtime; build; native; contentfiles; analyzers From 090d8a58e597db1d58f6d5f3f87812185d46a011 Mon Sep 17 00:00:00 2001 From: Per Kops Date: Tue, 7 May 2024 08:34:21 +0200 Subject: [PATCH 6/6] feat: convert to file-scoped namespaces, use latest c# language syntax, fix spelling mistakes, utilize GlobalUsings, sealing test classes and fixing 1 failing test --- .../Builder/HttpMessageFactory.cs | 33 +- .../Builder/IHttpMessageFactory.cs | 40 +- .../Builder/IMessageRequestBuilder.cs | 107 ++-- .../Builder/IMessageResponseBuilder.cs | 44 +- .../Builder/MessageRequestBuilder.cs | 298 +++++------ .../Builder/MessageResponseBuilder.cs | 229 +++++---- src/Atc.Rest.Client/EndpointResponse.cs | 85 ++-- .../EndpointResponse{TSuccess,TError}.cs | 42 +- .../EndpointResponse{TSuccess}.cs | 38 +- src/Atc.Rest.Client/GlobalUsings.cs | 12 + src/Atc.Rest.Client/IEndpointResponse.cs | 18 +- .../Options/AtcRestClientOptions.cs | 11 +- .../Options/ServiceCollectionExtensions.cs | 93 ++-- .../DefaultJsonContractSerializer.cs | 82 +-- .../Serialization/IContractSerializer.cs | 24 +- .../JsonSerializerOptionsExtensions.cs | 60 +-- test/Atc.Rest.Client.Tests/BadResponse.cs | 9 +- .../Builder/HttpMessageFactoryTests.cs | 51 +- .../Builder/MessageRequestBuilderTests.cs | 470 +++++++++--------- .../Builder/MessageResponseBuilderTests.cs | 362 +++++++------- .../EndpointResponseTests.cs | 133 +++-- test/Atc.Rest.Client.Tests/GlobalUsings.cs | 5 + test/Atc.Rest.Client.Tests/SuccessResponse.cs | 9 +- 23 files changed, 1109 insertions(+), 1146 deletions(-) create mode 100644 src/Atc.Rest.Client/GlobalUsings.cs create mode 100644 test/Atc.Rest.Client.Tests/GlobalUsings.cs diff --git a/src/Atc.Rest.Client/Builder/HttpMessageFactory.cs b/src/Atc.Rest.Client/Builder/HttpMessageFactory.cs index e33a7b2..8b4ef81 100644 --- a/src/Atc.Rest.Client/Builder/HttpMessageFactory.cs +++ b/src/Atc.Rest.Client/Builder/HttpMessageFactory.cs @@ -1,21 +1,24 @@ -using System.Net.Http; -using Atc.Rest.Client.Serialization; +namespace Atc.Rest.Client.Builder; -namespace Atc.Rest.Client.Builder +internal class HttpMessageFactory : IHttpMessageFactory { - internal class HttpMessageFactory : IHttpMessageFactory - { - private readonly IContractSerializer serializer; + private readonly IContractSerializer serializer; - public HttpMessageFactory(IContractSerializer serializer) - { - this.serializer = serializer; - } + public HttpMessageFactory( + IContractSerializer serializer) + { + this.serializer = serializer; + } - public IMessageRequestBuilder FromTemplate(string pathTemplate) - => new MessageRequestBuilder(pathTemplate, serializer); + public IMessageRequestBuilder FromTemplate( + string pathTemplate) + => new MessageRequestBuilder( + pathTemplate, + serializer); - public IMessageResponseBuilder FromResponse(HttpResponseMessage? response) - => new MessageResponseBuilder(response, serializer); - } + public IMessageResponseBuilder FromResponse( + HttpResponseMessage? response) + => new MessageResponseBuilder( + response, + serializer); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Builder/IHttpMessageFactory.cs b/src/Atc.Rest.Client/Builder/IHttpMessageFactory.cs index 6850a37..38abc19 100644 --- a/src/Atc.Rest.Client/Builder/IHttpMessageFactory.cs +++ b/src/Atc.Rest.Client/Builder/IHttpMessageFactory.cs @@ -1,27 +1,25 @@ -using System; -using System.Net.Http; +namespace Atc.Rest.Client.Builder; -namespace Atc.Rest.Client.Builder +/// +/// Represents an HTTP message factory that can create both the +/// and , used to provide input to +/// and processes responses from an HTTP exchange. +/// +public interface IHttpMessageFactory { /// - /// Represents a HTTP message factory that can create both the - /// and , used to provide input to - /// and processes responses from an HTTP exchange. + /// Start building a with the + /// returned , which will use the provided + /// as the request URI. /// - public interface IHttpMessageFactory - { - /// - /// Start building a with the - /// returned , which will use the provided - /// as the request URI. - /// - /// Thrown when is null. - /// The relative URI to request. Can contain tokens, - /// that will be replaced with real values passed to the - /// method. - /// A new . - IMessageRequestBuilder FromTemplate(string pathTemplate); + /// Thrown when is null. + /// The relative URI to request. Can contain tokens, + /// that will be replaced with real values passed to the + /// method. + /// A new . + IMessageRequestBuilder FromTemplate( + string pathTemplate); - IMessageResponseBuilder FromResponse(HttpResponseMessage? response); - } + IMessageResponseBuilder FromResponse( + HttpResponseMessage? response); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Builder/IMessageRequestBuilder.cs b/src/Atc.Rest.Client/Builder/IMessageRequestBuilder.cs index 0da8f8d..ce9f836 100644 --- a/src/Atc.Rest.Client/Builder/IMessageRequestBuilder.cs +++ b/src/Atc.Rest.Client/Builder/IMessageRequestBuilder.cs @@ -1,65 +1,60 @@ -using System; -using System.Net.Http; -using Atc.Rest.Client.Serialization; +namespace Atc.Rest.Client.Builder; -namespace Atc.Rest.Client.Builder +/// +/// A message request builder can be used to build a . +/// +public interface IMessageRequestBuilder { /// - /// A message request builder can be used to build a . + /// Adds a value to a path parameter in a path template passed to the constructor of the . /// - public interface IMessageRequestBuilder - { - /// - /// Adds a value to a path parameter in a path template passed to the constructor of the . - /// - /// - /// A implementation is expected to get passed a path template with - /// optional path parameters inside, which this method will replace with the . - /// - /// Name of the path parameter in the template path. - /// Value to use as the path parameter. - /// Thrown when is null or whitespace. - /// Thrown when is null or whitespace. - /// The . - IMessageRequestBuilder WithPathParameter(string name, object? value); + /// + /// A implementation is expected to get passed a path template with + /// optional path parameters inside, which this method will replace with the . + /// + /// Name of the path parameter in the template path. + /// Value to use as the path parameter. + /// Thrown when is null or whitespace. + /// Thrown when is null or whitespace. + /// The . + IMessageRequestBuilder WithPathParameter(string name, object? value); - /// - /// Adds a value to a header parameter in the headers. - /// - /// Name of the header parameter. - /// Value to use as the header parameter. - /// Thrown when is null or whitespace. - /// The . - IMessageRequestBuilder WithHeaderParameter(string name, object? value); + /// + /// Adds a value to a header parameter in the headers. + /// + /// Name of the header parameter. + /// Value to use as the header parameter. + /// Thrown when is null or whitespace. + /// The . + IMessageRequestBuilder WithHeaderParameter(string name, object? value); - /// - /// Adds a query parameter to the created request URL. - /// - /// - /// If the is null, the query parameter is not added. - /// - /// Name of the query parameter. - /// Value of the query parameter. - /// Thrown when is null or whitespace. - /// The . - IMessageRequestBuilder WithQueryParameter(string name, object? value); + /// + /// Adds a query parameter to the created request URL. + /// + /// + /// If the is null, the query parameter is not added. + /// + /// Name of the query parameter. + /// Value of the query parameter. + /// Thrown when is null or whitespace. + /// The . + IMessageRequestBuilder WithQueryParameter(string name, object? value); - /// - /// Adds the body of the request. - /// - /// - /// The builder should use a to serialize . - /// - /// The type of object to add as the body of the request. - /// The body to add to the request. - /// The . - IMessageRequestBuilder WithBody(TBody body); + /// + /// Adds the body of the request. + /// + /// + /// The builder should use a to serialize . + /// + /// The type of object to add as the body of the request. + /// The body to add to the request. + /// The . + IMessageRequestBuilder WithBody(TBody body); - /// - /// Builds a with the added content. - /// - /// The to use in the request. - /// The created . - HttpRequestMessage Build(HttpMethod method); - } + /// + /// Builds a with the added content. + /// + /// The to use in the request. + /// The created . + HttpRequestMessage Build(HttpMethod method); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Builder/IMessageResponseBuilder.cs b/src/Atc.Rest.Client/Builder/IMessageResponseBuilder.cs index 9066085..5215be3 100644 --- a/src/Atc.Rest.Client/Builder/IMessageResponseBuilder.cs +++ b/src/Atc.Rest.Client/Builder/IMessageResponseBuilder.cs @@ -1,31 +1,31 @@ -using System; -using System.Net; -using System.Threading; -using System.Threading.Tasks; +namespace Atc.Rest.Client.Builder; -namespace Atc.Rest.Client.Builder +public interface IMessageResponseBuilder { - public interface IMessageResponseBuilder - { - IMessageResponseBuilder AddSuccessResponse(HttpStatusCode statusCode); + IMessageResponseBuilder AddSuccessResponse( + HttpStatusCode statusCode); - IMessageResponseBuilder AddSuccessResponse(HttpStatusCode statusCode); + IMessageResponseBuilder AddSuccessResponse( + HttpStatusCode statusCode); - IMessageResponseBuilder AddErrorResponse(HttpStatusCode statusCode); + IMessageResponseBuilder AddErrorResponse( + HttpStatusCode statusCode); - IMessageResponseBuilder AddErrorResponse(HttpStatusCode statusCode); + IMessageResponseBuilder AddErrorResponse( + HttpStatusCode statusCode); - Task BuildResponseAsync( - Func factory, - CancellationToken cancellationToken); + Task BuildResponseAsync( + Func factory, + CancellationToken cancellationToken); - Task> - BuildResponseAsync(CancellationToken cancellationToken) - where TSuccessContent : class; + Task> + BuildResponseAsync( + CancellationToken cancellationToken) + where TSuccessContent : class; - Task> - BuildResponseAsync(CancellationToken cancellationToken) - where TSuccessContent : class - where TErrorContent : class; - } + Task> + BuildResponseAsync( + CancellationToken cancellationToken) + where TSuccessContent : class + where TErrorContent : class; } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Builder/MessageRequestBuilder.cs b/src/Atc.Rest.Client/Builder/MessageRequestBuilder.cs index 96887be..725d8a1 100644 --- a/src/Atc.Rest.Client/Builder/MessageRequestBuilder.cs +++ b/src/Atc.Rest.Client/Builder/MessageRequestBuilder.cs @@ -1,196 +1,196 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Text; -using Atc.Rest.Client.Serialization; -using Microsoft.AspNetCore.Http; - -namespace Atc.Rest.Client.Builder +namespace Atc.Rest.Client.Builder; + +internal class MessageRequestBuilder : IMessageRequestBuilder { - internal class MessageRequestBuilder : IMessageRequestBuilder + private readonly string template; + private readonly IContractSerializer serializer; + private readonly Dictionary pathMapper; + private readonly Dictionary headerMapper; + private readonly Dictionary queryMapper; + private string? content; + private List? contentFormFiles; + + public MessageRequestBuilder( + string pathTemplate, + IContractSerializer serializer) { - private readonly string template; - private readonly IContractSerializer serializer; - private readonly Dictionary pathMapper; - private readonly Dictionary headerMapper; - private readonly Dictionary queryMapper; - private string? content; - private List? contentFormFiles; - - public MessageRequestBuilder(string pathTemplate, IContractSerializer serializer) - { - this.template = pathTemplate ?? throw new ArgumentNullException(nameof(pathTemplate)); - this.serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); - pathMapper = new Dictionary(StringComparer.Ordinal); - headerMapper = new Dictionary(StringComparer.Ordinal); - queryMapper = new Dictionary(StringComparer.Ordinal); - WithHeaderParameter("accept", "application/json"); - } + this.template = pathTemplate ?? throw new ArgumentNullException(nameof(pathTemplate)); + this.serializer = serializer ?? throw new ArgumentNullException(nameof(serializer)); + pathMapper = new Dictionary(StringComparer.Ordinal); + headerMapper = new Dictionary(StringComparer.Ordinal); + queryMapper = new Dictionary(StringComparer.Ordinal); + WithHeaderParameter("accept", "application/json"); + } - [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "OK - ByteArrayContent can't be disposed.")] - public HttpRequestMessage Build(HttpMethod method) + [SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", Justification = "OK - ByteArrayContent can't be disposed.")] + public HttpRequestMessage Build( + HttpMethod method) + { + var message = new HttpRequestMessage(); + foreach (var parameter in headerMapper) { - var message = new HttpRequestMessage(); - foreach (var parameter in headerMapper) - { - message.Headers.Add(parameter.Key, parameter.Value); - } + message.Headers.Add(parameter.Key, parameter.Value); + } - message.RequestUri = BuildRequestUri(); - message.Method = method; + message.RequestUri = BuildRequestUri(); + message.Method = method; - if (content is not null) - { - message.Content = new StringContent(content); - message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); - } - else if (contentFormFiles is not null) + if (content is not null) + { + message.Content = new StringContent(content); + message.Content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json"); + } + else if (contentFormFiles is not null) + { + var formDataContent = new MultipartFormDataContent(); + foreach (var formFile in contentFormFiles) { - var formDataContent = new MultipartFormDataContent(); - foreach (var formFile in contentFormFiles) + byte[] bytes; + using (var binaryReader = new BinaryReader(formFile.OpenReadStream())) { - byte[] bytes; - using (var binaryReader = new BinaryReader(formFile.OpenReadStream())) - { - bytes = binaryReader.ReadBytes((int)formFile.OpenReadStream().Length); - } - - var bytesContent = new ByteArrayContent(bytes); - formDataContent.Add(bytesContent, "Request", formFile.FileName); + bytes = binaryReader.ReadBytes((int)formFile.OpenReadStream().Length); } - message.Headers.Remove("accept"); - message.Headers.Add("accept", "application/octet-stream"); - message.Content = formDataContent; + var bytesContent = new ByteArrayContent(bytes); + formDataContent.Add(bytesContent, "Request", formFile.FileName); } - return message; + message.Headers.Remove("accept"); + message.Headers.Add("accept", "application/octet-stream"); + message.Content = formDataContent; } - public IMessageRequestBuilder WithBody(TBody body) - { - if (body is null) - { - throw new ArgumentNullException(nameof(body)); - } - - switch (body) - { - case IFormFile formFile: - contentFormFiles = new List - { - formFile, - }; - break; - case List formFiles: - contentFormFiles = new List(); - contentFormFiles.AddRange(formFiles); - break; - default: - content = serializer.Serialize(body); - break; - } + return message; + } - return this; + public IMessageRequestBuilder WithBody( + TBody body) + { + if (body is null) + { + throw new ArgumentNullException(nameof(body)); } - public IMessageRequestBuilder WithPathParameter(string name, object? value) + switch (body) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); - } - - if (value is null || string.IsNullOrWhiteSpace(value.ToString())) - { - throw new ArgumentException($"'{nameof(value)}' cannot be null or whitespace", nameof(value)); - } + case IFormFile formFile: + contentFormFiles = new List + { + formFile, + }; + break; + case List formFiles: + contentFormFiles = new List(); + contentFormFiles.AddRange(formFiles); + break; + default: + content = serializer.Serialize(body); + break; + } - pathMapper[name] = value.ToString(); + return this; + } - return this; + public IMessageRequestBuilder WithPathParameter( + string name, + object? value) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); } - public IMessageRequestBuilder WithHeaderParameter(string name, object? value) + if (value is null || string.IsNullOrWhiteSpace(value.ToString())) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); - } + throw new ArgumentException($"'{nameof(value)}' cannot be null or whitespace", nameof(value)); + } - if (value is null) - { - return this; - } + pathMapper[name] = value.ToString(); - headerMapper[name] = value.ToString(); + return this; + } - return this; + public IMessageRequestBuilder WithHeaderParameter( + string name, + object? value) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); } - public IMessageRequestBuilder WithQueryParameter(string name, object? value) + if (value is null) { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); - } + return this; + } - if (value is null) - { - return this; - } + headerMapper[name] = value.ToString(); - var valueType = value.GetType(); - if (valueType.IsArray || valueType.IsGenericType) - { - var objects = ((IEnumerable)value).Cast().ToArray(); - var sb = new StringBuilder(); - for (int i = 0; i < objects.Length; i++) - { - sb.Append(i == 0 - ? Uri.EscapeDataString(objects[i].ToString()) - : $"&{name}={Uri.EscapeDataString(objects[i].ToString())}"); - } + return this; + } - queryMapper["#" + name] = sb.ToString(); - } - else - { - queryMapper[name] = value.ToString(); - } + public IMessageRequestBuilder WithQueryParameter( + string name, + object? value) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException($"'{nameof(name)}' cannot be null or whitespace", nameof(name)); + } + if (value is null) + { return this; } - private Uri BuildRequestUri() + var valueType = value.GetType(); + if (valueType.IsArray || valueType.IsGenericType) { - var urlBuilder = new StringBuilder(); - - urlBuilder.Append(template); - foreach (var parameter in pathMapper) + var objects = ((IEnumerable)value).Cast().ToArray(); + var sb = new StringBuilder(); + for (int i = 0; i < objects.Length; i++) { - urlBuilder.Replace($"{{{parameter.Key}}}", Uri.EscapeDataString(parameter.Value)); + sb.Append(i == 0 + ? Uri.EscapeDataString(objects[i].ToString()) + : $"&{name}={Uri.EscapeDataString(objects[i].ToString())}"); } - if (queryMapper.Any()) - { - urlBuilder.Append('?'); - urlBuilder.Append(string.Join("&", queryMapper.Select(BuildQueryKeyEqualValue))); - } + queryMapper["#" + name] = sb.ToString(); + } + else + { + queryMapper[name] = value.ToString(); + } - return new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute); + return this; + } + + private Uri BuildRequestUri() + { + var urlBuilder = new StringBuilder(); + + urlBuilder.Append(template); + foreach (var parameter in pathMapper) + { + urlBuilder.Replace($"{{{parameter.Key}}}", Uri.EscapeDataString(parameter.Value)); } - private string BuildQueryKeyEqualValue(KeyValuePair pair) + if (queryMapper.Count == 0) { - return pair.Key.StartsWith("#", StringComparison.Ordinal) - ? $"{pair.Key.Replace("#", string.Empty)}={pair.Value}" - : $"{pair.Key}={Uri.EscapeDataString(pair.Value)}"; + return new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute); } + + urlBuilder.Append('?'); + urlBuilder.Append(string.Join("&", queryMapper.Select(BuildQueryKeyEqualValue))); + + return new Uri(urlBuilder.ToString(), UriKind.RelativeOrAbsolute); + } + + private static string BuildQueryKeyEqualValue( + KeyValuePair pair) + { + return pair.Key.StartsWith("#", StringComparison.Ordinal) + ? $"{pair.Key.Replace("#", string.Empty)}={pair.Value}" + : $"{pair.Key}={Uri.EscapeDataString(pair.Value)}"; } } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Builder/MessageResponseBuilder.cs b/src/Atc.Rest.Client/Builder/MessageResponseBuilder.cs index 47ac856..499f20d 100644 --- a/src/Atc.Rest.Client/Builder/MessageResponseBuilder.cs +++ b/src/Atc.Rest.Client/Builder/MessageResponseBuilder.cs @@ -1,139 +1,150 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading; -using System.Threading.Tasks; -using Atc.Rest.Client.Serialization; - -namespace Atc.Rest.Client.Builder +namespace Atc.Rest.Client.Builder; + +internal class MessageResponseBuilder : IMessageResponseBuilder { - internal class MessageResponseBuilder : IMessageResponseBuilder + private static readonly EndpointResponse EmptyResponse + = new( + isSuccess: false, + HttpStatusCode.InternalServerError, + string.Empty, + contentObject: null, + new Dictionary>(StringComparer.Ordinal)); + + private readonly HttpResponseMessage? response; + private readonly IContractSerializer serializer; + private readonly Dictionary responseSerializers; + private readonly Dictionary responseCodes; + + public MessageResponseBuilder( + HttpResponseMessage? response, + IContractSerializer serializer) { - [SuppressMessage("StyleCop.CSharp.SpacingRules", "SA1000:Keywords should be spaced correctly", Justification = "False positive. new() syntax requires it.")] - private static readonly EndpointResponse EmptyResponse - = new(isSuccess: false, HttpStatusCode.InternalServerError, string.Empty, contentObject: null, new Dictionary>(StringComparer.Ordinal)); + this.response = response; + this.serializer = serializer; + responseSerializers = []; + responseCodes = []; + } - private readonly HttpResponseMessage? response; - private readonly IContractSerializer serializer; - private readonly Dictionary responseSerializers; - private readonly Dictionary responseCodes; + private delegate object? ContentSerializerDelegate( + string content); - public MessageResponseBuilder(HttpResponseMessage? response, IContractSerializer serializer) - { - this.response = response; - this.serializer = serializer; - responseSerializers = new Dictionary(); - responseCodes = new Dictionary(); - } + public IMessageResponseBuilder AddErrorResponse( + HttpStatusCode statusCode) + => AddEmptyResponse(statusCode, isSuccess: false); - private delegate object? ContentSerializerDelegate(string content); + public IMessageResponseBuilder AddErrorResponse( + HttpStatusCode statusCode) + => AddTypedResponse(statusCode, isSuccess: false); - public IMessageResponseBuilder AddErrorResponse(HttpStatusCode statusCode) - => AddEmptyResponse(statusCode, isSuccess: false); + public IMessageResponseBuilder AddSuccessResponse( + HttpStatusCode statusCode) + => AddEmptyResponse(statusCode, isSuccess: true); - public IMessageResponseBuilder AddErrorResponse(HttpStatusCode statusCode) - => AddTypedResponse(statusCode, isSuccess: false); + public IMessageResponseBuilder AddSuccessResponse( + HttpStatusCode statusCode) + => AddTypedResponse(statusCode, isSuccess: true); - public IMessageResponseBuilder AddSuccessResponse(HttpStatusCode statusCode) - => AddEmptyResponse(statusCode, isSuccess: true); - - public IMessageResponseBuilder AddSuccessResponse(HttpStatusCode statusCode) - => AddTypedResponse(statusCode, isSuccess: true); + public async Task BuildResponseAsync( + Func factory, + CancellationToken cancellationToken) + { + if (response is null) + { + return factory(EmptyResponse); + } - public async Task BuildResponseAsync(Func factory, CancellationToken cancellationToken) + if (UseReadAsStringFromContentDependingOnContentType(response.Content.Headers.ContentType)) { - if (response is null) - { - return factory(EmptyResponse); - } - - if (UseReadAsStringFromContentDependingOnContentType(response.Content.Headers.ContentType)) - { - var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); - - return factory( - new EndpointResponse( - IsSuccessStatus(response), - response.StatusCode, - content, - GetSerializer(response.StatusCode)?.Invoke(content), - GetHeaders(response))); - } - - var contentObject = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); + var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false); return factory( new EndpointResponse( IsSuccessStatus(response), response.StatusCode, - string.Empty, - contentObject, + content, + GetSerializer(response.StatusCode)?.Invoke(content), GetHeaders(response))); } - public Task> BuildResponseAsync( - CancellationToken cancellationToken) - where TSuccessContent : class => - BuildResponseAsync( - r => new EndpointResponse(r), - cancellationToken); - - public Task> - BuildResponseAsync(CancellationToken cancellationToken) - where TSuccessContent : class - where TErrorContent : class => - BuildResponseAsync( - r => new EndpointResponse(r), - cancellationToken); - - private static bool UseReadAsStringFromContentDependingOnContentType(MediaTypeHeaderValue? headersContentType) - => headersContentType?.MediaType is null || - headersContentType.MediaType.Contains("json") || - headersContentType.MediaType.Contains("text"); - - private static IReadOnlyDictionary> GetHeaders(HttpResponseMessage responseMessage) - { - var headers = responseMessage.Headers.ToDictionary(h => h.Key, h => h.Value, StringComparer.Ordinal); + var contentObject = await response.Content.ReadAsByteArrayAsync().ConfigureAwait(false); + + return factory( + new EndpointResponse( + IsSuccessStatus(response), + response.StatusCode, + string.Empty, + contentObject, + GetHeaders(response))); + } + + public Task> BuildResponseAsync( + CancellationToken cancellationToken) + where TSuccessContent : class => + BuildResponseAsync( + r => new EndpointResponse(r), + cancellationToken); - if (responseMessage.Content?.Headers is not null) - { - foreach (var item_ in responseMessage.Content.Headers) - { - headers[item_.Key] = item_.Value; - } - } + public Task> + BuildResponseAsync( + CancellationToken cancellationToken) + where TSuccessContent : class + where TErrorContent : class => + BuildResponseAsync( + r => new EndpointResponse(r), + cancellationToken); + + private static bool UseReadAsStringFromContentDependingOnContentType( + MediaTypeHeaderValue? headersContentType) + => headersContentType?.MediaType is null || + headersContentType.MediaType.Contains("json") || + headersContentType.MediaType.Contains("text"); + + private static IReadOnlyDictionary> GetHeaders( + HttpResponseMessage responseMessage) + { + var headers = responseMessage.Headers.ToDictionary(h => h.Key, h => h.Value, StringComparer.Ordinal); + if (responseMessage.Content?.Headers is null) + { return headers; } - private bool IsSuccessStatus(HttpResponseMessage responseMessage) - => responseCodes.TryGetValue(responseMessage.StatusCode, out var isSuccess) - ? isSuccess - : responseMessage.IsSuccessStatusCode; + foreach (var item_ in responseMessage.Content.Headers) + { + headers[item_.Key] = item_.Value; + } - private ContentSerializerDelegate? GetSerializer(HttpStatusCode statusCode) - => responseSerializers.TryGetValue(statusCode, out var deserializer) - ? deserializer - : null; + return headers; + } - private IMessageResponseBuilder AddEmptyResponse(HttpStatusCode statusCode, bool isSuccess) - { - responseSerializers[statusCode] = content => null; - responseCodes[statusCode] = isSuccess; + private bool IsSuccessStatus( + HttpResponseMessage responseMessage) + => responseCodes.TryGetValue(responseMessage.StatusCode, out var isSuccess) + ? isSuccess + : responseMessage.IsSuccessStatusCode; + + private ContentSerializerDelegate? GetSerializer( + HttpStatusCode statusCode) + => responseSerializers.TryGetValue(statusCode, out var deserializer) + ? deserializer + : null; + + private IMessageResponseBuilder AddEmptyResponse( + HttpStatusCode statusCode, + bool isSuccess) + { + responseSerializers[statusCode] = content => null; + responseCodes[statusCode] = isSuccess; - return this; - } + return this; + } - private IMessageResponseBuilder AddTypedResponse(HttpStatusCode statusCode, bool isSuccess) - { - responseSerializers[statusCode] = content => serializer.Deserialize(content); - responseCodes[statusCode] = isSuccess; + private IMessageResponseBuilder AddTypedResponse( + HttpStatusCode statusCode, bool isSuccess) + { + responseSerializers[statusCode] = content => serializer.Deserialize(content); + responseCodes[statusCode] = isSuccess; - return this; - } + return this; } } \ No newline at end of file diff --git a/src/Atc.Rest.Client/EndpointResponse.cs b/src/Atc.Rest.Client/EndpointResponse.cs index 80d4a3b..b0f15d2 100644 --- a/src/Atc.Rest.Client/EndpointResponse.cs +++ b/src/Atc.Rest.Client/EndpointResponse.cs @@ -1,50 +1,45 @@ -using System; -using System.Collections.Generic; -using System.Net; +namespace Atc.Rest.Client; -namespace Atc.Rest.Client +public class EndpointResponse : IEndpointResponse { - public class EndpointResponse : IEndpointResponse + public EndpointResponse(EndpointResponse response) + : this( + response?.IsSuccess ?? throw new System.ArgumentNullException(nameof(response)), + response.StatusCode, + response.Content, + response.ContentObject, + response.Headers) { - public EndpointResponse(EndpointResponse response) - : this( - response?.IsSuccess ?? throw new System.ArgumentNullException(nameof(response)), - response.StatusCode, - response.Content, - response.ContentObject, - response.Headers) - { - } - - public EndpointResponse( - bool isSuccess, - HttpStatusCode statusCode, - string content, - object? contentObject, - IReadOnlyDictionary> headers) - { - IsSuccess = isSuccess; - StatusCode = statusCode; - Content = content; - ContentObject = contentObject; - Headers = headers; - } - - public bool IsSuccess { get; } - - public HttpStatusCode StatusCode { get; } - - public string Content { get; } - - public object? ContentObject { get; } - - public IReadOnlyDictionary> Headers { get; } - - protected TResult CastContent() - where TResult : class - { - return ContentObject as TResult ?? - throw new InvalidCastException($"ContentObject is not of type {typeof(TResult).Name}"); - } + } + + public EndpointResponse( + bool isSuccess, + HttpStatusCode statusCode, + string content, + object? contentObject, + IReadOnlyDictionary> headers) + { + IsSuccess = isSuccess; + StatusCode = statusCode; + Content = content; + ContentObject = contentObject; + Headers = headers; + } + + public bool IsSuccess { get; } + + public HttpStatusCode StatusCode { get; } + + public string Content { get; } + + public object? ContentObject { get; } + + public IReadOnlyDictionary> Headers { get; } + + protected TResult CastContent() + where TResult : class + { + return ContentObject as TResult ?? + throw new InvalidCastException($"ContentObject is not of type {typeof(TResult).Name}"); } } \ No newline at end of file diff --git a/src/Atc.Rest.Client/EndpointResponse{TSuccess,TError}.cs b/src/Atc.Rest.Client/EndpointResponse{TSuccess,TError}.cs index c911818..8f52393 100644 --- a/src/Atc.Rest.Client/EndpointResponse{TSuccess,TError}.cs +++ b/src/Atc.Rest.Client/EndpointResponse{TSuccess,TError}.cs @@ -1,30 +1,26 @@ -using System.Collections.Generic; -using System.Net; +namespace Atc.Rest.Client; -namespace Atc.Rest.Client +public class EndpointResponse + : EndpointResponse + where TSuccess : class + where TError : class { - public class EndpointResponse - : EndpointResponse - where TSuccess : class - where TError : class + public EndpointResponse(EndpointResponse response) + : base(response) { - public EndpointResponse(EndpointResponse response) - : base(response) - { - } + } - public EndpointResponse( - bool isSuccess, - HttpStatusCode statusCode, - string content, - object? contentObject, - IReadOnlyDictionary> headers) - : base(isSuccess, statusCode, content, contentObject, headers) - { - } + public EndpointResponse( + bool isSuccess, + HttpStatusCode statusCode, + string content, + object? contentObject, + IReadOnlyDictionary> headers) + : base(isSuccess, statusCode, content, contentObject, headers) + { + } - public TSuccess? SuccessContent => IsSuccess ? CastContent() : null; + public TSuccess? SuccessContent => IsSuccess ? CastContent() : null; - public TError? ErrorContent => !IsSuccess ? CastContent() : null; - } + public TError? ErrorContent => !IsSuccess ? CastContent() : null; } \ No newline at end of file diff --git a/src/Atc.Rest.Client/EndpointResponse{TSuccess}.cs b/src/Atc.Rest.Client/EndpointResponse{TSuccess}.cs index 496f7c0..e4ffafa 100644 --- a/src/Atc.Rest.Client/EndpointResponse{TSuccess}.cs +++ b/src/Atc.Rest.Client/EndpointResponse{TSuccess}.cs @@ -1,27 +1,23 @@ -using System.Collections.Generic; -using System.Net; +namespace Atc.Rest.Client; -namespace Atc.Rest.Client +public class EndpointResponse + : EndpointResponse + where TSuccess : class { - public class EndpointResponse - : EndpointResponse - where TSuccess : class + public EndpointResponse(EndpointResponse response) + : base(response) { - public EndpointResponse(EndpointResponse response) - : base(response) - { - } - - public EndpointResponse( - bool isSuccess, - HttpStatusCode statusCode, - string content, - TSuccess? contentObject, - IReadOnlyDictionary> headers) - : base(isSuccess, statusCode, content, contentObject, headers) - { - } + } - public TSuccess? SuccessContent => IsSuccess ? CastContent() : null; + public EndpointResponse( + bool isSuccess, + HttpStatusCode statusCode, + string content, + TSuccess? contentObject, + IReadOnlyDictionary> headers) + : base(isSuccess, statusCode, content, contentObject, headers) + { } + + public TSuccess? SuccessContent => IsSuccess ? CastContent() : null; } \ No newline at end of file diff --git a/src/Atc.Rest.Client/GlobalUsings.cs b/src/Atc.Rest.Client/GlobalUsings.cs new file mode 100644 index 0000000..1be85be --- /dev/null +++ b/src/Atc.Rest.Client/GlobalUsings.cs @@ -0,0 +1,12 @@ +global using System.Collections; +global using System.ComponentModel; +global using System.Diagnostics.CodeAnalysis; +global using System.Net; +global using System.Net.Http.Headers; +global using System.Text; +global using System.Text.Json; +global using System.Text.Json.Serialization; +global using Atc.Rest.Client.Builder; +global using Atc.Rest.Client.Serialization; +global using Microsoft.AspNetCore.Http; +global using Microsoft.Extensions.DependencyInjection; \ No newline at end of file diff --git a/src/Atc.Rest.Client/IEndpointResponse.cs b/src/Atc.Rest.Client/IEndpointResponse.cs index 869599c..08a8674 100644 --- a/src/Atc.Rest.Client/IEndpointResponse.cs +++ b/src/Atc.Rest.Client/IEndpointResponse.cs @@ -1,18 +1,14 @@ -using System.Collections.Generic; -using System.Net; +namespace Atc.Rest.Client; -namespace Atc.Rest.Client +public interface IEndpointResponse { - public interface IEndpointResponse - { - bool IsSuccess { get; } + bool IsSuccess { get; } - HttpStatusCode StatusCode { get; } + HttpStatusCode StatusCode { get; } - string Content { get; } + string Content { get; } - object? ContentObject { get; } + object? ContentObject { get; } - IReadOnlyDictionary> Headers { get; } - } + IReadOnlyDictionary> Headers { get; } } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Options/AtcRestClientOptions.cs b/src/Atc.Rest.Client/Options/AtcRestClientOptions.cs index 6edc536..19b6b57 100644 --- a/src/Atc.Rest.Client/Options/AtcRestClientOptions.cs +++ b/src/Atc.Rest.Client/Options/AtcRestClientOptions.cs @@ -1,11 +1,8 @@ -using System; +namespace Atc.Rest.Client.Options; -namespace Atc.Rest.Client.Options +public class AtcRestClientOptions { - public class AtcRestClientOptions - { - public virtual Uri? BaseAddress { get; set; } + public virtual Uri? BaseAddress { get; set; } - public virtual TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); - } + public virtual TimeSpan Timeout { get; set; } = TimeSpan.FromSeconds(30); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Options/ServiceCollectionExtensions.cs b/src/Atc.Rest.Client/Options/ServiceCollectionExtensions.cs index 719f17a..9a68bb7 100644 --- a/src/Atc.Rest.Client/Options/ServiceCollectionExtensions.cs +++ b/src/Atc.Rest.Client/Options/ServiceCollectionExtensions.cs @@ -1,60 +1,53 @@ -using System; -using System.ComponentModel; -using Atc.Rest.Client.Builder; -using Atc.Rest.Client.Serialization; -using Microsoft.Extensions.DependencyInjection; +namespace Atc.Rest.Client.Options; -namespace Atc.Rest.Client.Options +public static class ServiceCollectionExtensions { - public static class ServiceCollectionExtensions + [EditorBrowsable(EditorBrowsableState.Never)] + public static IServiceCollection AddAtcRestClient( + this IServiceCollection services, + string clientName, + TOptions options, + Action? httpClientBuilder = default) + where TOptions : AtcRestClientOptions, new() { - [EditorBrowsable(EditorBrowsableState.Never)] - public static IServiceCollection AddAtcRestClient( - this IServiceCollection services, - string clientName, - TOptions options, - Action? httpClientBuilder = default) - where TOptions : AtcRestClientOptions, new() + services.AddSingleton(options); + + var clientBuilder = services.AddHttpClient(clientName, (s, c) => { - services.AddSingleton(options); - - var clientBuilder = services.AddHttpClient(clientName, (s, c) => - { - var o = s.GetRequiredService(); - c.BaseAddress = o.BaseAddress; - c.Timeout = o.Timeout; - }); - - httpClientBuilder?.Invoke(clientBuilder); - - // Register utilities - services.AddSingleton(); - services.AddSingleton(); - - return services; - } - - [EditorBrowsable(EditorBrowsableState.Never)] - public static IServiceCollection AddAtcRestClient( - this IServiceCollection services, - string clientName, - Uri baseAddress, - TimeSpan timeout, - Action? httpClientBuilder = default) + var o = s.GetRequiredService(); + c.BaseAddress = o.BaseAddress; + c.Timeout = o.Timeout; + }); + + httpClientBuilder?.Invoke(clientBuilder); + + // Register utilities + services.AddSingleton(); + services.AddSingleton(); + + return services; + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public static IServiceCollection AddAtcRestClient( + this IServiceCollection services, + string clientName, + Uri baseAddress, + TimeSpan timeout, + Action? httpClientBuilder = default) + { + var clientBuilder = services.AddHttpClient(clientName, (s, c) => { - var clientBuilder = services.AddHttpClient(clientName, (s, c) => - { - c.BaseAddress = baseAddress; - c.Timeout = timeout; - }); + c.BaseAddress = baseAddress; + c.Timeout = timeout; + }); - httpClientBuilder?.Invoke(clientBuilder); + httpClientBuilder?.Invoke(clientBuilder); - // Register utilities - services.AddSingleton(); - services.AddSingleton(); + // Register utilities + services.AddSingleton(); + services.AddSingleton(); - return services; - } + return services; } } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Serialization/DefaultJsonContractSerializer.cs b/src/Atc.Rest.Client/Serialization/DefaultJsonContractSerializer.cs index f1b703a..2ed18ea 100644 --- a/src/Atc.Rest.Client/Serialization/DefaultJsonContractSerializer.cs +++ b/src/Atc.Rest.Client/Serialization/DefaultJsonContractSerializer.cs @@ -1,42 +1,56 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; +namespace Atc.Rest.Client.Serialization; -namespace Atc.Rest.Client.Serialization +public class DefaultJsonContractSerializer : IContractSerializer { - public class DefaultJsonContractSerializer : IContractSerializer + private static readonly JsonSerializerOptions DefaultOptions = new() { - private static readonly JsonSerializerOptions DefaultOptions = new JsonSerializerOptions + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + WriteIndented = true, + Converters = { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - IgnoreNullValues = true, - WriteIndented = true, - Converters = - { - new JsonStringEnumConverter(), - }, - }; - - private readonly JsonSerializerOptions options; - - public DefaultJsonContractSerializer(JsonSerializerOptions? options = default) - { - this.options = options ?? DefaultOptions; - } - - public string Serialize(object value) - => JsonSerializer.Serialize(value, options); - - public T? Deserialize(string json) - => JsonSerializer.Deserialize(json, options); + new JsonStringEnumConverter(), + }, + }; - public T? Deserialize(byte[] utf8Json) - => JsonSerializer.Deserialize(utf8Json, options); + private readonly JsonSerializerOptions options; - public object? Deserialize(string json, Type returnType) - => JsonSerializer.Deserialize(json, returnType, options); - - public object? Deserialize(byte[] utf8Json, Type returnType) - => JsonSerializer.Deserialize(utf8Json, returnType, options); + public DefaultJsonContractSerializer( + JsonSerializerOptions? options = default) + { + this.options = options ?? DefaultOptions; } + + public string Serialize( + object value) + => JsonSerializer.Serialize( + value, + options); + + public T? Deserialize( + string json) + => JsonSerializer.Deserialize( + json, + options); + + public T? Deserialize( + byte[] utf8Json) + => JsonSerializer.Deserialize( + utf8Json, + options); + + public object? Deserialize( + string json, + Type returnType) + => JsonSerializer.Deserialize( + json, + returnType, + options); + + public object? Deserialize( + byte[] utf8Json, Type returnType) + => JsonSerializer.Deserialize( + utf8Json, + returnType, + options); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Serialization/IContractSerializer.cs b/src/Atc.Rest.Client/Serialization/IContractSerializer.cs index ead35ce..05fd7c8 100644 --- a/src/Atc.Rest.Client/Serialization/IContractSerializer.cs +++ b/src/Atc.Rest.Client/Serialization/IContractSerializer.cs @@ -1,17 +1,21 @@ -using System; +namespace Atc.Rest.Client.Serialization; -namespace Atc.Rest.Client.Serialization +public interface IContractSerializer { - public interface IContractSerializer - { - string Serialize(object value); + string Serialize( + object value); - T? Deserialize(string json); + T? Deserialize( + string json); - T? Deserialize(byte[] utf8Json); + T? Deserialize( + byte[] utf8Json); - object? Deserialize(string json, Type returnType); + object? Deserialize( + string json, + Type returnType); - object? Deserialize(byte[] utf8Json, Type returnType); - } + object? Deserialize( + byte[] utf8Json, + Type returnType); } \ No newline at end of file diff --git a/src/Atc.Rest.Client/Serialization/JsonSerializerOptionsExtensions.cs b/src/Atc.Rest.Client/Serialization/JsonSerializerOptionsExtensions.cs index 1d812fa..d0874e2 100644 --- a/src/Atc.Rest.Client/Serialization/JsonSerializerOptionsExtensions.cs +++ b/src/Atc.Rest.Client/Serialization/JsonSerializerOptionsExtensions.cs @@ -1,42 +1,36 @@ -using System; -using System.Linq; -using System.Text.Json; -using System.Text.Json.Serialization; +namespace Atc.Rest.Client.Serialization; -namespace Atc.Rest.Client.Serialization +public static class JsonSerializerOptionsExtensions { - public static class JsonSerializerOptionsExtensions + public static JsonSerializerOptions WithoutConverter( + this JsonSerializerOptions source, + params JsonConverter[] converters) { - public static JsonSerializerOptions WithoutConverter( - this JsonSerializerOptions source, - params JsonConverter[] converters) + if (source == null) { - if (source == null) - { - throw new ArgumentNullException(nameof(source)); - } - - var result = new JsonSerializerOptions - { - AllowTrailingCommas = source.AllowTrailingCommas, - DefaultBufferSize = source.DefaultBufferSize, - DictionaryKeyPolicy = source.DictionaryKeyPolicy, - Encoder = source.Encoder, - IgnoreNullValues = source.IgnoreNullValues, - IgnoreReadOnlyProperties = source.IgnoreReadOnlyProperties, - MaxDepth = source.MaxDepth, - PropertyNameCaseInsensitive = source.PropertyNameCaseInsensitive, - PropertyNamingPolicy = source.PropertyNamingPolicy, - ReadCommentHandling = source.ReadCommentHandling, - WriteIndented = source.WriteIndented, - }; + throw new ArgumentNullException(nameof(source)); + } - foreach (var converter in source.Converters.Except(converters)) - { - result.Converters.Add(converter); - } + var result = new JsonSerializerOptions + { + AllowTrailingCommas = source.AllowTrailingCommas, + DefaultBufferSize = source.DefaultBufferSize, + DictionaryKeyPolicy = source.DictionaryKeyPolicy, + Encoder = source.Encoder, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + IgnoreReadOnlyProperties = source.IgnoreReadOnlyProperties, + MaxDepth = source.MaxDepth, + PropertyNameCaseInsensitive = source.PropertyNameCaseInsensitive, + PropertyNamingPolicy = source.PropertyNamingPolicy, + ReadCommentHandling = source.ReadCommentHandling, + WriteIndented = source.WriteIndented, + }; - return result; + foreach (var converter in source.Converters.Except(converters)) + { + result.Converters.Add(converter); } + + return result; } } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/BadResponse.cs b/test/Atc.Rest.Client.Tests/BadResponse.cs index a641be3..562559b 100644 --- a/test/Atc.Rest.Client.Tests/BadResponse.cs +++ b/test/Atc.Rest.Client.Tests/BadResponse.cs @@ -1,7 +1,6 @@ -namespace Atc.Rest.Client.Tests +namespace Atc.Rest.Client.Tests; + +public class BadResponse { - public class BadResponse - { - public string? Error { get; set; } - } + public string? Error { get; set; } } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/Builder/HttpMessageFactoryTests.cs b/test/Atc.Rest.Client.Tests/Builder/HttpMessageFactoryTests.cs index 8b5bc67..55db094 100644 --- a/test/Atc.Rest.Client.Tests/Builder/HttpMessageFactoryTests.cs +++ b/test/Atc.Rest.Client.Tests/Builder/HttpMessageFactoryTests.cs @@ -1,37 +1,28 @@ -using System.Net.Http; -using Atc.Rest.Client.Builder; -using Atc.Rest.Client.Serialization; -using Atc.Test; -using FluentAssertions; -using NSubstitute; -using Xunit; +namespace Atc.Rest.Client.Tests.Builder; -namespace Atc.Rest.Client.Tests.Builder +public sealed class HttpMessageFactoryTests { - public class HttpMessageFactoryTests - { - private readonly IContractSerializer serializer = Substitute.For(); + private readonly IContractSerializer serializer = Substitute.For(); - private HttpMessageFactory CreateSut() - => new HttpMessageFactory(serializer); + private HttpMessageFactory CreateSut() => new(serializer); - [Theory, AutoNSubstituteData] - public void Should_Provide_MessageResponseBuilder_FromResponse( - HttpResponseMessage response) - => CreateSut().FromResponse(response) - .Should() - .NotBeNull(); + [Theory, AutoNSubstituteData] + public void Should_Provide_MessageResponseBuilder_FromResponse( + HttpResponseMessage response) + => CreateSut().FromResponse(response) + .Should() + .NotBeNull(); - [Fact] - public void Should_Provide_MessageResponseBuilder_From_Null_Response() - => CreateSut().FromResponse(null) - .Should() - .NotBeNull(); + [Fact] + public void Should_Provide_MessageResponseBuilder_From_Null_Response() + => CreateSut().FromResponse(null) + .Should() + .NotBeNull(); - [Theory, AutoNSubstituteData] - public void Should_Provide_MessageRequestBuilder_FromTemplate(string template) - => CreateSut().FromTemplate(template) - .Should() - .NotBeNull(); - } + [Theory, AutoNSubstituteData] + public void Should_Provide_MessageRequestBuilder_FromTemplate( + string template) + => CreateSut().FromTemplate(template) + .Should() + .NotBeNull(); } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/Builder/MessageRequestBuilderTests.cs b/test/Atc.Rest.Client.Tests/Builder/MessageRequestBuilderTests.cs index a8f7aa6..53cad1c 100644 --- a/test/Atc.Rest.Client.Tests/Builder/MessageRequestBuilderTests.cs +++ b/test/Atc.Rest.Client.Tests/Builder/MessageRequestBuilderTests.cs @@ -1,262 +1,250 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; -using System.Net.Http.Headers; -using System.Threading.Tasks; -using Atc.Rest.Client.Builder; -using Atc.Rest.Client.Serialization; -using Atc.Test; -using FluentAssertions; -using NSubstitute; -using Xunit; - -namespace Atc.Rest.Client.Tests.Builder +namespace Atc.Rest.Client.Tests.Builder; + +public sealed class MessageRequestBuilderTests { - public class MessageRequestBuilderTests + private readonly IContractSerializer serializer = Substitute.For(); + + private MessageRequestBuilder CreateSut( + string? pathTemplate = null) + => new(pathTemplate ?? "api/", serializer); + + [Fact] + public void Null_Path_Throws() { - private readonly IContractSerializer serializer = Substitute.For(); + Action ctor = () => new MessageRequestBuilder(null!, this.serializer); - private MessageRequestBuilder CreateSut(string? pathTemplate = null) - => new MessageRequestBuilder(pathTemplate ?? "api/", serializer); + ctor.Should() + .Throw() + .Which.ParamName.Should().Be("pathTemplate"); + } - [Fact] - public void Null_Path_Throws() - { - Action ctor = () => new MessageRequestBuilder(null!, this.serializer); + [Fact] + public void Null_ContractSerializer_Throws() + { + Action ctor = () => new MessageRequestBuilder(string.Empty, null!); - ctor.Should() - .Throw() - .Which.ParamName.Should().Be("pathTemplate"); - } + ctor.Should() + .Throw() + .Which.ParamName.Should().Be("serializer"); + } - [Fact] - public void Null_ContractSerializer_Throws() - { - Action ctor = () => new MessageRequestBuilder(string.Empty, null!); - - ctor.Should() - .Throw() - .Which.ParamName.Should().Be("serializer"); - } - - [Theory, AutoNSubstituteData] - public void Should_Use_HttpMethod(HttpMethod method) - => CreateSut().Build(method) - .Method - .Should() - .Be(method); - - [Fact] - public void Should_Add_ApplicationJson_To_Accept_Header_By_Default() - { - var message = CreateSut().Build(HttpMethod.Post); + [Theory, AutoNSubstituteData] + public void Should_Use_HttpMethod(HttpMethod method) + => CreateSut().Build(method) + .Method + .Should() + .Be(method); - message - .Headers - .Accept - .Should() - .BeEquivalentTo(MediaTypeWithQualityHeaderValue.Parse("application/json")); - } + [Fact] + public void Should_Add_ApplicationJson_To_Accept_Header_By_Default() + { + var message = CreateSut().Build(HttpMethod.Post); - [Theory, AutoNSubstituteData] - public void Should_Set_Content_MediaType_To_ApplicationJson_When_Body_Is_Present(string body) - { - var sut = CreateSut(); - - sut.WithBody(body); - - var message = sut.Build(HttpMethod.Post); - - message! - .Content! - .Headers - .ContentType - .Should() - .Be(MediaTypeHeaderValue.Parse("application/json")); - } - - [Theory] - [InlineData(null, "foo")] - [InlineData("", "foo")] - [InlineData(" ", "foo")] - [InlineData("foo", null)] - [InlineData("foo", "")] - [InlineData("foo", " ")] - public void WithPathParameter_Throws_If_Parameters_Are_Null_Or_WhiteSpace( - string name, - string value) - { - var sut = CreateSut(); - - sut.Invoking(x => x.WithPathParameter(name, value)) - .Should() - .Throw(); - } - - [Theory] - [InlineAutoNSubstituteData("/api/{foo}/bar/{baz}/biz")] - public void Should_Replace_Path_Parameters( - string template, - string fooValue, - int bazValue) - { - var sut = CreateSut(template); + message + .Headers + .Accept + .Should() + .BeEquivalentTo(new[] { MediaTypeWithQualityHeaderValue.Parse("application/json") }); + } - sut.WithPathParameter("foo", fooValue); - sut.WithPathParameter("baz", bazValue); - var message = sut.Build(HttpMethod.Post); + [Theory, AutoNSubstituteData] + public void Should_Set_Content_MediaType_To_ApplicationJson_When_Body_Is_Present( + string body) + { + var sut = CreateSut(); - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api/{fooValue}/bar/{bazValue}/biz"); - } + sut.WithBody(body); - [Fact] - public void WithHeaderParameter_Throws_If_Parameters_Are_Null_Or_WhiteSpace() - { - var sut = CreateSut(); - - sut.Invoking(x => x.WithHeaderParameter(null, "foo")) - .Should() - .Throw(); - } - - [Theory] - [InlineAutoNSubstituteData("/api")] - public void Should_Replace_Query_Parameters( - string template, - string fooValue, - int barValue) - { - var sut = CreateSut(template); - - sut.WithQueryParameter("foo", fooValue); - sut.WithQueryParameter("bar", barValue); - var message = sut.Build(HttpMethod.Post); - - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api?foo={fooValue}&bar={barValue}"); - } - - [Theory] - [InlineAutoNSubstituteData("/api")] - public void Should_Replace_Query_Parameters_WithNull( - string template, - string? fooValue, - int? barValue) - { - var sut = CreateSut(template); - - sut.WithQueryParameter("foo", fooValue); - sut.WithQueryParameter("bar", barValue); - var message = sut.Build(HttpMethod.Post); - - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api?foo={fooValue}&bar={barValue}"); - } - - [Theory] - [InlineAutoNSubstituteData("/api")] - public void Should_Replace_Query_Parameters_With_ArrayOfItems1( - string template) - { - var sut = CreateSut(template); - - var values = new[] - { - 1, - }; - - sut.WithQueryParameter("foo", values); - var message = sut.Build(HttpMethod.Get); - - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api?foo={values[0]}"); - } - - [Theory] - [InlineAutoNSubstituteData("/api")] - public void Should_Replace_Query_Parameters_With_ArrayOfItems3( - string template) - { - var sut = CreateSut(template); - - var values = new[] - { - 1, - 2, - 3, - }; - - sut.WithQueryParameter("foo", values); - var message = sut.Build(HttpMethod.Get); - - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api?foo={values[0]}&foo={values[1]}&foo={values[2]}"); - } - - [Theory] - [InlineAutoNSubstituteData("/api")] - public void Should_Replace_Query_Parameters_With_ListOfItems3( - string template) + var message = sut.Build(HttpMethod.Post); + + message! + .Content! + .Headers + .ContentType + .Should() + .Be(MediaTypeHeaderValue.Parse("application/json")); + } + + [Theory] + [InlineData("", "foo")] + [InlineData(" ", "foo")] + [InlineData("foo", null)] + [InlineData("foo", "")] + [InlineData("foo", " ")] + public void WithPathParameter_Throws_If_Parameters_Are_Null_Or_WhiteSpace( + string name, + string? value) + { + var sut = CreateSut(); + + sut.Invoking(x => x.WithPathParameter(name, value)) + .Should() + .Throw(); + } + + [Theory] + [InlineAutoNSubstituteData("/api/{foo}/bar/{baz}/biz")] + public void Should_Replace_Path_Parameters( + string template, + string fooValue, + int bazValue) + { + var sut = CreateSut(template); + + sut.WithPathParameter("foo", fooValue); + sut.WithPathParameter("baz", bazValue); + var message = sut.Build(HttpMethod.Post); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api/{fooValue}/bar/{bazValue}/biz"); + } + + [Fact] + public void WithHeaderParameter_Throws_If_Parameters_Are_Null_Or_WhiteSpace() + { + var sut = CreateSut(); + + sut.Invoking(x => x.WithHeaderParameter(null, "foo")) + .Should() + .Throw(); + } + + [Theory] + [InlineAutoNSubstituteData("/api")] + public void Should_Replace_Query_Parameters( + string template, + string fooValue, + int barValue) + { + var sut = CreateSut(template); + + sut.WithQueryParameter("foo", fooValue); + sut.WithQueryParameter("bar", barValue); + var message = sut.Build(HttpMethod.Post); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api?foo={fooValue}&bar={barValue}"); + } + + [Theory] + [InlineAutoNSubstituteData("/api")] + public void Should_Replace_Query_Parameters_WithNull( + string template, + string? fooValue, + int? barValue) + { + var sut = CreateSut(template); + + sut.WithQueryParameter("foo", fooValue); + sut.WithQueryParameter("bar", barValue); + var message = sut.Build(HttpMethod.Post); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api?foo={fooValue}&bar={barValue}"); + } + + [Theory] + [InlineAutoNSubstituteData("/api")] + public void Should_Replace_Query_Parameters_With_ArrayOfItems1( + string template) + { + var sut = CreateSut(template); + + var values = new[] { - var sut = CreateSut(template); - - var values = new List - { - Guid.NewGuid(), - Guid.NewGuid(), - Guid.NewGuid(), - }; - - sut.WithQueryParameter("foo", values); - var message = sut.Build(HttpMethod.Get); - - message! - .RequestUri! - .ToString() - .Should() - .Be($"/api?foo={values[0]}&foo={values[1]}&foo={values[2]}"); - } - - [Fact] - public void WithBody_Throws_If_Parameter_Is_Null() + 1, + }; + + sut.WithQueryParameter("foo", values); + var message = sut.Build(HttpMethod.Get); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api?foo={values[0]}"); + } + + [Theory] + [InlineAutoNSubstituteData("/api")] + public void Should_Replace_Query_Parameters_With_ArrayOfItems3( + string template) + { + var sut = CreateSut(template); + + var values = new[] { - var sut = CreateSut(); + 1, + 2, + 3, + }; + + sut.WithQueryParameter("foo", values); + var message = sut.Build(HttpMethod.Get); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api?foo={values[0]}&foo={values[1]}&foo={values[2]}"); + } - sut.Invoking(x => x.WithBody(null!)) - .Should() - .Throw(); - } + [Theory] + [InlineAutoNSubstituteData("/api")] + public void Should_Replace_Query_Parameters_With_ListOfItems3( + string template) + { + var sut = CreateSut(template); - [Theory, AutoNSubstituteData] - public async Task Should_Include_Body(string content) + var values = new List { - serializer.Serialize(default!).ReturnsForAnyArgs(x => x[0]); - var sut = CreateSut(); + Guid.NewGuid(), + Guid.NewGuid(), + Guid.NewGuid(), + }; + + sut.WithQueryParameter("foo", values); + var message = sut.Build(HttpMethod.Get); + + message! + .RequestUri! + .ToString() + .Should() + .Be($"/api?foo={values[0]}&foo={values[1]}&foo={values[2]}"); + } + + [Fact] + public void WithBody_Throws_If_Parameter_Is_Null() + { + var sut = CreateSut(); + + sut.Invoking(x => x.WithBody(null!)) + .Should() + .Throw(); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Include_Body(string content) + { + serializer.Serialize(default!).ReturnsForAnyArgs(x => x[0]); + var sut = CreateSut(); - sut.WithBody(content); + sut.WithBody(content); - var message = sut.Build(HttpMethod.Post); - var result = await message!.Content!.ReadAsStringAsync(); + var message = sut.Build(HttpMethod.Post); + var result = await message!.Content!.ReadAsStringAsync(); - result - .Should() - .Be(content); - } + result + .Should() + .Be(content); } } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/Builder/MessageResponseBuilderTests.cs b/test/Atc.Rest.Client.Tests/Builder/MessageResponseBuilderTests.cs index b0fe3e3..121fe22 100644 --- a/test/Atc.Rest.Client.Tests/Builder/MessageResponseBuilderTests.cs +++ b/test/Atc.Rest.Client.Tests/Builder/MessageResponseBuilderTests.cs @@ -1,195 +1,181 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Atc.Rest.Client.Builder; -using Atc.Rest.Client.Serialization; -using Atc.Test; -using FluentAssertions; -using NSubstitute; -using Xunit; - -namespace Atc.Rest.Client.Tests.Builder +namespace Atc.Rest.Client.Tests.Builder; + +public sealed class MessageResponseBuilderTests { - public class MessageResponseBuilderTests + private readonly IContractSerializer serializer = Substitute.For(); + + private MessageResponseBuilder CreateSut(HttpResponseMessage? response) + => new(response, serializer); + + [Theory, AutoNSubstituteData] + public async Task IsSuccess_Should_Respect_Configured_ErrorResponse( + HttpResponseMessage response, + CancellationToken cancellationToken) { - private readonly IContractSerializer serializer = Substitute.For(); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.NotFound; - private MessageResponseBuilder CreateSut(HttpResponseMessage? response) - => new MessageResponseBuilder(response, serializer); + var result = await sut.AddErrorResponse(response.StatusCode) + .BuildResponseAsync(res => res, cancellationToken); - [Theory, AutoNSubstituteData] - public async Task IsSuccess_Should_Respect_Configured_ErrorResponse( - HttpResponseMessage response, - CancellationToken cancellationToken) - { - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.NotFound; - - var result = await sut.AddErrorResponse(response.StatusCode) - .BuildResponseAsync(res => res, cancellationToken); - - result - .IsSuccess - .Should() - .BeFalse(); - } - - [Theory, AutoNSubstituteData] - public async Task IsSuccess_Should_Respect_Configured_SuccessResponse( - HttpResponseMessage response, - CancellationToken cancellationToken) - { - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.OK; - - var result = await sut.AddSuccessResponse(response.StatusCode) - .BuildResponseAsync(res => res, cancellationToken); - - result - .IsSuccess - .Should() - .BeTrue(); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Deserialize_Configured_SuccessResponseCode( - HttpResponseMessage response, - Guid expectedResponse, - CancellationToken cancellationToken) - { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.OK; - - var result = await sut.AddSuccessResponse(response.StatusCode) - .BuildResponseAsync(res => res, cancellationToken); - - result - .ContentObject - .Should() - .BeEquivalentTo(expectedResponse); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Deserialize_Configured_ErrorResponseCode( - HttpResponseMessage response, - Guid expectedResponse, - CancellationToken cancellationToken) - { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.BadRequest; - - var result = await sut.AddErrorResponse(response.StatusCode) - .BuildResponseAsync(res => res, cancellationToken); - - result - .ContentObject - .Should() - .BeEquivalentTo(expectedResponse); - } - - [Theory, AutoNSubstituteData] - public async Task Should_Return_Response_Headers( - HttpResponseMessage response, - CancellationToken cancellationToken) - { - var sut = CreateSut(response); - var expected = new Dictionary>(StringComparer.Ordinal) - { - { "responseHeader", new[] { "value" } }, - { "contentHeader", new[] { "value" } }, - }; - response.Headers.Add("responseHeader", "value"); - response.Content.Headers.Add("contentHeader", "value"); - response.StatusCode = HttpStatusCode.OK; - - var result = await sut.AddSuccessResponse(response.StatusCode) - .BuildResponseAsync(res => res, cancellationToken); - - result - .Headers - .Should() - .BeEquivalentTo(expected); - } - - [Theory, AutoNSubstituteData] - public async Task Should_SuccessContent_NotBeNull( - HttpResponseMessage response, - SuccessResponse expectedResponse, - CancellationToken cancellationToken) - { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.OK; - - var result = await sut.AddSuccessResponse(response.StatusCode) - .BuildResponseAsync(cancellationToken); - - result - .SuccessContent - .Should() - .BeEquivalentTo(expectedResponse); - } - - [Theory, AutoNSubstituteData] - public async Task Should_FailureContent_BeNull( - HttpResponseMessage response, - SuccessResponse expectedResponse, - CancellationToken cancellationToken) - { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.OK; - - var result = await sut.AddSuccessResponse(response.StatusCode) - .BuildResponseAsync(cancellationToken); - - result - .ErrorContent - .Should() - .BeNull(); - } - - [Theory, AutoNSubstituteData] - public async Task Should_FailureContent_NotBeNull( - HttpResponseMessage response, - BadResponse expectedResponse, - CancellationToken cancellationToken) - { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.BadRequest; - - var result = await sut.AddErrorResponse(response.StatusCode) - .BuildResponseAsync(cancellationToken); - - result - .ErrorContent - .Should() - .BeEquivalentTo(expectedResponse); - } - - [Theory, AutoNSubstituteData] - public async Task Should_SuccessContent_BeNull( - HttpResponseMessage response, - BadResponse expectedResponse, - CancellationToken cancellationToken) + result + .IsSuccess + .Should() + .BeFalse(); + } + + [Theory, AutoNSubstituteData] + public async Task IsSuccess_Should_Respect_Configured_SuccessResponse( + HttpResponseMessage response, + CancellationToken cancellationToken) + { + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.OK; + + var result = await sut.AddSuccessResponse(response.StatusCode) + .BuildResponseAsync(res => res, cancellationToken); + + result + .IsSuccess + .Should() + .BeTrue(); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Deserialize_Configured_SuccessResponseCode( + HttpResponseMessage response, + Guid expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.OK; + + var result = await sut.AddSuccessResponse(response.StatusCode) + .BuildResponseAsync(res => res, cancellationToken); + + result + .ContentObject + .Should() + .BeEquivalentTo(expectedResponse); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Deserialize_Configured_ErrorResponseCode( + HttpResponseMessage response, + Guid expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.BadRequest; + + var result = await sut.AddErrorResponse(response.StatusCode) + .BuildResponseAsync(res => res, cancellationToken); + + result + .ContentObject + .Should() + .BeEquivalentTo(expectedResponse); + } + + [Theory, AutoNSubstituteData] + public async Task Should_Return_Response_Headers( + HttpResponseMessage response, + CancellationToken cancellationToken) + { + var sut = CreateSut(response); + var expected = new Dictionary>(StringComparer.Ordinal) { - serializer.Deserialize(Arg.Any()).Returns(expectedResponse); - var sut = CreateSut(response); - response.StatusCode = HttpStatusCode.BadRequest; - - var result = await sut.AddErrorResponse(response.StatusCode) - .BuildResponseAsync(cancellationToken); - - result - .SuccessContent - .Should() - .BeNull(); - } + { "responseHeader", new[] { "value" } }, + { "contentHeader", new[] { "value" } }, + }; + response.Headers.Add("responseHeader", "value"); + response.Content.Headers.Add("contentHeader", "value"); + response.StatusCode = HttpStatusCode.OK; + + var result = await sut.AddSuccessResponse(response.StatusCode) + .BuildResponseAsync(res => res, cancellationToken); + + result + .Headers + .Should() + .BeEquivalentTo(expected); + } + + [Theory, AutoNSubstituteData] + public async Task Should_SuccessContent_NotBeNull( + HttpResponseMessage response, + SuccessResponse expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.OK; + + var result = await sut.AddSuccessResponse(response.StatusCode) + .BuildResponseAsync(cancellationToken); + + result + .SuccessContent + .Should() + .BeEquivalentTo(expectedResponse); + } + + [Theory, AutoNSubstituteData] + public async Task Should_FailureContent_BeNull( + HttpResponseMessage response, + SuccessResponse expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.OK; + + var result = await sut.AddSuccessResponse(response.StatusCode) + .BuildResponseAsync(cancellationToken); + + result + .ErrorContent + .Should() + .BeNull(); + } + + [Theory, AutoNSubstituteData] + public async Task Should_FailureContent_NotBeNull( + HttpResponseMessage response, + BadResponse expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.BadRequest; + + var result = await sut.AddErrorResponse(response.StatusCode) + .BuildResponseAsync(cancellationToken); + + result + .ErrorContent + .Should() + .BeEquivalentTo(expectedResponse); + } + + [Theory, AutoNSubstituteData] + public async Task Should_SuccessContent_BeNull( + HttpResponseMessage response, + BadResponse expectedResponse, + CancellationToken cancellationToken) + { + serializer.Deserialize(Arg.Any()).Returns(expectedResponse); + var sut = CreateSut(response); + response.StatusCode = HttpStatusCode.BadRequest; + + var result = await sut.AddErrorResponse(response.StatusCode) + .BuildResponseAsync(cancellationToken); + + result + .SuccessContent + .Should() + .BeNull(); } } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/EndpointResponseTests.cs b/test/Atc.Rest.Client.Tests/EndpointResponseTests.cs index e85586f..6f9f0e9 100644 --- a/test/Atc.Rest.Client.Tests/EndpointResponseTests.cs +++ b/test/Atc.Rest.Client.Tests/EndpointResponseTests.cs @@ -1,83 +1,74 @@ -using System; -using System.Collections.Generic; -using System.Net; -using System.Text.Json; -using Atc.Test; -using FluentAssertions; -using Xunit; +namespace Atc.Rest.Client.Tests; -namespace Atc.Rest.Client.Tests +public class EndpointResponseTests { - public class EndpointResponseTests + [Theory, AutoNSubstituteData] + public void SuccessContent_Returns_Response_Upon_Success( + SuccessResponse contentObject, + IReadOnlyDictionary> headers) { - [Theory, AutoNSubstituteData] - public void SuccessContent_Returns_Respose_Upon_Success( - SuccessResponse contentObject, - IReadOnlyDictionary> headers) - { - var sut = new EndpointResponse( - true, - HttpStatusCode.OK, - JsonSerializer.Serialize(contentObject), - contentObject, - headers); + var sut = new EndpointResponse( + true, + HttpStatusCode.OK, + JsonSerializer.Serialize(contentObject), + contentObject, + headers); - sut - .SuccessContent - .Should() - .Be(contentObject); - } + sut + .SuccessContent + .Should() + .Be(contentObject); + } - [Fact] - public void SuccessContent_Returns_Null_Upon_Failure() - { - var sut = new EndpointResponse( - false, - HttpStatusCode.BadRequest, - string.Empty, - null, - new Dictionary>(StringComparer.Ordinal)); + [Fact] + public void SuccessContent_Returns_Null_Upon_Failure() + { + var sut = new EndpointResponse( + false, + HttpStatusCode.BadRequest, + string.Empty, + null, + new Dictionary>(StringComparer.Ordinal)); - sut - .SuccessContent - .Should() - .BeNull(); - } + sut + .SuccessContent + .Should() + .BeNull(); + } - [Theory, AutoNSubstituteData] - public void FailureContent_Returns_Null_Upon_Success( - SuccessResponse contentObject, - IReadOnlyDictionary> headers) - { - var sut = new EndpointResponse( - true, - HttpStatusCode.OK, - JsonSerializer.Serialize(contentObject), - contentObject, - headers); + [Theory, AutoNSubstituteData] + public void FailureContent_Returns_Null_Upon_Success( + SuccessResponse contentObject, + IReadOnlyDictionary> headers) + { + var sut = new EndpointResponse( + true, + HttpStatusCode.OK, + JsonSerializer.Serialize(contentObject), + contentObject, + headers); - sut - .ErrorContent - .Should() - .BeNull(); - } + sut + .ErrorContent + .Should() + .BeNull(); + } - [Theory, AutoNSubstituteData] - public void FailureContent_Returns_Response_Upon_Failure( - BadResponse contentObject, - IReadOnlyDictionary> headers) - { - var sut = new EndpointResponse( - false, - HttpStatusCode.BadRequest, - JsonSerializer.Serialize(contentObject), - contentObject, - headers); + [Theory, AutoNSubstituteData] + public void FailureContent_Returns_Response_Upon_Failure( + BadResponse contentObject, + IReadOnlyDictionary> headers) + { + var sut = new EndpointResponse( + false, + HttpStatusCode.BadRequest, + JsonSerializer.Serialize(contentObject), + contentObject, + headers); - sut - .ErrorContent - .Should() - .Be(contentObject); - } + sut + .ErrorContent + .Should() + .Be(contentObject); } } \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/GlobalUsings.cs b/test/Atc.Rest.Client.Tests/GlobalUsings.cs new file mode 100644 index 0000000..a28f48c --- /dev/null +++ b/test/Atc.Rest.Client.Tests/GlobalUsings.cs @@ -0,0 +1,5 @@ +global using System.Net; +global using System.Net.Http.Headers; +global using System.Text.Json; +global using Atc.Rest.Client.Builder; +global using Atc.Rest.Client.Serialization; \ No newline at end of file diff --git a/test/Atc.Rest.Client.Tests/SuccessResponse.cs b/test/Atc.Rest.Client.Tests/SuccessResponse.cs index 9b81a82..f09e6ba 100644 --- a/test/Atc.Rest.Client.Tests/SuccessResponse.cs +++ b/test/Atc.Rest.Client.Tests/SuccessResponse.cs @@ -1,7 +1,6 @@ -namespace Atc.Rest.Client.Tests +namespace Atc.Rest.Client.Tests; + +public class SuccessResponse { - public class SuccessResponse - { - public string? Name { get; set; } - } + public string? Name { get; set; } } \ No newline at end of file