Skip to content

Commit

Permalink
Merged PR 4030: [5.1.2] Fix | Adding type convertor support for SqlCo…
Browse files Browse the repository at this point in the history
…nnectionEncryptOption (#2057)

Ports [#2057](#2057)
  • Loading branch information
DavoudEshtehari committed Oct 16, 2023
1 parent 80d1f47 commit 0b51b63
Show file tree
Hide file tree
Showing 7 changed files with 226 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,9 @@
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOption.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionEncryptOptionConverter.cs</Link>
</Compile>
<Compile Include="..\..\src\Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs">
<Link>Microsoft\Data\SqlClient\SqlConnectionPoolGroupProviderInfo.cs</Link>
</Compile>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
// See the LICENSE file in the project root for more information.

using System;
using System.ComponentModel;
using Microsoft.Data.Common;

namespace Microsoft.Data.SqlClient
{
/// <include file='../../../../../../doc/snippets/Microsoft.Data.SqlClient/SqlConnectionEncryptOption.xml' path='docs/members[@name="SqlConnectionEncryptOption"]/SqlConnectionEncryptOption/*'/>
[TypeConverter(typeof(SqlConnectionEncryptOptionConverter))]
public sealed class SqlConnectionEncryptOption
{
private const string TRUE = "True";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.ComponentModel;
using System.Globalization;
using Microsoft.Data.Common;

namespace Microsoft.Data.SqlClient
{
internal class SqlConnectionEncryptOptionConverter : TypeConverter
{
// Overrides the CanConvertFrom method of TypeConverter.
// The ITypeDescriptorContext interface provides the context for the
// conversion. Typically, this interface is used at design time to
// provide information about the design-time container.
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertFrom(context, sourceType);
}

// Overrides the CanConvertTo method of TypeConverter.
public override bool CanConvertTo(ITypeDescriptorContext context, Type sourceType)
{
if (sourceType == typeof(string))
{
return true;
}
return base.CanConvertTo(context, sourceType);
}

// Overrides the ConvertFrom method of TypeConverter.
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string)
{
return SqlConnectionEncryptOption.Parse(value.ToString());
}
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
}

// Overrides the ConvertTo method of TypeConverter.
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (destinationType == typeof(string))
{
return base.ConvertTo(context, culture, value, destinationType);
}
throw ADP.ConvertFailed(value.GetType(), typeof(SqlConnectionEncryptOption), null);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
<Compile Include="..\..\src\Microsoft\Data\Common\MultipartIdentifier.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="$(MicrosoftExtensionsHosting)" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(MicrosoftNETTestSdkVersion)" />
<PackageReference Include="System.Diagnostics.DiagnosticSource" Version="$(SystemDiagnosticsDiagnosticSourceVersion)" />
<PackageReference Include="Newtonsoft.Json" Version="$(NewtonsoftJsonVersion)" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Text;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Xunit;

namespace Microsoft.Data.SqlClient.Tests
Expand Down Expand Up @@ -446,6 +453,159 @@ public void EncryptTryParseInvalidValuesReturnsFalse(string value)
Assert.Null(result);
}

#region SqlConnectionEncryptOptionCoverterTests
[Fact]
public void ConnectionStringFromJsonTests()
{
UserDbConnectionStringSettings settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("false");
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("true");
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("strict");
Assert.Equal(SqlConnectionEncryptOption.Strict, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("mandatory");
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("optional");
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("yes");
Assert.Equal(SqlConnectionEncryptOption.Mandatory, settings.UserDb.UserComponents.Encrypt);

settings = LoadSettingsFromJsonStream<UserDbConnectionStringSettings>("no");
Assert.Equal(SqlConnectionEncryptOption.Optional, settings.UserDb.UserComponents.Encrypt);
}

[Theory]
[InlineData("absolutely")]
[InlineData("affirmative")]
[InlineData("never")]
[InlineData("always")]
[InlineData("none")]
[InlineData(" for sure ")]
public void ConnectionStringFromJsonThrowsException(string value)
{
ExecuteConnectionStringFromJsonThrowsException(value);
}

[Fact]
public void SqlConnectionEncryptOptionConverterCanConvertFromTest()
{
// Get a converter
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
// Use the converter to determine if can convert from string data type
Assert.True(converter.CanConvertFrom(null, typeof(string)), "Expecting to convert from a string type.");
// Use the same converter to determine if can convert from int or bool data types
Assert.False(converter.CanConvertFrom(null, typeof(int)), "Not expecting to convert from integer type.");
Assert.False(converter.CanConvertFrom(null, typeof(bool)), "Not expecting to convert from boolean type.");
}

[Fact]
public void SqlConnectionEncryptOptionConverterCanConvertToTest()
{
// Get a converter
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
// Use the converter to check if can convert from stirng
Assert.True(converter.CanConvertTo(null, typeof(string)), "Expecting to convert to a string type.");
// Use the same convert to check if can convert to int or bool
Assert.False(converter.CanConvertTo(null, typeof(int)), "Not expecting to convert from integer type.");
Assert.False(converter.CanConvertTo(null, typeof(bool)), "Not expecting to convert from boolean type.");
}

[Fact]
public void SqlConnectionEncryptOptionConverterConvertFromTest()
{
// Create a converter
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
// Use the converter to convert all possible valid values
Assert.Equal(SqlConnectionEncryptOption.Parse("false"), converter.ConvertFrom("false"));
Assert.Equal(SqlConnectionEncryptOption.Parse("true"), converter.ConvertFrom("true"));
Assert.Equal(SqlConnectionEncryptOption.Parse("strict"), converter.ConvertFrom("strict"));
Assert.Equal(SqlConnectionEncryptOption.Parse("mandatory"), converter.ConvertFrom("mandatory"));
Assert.Equal(SqlConnectionEncryptOption.Parse("optional"), converter.ConvertFrom("optional"));
Assert.Equal(SqlConnectionEncryptOption.Parse("yes"), converter.ConvertFrom("yes"));
Assert.Equal(SqlConnectionEncryptOption.Parse("no"), converter.ConvertFrom("no"));
// Use the converter to covert invalid value
Assert.Throws<ArgumentException>(() => converter.ConvertFrom("affirmative"));
// Use the same converter to convert from bad data types
Assert.Throws<ArgumentException>(() => converter.ConvertFrom(1));
Assert.Throws<ArgumentException>(() => converter.ConvertFrom(true));
}

[Fact]
public void SqlConnectionEncryptOptionConverterConvertToTest()
{
// Get a converter
SqlConnectionEncryptOption option = SqlConnectionEncryptOption.Parse("false");
TypeConverter converter = TypeDescriptor.GetConverter(option.GetType());
// Use the converter to convert all possible valid values to string
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(string)));
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("true"), typeof(string)));
Assert.Equal("Strict", converter.ConvertTo(SqlConnectionEncryptOption.Parse("strict"), typeof(string)));
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("mandatory"), typeof(string)));
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("optional"), typeof(string)));
Assert.Equal("True", converter.ConvertTo(SqlConnectionEncryptOption.Parse("yes"), typeof(string)));
Assert.Equal("False", converter.ConvertTo(SqlConnectionEncryptOption.Parse("no"), typeof(string)));
// Use the same converter to try convert to bad data types
Assert.Throws<ArgumentException>(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(int)));
Assert.Throws<ArgumentException>(() => converter.ConvertTo(SqlConnectionEncryptOption.Parse("false"), typeof(bool)));
}

