diff --git a/Core/Attributes/MapToAttribute.cs b/Core/Attributes/MapToAttribute.cs index 3877043..3a6cbec 100644 --- a/Core/Attributes/MapToAttribute.cs +++ b/Core/Attributes/MapToAttribute.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; namespace Supabase.Core.Attributes { @@ -14,7 +12,7 @@ public class MapToAttribute : Attribute /// The externally specified target value. /// public string Mapping { get; set; } - + /// /// A formatter to be passed into the method. /// @@ -29,8 +27,8 @@ public class MapToAttribute : Attribute /// public MapToAttribute(string mapping, string? formatter = null) { - Mapping = mapping; - Formatter = formatter; + this.Mapping = mapping; + this.Formatter = formatter; } } } diff --git a/Core/Diagnostics/ActivityExtensions.cs b/Core/Diagnostics/ActivityExtensions.cs index 93bfebc..e8f9ec6 100644 --- a/Core/Diagnostics/ActivityExtensions.cs +++ b/Core/Diagnostics/ActivityExtensions.cs @@ -14,60 +14,67 @@ namespace Supabase.Core.Diagnostics /// public static class ActivityExtensions { - /// - /// Tags an outgoing HTTP request following OpenTelemetry HTTP client conventions. - /// The URL is sanitized to scheme/host/port/path; the query string is never recorded. - /// /// The activity to tag, or null when nothing is listening. - /// The HTTP method, e.g. POST. - /// The request URI, sanitized before tagging. - public static Activity? SetHttpRequestTags(this Activity? activity, string method, Uri uri) + extension(Activity? activity) { - if (activity == null) - return null; + /// + /// Tags an outgoing HTTP request following OpenTelemetry HTTP client conventions. + /// The URL is sanitized to scheme/host/port/path; the query string is never recorded. + /// + /// The HTTP method, e.g. POST. + /// The request URI, sanitized before tagging. + public Activity? SetHttpRequestTags(string method, Uri uri) + { + if (activity == null) + return null; - activity.SetTag("http.request.method", method); - activity.SetTag("server.address", uri.Host); - if (!uri.IsDefaultPort) - activity.SetTag("server.port", uri.Port); + activity.SetTag("http.request.method", method); + activity.SetTag("server.address", uri.Host); + if (!uri.IsDefaultPort) + activity.SetTag("server.port", uri.Port); - activity.SetTag("url.full", UrlSanitizer.Sanitize(uri)); - return activity; - } + activity.SetTag("url.full", UrlSanitizer.Sanitize(uri)); + return activity; + } - /// - /// Tags the response status code, marking the activity as failed for 4xx/5xx responses. - /// - /// The activity to tag, or null when nothing is listening. - /// The HTTP response status code. - public static Activity? SetHttpResponseTags(this Activity? activity, int statusCode) - { - if (activity == null) - return null; + /// + /// Tags the response status code, marking the activity as failed for 4xx/5xx responses. + /// + /// The HTTP response status code. + public Activity? SetHttpResponseTags(int statusCode) + { + if (activity == null) + return null; + + activity.SetTag("http.response.status_code", statusCode); + return statusCode >= 400 ? activity.SetErrorTags( statusCode) : activity; + } - activity.SetTag("http.response.status_code", statusCode); - if (statusCode >= 400) + /// + /// Marks the activity as failed with the exception type as error.type. + /// + /// The exception whose type and message describe the failure. + public Activity? SetFailure(Exception exception) { - activity.SetTag("error.type", statusCode.ToString()); - activity.SetStatus(ActivityStatusCode.Error); + if (activity == null) + return null; + + activity.SetTag("error.type", exception.GetType().FullName); + activity.SetStatus(ActivityStatusCode.Error, exception.Message); + return activity; } - return activity; - } - /// - /// Marks the activity as failed with the exception type as error.type. - /// - /// The activity to tag, or null when nothing is listening. - /// The exception whose type and message describe the failure. - public static Activity? SetFailure(this Activity? activity, Exception exception) - { - if (activity == null) - return null; + private Activity? SetErrorTags( int statusCode) + { + if (activity == null) + return null; - activity.SetTag("error.type", exception.GetType().FullName); - activity.SetStatus(ActivityStatusCode.Error, exception.Message); - return activity; + activity.SetTag("error.type", statusCode.ToString()); + activity.SetStatus(ActivityStatusCode.Error); + return activity; + } } + } } diff --git a/Core/Diagnostics/Instrumentation.cs b/Core/Diagnostics/Instrumentation.cs index ec81799..d2d8efb 100644 --- a/Core/Diagnostics/Instrumentation.cs +++ b/Core/Diagnostics/Instrumentation.cs @@ -1,4 +1,3 @@ -using System; using System.Diagnostics; using System.Diagnostics.Metrics; using System.Reflection; @@ -21,8 +20,7 @@ public static class Instrumentation /// /// The assembly whose version identifies the emitting library. /// The source name, e.g. Supabase.Gotrue. - public static ActivitySource CreateActivitySource(Assembly assembly, string name) => - new ActivitySource(name, GetVersion(assembly)); + public static ActivitySource CreateActivitySource(Assembly assembly, string name) => new(name, GetVersion(assembly)); /// /// Creates the for a Supabase client library, versioned from the @@ -30,8 +28,7 @@ public static ActivitySource CreateActivitySource(Assembly assembly, string name /// /// The assembly whose version identifies the emitting library. /// The meter name, e.g. Supabase.Gotrue. - public static Meter CreateMeter(Assembly assembly, string name) => - new Meter(name, GetVersion(assembly)); + public static Meter CreateMeter(Assembly assembly, string name) => new(name, GetVersion(assembly)); /// /// Resolves the version of an assembly, preferring the informational (package) version diff --git a/Core/Diagnostics/UrlSanitizer.cs b/Core/Diagnostics/UrlSanitizer.cs index 75ef9e8..83b58c6 100644 --- a/Core/Diagnostics/UrlSanitizer.cs +++ b/Core/Diagnostics/UrlSanitizer.cs @@ -42,7 +42,7 @@ public static string Sanitize(string url) => private static string StripAfterPath(string url) { - var delimiterIndex = url.IndexOfAny(new[] { '?', '#' }); + var delimiterIndex = url.IndexOfAny(['?', '#']); return delimiterIndex < 0 ? url : url.Substring(0, delimiterIndex); } } diff --git a/Core/Extensions/DictionaryExtensions.cs b/Core/Extensions/DictionaryExtensions.cs index c3bf09e..a9a07ff 100644 --- a/Core/Extensions/DictionaryExtensions.cs +++ b/Core/Extensions/DictionaryExtensions.cs @@ -1,7 +1,5 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; namespace Supabase.Core.Extensions { @@ -12,32 +10,26 @@ public static class DictionaryExtensions { /// /// Merges two dictionaries, allowing overwrite priorities leftward. - /// + /// /// Works in C#3/VS2008: /// Returns a new dictionary of this ... others merged leftward. /// Keeps the type of 'this', which must be default-instantiable. - /// Example: + /// Example: /// result = map.MergeLeft(other1, other2, ...) /// From: https://stackoverflow.com/a/2679857/3629438 /// /// /// /// - /// - /// + /// + /// /// - public static T MergeLeft(this T me, params IDictionary[] others) - where T : IDictionary, new() - { - T newMap = new T(); - foreach (IDictionary src in (new List> { me }).Concat(others)) + public static T MergeLeft(this T me, params IDictionary[] others) + where T : IDictionary, new() => + others.Prepend(me).SelectMany(pairs => pairs).Aggregate(new T(), (newMap, pair) => { - foreach (KeyValuePair p in src) - { - newMap[p.Key] = p.Value; - } - } - return newMap; - } + newMap[pair.Key] = pair.Value; + return newMap; + }); } } diff --git a/Core/Helpers.cs b/Core/Helpers.cs index 54ba0f2..d4acee2 100644 --- a/Core/Helpers.cs +++ b/Core/Helpers.cs @@ -1,8 +1,6 @@ using Supabase.Core.Attributes; using System; -using System.Collections.Generic; using System.Linq; -using System.Text; namespace Supabase.Core { @@ -19,7 +17,7 @@ public static class Helpers /// /// public static T GetPropertyValue(object obj, string propName) => (T)obj.GetType().GetProperty(propName).GetValue(obj, null); - + /// /// Returns a cast Custom Attribute from a given object. /// @@ -27,7 +25,7 @@ public static class Helpers /// /// public static T GetCustomAttribute(object obj) where T : Attribute => (T)Attribute.GetCustomAttribute(obj.GetType(), typeof(T)); - + /// /// Returns a cast Custom Attribute from a given type. /// @@ -45,7 +43,6 @@ public static MapToAttribute GetMappedToAttr(Enum obj) { var type = obj.GetType(); var name = Enum.GetName(type, obj); - return type.GetField(name).GetCustomAttributes(false).OfType().SingleOrDefault(); } } diff --git a/Core/Interfaces/IGettableHeaders.cs b/Core/Interfaces/IGettableHeaders.cs index 4c913c6..b601e09 100644 --- a/Core/Interfaces/IGettableHeaders.cs +++ b/Core/Interfaces/IGettableHeaders.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Text; namespace Supabase.Core.Interfaces { diff --git a/Core/Util.cs b/Core/Util.cs index 3ad8f02..0ba9e0c 100644 --- a/Core/Util.cs +++ b/Core/Util.cs @@ -26,7 +26,7 @@ public static string GetAssemblyVersion(Type clientType) => // Testability seam: the platform and framework probes read process-wide ambient state // (RuntimeInformation, the loaded assembly set) that a host cannot vary at runtime. This - // overload takes those reads as parameters so every branch is reachable hermetically; the + // overload takes those reads as parameters, so every branch is reachable hermetically; the // public entry point above supplies the real values. Internal — not part of the public API. internal static string GetAssemblyVersion(Type clientType, string osDescription, Func isOsPlatform, IReadOnlyCollection loadedAssemblies) => $"{GetClientName(clientType)}-csharp/{GetClientVersion(clientType)}{BuildMetadata(osDescription, isOsPlatform, loadedAssemblies)}"; @@ -51,11 +51,11 @@ internal MetadataEntry(string key, string value, string? version = null) this.version = version; } - public override string ToString() => string.IsNullOrEmpty(version) - ? $"; {key}={value}" - : $"; {key}={value}; {key}-version={version}"; - - internal static MetadataEntry Unknown(string key) => new MetadataEntry(key, "unknown"); + public override string ToString() => string.IsNullOrEmpty(this.version) + ? $"; {this.key}={this.value}" + : $"; {this.key}={this.value}; {this.key}-version={this.version}"; + + internal static MetadataEntry Unknown(string key) => new(key, "unknown"); } private static string GetPlatform(string osDescription, Func isOsPlatform) @@ -69,9 +69,9 @@ private static string GetPlatform(string osDescription, Func i return osDescription; } - private static MetadataEntry GetPlatformInfo(string osDescription, Func isOsPlatform) => new MetadataEntry("platform", GetPlatform(osDescription, isOsPlatform), Environment.OSVersion.Version.ToString()); + private static MetadataEntry GetPlatformInfo(string osDescription, Func isOsPlatform) => new("platform", GetPlatform(osDescription, isOsPlatform), Environment.OSVersion.Version.ToString()); - private static MetadataEntry GetRuntimeInfo() => new MetadataEntry("runtime", "dotnet", Environment.Version.ToString()); + private static MetadataEntry GetRuntimeInfo() => new("runtime", "dotnet", Environment.Version.ToString()); // Priority is explicit: MAUI wins over Blazor in hybrid apps where both assemblies are present. // Unity version uses GetCustomAttributesData() rather than member reflection — safe under IL2CPP. @@ -96,7 +96,7 @@ private static MetadataEntry GetFrameworkInfo(IReadOnlyCollection load private static CustomAttributeData[] SafeGetCustomAttributesData(Assembly assembly) { try { return assembly.GetCustomAttributesData().ToArray(); } - catch { return Array.Empty(); } + catch { return []; } } private static string? GetUnityVersion(IReadOnlyCollection loadedAssemblies) diff --git a/CoreTests/Diagnostics/ActivityExtensionsTests.cs b/CoreTests/Diagnostics/ActivityExtensionsTests.cs index 4541003..d1c4806 100644 --- a/CoreTests/Diagnostics/ActivityExtensionsTests.cs +++ b/CoreTests/Diagnostics/ActivityExtensionsTests.cs @@ -22,27 +22,27 @@ public class ActivityExtensionsTests public ActivityExtensionsTests() { - listener = new ActivityListener + this.listener = new ActivityListener { - ShouldListenTo = candidate => candidate == source, + ShouldListenTo = candidate => candidate == this.source, Sample = (ref ActivityCreationOptions _) => ActivitySamplingResult.AllDataAndRecorded }; - ActivitySource.AddActivityListener(listener); + ActivitySource.AddActivityListener(this.listener); } [TestCleanup] public void Cleanup() { - listener.Dispose(); - source.Dispose(); + this.listener.Dispose(); + this.source.Dispose(); } - private Activity StartActivity() => source.StartActivity("operation")!; + private Activity StartActivity() => this.source.StartActivity("operation")!; [TestMethod] public void SetHttpRequestTags_ShouldTagMethodHostAndSanitizedUrl() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpRequestTags("POST", new Uri("https://project.supabase.co/auth/v1/token?apikey=secret")); using (new AssertionScope()) { @@ -56,7 +56,7 @@ public void SetHttpRequestTags_ShouldTagMethodHostAndSanitizedUrl() [TestMethod] public void SetHttpRequestTags_ShouldTagPort_GivenNonDefaultPort() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpRequestTags("GET", new Uri("http://127.0.0.1:54321/auth/v1/token")); activity.GetTagItem("server.port").Should().Be(54321); } @@ -64,7 +64,7 @@ public void SetHttpRequestTags_ShouldTagPort_GivenNonDefaultPort() [TestMethod] public void SetHttpRequestTags_ShouldNotTagPort_GivenDefaultPort() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpRequestTags("GET", new Uri("https://project.supabase.co/auth/v1/token")); activity.GetTagItem("server.port").Should().BeNull(); } @@ -76,7 +76,7 @@ public void SetHttpRequestTags_ShouldReturnNull_GivenNullActivity() => [TestMethod] public void SetHttpResponseTags_ShouldTagStatusCode() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpResponseTags(200); using (new AssertionScope()) { @@ -88,7 +88,7 @@ public void SetHttpResponseTags_ShouldTagStatusCode() [TestMethod] public void SetHttpResponseTags_ShouldMarkError_GivenStatusAtErrorBoundary() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpResponseTags(400); using (new AssertionScope()) { @@ -100,7 +100,7 @@ public void SetHttpResponseTags_ShouldMarkError_GivenStatusAtErrorBoundary() [TestMethod] public void SetHttpResponseTags_ShouldNotMarkError_GivenLastSuccessStatus() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetHttpResponseTags(399); using (new AssertionScope()) { @@ -116,7 +116,7 @@ public void SetHttpResponseTags_ShouldReturnNull_GivenNullActivity() => [TestMethod] public void SetFailure_ShouldTagExceptionTypeAndErrorStatus() { - using var activity = StartActivity(); + using var activity = this.StartActivity(); activity.SetFailure(new InvalidOperationException("boom")); using (new AssertionScope()) { diff --git a/CoreTests/Extensions/DictionaryExtensionsTests.cs b/CoreTests/Extensions/DictionaryExtensionsTests.cs index 36537ea..85efd24 100644 --- a/CoreTests/Extensions/DictionaryExtensionsTests.cs +++ b/CoreTests/Extensions/DictionaryExtensionsTests.cs @@ -6,7 +6,7 @@ namespace CoreTests.Extensions; /// -/// Covers : a new dictionary combining every +/// Covers : a new dictionary combining every /// source, with later sources overwriting earlier keys, leaving the originals untouched. /// [TestClass] diff --git a/CoreTests/TestDoubles/UnityApiCompatibilityVersionAttribute.cs b/CoreTests/TestDoubles/UnityApiCompatibilityVersionAttribute.cs index 3eccdee..d98e676 100644 --- a/CoreTests/TestDoubles/UnityApiCompatibilityVersionAttribute.cs +++ b/CoreTests/TestDoubles/UnityApiCompatibilityVersionAttribute.cs @@ -12,7 +12,7 @@ namespace CoreTests.TestDoubles; [AttributeUsage(AttributeTargets.Assembly)] internal sealed class UnityAPICompatibilityVersionAttribute : Attribute { - public UnityAPICompatibilityVersionAttribute(string version) => Version = version; + public UnityAPICompatibilityVersionAttribute(string version) => this.Version = version; public string Version { get; } } diff --git a/CoreTests/UtilTests.cs b/CoreTests/UtilTests.cs index b992f51..d59c32b 100644 --- a/CoreTests/UtilTests.cs +++ b/CoreTests/UtilTests.cs @@ -21,7 +21,7 @@ namespace CoreTests; public class UtilTests { private static readonly Func NoPlatform = _ => false; - private static readonly IReadOnlyCollection NoFrameworks = Array.Empty(); + private static readonly IReadOnlyCollection NoFrameworks = []; [TestMethod] public void GetAssemblyVersion_ShouldStartWithClientNameAndVersion() => @@ -84,25 +84,25 @@ public void GetAssemblyVersion_ShouldReportUnknownFramework_GivenNoKnownFramewor [TestMethod] public void GetAssemblyVersion_ShouldReportMauiFramework_GivenMauiAssembly() => - HeaderWith(new[] { FakeAssembly.Named("Microsoft.Maui") }).Should().Contain("; framework=maui"); + HeaderWith([FakeAssembly.Named("Microsoft.Maui")]).Should().Contain("; framework=maui"); [TestMethod] public void GetAssemblyVersion_ShouldReportBlazorFramework_GivenBlazorAssembly() => - HeaderWith(new[] { FakeAssembly.Named("Microsoft.AspNetCore.Components") }).Should().Contain("; framework=blazor"); + HeaderWith([FakeAssembly.Named("Microsoft.AspNetCore.Components")]).Should().Contain("; framework=blazor"); [TestMethod] public void GetAssemblyVersion_ShouldPreferMauiOverBlazor_GivenBothAssemblies() => - HeaderWith(new[] { FakeAssembly.Named("Microsoft.AspNetCore.Components"), FakeAssembly.Named("Microsoft.Maui") }) + HeaderWith([FakeAssembly.Named("Microsoft.AspNetCore.Components"), FakeAssembly.Named("Microsoft.Maui")]) .Should().Contain("; framework=maui", "MAUI wins over Blazor in hybrid apps where both are present"); [TestMethod] public void GetAssemblyVersion_ShouldReportUnityFrameworkWithVersion_GivenUnityAssembly() => - HeaderWith(new[] { FakeAssembly.Named("UnityEngine.CoreModule"), typeof(UtilTests).Assembly }) + HeaderWith([FakeAssembly.Named("UnityEngine.CoreModule"), typeof(UtilTests).Assembly]) .Should().Contain("; framework=unity; framework-version=2022.3.5f1"); [TestMethod] public void GetAssemblyVersion_ShouldReportUnityFrameworkWithoutVersion_GivenNoUnityVersionAttribute() => - HeaderWith(new[] { FakeAssembly.Named("UnityEngine.CoreModule") }) + HeaderWith([FakeAssembly.Named("UnityEngine.CoreModule")]) .Should().Contain("; framework=unity").And.NotContain("framework-version", "an absent Unity version attribute must resolve to no version, not throw");