Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ on:
jobs:
publish:
runs-on: ubuntu-latest

permissions:
id-token: write

steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.0.x
8.0.x
9.0.x
10.0.x

- name: NuGet login
uses: NuGet/login@v1
id: login
with:
user: ${{ secrets.NUGET_USER }}

- name: Restore
run: dotnet restore
Expand All @@ -32,4 +39,4 @@ jobs:
if: "!github.event.release.prerelease"

- name: Publish
run: dotnet nuget push "*.nupkg" -k ${{ secrets.NUGET_KEY }} --skip-duplicate -s https://api.nuget.org/v3/index.json
run: dotnet nuget push "*.nupkg" -k ${{ steps.login.outputs.NUGET_API_KEY }} --skip-duplicate -s https://api.nuget.org/v3/index.json
7 changes: 3 additions & 4 deletions .github/workflows/unit-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,13 @@ jobs:
- uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Build
run: dotnet build -c Release

- name: Unit Tests (Roslyn)
run: dotnet test -f net8.0 -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Roslyn.Tests
run: dotnet test -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Roslyn.Tests

- name: Unit Tests
run: dotnet test -f net8.0 -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Tests
run: dotnet test -c Release --no-build --no-restore --verbosity normal DragonFruit.Data.Tests
2 changes: 1 addition & 1 deletion DragonFruit.Data.Roslyn.Tests/ApiRequestAnalyzerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ private async Task PerformTest(AnalyzerTest<DefaultVerifier> test)
await test.RunAsync();
}

private string GetSolutionRoot()
private static string GetSolutionRoot()
{
var currentDirectory = Directory.GetCurrentDirectory();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing" Version="1.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />

<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
4 changes: 2 additions & 2 deletions DragonFruit.Data.Roslyn/DragonFruit.Data.Roslyn.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.11.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="HtmlAgilityPack" Version="1.11.71" />
<PackageReference Include="HtmlAgilityPack" Version="1.12.4" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion DragonFruit.Data.Serializers.Newtonsoft/JsonArrayPool.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace DragonFruit.Data.Serializers.Newtonsoft
/// </remarks>
internal class JsonArrayPool : IArrayPool<char>
{
public static readonly JsonArrayPool Instance = new JsonArrayPool();
public static readonly JsonArrayPool Instance = new();

public char[] Rent(int minimumLength)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,12 @@ namespace DragonFruit.Data.Serializers.Newtonsoft
{
public class NewtonsoftJsonSerializer : ApiSerializer
{
private JsonSerializer _serializer;

public override string ContentType => "application/json";

public JsonSerializer Serializer
{
get => _serializer ??= new JsonSerializer { Culture = CultureInfo.InvariantCulture };
set => _serializer = value;
get => field ??= new JsonSerializer { Culture = CultureInfo.InvariantCulture };
set;
}

public override HttpContent Serialize<T>(T input)
Expand Down
6 changes: 3 additions & 3 deletions DragonFruit.Data.Tests/ConverterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

if (builder.Length == 0)
{
Assert.True(false, "Builder was empty");
Assert.Fail("Builder was empty");
}

builder.Length--; // remove the trailing &
Expand All @@ -48,7 +48,7 @@

if (builder.Length == 0)
{
Assert.True(false, "Builder was empty");
Assert.Fail("Builder was empty");
}

builder.Length--; // remove the trailing &
Expand All @@ -61,7 +61,7 @@
return new KeyValuePair<string, string>(segments[0], segments[1]);
});

Assert.True(EnumerableConverter.GetPairs(testData, mode, DefaultParameterName, null).SequenceEqual(resultPairs));

Check warning on line 64 in DragonFruit.Data.Tests/ConverterTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/ConverterTests.cs#L64 <UsageOfDefaultStructEquality>(https://www.jetbrains.com/help/resharper/UsageOfDefaultStructEquality.html)

Struct 'KeyValuePair' is checked for equality using an inefficient runtime-provided implementation
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":21,"uri":"DragonFruit.Data.Tests/ConverterTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":13,"charOffset":2965,"endColumn":111,"endLine":64,"startColumn":98,"startLine":64}}}],"message":{"text":"Struct 'KeyValuePair' is checked for equality using an inefficient runtime-provided implementation"},"partialFingerprints":{"contextRegionHash/v1":"01B9F4CF287E014C2E6F35C273397D50DC9F20ED939F3D8538A7FCB52E78B5C8"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"UsageOfDefaultStructEquality","ruleIndex":14}
}

[Fact]
Expand All @@ -79,7 +79,7 @@

if (builder.Length == 0)
{
Assert.True(false, "Builder was empty");
Assert.Fail("Builder was empty");
}

builder.Length--; // remove the trailing &
Expand Down
8 changes: 4 additions & 4 deletions DragonFruit.Data.Tests/DragonFruit.Data.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<PropertyGroup>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="xunit" Version="2.9.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
Expand Down
6 changes: 3 additions & 3 deletions DragonFruit.Data.Tests/RequestTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

Assert.NotNull(request);

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 30 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L30 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":905,"endColumn":60,"endLine":30,"startColumn":43,"startLine":30}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"A176128CED63C4B01AA9BEE5C88CCE55FC2EF0B65F9B1E445633AF79BAE06C21"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

Assert.NotNull(sourceGenMessage.RequestUri);
Expand All @@ -43,11 +43,11 @@
}

