diff --git a/.github/dotnet-format-problem-matcher.json b/.github/dotnet-format-problem-matcher.json new file mode 100644 index 0000000..a24d505 --- /dev/null +++ b/.github/dotnet-format-problem-matcher.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "dotnet-format", + "pattern": [ + { + "regexp": "^\\s*(.*)\\((\\d+),(\\d+)\\):\\s+(.*)\\s+(.*):\\s+(.*)\\s+\\[(.*)\\]$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6 + } + ] + } + ] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 810adfe..61bb37a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -19,7 +19,7 @@ jobs: with: dotnet-version: '7.0.x' - uses: actions/checkout@v3 - - name: Retore + - name: Restore run: dotnet restore --nologo - name: Build run: dotnet build --no-restore --nologo --configuration Release @@ -36,7 +36,7 @@ jobs: uses: actions/setup-dotnet@v3 with: dotnet-version: '7.0.x' - - name: Retore + - name: Restore run: dotnet restore --nologo - name: .Net Format run: dotnet format --no-restore --verify-no-changes -v diag diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml index 971ab40..0421d34 100644 --- a/.github/workflows/pr-build.yml +++ b/.github/workflows/pr-build.yml @@ -18,7 +18,7 @@ jobs: with: dotnet-version: '7.0.x' - uses: actions/checkout@v3 - - name: Retore + - name: Restore run: dotnet restore --nologo - name: Build run: dotnet build --no-restore --nologo --configuration Release @@ -31,11 +31,36 @@ jobs: DOTNET_CLI_TELEMETRY_OPTOUT: 'true' steps: - uses: actions/checkout@v3 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 - name: Setup .Net 7.0.x uses: actions/setup-dotnet@v3 with: dotnet-version: '7.0.x' - - name: Retore + - name: Restore run: dotnet restore --nologo + - name: Add .Net Format Problem Matcher + shell: bash + run: echo "::add-matcher::${{ github.workspace }}/.github/dotnet-format-problem-matcher.json" - name: .Net Format + id: format run: dotnet format --no-restore --verify-no-changes -v diag + - name: Remove .Net Format Problem Matcher + shell: bash + if: always() + run: echo "::remove-matcher owner=dotnet-format::" + - name: .Net Format + if: failure() && steps.format.outcome == 'failure' + run: dotnet format --no-restore -v diag + - name: Commit files + if: failure() && steps.format.outcome == 'failure' + run: | + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + git commit -a -m "Fix CodeStyle" + - name: Push changes + if: failure() && steps.format.outcome == 'failure' + uses: ad-m/github-push-action@master + with: + branch: ${{ github.head_ref }} diff --git a/ReportingApi.sln b/ReportingApi.sln index a4cbf3b..6bbb2e3 100644 --- a/ReportingApi.sln +++ b/ReportingApi.sln @@ -4,6 +4,23 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportingApi", "src\Reporti EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ReportingApi.Tests", "src\ReportingApi.Tests\ReportingApi.Tests.csproj", "{6001C0E3-D5B9-4537-ACE2-1187D28799BF}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{392035D4-EFC8-47FB-A388-44DA18A853CD}" + ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig + .gitattributes = .gitattributes + .gitignore = .gitignore + README.md = README.md + .github\dependabot.yml = .github\dependabot.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{B5569CC8-011A-43C4-BAD8-2733CAC14274}" + ProjectSection(SolutionItems) = preProject + .github\workflows\build.yml = .github\workflows\build.yml + .github\workflows\nightly-release.yml = .github\workflows\nightly-release.yml + .github\workflows\pr-build.yml = .github\workflows\pr-build.yml + .github\workflows\release.yml = .github\workflows\release.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,4 +36,7 @@ Global {6001C0E3-D5B9-4537-ACE2-1187D28799BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {6001C0E3-D5B9-4537-ACE2-1187D28799BF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B5569CC8-011A-43C4-BAD8-2733CAC14274} = {392035D4-EFC8-47FB-A388-44DA18A853CD} + EndGlobalSection EndGlobal diff --git a/src/ReportingApi.Tests/ReportRequestConverterTests.cs b/src/ReportingApi.Tests/ReportRequestConverterTests.cs index 2ead0fb..55a7740 100644 --- a/src/ReportingApi.Tests/ReportRequestConverterTests.cs +++ b/src/ReportingApi.Tests/ReportRequestConverterTests.cs @@ -33,11 +33,47 @@ public void CspViolationTest() Assert.NotNull(reportRequest); Assert.Equal(1, reportRequest.Age); - Assert.Equal("https://csplite.com/tst/test_frame.php?ID=229&hash=da964209653e467d337313e51876e27d", reportRequest.Url); - Assert.Equal("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36", reportRequest.UserAgent); + Assert.Equal("https://csplite.com/tst/test_frame.php?ID=229&hash=da964209653e467d337313e51876e27d", + reportRequest.Url); + Assert.Equal( + "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.88 Safari/537.36", + reportRequest.UserAgent); var cspReport = Assert.IsType>(reportRequest); Assert.Equal("csp-violation", cspReport.Type); Assert.NotNull(cspReport.Body); } + + [Fact] + public void NetworkErrorTest() + { + var sourceJson = """ + { + "age": 2, + "type": "network-error", + "url": "https://widget.com/thing.js", + "body": { + "sampling_fraction": 1.0, + "referrer": "https://www.example.com/", + "server_ip": "", + "protocol": "", + "method": "GET", + "status_code": 0, + "elapsed_time": 143, + "type": "dns.name_not_resolved" + } + } + """; + + var reportRequest = JsonSerializer.Deserialize(sourceJson); + + Assert.NotNull(reportRequest); + Assert.Equal(2, reportRequest.Age); + Assert.Equal("https://widget.com/thing.js", reportRequest.Url); + Assert.Null(reportRequest.UserAgent); + + var cspReport = Assert.IsType>(reportRequest); + Assert.Equal("network-error", cspReport.Type); + Assert.NotNull(cspReport.Body); + } } diff --git a/src/ReportingApi/JsonConverters/ReportRequestConverter.cs b/src/ReportingApi/JsonConverters/ReportRequestConverter.cs index 9eecc3b..57f0259 100644 --- a/src/ReportingApi/JsonConverters/ReportRequestConverter.cs +++ b/src/ReportingApi/JsonConverters/ReportRequestConverter.cs @@ -75,6 +75,11 @@ private Type GetBodyType(ReadOnlySpan type) return typeof(CspReport); } + if (type.SequenceEqual("network-error"u8)) + { + return typeof(NetworkErrorReport); + } + var typeString = Encoding.UTF8.GetString(type); throw new JsonException($"Type '{typeString}' does not have defined body type"); } diff --git a/src/ReportingApi/Models/NetworkErrorReport.cs b/src/ReportingApi/Models/NetworkErrorReport.cs new file mode 100644 index 0000000..f41d12d --- /dev/null +++ b/src/ReportingApi/Models/NetworkErrorReport.cs @@ -0,0 +1,30 @@ +using System.Text.Json.Serialization; + +namespace ReportingApi.Models; + +public sealed record NetworkErrorReport : IReportBody +{ + [JsonPropertyName("sampling_fraction")] + public decimal SamplingFraction { get; set; } + + [JsonPropertyName("referrer")] + public string Referrer { get; set; } = null!; + + [JsonPropertyName("server_ip")] + public string ServerIp { get; set; } = null!; + + [JsonPropertyName("protocol")] + public string Protocol { get; set; } = null!; + + [JsonPropertyName("method")] + public string Method { get; set; } = null!; + + [JsonPropertyName("status_code")] + public int StatusCode { get; set; } + + [JsonPropertyName("elapsed_time")] + public int ElapsedTime { get; set; } + + [JsonPropertyName("type")] + public string Type { get; set; } = null!; +} diff --git a/src/ReportingApi/Models/ReportRequest.cs b/src/ReportingApi/Models/ReportRequest.cs index f4f252b..668f1be 100644 --- a/src/ReportingApi/Models/ReportRequest.cs +++ b/src/ReportingApi/Models/ReportRequest.cs @@ -13,7 +13,7 @@ public record ReportRequest public string Url { get; set; } = null!; [JsonPropertyName("user_agent")] - public string UserAgent { get; set; } = null!; + public string? UserAgent { get; set; } } public sealed record ReportRequest : ReportRequest