internal class UserDbConnectionStringSettings
{
[Required]
public UserSqlConnectionString UserDb { get; set; }
}

internal class UserSqlConnectionString
{
public SqlConnectionStringBuilder UserComponents { get; set; } = new();

public override string ToString()
{
return UserComponents!.ConnectionString;
}
}

internal static void ExecuteConnectionStringFromJsonThrowsException(string encryptOption)
{
var exception = Assert.Throws<InvalidOperationException>(() => LoadSettingsFromJsonStream<UserDbConnectionStringSettings>(encryptOption));
Assert.Contains("Failed to convert configuration", exception.Message, StringComparison.Ordinal);
}

private static TSettings LoadSettingsFromJsonStream<TSettings>(string encryptOption) where TSettings : class
{
TSettings settingsOut = null;

Host.CreateDefaultBuilder()
.ConfigureAppConfiguration((ctx, configBuilder) =>
{
// Note: Inside string interpolation, a { should be {{ and a } should be }}
// First, declare a stringified JSON
var json = $"{{ \"UserDb\": {{ \"UserComponents\": {{ \"NetworkLibrary\": \"DBMSSOCN\", \"UserID\": \"user\", \"Password\": \"password\", \"DataSource\": \"localhost\", \"InitialCatalog\": \"catalog\", \"Encrypt\": \"{encryptOption}\" }}}}}}";
// Load the stringified JSON as a stream into the configuration builder
configBuilder.AddJsonStream(new MemoryStream(Encoding.ASCII.GetBytes(json)));
configBuilder.AddEnvironmentVariables();
})
.ConfigureServices((ctx, services) =>
{
var configuration = ctx.Configuration;
services.AddOptions();
services.Configure<TSettings>(ctx.Configuration);
settingsOut = configuration.Get<TSettings>();
})
.Build();

return settingsOut;
}
#endregion

internal void ExecuteConnectionStringTests(string connectionString)
{
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder(connectionString);
Expand Down
1 change: 1 addition & 0 deletions tools/props/Versions.props
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
<MicrosoftSqlServerTypesVersion>10.50.1600.1</MicrosoftSqlServerTypesVersion>
<BenchmarkDotNetVersion>0.13.2</BenchmarkDotNetVersion>
<SystemServiceProcessServiceControllerVersion>6.0.0</SystemServiceProcessServiceControllerVersion>
<MicrosoftExtensionsHosting>6.0.0</MicrosoftExtensionsHosting>
</PropertyGroup>
<PropertyGroup>
<TestAKVProviderVersion>$(NugetPackageVersion)</TestAKVProviderVersion>
Expand Down

0 comments on commit 0b51b63

Please sign in to comment.