< Summary

Class:Microsoft.Extensions.Azure.ClientFactory
Assembly:Microsoft.Extensions.Azure
File(s):C:\Git\azure-sdk-for-net\sdk\core\Microsoft.Extensions.Azure\src\Internal\ClientFactory.cs
Covered lines:101
Uncovered lines:2
Coverable lines:103
Total lines:258
Line coverage:98% (101 of 103)
Covered branches:64
Total branches:68
Branch coverage:94.1% (64 of 68)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
CreateClient(...)-100%100%
CreateCredential(...)-96.43%94.44%
IsCredentialParameter(...)-100%100%
IsOptionsParameter(...)-100%100%
BuildErrorMessage(...)-100%100%
IsApplicableConstructor(...)-100%50%
TryConvertArgument(...)-85.71%83.33%
TryConvertFromString(...)-100%100%
TryCreateObject(...)-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Microsoft.Extensions.Azure\src\Internal\ClientFactory.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Linq;
 7using System.Reflection;
 8using System.Security.Cryptography.X509Certificates;
 9using System.Text;
 10using Azure.Core;
 11using Azure.Identity;
 12using Microsoft.Extensions.Configuration;
 13
 14namespace Microsoft.Extensions.Azure
 15{
 16    internal static class ClientFactory
 17    {
 18        public static object CreateClient(Type clientType, Type optionsType, object options, IConfiguration configuratio
 19        {
 2220            List<object> arguments = new List<object>();
 24621            foreach (var constructor in clientType.GetConstructors().OrderByDescending(c => c.GetParameters().Length))
 22            {
 6223                if (!IsApplicableConstructor(constructor, optionsType))
 24                {
 25                    continue;
 26                }
 27
 6028                arguments.Clear();
 29
 6030                bool match = true;
 23631                foreach (var parameter in constructor.GetParameters())
 32                {
 8833                    if (IsCredentialParameter(parameter))
 34                    {
 435                        if (credential == null)
 36                        {
 237                            match = false;
 238                            break;
 39                        }
 40
 241                        arguments.Add(credential);
 242                        continue;
 43                    }
 44
 8445                    if (IsOptionsParameter(parameter, optionsType))
 46                    {
 47                        break;
 48                    }
 49
 6650                    if (!TryConvertArgument(configuration, parameter.Name, parameter.ParameterType, out object argument)
 51                    {
 3852                        match = false;
 3853                        break;
 54                    }
 55
 2656                    arguments.Add(argument);
 57                }
 58
 5859                if (!match)
 60                {
 61                    continue;
 62                }
 63
 1864                arguments.Add(options);
 65
 1866                return constructor.Invoke(arguments.ToArray());
 67            }
 68
 269            throw new InvalidOperationException(BuildErrorMessage(clientType, optionsType));
 1870        }
 71
 72        internal static TokenCredential CreateCredential(IConfiguration configuration, TokenCredentialOptions identityCl
 73        {
 1874            var clientId = configuration["clientId"];
 1875            var tenantId = configuration["tenantId"];
 1876            var clientSecret = configuration["clientSecret"];
 1877            var certificate = configuration["clientCertificate"];
 1878            var certificateStoreName = configuration["clientCertificateStoreName"];
 1879            var certificateStoreLocation = configuration["clientCertificateStoreLocation"];
 80
 1881            if (!string.IsNullOrWhiteSpace(tenantId) &&
 1882                !string.IsNullOrWhiteSpace(clientId) &&
 1883                !string.IsNullOrWhiteSpace(clientSecret))
 84            {
 685                return new ClientSecretCredential(tenantId, clientId, clientSecret, identityClientOptions);
 86            }
 87
 1288            if (!string.IsNullOrWhiteSpace(tenantId) &&
 1289                !string.IsNullOrWhiteSpace(clientId) &&
 1290                !string.IsNullOrWhiteSpace(certificate))
 91            {
 692                StoreLocation storeLocation = StoreLocation.CurrentUser;
 93
 694                if (!string.IsNullOrWhiteSpace(certificateStoreLocation))
 95                {
 496                    storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), certificateStoreLocation, true);
 97                }
 98
 699                if (string.IsNullOrWhiteSpace(certificateStoreName))
 100                {
 2101                    certificateStoreName = "MY"; // MY is the default used in X509Store
 102                }
 103
 6104                using var store = new X509Store(certificateStoreName, storeLocation);
 6105                store.Open(OpenFlags.ReadOnly);
 6106                X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindByThumbprint, certificate, f
 107
 6108                if (certs.Count == 0)
 109                {
 0110                    throw new InvalidOperationException($"Unable to find a certificate with thumbprint '{certificate}'")
 111                }
 112
 6113                var credential = new ClientCertificateCredential(tenantId, clientId, certs[0], identityClientOptions);
 6114                store.Close();
 115
 6116                return credential;
 117            }
 118
 119            // TODO: More logging
 6120            return null;
 6121        }
 122
 123        private static bool IsCredentialParameter(ParameterInfo parameter)
 124        {
 88125            return parameter.ParameterType == typeof(TokenCredential);
 126        }
 127
 128        private static bool IsOptionsParameter(ParameterInfo parameter, Type optionsType)
 129        {
 174130            return parameter.ParameterType.IsAssignableFrom(optionsType) &&
 174131                   parameter.Position == ((ConstructorInfo)parameter.Member).GetParameters().Length - 1;
 132        }
 133
 134        private static string BuildErrorMessage(Type clientType, Type optionsType)
 135        {
 2136            var builder = new StringBuilder();
 2137            builder.AppendLine("Unable to find matching constructor. Define one of the follow sets of configuration para
 138
 2139            int counter = 1;
 140
 24141            foreach (var constructor in clientType.GetConstructors())
 142            {
 10143                if (!IsApplicableConstructor(constructor, optionsType))
 144                {
 145                    continue;
 146                }
 147
 8148                builder.Append(counter).Append(". ");
 149
 8150                bool first = true;
 151
 44152                foreach (var parameter in constructor.GetParameters())
 153                {
 18154                    if (IsOptionsParameter(parameter, optionsType))
 155                    {
 156                        break;
 157                    }
 158
 10159                    if (first)
 160                    {
 8161                        first = false;
 162                    }
 163                    else
 164                    {
 2165                        builder.Append(", ");
 166                    }
 167
 10168                    builder.Append(parameter.Name);
 169                }
 170
 8171                builder.AppendLine();
 8172                counter++;
 173            }
 174
 2175            return builder.ToString();
 176        }
 177
 178        private static bool IsApplicableConstructor(ConstructorInfo constructorInfo, Type optionsType)
 179        {
 72180            var parameters = constructorInfo.GetParameters();
 181
 72182            return constructorInfo.IsPublic &&
 72183                   parameters.Length > 0 &&
 72184                   IsOptionsParameter(parameters[parameters.Length - 1], optionsType);
 185        }
 186
 187        private static bool TryConvertArgument(IConfiguration configuration, string parameterName, Type parameterType, o
 188        {
 88189            if (parameterType == typeof(string))
 190            {
 40191                return TryConvertFromString(configuration, parameterName, s => s, out value);
 192            }
 193
 56194            if (parameterType == typeof(Uri))
 195            {
 62196                return TryConvertFromString(configuration, parameterName, s => new Uri(s), out value);
 197            }
 198
 14199            if (configuration[parameterName] != null)
 200            {
 0201                throw new InvalidOperationException($"Unable to convert value '{configuration[parameterName]}' to parame
 202            }
 203
 14204            return TryCreateObject(parameterType, configuration.GetSection(parameterName), out value);
 205        }
 206
 207        private static bool TryConvertFromString(IConfiguration configuration, string parameterName, Func<string, object
 208        {
 74209            string stringValue = configuration[parameterName];
 74210            if (stringValue == null)
 211            {
 46212                value = null;
 46213                return false;
 214            }
 215
 28216            value = func(stringValue);
 28217            return true;
 218        }
 219
 220        internal static bool TryCreateObject(Type type, IConfigurationSection configuration, out object value)
 221        {
 14222            if (!configuration.GetChildren().Any())
 223            {
 6224                value = null;
 6225                return false;
 226            }
 227
 8228            List<object> arguments = new List<object>();
 74229            foreach (var constructor in type.GetConstructors().OrderByDescending(c => c.GetParameters().Length))
 230            {
 20231                arguments.Clear();
 232
 20233                bool match = true;
 70234                foreach (var parameter in constructor.GetParameters())
 235                {
 22236                    if (!TryConvertArgument(configuration, parameter.Name, parameter.ParameterType, out object argument)
 237                    {
 14238                        match = false;
 14239                        break;
 240                    }
 241
 8242                    arguments.Add(argument);
 243                }
 244
 20245                if (!match)
 246                {
 247                    continue;
 248                }
 249
 6250                value = constructor.Invoke(arguments.ToArray());
 6251                return true;
 252            }
 253
 2254            throw new InvalidOperationException($"Unable to convert section '{configuration.Path}' to parameter type '{t
 6255        }
 256
 257    }
 258}