diff --git a/.github/workflows/linux-macOS-CI.yml b/.github/workflows/linux-macOS-CI.yml index 6dbd7d80..b7babcfb 100644 --- a/.github/workflows/linux-macOS-CI.yml +++ b/.github/workflows/linux-macOS-CI.yml @@ -27,10 +27,12 @@ jobs: 3.1.x 5.0.x 6.0.x + 8.0.x - name: Restore run: dotnet restore src - name: Test run: | dotnet test src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj -c Release -f netcoreapp3.1 --no-restore dotnet test src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj -c Release -f net5.0 --no-restore - dotnet test src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj -c Release -f net6.0 --no-restore \ No newline at end of file + dotnet test src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj -c Release -f net6.0 --no-restore + dotnet test src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj -c Release -f net8.0 --no-restore \ No newline at end of file diff --git a/.github/workflows/sonar-analysis.yml b/.github/workflows/sonar-analysis.yml index 5b9f2387..730614cf 100644 --- a/.github/workflows/sonar-analysis.yml +++ b/.github/workflows/sonar-analysis.yml @@ -18,9 +18,10 @@ jobs: runs-on: windows-2022 steps: - name: Set up JDK 11 - uses: actions/setup-java@v1 + uses: actions/setup-java@v3 with: - java-version: 1.11 + java-version: 17 + distribution: zulu - uses: actions/checkout@v2 with: fetch-depth: 0 @@ -51,4 +52,4 @@ jobs: run: | .\.sonar\scanner\dotnet-sonarscanner begin /k:"net-sdk" /d:sonar.host.url="https://sonarcloud.io" /o:"configcat" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.cs.opencover.reportsPaths="**/TestResults/**/coverage.opencover.xml" -d:sonar.cs.vstest.reportsPaths="**/TestResults/*.trx" /v:"${{ github.run_number }}" /d:sonar.exclusions="ConfigCatClient/Versioning/*" /d:sonar.coverage.exclusions="ConfigCatClient/Versioning/*" dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -c Release --logger trx --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=opencover - .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}" diff --git a/appveyor.yml b/appveyor.yml index ecf0a59c..84b48a45 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,5 @@ environment: - build_version: 8.2.0 + build_version: 9.0.0 version: $(build_version)-{build} image: Visual Studio 2022 configuration: Release @@ -16,11 +16,14 @@ dotnet_csproj: file_version: $(build_version) informational_version: $(build_version) install: -- cmd: dotnet tool install -g InheritDocTool -build_script: +- ps: | + dotnet tool install -g InheritDocTool + Invoke-WebRequest 'https://dot.net/v1/dotnet-install.ps1' -OutFile dotnet-install.ps1 + ./dotnet-install.ps1 -Channel 8.0 +build_script: - cmd: echo __BUILD__ - dotnet restore src/ConfigCatClient.sln -- dotnet build -c %configuration% /p:ContinuousIntegrationBuild=true src/ConfigCatClient.sln +- dotnet build -c %configuration% /p:ContinuousIntegrationBuild=true src/ConfigCatClient.sln after_build: - cmd: echo __PACK__ - inheritdoc -o @@ -31,9 +34,10 @@ test_script: - dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f netcoreapp3.1 -c %configuration% --no-build - dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net5.0 -c %configuration% --no-build - dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net6.0 -c %configuration% --no-build +- dotnet test src\ConfigCat.Client.Tests\ConfigCat.Client.Tests.csproj -f net8.0 -c %configuration% --no-build artifacts: - path: artifacts\ConfigCat.Client.*.*nupkg - name: NuGet + name: NuGet notifications: - provider: Email to: diff --git a/samples/ASP.NETCore/WebApplication/Controllers/HomeController.cs b/samples/ASP.NETCore/WebApplication/Controllers/HomeController.cs index e3ebccae..2aee4bf6 100644 --- a/samples/ASP.NETCore/WebApplication/Controllers/HomeController.cs +++ b/samples/ASP.NETCore/WebApplication/Controllers/HomeController.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using ConfigCat.Client; using Microsoft.AspNetCore.Mvc; @@ -23,7 +23,7 @@ public IActionResult Index() { Email = "configcat@example.com", Country = "Canada", - Custom = new Dictionary + Custom = { {"SubscriptionType", "Pro"}, {"Version", "1.0.0"} diff --git a/samples/ASP.NETCore/WebApplication/WebApplication.csproj b/samples/ASP.NETCore/WebApplication/WebApplication.csproj index a08f56de..1eaeb5ac 100644 --- a/samples/ASP.NETCore/WebApplication/WebApplication.csproj +++ b/samples/ASP.NETCore/WebApplication/WebApplication.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/ConsoleApp/ConsoleApp.csproj b/samples/ConsoleApp/ConsoleApp.csproj index f5928dab..9213c559 100644 --- a/samples/ConsoleApp/ConsoleApp.csproj +++ b/samples/ConsoleApp/ConsoleApp.csproj @@ -9,7 +9,7 @@ - + diff --git a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj index 784def52..c3933b03 100644 --- a/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj +++ b/src/ConfigCat.Client.Tests/ConfigCat.Client.Tests.csproj @@ -1,7 +1,7 @@ - net45;net461;netcoreapp3.1;net5.0;net6.0 + net45;net461;netcoreapp3.1;net5.0;net6.0;net8.0 false ConfigCatClientTests true diff --git a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs index 1ed18ede..a2b5a4ba 100644 --- a/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs +++ b/src/ConfigCat.Client.Tests/Helpers/LoggingHelper.cs @@ -1,6 +1,6 @@ -using Moq; -using System.Collections.Generic; using System; +using System.Collections.Generic; +using Moq; namespace ConfigCat.Client; diff --git a/src/ConfigCat.Client.Tests/UserTests.cs b/src/ConfigCat.Client.Tests/UserTests.cs index c0b9f222..dbae79db 100644 --- a/src/ConfigCat.Client.Tests/UserTests.cs +++ b/src/ConfigCat.Client.Tests/UserTests.cs @@ -1,5 +1,3 @@ -using System; -using System.Globalization; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace ConfigCat.Client.Tests; diff --git a/src/ConfigCatClient/ConfigCatClient.cs b/src/ConfigCatClient/ConfigCatClient.cs index 876389ed..0974ebc0 100644 --- a/src/ConfigCatClient/ConfigCatClient.cs +++ b/src/ConfigCatClient/ConfigCatClient.cs @@ -192,26 +192,22 @@ private void Dispose(bool disposing) if (disposing) { - if (this.configService is IDisposable disposable) - { - disposable.Dispose(); - } - - this.overrideDataSource?.Dispose(); + (this.configService as IDisposable)?.Dispose(); + (this.overrideDataSource as IDisposable)?.Dispose(); } else { // Execution gets here when consumer forgets to dispose the client instance. // In this case we need to make sure that background work is stopped, // otherwise it would go on endlessly, that is, we'd end up with a memory leak. - var autoPollConfigService = this.configService as AutoPollConfigService; - var localFileDataSource = this.overrideDataSource as LocalFileDataSource; - if (autoPollConfigService is not null || localFileDataSource is not null) + var configService = this.configService as IDisposable; + var localFileDataSource = this.overrideDataSource as IDisposable; + if (configService is not null || localFileDataSource is not null) { Task.Run(() => { - autoPollConfigService?.StopScheduler(); - localFileDataSource?.StopWatch(); + configService?.Dispose(); + localFileDataSource?.Dispose(); }); } } diff --git a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs index 584cad75..a39d2880 100644 --- a/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs +++ b/src/ConfigCatClient/Evaluation/EvaluateLogHelper.cs @@ -1,5 +1,5 @@ -using ConfigCat.Client.Utils; using System.Globalization; +using ConfigCat.Client.Utils; namespace ConfigCat.Client.Evaluation; diff --git a/src/ConfigCatClient/Evaluation/EvaluationDetails.cs b/src/ConfigCatClient/Evaluation/EvaluationDetails.cs index 5cee4682..2349e348 100644 --- a/src/ConfigCatClient/Evaluation/EvaluationDetails.cs +++ b/src/ConfigCatClient/Evaluation/EvaluationDetails.cs @@ -6,7 +6,7 @@ namespace ConfigCat.Client; /// /// The evaluated value and additional information about the evaluation of a feature flag or setting. /// -public abstract record class EvaluationDetails +public abstract class EvaluationDetails { internal static EvaluationDetails FromEvaluateResult(string key, TValue value, in EvaluateResult evaluateResult, DateTime? fetchTime, User? user) @@ -106,7 +106,7 @@ private protected EvaluationDetails(string key) } /// -public sealed record class EvaluationDetails : EvaluationDetails +public sealed class EvaluationDetails : EvaluationDetails { /// /// Initializes a new instance of the class. diff --git a/src/ConfigCatClient/Extensions/ObjectExtensions.cs b/src/ConfigCatClient/Extensions/ObjectExtensions.cs index 00606d83..49800c2c 100644 --- a/src/ConfigCatClient/Extensions/ObjectExtensions.cs +++ b/src/ConfigCatClient/Extensions/ObjectExtensions.cs @@ -1,6 +1,5 @@ using System.Diagnostics; using System.Globalization; -using System.Runtime.CompilerServices; using ConfigCat.Client; #if USE_NEWTONSOFT_JSON diff --git a/src/ConfigCatClient/Extensions/SerializationExtensions.cs b/src/ConfigCatClient/Extensions/SerializationExtensions.cs index 684b3818..2f3d3e86 100644 --- a/src/ConfigCatClient/Extensions/SerializationExtensions.cs +++ b/src/ConfigCatClient/Extensions/SerializationExtensions.cs @@ -20,22 +20,24 @@ internal static class SerializationExtensions }; #endif - public static T? Deserialize(this string json) => json.AsSpan().Deserialize(); + public static T? Deserialize(this string json) => json.AsMemory().Deserialize(); - public static T? Deserialize(this ReadOnlySpan json) + // NOTE: It would be better to use ReadOnlySpan, however when the full string is wrapped in a span, json.ToString() result in a copy of the string. + // This is not the case with ReadOnlyMemory, so we use that until support for .NET 4.5 support is dropped. + public static T? Deserialize(this ReadOnlyMemory json) { #if USE_NEWTONSOFT_JSON using var stringReader = new StringReader(json.ToString()); using var reader = new JsonTextReader(stringReader); return Serializer.Deserialize(reader); #else - return JsonSerializer.Deserialize(json); + return JsonSerializer.Deserialize(json.Span); #endif } - public static T? DeserializeOrDefault(this string json) => json.AsSpan().DeserializeOrDefault(); + public static T? DeserializeOrDefault(this string json) => json.AsMemory().DeserializeOrDefault(); - public static T? DeserializeOrDefault(this ReadOnlySpan json) + public static T? DeserializeOrDefault(this ReadOnlyMemory json) { try { diff --git a/src/ConfigCatClient/Extensions/TaskExtensions.cs b/src/ConfigCatClient/Extensions/TaskExtensions.cs index b28fd899..bc9adb85 100644 --- a/src/ConfigCatClient/Extensions/TaskExtensions.cs +++ b/src/ConfigCatClient/Extensions/TaskExtensions.cs @@ -61,7 +61,16 @@ static async Task Awaited(Task task, CancellationToken cancellationToken) using (tokenRegistration) { var completedTask = await Task.WhenAny(task, cancellationTask).ConfigureAwait(false); - return completedTask is Task taskWithResult ? taskWithResult.GetAwaiter().GetResult() : default!; + if (completedTask is Task taskWithResult) + { + return taskWithResult.GetAwaiter().GetResult(); + } + else + { + // Although the task has no return value, the potential cancellation or exception still needs to be propagated. + completedTask.GetAwaiter().GetResult(); + return default!; + } } } } diff --git a/src/ConfigCatClient/FetchResult.cs b/src/ConfigCatClient/FetchResult.cs index 6bdb73a4..4c92aff3 100644 --- a/src/ConfigCatClient/FetchResult.cs +++ b/src/ConfigCatClient/FetchResult.cs @@ -3,7 +3,7 @@ namespace ConfigCat.Client; -internal readonly record struct FetchResult +internal readonly struct FetchResult { private static readonly object NotModifiedToken = new(); diff --git a/src/ConfigCatClient/Override/IOverrideDataSource.cs b/src/ConfigCatClient/Override/IOverrideDataSource.cs index 31ea4f0e..d5a6a196 100644 --- a/src/ConfigCatClient/Override/IOverrideDataSource.cs +++ b/src/ConfigCatClient/Override/IOverrideDataSource.cs @@ -1,11 +1,10 @@ -using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; namespace ConfigCat.Client.Override; -internal interface IOverrideDataSource : IDisposable +internal interface IOverrideDataSource { Dictionary GetOverrides(); diff --git a/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs b/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs index 55af9791..fcdfe511 100644 --- a/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs +++ b/src/ConfigCatClient/Override/LocalDictionaryDataSource.cs @@ -20,8 +20,6 @@ public LocalDictionaryDataSource(IDictionary overrideValues, boo } } - public void Dispose() { /* no need to dispose anything */ } - public Dictionary GetOverrides() => GetSettingsFromSource(); public Task> GetOverridesAsync(CancellationToken cancellationToken = default) => Task.FromResult(GetSettingsFromSource()); diff --git a/src/ConfigCatClient/Override/LocalFileDataSource.cs b/src/ConfigCatClient/Override/LocalFileDataSource.cs index 67579fd1..33f3bb6e 100644 --- a/src/ConfigCatClient/Override/LocalFileDataSource.cs +++ b/src/ConfigCatClient/Override/LocalFileDataSource.cs @@ -8,7 +8,7 @@ namespace ConfigCat.Client.Override; -internal sealed class LocalFileDataSource : IOverrideDataSource +internal sealed class LocalFileDataSource : IOverrideDataSource, IDisposable { private const int WAIT_TIME_FOR_UNLOCK = 200; // ms private const int MAX_WAIT_ITERATIONS = 50; // ms diff --git a/src/ConfigCatClient/ProjectConfig.cs b/src/ConfigCatClient/ProjectConfig.cs index 87b22cf8..fb93389c 100644 --- a/src/ConfigCatClient/ProjectConfig.cs +++ b/src/ConfigCatClient/ProjectConfig.cs @@ -81,7 +81,7 @@ public static ProjectConfig Deserialize(string value) var httpETagSpan = value.AsSpan(index, endIndex - index); index = endIndex + 1; - var configJsonSpan = value.AsSpan(index); + var configJsonSpan = value.AsMemory(index); Config? config; string? configJson; diff --git a/src/ConfigCatClient/RefreshResult.cs b/src/ConfigCatClient/RefreshResult.cs index b400a4ca..4d0bcd73 100644 --- a/src/ConfigCatClient/RefreshResult.cs +++ b/src/ConfigCatClient/RefreshResult.cs @@ -6,7 +6,7 @@ namespace ConfigCat.Client; /// /// Contains the result of an or operation. /// -public readonly record struct RefreshResult +public readonly struct RefreshResult { /// /// Creates an instance of the struct which indicates that the operation was successful. diff --git a/src/ConfigCatClient/User.cs b/src/ConfigCatClient/User.cs index 2291ba2c..f49ab126 100644 --- a/src/ConfigCatClient/User.cs +++ b/src/ConfigCatClient/User.cs @@ -43,7 +43,7 @@ public class User /// /// The set of allowed attribute values depends on the comparison type of the condition which references the User Object attribute.
/// values are supported by all comparison types (in some cases they need to be provided in a specific format though).
- /// Some of the comparison types work with other types of values, as descibed below. + /// Some of the comparison types work with other types of values, as described below. /// /// Text-based comparisons (EQUALS, IS ONE OF, etc.)
/// * accept values,