diff --git a/src/Passwordless/IPasswordlessClient.cs b/src/Passwordless/IPasswordlessClient.cs index 2537a62..70eb7df 100644 --- a/src/Passwordless/IPasswordlessClient.cs +++ b/src/Passwordless/IPasswordlessClient.cs @@ -1,4 +1,4 @@ -using Passwordless.Models; +using Passwordless.Models; namespace Passwordless; @@ -43,6 +43,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 4d8e036..6cb7c20 100644 --- a/src/Passwordless/PasswordlessClient.cs +++ b/src/Passwordless/PasswordlessClient.cs @@ -7,15 +7,16 @@ 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; + /// + /// Initializes an instance of . + /// public static PasswordlessClient Create(PasswordlessOptions options, IHttpClientFactory factory) { var client = factory.CreateClient(); @@ -24,123 +25,136 @@ public static PasswordlessClient Create(PasswordlessOptions options, IHttpClient return new PasswordlessClient(client); } + /// + /// Initializes an instance of . + /// public PasswordlessClient(PasswordlessOptions passwordlessOptions) { - _needsDisposing = true; _client = new HttpClient { BaseAddress = new Uri(passwordlessOptions.ApiUrl), }; _client.DefaultRequestHeaders.Add("ApiSecret", passwordlessOptions.ApiSecret); + _disposeClient = true; } + /// + /// Initializes an instance of . + /// public PasswordlessClient(HttpClient client) { - _needsDisposing = false; _client = client; + _disposeClient = false; } - /// + /// public async Task SetAliasAsync(SetAliasRequest request, CancellationToken cancellationToken) { - var res = await _client.PostAsJsonAsync("alias", + using var response = await _client.PostAsJsonAsync("alias", request, PasswordlessSerializerContext.Default.SetAliasRequest, 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 +188,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/Polyfill.CodeAnalysis.cs b/src/Passwordless/Polyfill.CodeAnalysis.cs index 3b9f508..559ed10 100644 --- a/src/Passwordless/Polyfill.CodeAnalysis.cs +++ b/src/Passwordless/Polyfill.CodeAnalysis.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;