diff --git a/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs b/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs index eaf7fef..679452d 100644 --- a/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs +++ b/SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs @@ -35,14 +35,6 @@ public class HttpClientResolverTests } } - [TestMethod] - public void Resolve_TypedClient_ReturnsIHttpClient() - { - var client = this.httpClientResolver.Resolve(this.serviceProviderMock, typeof(IHttpClient)); - - client.Should().BeAssignableTo>(); - } - [TestMethod] public void Resolve_NonGenericType_Throws() { diff --git a/SystemExtensions.DependencyInjection.Tests/WebSockets/ClientWebSocketBuilderTests.cs b/SystemExtensions.DependencyInjection.Tests/WebSockets/ClientWebSocketBuilderTests.cs new file mode 100644 index 0000000..111591a --- /dev/null +++ b/SystemExtensions.DependencyInjection.Tests/WebSockets/ClientWebSocketBuilderTests.cs @@ -0,0 +1,70 @@ +using FluentAssertions; +using global::SystemExtensions.NetStandard.DependencyInjection.Tests.Http.Models; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using System; +using System.Extensions; +using System.Linq; +using System.Net.Http; +using System.Net.WebSockets; +using System.Threading.Tasks; + +namespace SystemExtensions.NetStandard.DependencyInjection.Tests.WebSockets; +[TestClass] +public sealed class HttpClientBuilderTests +{ + private readonly ClientWebSocketBuilder clientWebSocketBuilder; + private readonly IServiceCollection serviceProducerMock = Substitute.For(); + + public HttpClientBuilderTests() + { + this.clientWebSocketBuilder = new ClientWebSocketBuilder(this.serviceProducerMock); + } + + [TestMethod] + public void Constructor_NullServiceProducer_Throws() + { + var action = () => new ClientWebSocketBuilder(null); + + action.Should().Throw(); + } + + [TestMethod] + public void WithInnerWebSocket_NullHandler_Throws() + { + var action = () => this.clientWebSocketBuilder.WithInnerWebSocket(null); + + action.Should().Throw(); + } + + [TestMethod] + public void WIthInnerWebSocketFactory_NullBaseAddress_Throws() + { + var action = () => this.clientWebSocketBuilder.WithInnerWebSocketFactory(null); + + action.Should().Throw(); + } + + [TestMethod] + public void Build_ReturnsServiceProducer() + { + var producer = this.clientWebSocketBuilder.Build(); + + producer.Should().BeEquivalentTo(this.serviceProducerMock); + } + + [TestMethod] + public async Task ClientWebSocketBuilder_RegistersClientCorrectly_IServiceProviderReturnsExpected() + { + var container = new ServiceCollection(); + var innerWebSocket = new ClientWebSocket(); + container.RegisterClientWebSocket() + .WithInnerWebSocket(innerWebSocket) + .Build(); + + var di = container.BuildServiceProvider(); + var client = di.GetService>(); + client.Should().NotBeNull(); + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs b/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs index d1f57a8..cc9d9c7 100644 --- a/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs +++ b/SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs @@ -2,6 +2,7 @@ using Microsoft.Extensions.Logging; using Slim; using System.Net.Http; +using System.Net.WebSockets; namespace System.Extensions; @@ -34,6 +35,19 @@ public static class ServiceManagerExtensions return new HttpClientBuilder(services); } + /// + /// Register a scoped to be used by the DI engine. + /// + /// Type of the scoped . + /// . + /// to build the websocket client. + public static ClientWebSocketBuilder RegisterClientWebSocket(this IServiceCollection services) + { + services.ThrowIfNull(nameof(services)); + + return new ClientWebSocketBuilder(services); + } + [Obsolete($"{nameof(RegisterHttpFactory)} is obsolete. Please use {nameof(RegisterHttpClient)} for each service that requires an instance of {nameof(IHttpClient)}.")] public static IServiceProducer RegisterHttpFactory(this IServiceProducer serviceProducer) { diff --git a/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj b/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj index 14e3b89..4d629ea 100644 --- a/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj +++ b/SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj @@ -6,10 +6,11 @@ LICENSE true latest - 1.7 + 1.7.1 Alexandru Macocian https://github.com/AlexMacocian/SystemExtensions Extensions for the Slim Dependency Injection engine + enable diff --git a/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketBuilder.cs b/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketBuilder.cs new file mode 100644 index 0000000..cfb560f --- /dev/null +++ b/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketBuilder.cs @@ -0,0 +1,58 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System.Extensions; +using System.Net.WebSockets; + +namespace System.Net.Http; + +public sealed class ClientWebSocketBuilder +{ + private readonly IServiceCollection services; + + private ClientWebSocket? innerWebSocket; + private Func? innerWebSocketFactory; + + internal ClientWebSocketBuilder(IServiceCollection services) + { + services.ThrowIfNull(nameof(services)); + this.services = services; + } + + public ClientWebSocketBuilder WithInnerWebSocket(ClientWebSocket innerWebSocket) + { + this.innerWebSocket = innerWebSocket.ThrowIfNull(nameof(innerWebSocket)); + return this; + } + + public ClientWebSocketBuilder WithInnerWebSocketFactory(Func innerWebSocketFactory) + { + this.innerWebSocketFactory = innerWebSocketFactory.ThrowIfNull(nameof(innerWebSocketFactory)); + return this; + } + + public IServiceCollection Build() + { + this.services.AddScoped>(sp => + { + var logger = sp.GetService>(); + if (logger is null) + { + return new ClientWebSocket(); + } + + if (this.innerWebSocketFactory is not null) + { + return new ClientWebSocket(this.innerWebSocketFactory(sp), logger); + } + + if (this.innerWebSocket is not null) + { + return new ClientWebSocket(this.innerWebSocket, logger); + } + + return new ClientWebSocket(logger); + }); + + return this.services; + } +} diff --git a/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketResolver.cs b/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketResolver.cs index dec392c..ee9c4da 100644 --- a/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketResolver.cs +++ b/SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketResolver.cs @@ -2,6 +2,8 @@ using Slim.Resolvers; namespace System.Net.WebSockets; + +[Obsolete($"Please use {nameof(Extensions.ServiceManagerExtensions.RegisterClientWebSocket)} for each use case of {nameof(IClientWebSocket)}")] public sealed class ClientWebSocketResolver : IDependencyResolver { private static readonly Type ClientType = typeof(ClientWebSocket<>); diff --git a/SystemExtensions.NetStandard.Security/Encryption/SecureString.cs b/SystemExtensions.NetStandard.Security/Encryption/SecureString.cs index d5f8823..610ffb1 100644 --- a/SystemExtensions.NetStandard.Security/Encryption/SecureString.cs +++ b/SystemExtensions.NetStandard.Security/Encryption/SecureString.cs @@ -9,8 +9,10 @@ public sealed class SecureString private byte[] encryptedValue; public string Value { - get => this.encryptedValue is not null ? Encoding.UTF8.GetString(ProtectedData.Unprotect(this.encryptedValue, optionalEntropy, DataProtectionScope.CurrentUser)) : null; - private set => this.encryptedValue = ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser); + get => this.encryptedValue is not null ? Encoding.UTF8.GetString(ProtectedData.Unprotect(this.encryptedValue, optionalEntropy, DataProtectionScope.CurrentUser)) : string.Empty; + private set => this.encryptedValue = value is not null + ? ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser) + : null; } public SecureString(string value) @@ -42,7 +44,7 @@ public sealed class SecureString return this.Value; } - public static readonly SecureString Empty = new(string.Empty); + public static readonly SecureString Empty = new(null); public static implicit operator string(SecureString ss) => ss is null ? string.Empty : ss.Value; public static implicit operator SecureString(string s) => new(s); public static SecureString operator +(SecureString ss1, SecureString ss2) @@ -79,6 +81,11 @@ public sealed class SecureString } public static bool operator ==(SecureString ss1, SecureString ss2) { + if (ss1 is null && ss2 is null) + { + return true; + } + return ss1?.Value == ss2?.Value; } public static bool operator !=(SecureString ss1, SecureString ss2) diff --git a/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj b/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj index 768e870..be91fa9 100644 --- a/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj +++ b/SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj @@ -7,7 +7,7 @@ LICENSE true System - 1.3.0 + 1.3.1 Alexandru Macocian https://github.com/AlexMacocian/SystemExtensions Security extensions for the System namespace