[Fact]
public async void TestInheritedRequest()
public async Task TestInheritedRequest()
{
var request = new InheritedEchoRequest();

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 50 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L50 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":1868,"endColumn":60,"endLine":50,"startColumn":43,"startLine":50}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"A176128CED63C4B01AA9BEE5C88CCE55FC2EF0B65F9B1E445633AF79BAE06C21"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

Assert.NotNull(sourceGenMessage.Content);
Expand All @@ -64,14 +64,14 @@
public void TestInheritedMultipartRequest()
{
var request = new InheritedMultipartEchoRequest();
using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 67 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L67 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedMultipartEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":2629,"endColumn":60,"endLine":67,"startColumn":43,"startLine":67}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedMultipartEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"D8EE436980518097C3C35E5334DB20EC9D96C9033E1D2F8D150607863E0C46F4"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

Assert.True(sourceGenMessage.Content is MultipartFormDataContent);
Assert.True(reflectionGenMessage.Content is MultipartFormDataContent);

var baseRequest = new InheritedEchoRequest();
using var baseSourceGenMessage = ((IRequestBuilder)baseRequest).BuildRequest(null);

Check warning on line 74 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L74 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":3061,"endColumn":64,"endLine":74,"startColumn":47,"startLine":74}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.InheritedEchoRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"F09BB0FC103A1847EEA6E1A9CB01866D6F3F74E233AEC812A4CBED3F61AFBC3A"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var baseReflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(baseRequest, null);

Assert.True(baseSourceGenMessage.Content is StringContent);
Expand All @@ -79,7 +79,7 @@
}

[Fact]
public async void TestMultipartFormRequest()
public async Task TestMultipartFormRequest()
{
// actually send requests because it's easier than inspecting the multipart content
using var httpClient = new HttpClient();
Expand All @@ -90,7 +90,7 @@
var formats = new[]
{
// sourcegen
((IRequestBuilder)request).BuildRequest(null),

Check warning on line 93 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L93 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.MultipartFormRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":3829,"endColumn":35,"endLine":93,"startColumn":18,"startLine":93}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.MultipartFormRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"E6613147B31E0BFD14B92FF2AF097741384794FF3273B6384B470B9D5ADD34B6"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}

// reflection
ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null)
Expand All @@ -109,11 +109,11 @@
}

// check querystring
Assert.Equal("content", json["args"]["c"].ToString());

Check warning on line 112 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L112 <PossibleNullReferenceException>(https://www.jetbrains.com/help/resharper/PossibleNullReferenceException.html)

Possible 'System.NullReferenceException'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":12,"charOffset":4511,"endColumn":53,"endLine":112,"startColumn":41,"startLine":112}}}],"message":{"text":"Possible 'System.NullReferenceException'"},"partialFingerprints":{"contextRegionHash/v1":"3336554063B0D9427F5CF00A65063AEAA44E44A3F90CCC88C6D62EF0B09E29DE"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PossibleNullReferenceException","ruleIndex":11}

Check warning on line 112 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L112 <PossibleNullReferenceException>(https://www.jetbrains.com/help/resharper/PossibleNullReferenceException.html)

Possible 'System.NullReferenceException'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":4511,"endColumn":58,"endLine":112,"startColumn":41,"startLine":112}}}],"message":{"text":"Possible 'System.NullReferenceException'"},"partialFingerprints":{"contextRegionHash/v1":"3336554063B0D9427F5CF00A65063AEAA44E44A3F90CCC88C6D62EF0B09E29DE"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PossibleNullReferenceException","ruleIndex":11}

// check form contents
Assert.Equal("content", json["form"]["file"].ToString());

Check warning on line 115 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L115 <PossibleNullReferenceException>(https://www.jetbrains.com/help/resharper/PossibleNullReferenceException.html)

Possible 'System.NullReferenceException'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":12,"charOffset":4622,"endColumn":53,"endLine":115,"startColumn":41,"startLine":115}}}],"message":{"text":"Possible 'System.NullReferenceException'"},"partialFingerprints":{"contextRegionHash/v1":"23A01313E3B87D6F454FE3FE1BDACBC08A35EE698D8C9E480756887C76EA4603"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PossibleNullReferenceException","ruleIndex":11}

Check warning on line 115 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L115 <PossibleNullReferenceException>(https://www.jetbrains.com/help/resharper/PossibleNullReferenceException.html)

Possible 'System.NullReferenceException'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":20,"charOffset":4622,"endColumn":61,"endLine":115,"startColumn":41,"startLine":115}}}],"message":{"text":"Possible 'System.NullReferenceException'"},"partialFingerprints":{"contextRegionHash/v1":"23A01313E3B87D6F454FE3FE1BDACBC08A35EE698D8C9E480756887C76EA4603"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PossibleNullReferenceException","ruleIndex":11}
Assert.Equal("content", json["form"]["bytes"].ToString());

Check warning on line 116 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L116 <PossibleNullReferenceException>(https://www.jetbrains.com/help/resharper/PossibleNullReferenceException.html)

Possible 'System.NullReferenceException'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":21,"charOffset":4696,"endColumn":62,"endLine":116,"startColumn":41,"startLine":116}}}],"message":{"text":"Possible 'System.NullReferenceException'"},"partialFingerprints":{"contextRegionHash/v1":"B2215D9D9765ADD1F386C296CB75A75B2FE582689E2BB0616D57081963D74426"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PossibleNullReferenceException","ruleIndex":11}

json.Remove("headers");
processedResponses.Add(JsonSerializer.SerializeToUtf8Bytes(json));
Expand All @@ -128,7 +128,7 @@
{
var request = new SpecialTypeRequest();

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 131 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L131 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.SpecialTypeRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":5174,"endColumn":60,"endLine":131,"startColumn":43,"startLine":131}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.SpecialTypeRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"A176128CED63C4B01AA9BEE5C88CCE55FC2EF0B65F9B1E445633AF79BAE06C21"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

// check query strings match expected output
Expand All @@ -139,11 +139,11 @@
}

[Fact]
public async void TestAdditionalSpecialTypeHandling()
public async Task TestAdditionalSpecialTypeHandling()
{
var request = new SpecialTypeAdditionalLocationsRequest();

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 146 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L146 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.SpecialTypeAdditionalLocationsRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":5872,"endColumn":60,"endLine":146,"startColumn":43,"startLine":146}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.SpecialTypeAdditionalLocationsRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"A176128CED63C4B01AA9BEE5C88CCE55FC2EF0B65F9B1E445633AF79BAE06C21"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

// test headers
Expand All @@ -165,7 +165,7 @@
{
var request = new CustomHttpContentRequest();

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(null);

Check warning on line 168 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L168 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.CustomHttpContentRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":6990,"endColumn":60,"endLine":168,"startColumn":43,"startLine":168}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.CustomHttpContentRequest' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"A176128CED63C4B01AA9BEE5C88CCE55FC2EF0B65F9B1E445633AF79BAE06C21"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, null);

Assert.NotNull(sourceGenMessage.Content);
Expand All @@ -188,7 +188,7 @@
var request = new CustomSerializedContentRequest<TestRecord>(record);
var serializers = new SerializerResolver(new ApiJsonSerializer());

using var sourceGenMessage = ((IRequestBuilder)request).BuildRequest(serializers);

Check warning on line 191 in DragonFruit.Data.Tests/RequestTests.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/RequestTests.cs#L191 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.CustomSerializedContentRequest<DragonFruit.Data.Tests.RequestTests.TestRecord>' and 'DragonFruit.Data.Requests.IRequestBuilder'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":18,"uri":"DragonFruit.Data.Tests/RequestTests.cs","uriBaseId":"solutionDir"},"region":{"charLength":17,"charOffset":8041,"endColumn":60,"endLine":191,"startColumn":43,"startLine":191}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.Tests.Requests.CustomSerializedContentRequest\u003cDragonFruit.Data.Tests.RequestTests.TestRecord\u003e' and 'DragonFruit.Data.Requests.IRequestBuilder'"},"partialFingerprints":{"contextRegionHash/v1":"16D1028DE555C403F1E3B2E4CAA6064C6A74AC9873BCAF48E8544D943DF9C133"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
using var reflectionGenMessage = ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, serializers);

using (var sourceGenStream = await sourceGenMessage.Content!.ReadAsStreamAsync())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,13 @@

namespace DragonFruit.Data.Tests.Requests
{
public partial class CustomSerializedContentRequest<T> : ApiRequest where T : class
public partial class CustomSerializedContentRequest<T>(T requestContent) : ApiRequest

Check warning on line 9 in DragonFruit.Data.Tests/Requests/CustomSerializedContentRequest.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/Requests/CustomSerializedContentRequest.cs#L9 <PartialTypeWithSinglePart>(https://www.jetbrains.com/help/resharper/PartialTypeWithSinglePart.html)

Partial class with single part
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":11,"uri":"DragonFruit.Data.Tests/Requests/CustomSerializedContentRequest.cs","uriBaseId":"solutionDir"},"region":{"charLength":7,"charOffset":271,"endColumn":19,"endLine":9,"startColumn":12,"startLine":9}}}],"message":{"text":"Partial class with single part"},"partialFingerprints":{"contextRegionHash/v1":"A566CA7EC5D8575EF766E5E98DB65EB3F4FF29925A3D4746431C220AE57B7A14"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PartialTypeWithSinglePart","ruleIndex":10}
where T : class
{
public override string RequestPath => "https://postman-echo.com/patch";
public override HttpMethod RequestMethod => HttpMethod.Patch;

public CustomSerializedContentRequest(T requestContent)
{
RequestContent = requestContent;
}

[RequestBody]
public T RequestContent { get; }
public T RequestContent { get; } = requestContent;
}
}
9 changes: 2 additions & 7 deletions DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@

namespace DragonFruit.Data.Tests.Requests
{
public partial class DummyFileDownloadRequest : ApiRequest
public partial class DummyFileDownloadRequest(string fileSize) : ApiRequest

Check warning on line 6 in DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs#L6 <PartialTypeWithSinglePart>(https://www.jetbrains.com/help/resharper/PartialTypeWithSinglePart.html)

Partial class with single part
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs","uriBaseId":"solutionDir"},"region":{"charLength":7,"charOffset":214,"endColumn":19,"endLine":6,"startColumn":12,"startLine":6}}}],"message":{"text":"Partial class with single part"},"partialFingerprints":{"contextRegionHash/v1":"ED1E70E9E090D7CC7C223A966B5199A61AB99A7427D9857CC68C252586097DD7"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PartialTypeWithSinglePart","ruleIndex":10}
{
public override string RequestPath => $"http://xcal1.vodafone.co.uk/{FileSize}.zip";

public DummyFileDownloadRequest(string fileSize)
{
FileSize = fileSize;
}

public string FileSize { get; set; }
public string FileSize { get; set; } = fileSize;

Check warning on line 10 in DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs#L10 <AutoPropertyCanBeMadeGetOnly.Global>(https://www.jetbrains.com/help/resharper/AutoPropertyCanBeMadeGetOnly.Global.html)

Auto-property can be made get-only
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":0,"uri":"DragonFruit.Data.Tests/Requests/DummyFileDownloadRequest.cs","uriBaseId":"solutionDir"},"region":{"charLength":4,"charOffset":421,"endColumn":43,"endLine":10,"startColumn":39,"startLine":10}}}],"message":{"text":"Auto-property can be made get-only"},"partialFingerprints":{"contextRegionHash/v1":"485510F06D587C87EFCE9963DA363DB2CBAB0BF20D6478EC70913CB3BC685CB3"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"AutoPropertyCanBeMadeGetOnly.Global","ruleIndex":0}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@

namespace DragonFruit.Data.Tests.Requests
{
public partial class SpecialTypeAdditionalLocationsRequest : ApiRequest

Check warning on line 9 in DragonFruit.Data.Tests/Requests/SpecialTypeAdditionalLocationsRequest.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/Requests/SpecialTypeAdditionalLocationsRequest.cs#L9 <PartialTypeWithSinglePart>(https://www.jetbrains.com/help/resharper/PartialTypeWithSinglePart.html)

Partial class with single part
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":15,"uri":"DragonFruit.Data.Tests/Requests/SpecialTypeAdditionalLocationsRequest.cs","uriBaseId":"solutionDir"},"region":{"charLength":7,"charOffset":282,"endColumn":19,"endLine":9,"startColumn":12,"startLine":9}}}],"message":{"text":"Partial class with single part"},"partialFingerprints":{"contextRegionHash/v1":"CA0A8FF0F05AD2E024DF314E9E15AE641580D37346FE0DE16A95880758EA2B70"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PartialTypeWithSinglePart","ruleIndex":10}
{
private readonly string[] _ids =
{
[
"5c2a5585-2682-4c60-9bc7-b11874582af6",
"d85cf845-a35a-48e9-b629-700f77ab1c5d",
"885f37a8-6bf6-4d52-8092-71276ab706fd"
};
];

public override string RequestPath => "https://example.com";

Expand Down
4 changes: 2 additions & 2 deletions DragonFruit.Data.Tests/Requests/SpecialTypeRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@

namespace DragonFruit.Data.Tests.Requests
{
public partial class SpecialTypeRequest : ApiRequest

Check warning on line 9 in DragonFruit.Data.Tests/Requests/SpecialTypeRequest.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data.Tests/Requests/SpecialTypeRequest.cs#L9 <PartialTypeWithSinglePart>(https://www.jetbrains.com/help/resharper/PartialTypeWithSinglePart.html)

Partial class with single part
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":16,"uri":"DragonFruit.Data.Tests/Requests/SpecialTypeRequest.cs","uriBaseId":"solutionDir"},"region":{"charLength":7,"charOffset":282,"endColumn":19,"endLine":9,"startColumn":12,"startLine":9}}}],"message":{"text":"Partial class with single part"},"partialFingerprints":{"contextRegionHash/v1":"A4B2B271E767C1808657A434B96629078C56B454018BD922605218A0D36C5123"},"properties":{"tags":["C#",".NET 10.0"]},"ruleId":"PartialTypeWithSinglePart","ruleIndex":10}
{
public override string RequestPath => "https://postman-echo.com/get";

[RequestParameter(ParameterType.Query, "users")]
[EnumerableOptions(EnumerableOption.Concatenated, ":")]
public IReadOnlyCollection<string> Usernames => new[] { "test", "test_1a" };
public IReadOnlyCollection<string> Usernames => ["test", "test_1a"];

[RequestParameter(ParameterType.Query, "ids")]
[EnumerableOptions(EnumerableOption.Concatenated)]
public IEnumerable<int> UserIds => new[] { 1, 2 };
public IEnumerable<int> UserIds => [1, 2];
}
}
27 changes: 9 additions & 18 deletions DragonFruit.Data/ApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,10 @@
/// <summary>
/// The <see cref="ApiClient"/> responsible for building, submitting and processing HTTP requests
/// </summary>
public class ApiClient
public class ApiClient(ApiSerializer serializer)
{
private HttpClient _client;
private Uri _baseAddress;

public ApiClient(ApiSerializer serializer)
{
Serializers = new SerializerResolver(serializer);
}
private HttpClient? _client;
private Uri? _baseAddress;

public ApiClient(ApiSerializer serializer, Uri baseAddress)
: this(serializer)
Expand Down Expand Up @@ -90,30 +85,26 @@
/// <summary>
/// Gets or sets the <see cref="Uri"/> that should act as the base address for relative-URI requests
/// </summary>
public Uri BaseAddress
public Uri? BaseAddress
{
get => _client?.BaseAddress ?? _baseAddress;
set
{
_baseAddress = value;

if (_client != null)
{
_client.BaseAddress = value;
}
_client?.BaseAddress = value;
}
}

/// <summary>
/// The <see cref="SerializerResolver"/> instance used to resolve serializers for requests.
/// Caches and reused serializers where possible.
/// </summary>
public SerializerResolver Serializers { get; }
public SerializerResolver Serializers { get; } = new(serializer);

/// <summary>
/// User-Controlled method to create a <see cref="HttpMessageHandler"/>
/// </summary>
public Func<HttpMessageHandler> Handler { get; set; }
public Func<HttpMessageHandler>? Handler { get; set; }

/// <summary>
/// Gets the header container for the underlying <see cref="HttpClient"/>
Expand Down Expand Up @@ -209,7 +200,7 @@
/// <param name="truncate">(Optional) whether to truncate the destination stream, if content is written. Defaults to true</param>
/// <param name="safe">(Optional) whether to copy to a temporary buffer before writing to destination. When enabled provides greater redundancy from network failure. Defaults to false</param>
/// <param name="cancellationToken">(Optional) cancellation request</param>
public async Task<HttpStatusCode> PerformDownload(ApiRequest request, Stream destination, IProgress<(long, long?)> progress = null, bool truncate = true, bool safe = false, CancellationToken cancellationToken = default)
public async Task<HttpStatusCode> PerformDownload(ApiRequest request, Stream destination, IProgress<(long, long?)>? progress = null, bool truncate = true, bool safe = false, CancellationToken cancellationToken = default)
{
using var requestMessage = await BuildRequest(request, "*/*").ConfigureAwait(false);
return await PerformDownload(requestMessage, destination, progress, truncate, safe, cancellationToken).ConfigureAwait(false);
Expand All @@ -227,7 +218,7 @@
/// <param name="truncate">(Optional) whether to truncate the destination stream, if content is written. Defaults to true</param>
/// <param name="safe">(Optional) whether to copy to a temporary buffer before writing to destination. When enabled provides greater redundancy from network failure. Defaults to false</param>
/// <param name="cancellationToken">(Optional) cancellation request</param>
public async Task<HttpStatusCode> PerformDownload(HttpRequestMessage request, Stream destination, IProgress<(long, long?)> progress = null, bool truncate = true, bool safe = false, CancellationToken cancellationToken = default)
public async Task<HttpStatusCode> PerformDownload(HttpRequestMessage request, Stream destination, IProgress<(long, long?)>? progress = null, bool truncate = true, bool safe = false, CancellationToken cancellationToken = default)
{
using var responseMessage = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);

Expand Down Expand Up @@ -400,16 +391,16 @@
{
switch (request)
{
case IRequestExecutingCallback callback:

Check warning on line 394 in DragonFruit.Data/ApiClient.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data/ApiClient.cs#L394 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious type check: there is no type in the solution that is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestExecutingCallback'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":20,"uri":"DragonFruit.Data/ApiClient.cs","uriBaseId":"solutionDir"},"region":{"charLength":30,"charOffset":17368,"endColumn":47,"endLine":394,"startColumn":17,"startLine":394}}}],"message":{"text":"Suspicious type check: there is no type in the solution that is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestExecutingCallback'"},"partialFingerprints":{"contextRegionHash/v1":"DDEC6BE26CC4E77CC67C195F248BEF0A14C2D603077C4215C7D086D05EC2226C"},"properties":{"tags":["C#",".NETStandard 2.0",".NET 10.0",".NET 6.0",".NET 8.0",".NET 9.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
callback.OnRequestExecuting(this);
break;

case IAsyncRequestExecutingCallback asyncCallback:

Check warning on line 398 in DragonFruit.Data/ApiClient.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data/ApiClient.cs#L398 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious type check: there is no type in the solution that is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IAsyncRequestExecutingCallback'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":20,"uri":"DragonFruit.Data/ApiClient.cs","uriBaseId":"solutionDir"},"region":{"charLength":35,"charOffset":17508,"endColumn":52,"endLine":398,"startColumn":17,"startLine":398}}}],"message":{"text":"Suspicious type check: there is no type in the solution that is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IAsyncRequestExecutingCallback'"},"partialFingerprints":{"contextRegionHash/v1":"3E35D8CF0149BF9656DC83149D748DBA050B390AAF34A826281388B91E897841"},"properties":{"tags":["C#",".NETStandard 2.0",".NET 10.0",".NET 6.0",".NET 8.0",".NET 9.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
await asyncCallback.OnRequestExecuting(this);
break;
}

var requestMessage = (request as IRequestBuilder)?.BuildRequest(Serializers) ?? ReflectionRequestMessageBuilder.CreateHttpRequestMessage(request, Serializers);

Check warning on line 403 in DragonFruit.Data/ApiClient.cs

View workflow job for this annotation

GitHub Actions / sarif

[InspectCode] DragonFruit.Data/ApiClient.cs#L403 <SuspiciousTypeConversion.Global>(https://www.jetbrains.com/help/resharper/SuspiciousTypeConversion.Global.html)

Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestBuilder?'
Raw output
{"level":"warning","locations":[{"physicalLocation":{"artifactLocation":{"index":20,"uri":"DragonFruit.Data/ApiClient.cs","uriBaseId":"solutionDir"},"region":{"charLength":18,"charOffset":17709,"endColumn":61,"endLine":403,"startColumn":43,"startLine":403}}}],"message":{"text":"Suspicious cast: there is no type in the solution which is inherited from both 'DragonFruit.Data.ApiRequest' and 'DragonFruit.Data.Requests.IRequestBuilder?'"},"partialFingerprints":{"contextRegionHash/v1":"97CB7E914DC02F05C2DFE5739307DE483491E5389900B897EC81264550FCE2F5"},"properties":{"tags":["C#",".NETStandard 2.0",".NET 10.0",".NET 6.0",".NET 8.0",".NET 9.0"]},"ruleId":"SuspiciousTypeConversion.Global","ruleIndex":13}
requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue(expectedContentType));

return requestMessage;
Expand Down
6 changes: 2 additions & 4 deletions DragonFruit.Data/ApiSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ namespace DragonFruit.Data
{
public abstract class ApiSerializer
{
private Encoding _encoding;

/// <summary>
/// The Content-Type/Accept header value
/// </summary>
Expand All @@ -30,8 +28,8 @@ public abstract class ApiSerializer
/// </summary>
public virtual Encoding Encoding
{
get => _encoding ??= new UTF8Encoding(false);
set => _encoding = value;
get => field ??= new UTF8Encoding(false);
set;
}

/// <summary>
Expand Down
Loading
Loading