Skip to content

Commit a4d0d5c

Browse files
committed
IClientWebSocket builder
1 parent 3b47d0b commit a4d0d5c

File tree

8 files changed

+157
-13
lines changed

8 files changed

+157
-13
lines changed

SystemExtensions.DependencyInjection.Tests/Http/HttpClientResolverTests.cs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,6 @@ public void CanResolve_AnythingElse_ReturnsFalse()
3535
}
3636
}
3737

38-
[TestMethod]
39-
public void Resolve_TypedClient_ReturnsIHttpClient()
40-
{
41-
var client = this.httpClientResolver.Resolve(this.serviceProviderMock, typeof(IHttpClient<string>));
42-
43-
client.Should().BeAssignableTo<IHttpClient<string>>();
44-
}
45-
4638
[TestMethod]
4739
public void Resolve_NonGenericType_Throws()
4840
{
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using FluentAssertions;
2+
using global::SystemExtensions.NetStandard.DependencyInjection.Tests.Http.Models;
3+
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.VisualStudio.TestTools.UnitTesting;
5+
using NSubstitute;
6+
using System;
7+
using System.Extensions;
8+
using System.Linq;
9+
using System.Net.Http;
10+
using System.Net.WebSockets;
11+
using System.Threading.Tasks;
12+
13+
namespace SystemExtensions.NetStandard.DependencyInjection.Tests.WebSockets;
14+
[TestClass]
15+
public sealed class HttpClientBuilderTests
16+
{
17+
private readonly ClientWebSocketBuilder<object> clientWebSocketBuilder;
18+
private readonly IServiceCollection serviceProducerMock = Substitute.For<IServiceCollection>();
19+
20+
public HttpClientBuilderTests()
21+
{
22+
this.clientWebSocketBuilder = new ClientWebSocketBuilder<object>(this.serviceProducerMock);
23+
}
24+
25+
[TestMethod]
26+
public void Constructor_NullServiceProducer_Throws()
27+
{
28+
var action = () => new ClientWebSocketBuilder<object>(null);
29+
30+
action.Should().Throw<ArgumentNullException>();
31+
}
32+
33+
[TestMethod]
34+
public void WithInnerWebSocket_NullHandler_Throws()
35+
{
36+
var action = () => this.clientWebSocketBuilder.WithInnerWebSocket(null);
37+
38+
action.Should().Throw<ArgumentNullException>();
39+
}
40+
41+
[TestMethod]
42+
public void WIthInnerWebSocketFactory_NullBaseAddress_Throws()
43+
{
44+
var action = () => this.clientWebSocketBuilder.WithInnerWebSocketFactory(null);
45+
46+
action.Should().Throw<ArgumentNullException>();
47+
}
48+
49+
[TestMethod]
50+
public void Build_ReturnsServiceProducer()
51+
{
52+
var producer = this.clientWebSocketBuilder.Build();
53+
54+
producer.Should().BeEquivalentTo(this.serviceProducerMock);
55+
}
56+
57+
[TestMethod]
58+
public async Task ClientWebSocketBuilder_RegistersClientCorrectly_IServiceProviderReturnsExpected()
59+
{
60+
var container = new ServiceCollection();
61+
var innerWebSocket = new ClientWebSocket();
62+
container.RegisterClientWebSocket<object>()
63+
.WithInnerWebSocket(innerWebSocket)
64+
.Build();
65+
66+
var di = container.BuildServiceProvider();
67+
var client = di.GetService<IClientWebSocket<object>>();
68+
client.Should().NotBeNull();
69+
}
70+
}

SystemExtensions.NetStandard.DependencyInjection/Extensions/ServiceManagerExtensions.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.Extensions.Logging;
33
using Slim;
44
using System.Net.Http;
5+
using System.Net.WebSockets;
56

67
namespace System.Extensions;
78

@@ -34,6 +35,19 @@ public static HttpClientBuilder<T> RegisterHttpClient<T>(this IServiceCollection
3435
return new HttpClientBuilder<T>(services);
3536
}
3637

38+
/// <summary>
39+
/// Register a scoped <see cref="IClientWebSocket{TScope}"/> to be used by the DI engine.
40+
/// </summary>
41+
/// <typeparam name="T">Type of the scoped <see cref="IClientWebSocket{TScope}"/>.</typeparam>
42+
/// <param name="services"><see cref="IServiceCollection"/>.</param>
43+
/// <returns><see cref="ClientWebSocketBuilder{T}"/> to build the websocket client.</returns>
44+
public static ClientWebSocketBuilder<T> RegisterClientWebSocket<T>(this IServiceCollection services)
45+
{
46+
services.ThrowIfNull(nameof(services));
47+
48+
return new ClientWebSocketBuilder<T>(services);
49+
}
50+
3751
[Obsolete($"{nameof(RegisterHttpFactory)} is obsolete. Please use {nameof(RegisterHttpClient)} for each service that requires an instance of {nameof(IHttpClient<object>)}.")]
3852
public static IServiceProducer RegisterHttpFactory(this IServiceProducer serviceProducer)
3953
{

SystemExtensions.NetStandard.DependencyInjection/SystemExtensions.NetStandard.DependencyInjection.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,11 @@
66
<PackageLicenseFile>LICENSE</PackageLicenseFile>
77
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
88
<LangVersion>latest</LangVersion>
9-
<Version>1.7</Version>
9+
<Version>1.7.1</Version>
1010
<Authors>Alexandru Macocian</Authors>
1111
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
1212
<Description>Extensions for the Slim Dependency Injection engine</Description>
13+
<Nullable>enable</Nullable>
1314
</PropertyGroup>
1415

1516
<ItemGroup>
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Microsoft.Extensions.DependencyInjection;
2+
using Microsoft.Extensions.Logging;
3+
using System.Extensions;
4+
using System.Net.WebSockets;
5+
6+
namespace System.Net.Http;
7+
8+
public sealed class ClientWebSocketBuilder<T>
9+
{
10+
private readonly IServiceCollection services;
11+
12+
private ClientWebSocket? innerWebSocket;
13+
private Func<IServiceProvider, ClientWebSocket>? innerWebSocketFactory;
14+
15+
internal ClientWebSocketBuilder(IServiceCollection services)
16+
{
17+
services.ThrowIfNull(nameof(services));
18+
this.services = services;
19+
}
20+
21+
public ClientWebSocketBuilder<T> WithInnerWebSocket(ClientWebSocket innerWebSocket)
22+
{
23+
this.innerWebSocket = innerWebSocket.ThrowIfNull(nameof(innerWebSocket));
24+
return this;
25+
}
26+
27+
public ClientWebSocketBuilder<T> WithInnerWebSocketFactory(Func<IServiceProvider, ClientWebSocket> innerWebSocketFactory)
28+
{
29+
this.innerWebSocketFactory = innerWebSocketFactory.ThrowIfNull(nameof(innerWebSocketFactory));
30+
return this;
31+
}
32+
33+
public IServiceCollection Build()
34+
{
35+
this.services.AddScoped<IClientWebSocket<T>>(sp =>
36+
{
37+
var logger = sp.GetService<ILogger<T>>();
38+
if (logger is null)
39+
{
40+
return new ClientWebSocket<T>();
41+
}
42+
43+
if (this.innerWebSocketFactory is not null)
44+
{
45+
return new ClientWebSocket<T>(this.innerWebSocketFactory(sp), logger);
46+
}
47+
48+
if (this.innerWebSocket is not null)
49+
{
50+
return new ClientWebSocket<T>(this.innerWebSocket, logger);
51+
}
52+
53+
return new ClientWebSocket<T>(logger);
54+
});
55+
56+
return this.services;
57+
}
58+
}

SystemExtensions.NetStandard.DependencyInjection/WebSockets/ClientWebSocketResolver.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using Slim.Resolvers;
33

44
namespace System.Net.WebSockets;
5+
6+
[Obsolete($"Please use {nameof(Extensions.ServiceManagerExtensions.RegisterClientWebSocket)} for each use case of {nameof(IClientWebSocket<object>)}")]
57
public sealed class ClientWebSocketResolver : IDependencyResolver
68
{
79
private static readonly Type ClientType = typeof(ClientWebSocket<>);

SystemExtensions.NetStandard.Security/Encryption/SecureString.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ public sealed class SecureString
99
private byte[] encryptedValue;
1010

1111
public string Value {
12-
get => this.encryptedValue is not null ? Encoding.UTF8.GetString(ProtectedData.Unprotect(this.encryptedValue, optionalEntropy, DataProtectionScope.CurrentUser)) : null;
13-
private set => this.encryptedValue = ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser);
12+
get => this.encryptedValue is not null ? Encoding.UTF8.GetString(ProtectedData.Unprotect(this.encryptedValue, optionalEntropy, DataProtectionScope.CurrentUser)) : string.Empty;
13+
private set => this.encryptedValue = value is not null
14+
? ProtectedData.Protect(Encoding.UTF8.GetBytes(value), optionalEntropy, DataProtectionScope.CurrentUser)
15+
: null;
1416
}
1517

1618
public SecureString(string value)
@@ -42,7 +44,7 @@ public override string ToString()
4244
return this.Value;
4345
}
4446

45-
public static readonly SecureString Empty = new(string.Empty);
47+
public static readonly SecureString Empty = new(null);
4648
public static implicit operator string(SecureString ss) => ss is null ? string.Empty : ss.Value;
4749
public static implicit operator SecureString(string s) => new(s);
4850
public static SecureString operator +(SecureString ss1, SecureString ss2)
@@ -79,6 +81,11 @@ public override string ToString()
7981
}
8082
public static bool operator ==(SecureString ss1, SecureString ss2)
8183
{
84+
if (ss1 is null && ss2 is null)
85+
{
86+
return true;
87+
}
88+
8289
return ss1?.Value == ss2?.Value;
8390
}
8491
public static bool operator !=(SecureString ss1, SecureString ss2)

SystemExtensions.NetStandard.Security/SystemExtensions.NetStandard.Security.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PackageLicenseFile>LICENSE</PackageLicenseFile>
88
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
99
<RootNamespace>System</RootNamespace>
10-
<Version>1.3.0</Version>
10+
<Version>1.3.1</Version>
1111
<Authors>Alexandru Macocian</Authors>
1212
<RepositoryUrl>https://github.com/AlexMacocian/SystemExtensions</RepositoryUrl>
1313
<Description>Security extensions for the System namespace</Description>

0 commit comments

Comments
 (0)