From 7713bc4c32f99db5353d54491bb74ca7722889ee Mon Sep 17 00:00:00 2001
From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 25 Sep 2023 14:57:48 +0300
Subject: [PATCH 1/2] Fix warnings and optimize constructor calls
---
src/Passwordless/IPasswordlessClient.cs | 6 +-
src/Passwordless/Models/RegisterOptions.cs | 4 +-
.../Models/RegisterTokenResponse.cs | 4 +-
src/Passwordless/Passwordless.csproj | 5 -
src/Passwordless/PasswordlessApiException.cs | 2 -
src/Passwordless/PasswordlessClient.cs | 108 ++++---
.../PasswordlessDelegatingHandler.cs | 1 -
.../PasswordlessHttpRequestExtensions.cs | 2 -
src/Passwordless/Pollyfill.cs | 269 +++++++++---------
.../ServiceCollectionExtensions.cs | 7 +-
.../PasswordlessClientTests.cs | 1 -
11 files changed, 211 insertions(+), 198 deletions(-)
diff --git a/src/Passwordless/IPasswordlessClient.cs b/src/Passwordless/IPasswordlessClient.cs
index 5e21326..4ea635a 100644
--- a/src/Passwordless/IPasswordlessClient.cs
+++ b/src/Passwordless/IPasswordlessClient.cs
@@ -12,7 +12,6 @@ public interface IPasswordlessClient
///
///
///
- ///
Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken = default);
///
@@ -43,6 +42,11 @@ public interface IPasswordlessClient
/// An exception containing details abaout the reason for failure.
Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default);
+ ///
+ /// Gets the users count.
+ ///
+ Task GetUsersCountAsync(CancellationToken cancellationToken = default);
+
///
/// List all the for a given user.
///
diff --git a/src/Passwordless/Models/RegisterOptions.cs b/src/Passwordless/Models/RegisterOptions.cs
index f7aecb1..06f1203 100644
--- a/src/Passwordless/Models/RegisterOptions.cs
+++ b/src/Passwordless/Models/RegisterOptions.cs
@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace Passwordless;
+namespace Passwordless;
///
///
diff --git a/src/Passwordless/Models/RegisterTokenResponse.cs b/src/Passwordless/Models/RegisterTokenResponse.cs
index 41c2e9a..19989a2 100644
--- a/src/Passwordless/Models/RegisterTokenResponse.cs
+++ b/src/Passwordless/Models/RegisterTokenResponse.cs
@@ -1,6 +1,4 @@
-using System.Text.Json.Serialization;
-
-namespace Passwordless.Models;
+namespace Passwordless.Models;
public class RegisterTokenResponse
{
diff --git a/src/Passwordless/Passwordless.csproj b/src/Passwordless/Passwordless.csproj
index f187606..24a033c 100644
--- a/src/Passwordless/Passwordless.csproj
+++ b/src/Passwordless/Passwordless.csproj
@@ -23,11 +23,6 @@
embedded
-
-
- $(DefineConstants);INCLUDE_DYNAMICALLY_ACCESSED_MEMBERS
-
-
diff --git a/src/Passwordless/PasswordlessApiException.cs b/src/Passwordless/PasswordlessApiException.cs
index 2fb6858..91c1294 100644
--- a/src/Passwordless/PasswordlessApiException.cs
+++ b/src/Passwordless/PasswordlessApiException.cs
@@ -1,5 +1,3 @@
-using System.Diagnostics;
-using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
diff --git a/src/Passwordless/PasswordlessClient.cs b/src/Passwordless/PasswordlessClient.cs
index d5c9b01..fea7a9b 100644
--- a/src/Passwordless/PasswordlessClient.cs
+++ b/src/Passwordless/PasswordlessClient.cs
@@ -7,140 +7,157 @@
namespace Passwordless;
-///
-/// TODO: FILL IN
-///
+///
[DebuggerDisplay("{DebuggerToString(),nq}")]
public class PasswordlessClient : IPasswordlessClient, IDisposable
{
- private readonly bool _needsDisposing;
private readonly HttpClient _client;
+ private readonly bool _disposeClient;
- public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory)
+ ///
+ /// Initializes an instance of using the provided
+ /// and .
+ ///
+ public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory) =>
+ new(factory.CreateClient(), options, false);
+
+ private PasswordlessClient(HttpClient client, bool disposeClient)
+ {
+ _client = client;
+ _disposeClient = disposeClient;
+ }
+
+ private PasswordlessClient(HttpClient client, PasswordlessOptions options, bool disposeClient)
+ : this(client, disposeClient)
{
- var client = factory.CreateClient();
client.BaseAddress = new Uri(options.ApiUrl);
client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret);
- return new PasswordlessClient(client);
}
- public PasswordlessClient(PasswordlessOptions passwordlessOptions)
+ ///
+ /// Initializes an instance of .
+ ///
+ public PasswordlessClient(HttpClient client)
+ : this(client, false)
{
- _needsDisposing = true;
- _client = new HttpClient
- {
- BaseAddress = new Uri(passwordlessOptions.ApiUrl),
- };
- _client.DefaultRequestHeaders.Add("ApiSecret", passwordlessOptions.ApiSecret);
}
- public PasswordlessClient(HttpClient client)
+ ///
+ /// Initializes an instance of .
+ ///
+ public PasswordlessClient(PasswordlessOptions options)
+ : this(new HttpClient(), options, true)
{
- _needsDisposing = false;
- _client = client;
}
- ///
+ ///
public async Task AddAliasAsync(AddAliasRequest request, CancellationToken cancellationToken)
{
- var res = await _client.PostAsJsonAsync("alias",
+ using var response = await _client.PostAsJsonAsync("alias",
request,
PasswordlessSerializerContext.Default.AddAliasRequest,
cancellationToken);
- res.EnsureSuccessStatusCode();
+
+ response.EnsureSuccessStatusCode();
}
- ///
+ ///
public async Task CreateRegisterTokenAsync(RegisterOptions registerOptions, CancellationToken cancellationToken = default)
{
- var res = await _client.PostAsJsonAsync("register/token",
+ using var response = await _client.PostAsJsonAsync("register/token",
registerOptions,
PasswordlessSerializerContext.Default.RegisterOptions,
cancellationToken);
- res.EnsureSuccessStatusCode();
- return (await res.Content.ReadFromJsonAsync(
+
+ response.EnsureSuccessStatusCode();
+
+ return (await response.Content.ReadFromJsonAsync(
PasswordlessSerializerContext.Default.RegisterTokenResponse,
cancellationToken))!;
}
- ///
+ ///
public async Task VerifyTokenAsync(string verifyToken, CancellationToken cancellationToken = default)
{
- var request = new HttpRequestMessage(HttpMethod.Post, "signin/verify")
- {
- // TODO: No JsonTypeInfo overload yet?
- Content = JsonContent.Create(new VerifyTokenRequest(verifyToken)),
- };
+ using var request = new HttpRequestMessage(HttpMethod.Post, "signin/verify");
+
+ // TODO: No JsonTypeInfo overload yet?
+ request.Content = JsonContent.Create(new VerifyTokenRequest(verifyToken));
// We just want to return null if there is a problem.
request.SkipErrorHandling();
- var response = await _client.SendAsync(request, cancellationToken);
+ using var response = await _client.SendAsync(request, cancellationToken);
if (response.IsSuccessStatusCode)
{
var res = await response.Content.ReadFromJsonAsync(
PasswordlessSerializerContext.Default.VerifiedUser,
cancellationToken);
+
return res;
}
return null;
}
- ///
+ ///
public async Task DeleteUserAsync(string userId, CancellationToken cancellationToken = default)
{
- await _client.PostAsJsonAsync("users/delete",
+ using var response = await _client.PostAsJsonAsync("users/delete",
new DeleteUserRequest(userId),
PasswordlessSerializerContext.Default.DeleteUserRequest,
cancellationToken);
}
- ///
+ ///
public async Task> ListUsersAsync(CancellationToken cancellationToken = default)
{
var response = await _client.GetFromJsonAsync(
"users/list",
PasswordlessSerializerContext.Default.ListResponsePasswordlessUserSummary,
cancellationToken);
+
return response!.Values;
}
- ///
+ ///
public async Task> ListAliasesAsync(string userId, CancellationToken cancellationToken = default)
{
var response = await _client.GetFromJsonAsync(
$"alias/list?userid={userId}",
PasswordlessSerializerContext.Default.ListResponseAliasPointer,
cancellationToken);
+
return response!.Values;
}
- ///
+ ///
public async Task> ListCredentialsAsync(string userId, CancellationToken cancellationToken = default)
{
var response = await _client.GetFromJsonAsync(
$"credentials/list?userid={userId}",
PasswordlessSerializerContext.Default.ListResponseCredential,
cancellationToken);
+
return response!.Values;
}
- ///
+ ///
public async Task DeleteCredentialAsync(string id, CancellationToken cancellationToken = default)
{
- await _client.PostAsJsonAsync("credentials/delete",
+ using var response = await _client.PostAsJsonAsync("credentials/delete",
new DeleteCredentialRequest(id),
PasswordlessSerializerContext.Default.DeleteCredentialRequest,
cancellationToken);
}
- ///
+ ///
public async Task DeleteCredentialAsync(byte[] id, CancellationToken cancellationToken = default)
{
await DeleteCredentialAsync(Base64Url.Encode(id), cancellationToken);
}
+ ///
public async Task GetUsersCountAsync(CancellationToken cancellationToken = default)
{
return (await _client.GetFromJsonAsync(
@@ -174,18 +191,19 @@ private string DebuggerToString()
return sb.ToString();
}
+ ///
public void Dispose()
{
- if (_needsDisposing)
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
+ Dispose(true);
+ GC.SuppressFinalize(this);
}
+ ///
+ /// Releases unmanaged resources.
+ ///
protected virtual void Dispose(bool disposing)
{
- if (disposing)
+ if (disposing && _disposeClient)
{
_client.Dispose();
}
diff --git a/src/Passwordless/PasswordlessDelegatingHandler.cs b/src/Passwordless/PasswordlessDelegatingHandler.cs
index f467ed1..bdc0387 100644
--- a/src/Passwordless/PasswordlessDelegatingHandler.cs
+++ b/src/Passwordless/PasswordlessDelegatingHandler.cs
@@ -1,4 +1,3 @@
-using System.Net.Http;
using System.Net.Http.Json;
using Passwordless.Helpers;
diff --git a/src/Passwordless/PasswordlessHttpRequestExtensions.cs b/src/Passwordless/PasswordlessHttpRequestExtensions.cs
index 0347c6a..a3aaaa7 100644
--- a/src/Passwordless/PasswordlessHttpRequestExtensions.cs
+++ b/src/Passwordless/PasswordlessHttpRequestExtensions.cs
@@ -1,5 +1,3 @@
-using System.Net.Http;
-
namespace Passwordless;
internal static class PasswordlessHttpRequestExtensions
diff --git a/src/Passwordless/Pollyfill.cs b/src/Passwordless/Pollyfill.cs
index 3b9f508..559ed10 100644
--- a/src/Passwordless/Pollyfill.cs
+++ b/src/Passwordless/Pollyfill.cs
@@ -1,143 +1,144 @@
+#if !NET6_0_OR_GREATER
+
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-namespace System.Diagnostics.CodeAnalysis
+namespace System.Diagnostics.CodeAnalysis;
+
+///
+/// Indicates that certain members on a specified are accessed dynamically,
+/// for example through .
+///
+///
+/// This allows tools to understand which members are being accessed during the execution
+/// of a program.
+///
+/// This attribute is valid on members whose type is or .
+///
+/// When this attribute is applied to a location of type , the assumption is
+/// that the string represents a fully qualified type name.
+///
+/// When this attribute is applied to a class, interface, or struct, the members specified
+/// can be accessed dynamically on instances returned from calling
+/// on instances of that class, interface, or struct.
+///
+/// If the attribute is applied to a method it's treated as a special case and it implies
+/// the attribute should be applied to the "this" parameter of the method. As such the attribute
+/// should only be used on instance methods of types assignable to System.Type (or string, but no methods
+/// will use it there).
+///
+[AttributeUsage(
+ AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
+ AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
+ AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
+ Inherited = false)]
+internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
-#if INCLUDE_DYNAMICALLY_ACCESSED_MEMBERS
- ///
- /// Indicates that certain members on a specified are accessed dynamically,
- /// for example through .
- ///
- ///
- /// This allows tools to understand which members are being accessed during the execution
- /// of a program.
- ///
- /// This attribute is valid on members whose type is or .
- ///
- /// When this attribute is applied to a location of type , the assumption is
- /// that the string represents a fully qualified type name.
- ///
- /// When this attribute is applied to a class, interface, or struct, the members specified
- /// can be accessed dynamically on instances returned from calling
- /// on instances of that class, interface, or struct.
- ///
- /// If the attribute is applied to a method it's treated as a special case and it implies
- /// the attribute should be applied to the "this" parameter of the method. As such the attribute
- /// should only be used on instance methods of types assignable to System.Type (or string, but no methods
- /// will use it there).
- ///
- [AttributeUsage(
- AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
- AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
- AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
- Inherited = false)]
- internal sealed class DynamicallyAccessedMembersAttribute : Attribute
+ ///
+ /// Initializes a new instance of the class
+ /// with the specified member types.
+ ///
+ /// The types of members dynamically accessed.
+ public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
- ///
- /// Initializes a new instance of the class
- /// with the specified member types.
- ///
- /// The types of members dynamically accessed.
- public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
- {
- MemberTypes = memberTypes;
- }
-
- ///
- /// Gets the which specifies the type
- /// of members dynamically accessed.
- ///
- public DynamicallyAccessedMemberTypes MemberTypes { get; }
+ MemberTypes = memberTypes;
}
///
- /// Specifies the types of members that are dynamically accessed.
- ///
- /// This enumeration has a attribute that allows a
- /// bitwise combination of its member values.
+ /// Gets the which specifies the type
+ /// of members dynamically accessed.
///
- [Flags]
- internal enum DynamicallyAccessedMemberTypes
- {
- ///
- /// Specifies no members.
- ///
- None = 0,
-
- ///
- /// Specifies the default, parameterless public constructor.
- ///
- PublicParameterlessConstructor = 0x0001,
-
- ///
- /// Specifies all public constructors.
- ///
- PublicConstructors = 0x0002 | PublicParameterlessConstructor,
-
- ///
- /// Specifies all non-public constructors.
- ///
- NonPublicConstructors = 0x0004,
-
- ///
- /// Specifies all public methods.
- ///
- PublicMethods = 0x0008,
-
- ///
- /// Specifies all non-public methods.
- ///
- NonPublicMethods = 0x0010,
-
- ///
- /// Specifies all public fields.
- ///
- PublicFields = 0x0020,
-
- ///
- /// Specifies all non-public fields.
- ///
- NonPublicFields = 0x0040,
-
- ///
- /// Specifies all public nested types.
- ///
- PublicNestedTypes = 0x0080,
-
- ///
- /// Specifies all non-public nested types.
- ///
- NonPublicNestedTypes = 0x0100,
-
- ///
- /// Specifies all public properties.
- ///
- PublicProperties = 0x0200,
-
- ///
- /// Specifies all non-public properties.
- ///
- NonPublicProperties = 0x0400,
-
- ///
- /// Specifies all public events.
- ///
- PublicEvents = 0x0800,
-
- ///
- /// Specifies all non-public events.
- ///
- NonPublicEvents = 0x1000,
-
- ///
- /// Specifies all interfaces implemented by the type.
- ///
- Interfaces = 0x2000,
-
- ///
- /// Specifies all members.
- ///
- All = ~None
- }
-#endif
-}
\ No newline at end of file
+ public DynamicallyAccessedMemberTypes MemberTypes { get; }
+}
+
+///
+/// Specifies the types of members that are dynamically accessed.
+///
+/// This enumeration has a attribute that allows a
+/// bitwise combination of its member values.
+///
+[Flags]
+internal enum DynamicallyAccessedMemberTypes
+{
+ ///
+ /// Specifies no members.
+ ///
+ None = 0,
+
+ ///
+ /// Specifies the default, parameterless public constructor.
+ ///
+ PublicParameterlessConstructor = 0x0001,
+
+ ///
+ /// Specifies all public constructors.
+ ///
+ PublicConstructors = 0x0002 | PublicParameterlessConstructor,
+
+ ///
+ /// Specifies all non-public constructors.
+ ///
+ NonPublicConstructors = 0x0004,
+
+ ///
+ /// Specifies all public methods.
+ ///
+ PublicMethods = 0x0008,
+
+ ///
+ /// Specifies all non-public methods.
+ ///
+ NonPublicMethods = 0x0010,
+
+ ///
+ /// Specifies all public fields.
+ ///
+ PublicFields = 0x0020,
+
+ ///
+ /// Specifies all non-public fields.
+ ///
+ NonPublicFields = 0x0040,
+
+ ///
+ /// Specifies all public nested types.
+ ///
+ PublicNestedTypes = 0x0080,
+
+ ///
+ /// Specifies all non-public nested types.
+ ///
+ NonPublicNestedTypes = 0x0100,
+
+ ///
+ /// Specifies all public properties.
+ ///
+ PublicProperties = 0x0200,
+
+ ///
+ /// Specifies all non-public properties.
+ ///
+ NonPublicProperties = 0x0400,
+
+ ///
+ /// Specifies all public events.
+ ///
+ PublicEvents = 0x0800,
+
+ ///
+ /// Specifies all non-public events.
+ ///
+ NonPublicEvents = 0x1000,
+
+ ///
+ /// Specifies all interfaces implemented by the type.
+ ///
+ Interfaces = 0x2000,
+
+ ///
+ /// Specifies all members.
+ ///
+ All = ~None
+}
+
+#endif
\ No newline at end of file
diff --git a/src/Passwordless/ServiceCollectionExtensions.cs b/src/Passwordless/ServiceCollectionExtensions.cs
index 8febcb3..b228445 100644
--- a/src/Passwordless/ServiceCollectionExtensions.cs
+++ b/src/Passwordless/ServiceCollectionExtensions.cs
@@ -1,13 +1,18 @@
using System.Diagnostics.CodeAnalysis;
-using System.Net.Http;
using Microsoft.Extensions.Options;
using Passwordless;
// This is a trick to always show up in a class when people are registering services
namespace Microsoft.Extensions.DependencyInjection;
+///
+/// Service registration extensions for Passwordless.
+///
public static class ServiceCollectionExtensions
{
+ ///
+ /// Adds and configures Passwordless-related services.
+ ///
public static IServiceCollection AddPasswordlessSdk(this IServiceCollection services, Action configureOptions)
{
services.AddOptions()
diff --git a/tests/Passwordless.Tests/PasswordlessClientTests.cs b/tests/Passwordless.Tests/PasswordlessClientTests.cs
index 032f20f..1fa3990 100644
--- a/tests/Passwordless.Tests/PasswordlessClientTests.cs
+++ b/tests/Passwordless.Tests/PasswordlessClientTests.cs
@@ -1,4 +1,3 @@
-using System.Net.Http;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
From 91a9cc28121dbbf3c0d393edcd5f04d786422472 Mon Sep 17 00:00:00 2001
From: Oleksii Holub <1935960+Tyrrrz@users.noreply.github.com>
Date: Mon, 25 Sep 2023 20:19:23 +0300
Subject: [PATCH 2/2] Revert ctor changes
---
src/Passwordless/PasswordlessClient.cs | 31 ++++++++++++--------------
1 file changed, 14 insertions(+), 17 deletions(-)
diff --git a/src/Passwordless/PasswordlessClient.cs b/src/Passwordless/PasswordlessClient.cs
index fea7a9b..dae2d34 100644
--- a/src/Passwordless/PasswordlessClient.cs
+++ b/src/Passwordless/PasswordlessClient.cs
@@ -15,39 +15,36 @@ public class PasswordlessClient : IPasswordlessClient, IDisposable
private readonly bool _disposeClient;
///
- /// Initializes an instance of using the provided
- /// and .
+ /// Initializes an instance of .
///
- public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory) =>
- new(factory.CreateClient(), options, false);
-
- private PasswordlessClient(HttpClient client, bool disposeClient)
- {
- _client = client;
- _disposeClient = disposeClient;
- }
-
- private PasswordlessClient(HttpClient client, PasswordlessOptions options, bool disposeClient)
- : this(client, disposeClient)
+ public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory)
{
+ var client = factory.CreateClient();
client.BaseAddress = new Uri(options.ApiUrl);
client.DefaultRequestHeaders.Add("ApiSecret", options.ApiSecret);
+ return new PasswordlessClient(client);
}
///
/// Initializes an instance of .
///
- public PasswordlessClient(HttpClient client)
- : this(client, false)
+ public PasswordlessClient(PasswordlessOptions passwordlessOptions)
{
+ _client = new HttpClient
+ {
+ BaseAddress = new Uri(passwordlessOptions.ApiUrl),
+ };
+ _client.DefaultRequestHeaders.Add("ApiSecret", passwordlessOptions.ApiSecret);
+ _disposeClient = true;
}
///
/// Initializes an instance of .
///
- public PasswordlessClient(PasswordlessOptions options)
- : this(new HttpClient(), options, true)
+ public PasswordlessClient(HttpClient client)
{
+ _client = client;
+ _disposeClient = false;
}
///