Skip to content

Commit

Permalink
Merge branch 'develop' into release/8.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
rjmurillo committed Jun 27, 2017
2 parents d862880 + 9620623 commit 2edb6e1
Show file tree
Hide file tree
Showing 25 changed files with 485 additions and 162 deletions.
11 changes: 5 additions & 6 deletions src/Qwiq.Core/IdentityFieldValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public IdentityFieldValue([NotNull] ITeamFoundationIdentity identity)
: this(identity.DisplayName, identity.Descriptor?.Identifier, identity.TeamFoundationId.ToString())
{
Contract.Requires(identity != null);

if (identity == null) throw new ArgumentNullException(nameof(identity));
}

Expand Down Expand Up @@ -196,22 +196,21 @@ public string IdentityName
public string TeamFoundationId { get; }

/// <summary>
/// Performs an explicit conversion from <see cref="IdentityFieldValue"/> to <see cref="string"/>.
/// Performs an implicit conversion from <see cref="IdentityFieldValue"/> to <see cref="string"/>.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>If <paramref name="value"/> is null, null; otherwise, <see cref="IdentityName"/>.</returns>
public static explicit operator string(IdentityFieldValue value)
public static implicit operator string(IdentityFieldValue value)
{
if (value == null) return null;
return value.IdentityName;
return value?.ToString();
}

/// <inheritdoc />
public override string ToString()
{
return string.IsNullOrEmpty(IdentityName)
? DisplayName
: $"{DisplayName} <{AccountName}>".ToString(CultureInfo.InvariantCulture);
: IdentityName;
}

private static bool TryGetAccountName(string search, out string acccountName)
Expand Down
24 changes: 22 additions & 2 deletions src/Qwiq.Core/TypeParser.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using JetBrains.Annotations;
using System;
using System.Collections;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
Expand All @@ -11,6 +12,8 @@ namespace Microsoft.Qwiq
/// </summary>
public class TypeParser : ITypeParser
{
private static readonly Hashtable TypeConverters = new Hashtable();

private TypeParser()
{
}
Expand Down Expand Up @@ -211,7 +214,8 @@ private static bool TryConvert(Type destinationType, object value, out object re
}

var valueType = value.GetType();
var typeConverter = TypeDescriptor.GetConverter(valueType);
var typeConverter = GetTypeConverter(valueType);

if (typeConverter.CanConvertTo(destinationType))
try
{
Expand All @@ -224,7 +228,7 @@ private static bool TryConvert(Type destinationType, object value, out object re
{
}

typeConverter = TypeDescriptor.GetConverter(destinationType);
typeConverter = GetTypeConverter(destinationType);
if (typeConverter.CanConvertFrom(valueType))
try
{
Expand Down Expand Up @@ -258,6 +262,22 @@ private static bool TryConvert(Type destinationType, object value, out object re
return false;
}

[MustUseReturnValue]
private static TypeConverter GetTypeConverter([NotNull] Type valueType)
{
var hashtable = TypeConverters;

var typeConverter = (TypeConverter) hashtable[valueType];
if (typeConverter != null) return typeConverter;

lock (hashtable)
{
typeConverter = TypeDescriptor.GetConverter(valueType);
hashtable[valueType] = typeConverter;
}
return typeConverter;
}

[ContractAnnotation("value:null => true")]
private static bool ValueRepresentsNull([CanBeNull] object value)
{
Expand Down
29 changes: 13 additions & 16 deletions src/Qwiq.Identity/DisplayNameToAliasValueConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ namespace Microsoft.Qwiq.Identity
/// <summary>
/// Converts a <see cref="string"/> representing an identity to an alias
/// </summary>
/// <seealso cref="IIdentityValueConverter" />
public class DisplayNameToAliasValueConverter : IIdentityValueConverter
public class DisplayNameToAliasValueConverter : IdentityValueConverterBase
{
private static readonly IReadOnlyDictionary<string, object> Empty = new Dictionary<string, object>();
private readonly IIdentityManagementService _identityManagementService;

/// <summary>
Expand All @@ -26,25 +26,19 @@ public class DisplayNameToAliasValueConverter : IIdentityValueConverter
public DisplayNameToAliasValueConverter([NotNull] IIdentityManagementService identityManagementService)
{
Contract.Requires(identityManagementService != null);

_identityManagementService = identityManagementService ?? throw new ArgumentNullException(nameof(identityManagementService));
}

/// <summary>
/// Converts the specified <paramref name="value" /> to an <see cref="T:Dictionary{string, string}" />.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>A <see cref="T:Dictionary{string, object}" /> instance whose key is the <paramref name="value"/> and value is is equivalent to the value of <paramref name="value" />.</returns>
public object Map([CanBeNull] object value)
public override IReadOnlyDictionary<string, object> Map(IEnumerable<string> values)
{
if (value is string stringValue) return GetIdentityNames(stringValue);
if (values == null) return Empty;
return GetIdentityNames(values.ToArray());
}

if (value is IEnumerable<string> stringArray) return GetIdentityNames(stringArray.ToArray());

return value;
}

private Dictionary<string, string> GetIdentityNames(params string[] displayNames)
private Dictionary<string, object> GetIdentityNames(params string[] displayNames)
{
return
GetAliasesForDisplayNames(displayNames)
Expand All @@ -56,9 +50,12 @@ private Dictionary<string, string> GetIdentityNames(params string[] displayNames
var retval = kvp.Value.FirstOrDefault();
if (kvp.Value.Length > 1)
{
Trace.TraceWarning("Multiple identities found matching '{0}':\r\n\r\n- {1}\r\n\r\nChoosing '{2}'.", kvp.Key, string.Join("\r\n- ", kvp.Value), retval);
var m =
$"Multiple identities found matching '{kvp.Key}'. Please specify one of the following identities:{string.Join("\r\n- ", kvp.Value)}";

throw new MultipleIdentitiesFoundException(m);
}
return retval;
return (object)retval;
},
Comparer.OrdinalIgnoreCase);
}
Expand Down
13 changes: 11 additions & 2 deletions src/Qwiq.Identity/IIdentityValueConverter.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,27 @@
using System;
using JetBrains.Annotations;
using System.Collections.Generic;

namespace Microsoft.Qwiq.Identity
{
/// <summary>
/// Defines a method that converts the value of the implementing reference or value type to another reference or value type.
/// </summary>
public interface IIdentityValueConverter
public interface IIdentityValueConverter<T, U>
{
/// <summary>
/// Converts the specified <paramref name="value"/> to an <see cref="object"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
/// <returns>An <see cref="object"/> instance whose value is equivalent to the value of <paramref name="value"/>.</returns>
[ContractAnnotation("null => null; notnull => notnull")]
object Map([CanBeNull] object value);
U Map([CanBeNull] T value);

IReadOnlyDictionary<T,U> Map(IEnumerable<T> values);

[Obsolete("This method is depreciated and will be removed in a future version.")]
object Map(object value);
}


}
46 changes: 25 additions & 21 deletions src/Qwiq.Identity/IdentityAliasValueConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ namespace Microsoft.Qwiq.Identity
/// <summary>
/// Converts a <see cref="string"/> representing an alias to a user principal name.
/// </summary>
/// <seealso cref="IIdentityValueConverter" />
/// <seealso cref="IIdentityManagementService"/>
public class IdentityAliasValueConverter : IIdentityValueConverter
public class IdentityAliasValueConverter : IdentityValueConverterBase
{
private static readonly IReadOnlyDictionary<string, object> Empty = new Dictionary<string, object>();
private readonly string[] _domains;

private readonly IIdentityManagementService _identityManagementService;
Expand Down Expand Up @@ -53,29 +53,33 @@ public IdentityAliasValueConverter(
_domains = domains;
}

/// <summary>
/// Converts the specified <paramref name="value" /> to an <see cref="object" />.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>An <see cref="object" /> instance whose value is equivilent to the value of <paramref name="value" />.</returns>
/// <example>"danj" becomes "[email protected]"</example>
[ContractAnnotation("null => null; notnull => notnull")]
public object Map([CanBeNull] object value)
public override IReadOnlyDictionary<string, object> Map(IEnumerable<string> values)
{
if (value is string stringValue) return GetIdentityNames(stringValue).Single();

if (value is IEnumerable<string> stringArray) return GetIdentityNames(stringArray.ToArray());
if (values == null) return Empty;
var r = GetIdentityForAliases(values.ToList(), _tenantId, _domains);
var retval = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (var result in r)
{
var v = result.Value as ITeamFoundationIdentity;
retval.Add(result.Key, v?.GetIdentityName() ?? result.Key);
}

return value;
return retval;
}

private string[] GetIdentityNames(params string[] aliases)
[Obsolete("This method is depreciated and will be removed in a future version.")]
public override object Map(object value)
{
var identities = GetIdentityForAliases(aliases.ToList(), _tenantId, _domains);
return identities.Select(i => i.Value.GetIdentityName() ?? i.Key).ToArray();
if (value is string stringValue) return Map(stringValue);
if (value is IEnumerable<string> stringArray)
{
return Map(stringArray).Select(s => (string)s.Value).ToArray();
}

return value;
}

private IDictionary<string, ITeamFoundationIdentity> GetIdentityForAliases(
private Dictionary<string, object> GetIdentityForAliases(
ICollection<string> logonNames,
string tenantId,
params string[] domains)
Expand All @@ -96,7 +100,7 @@ private IDictionary<string, ITeamFoundationIdentity> GetIdentityForAliases(
return identities;
}

private IDictionary<string, ICollection<IIdentityDescriptor>> CreatePossibleIdentityDescriptors(
private Dictionary<string, ICollection<IIdentityDescriptor>> CreatePossibleIdentityDescriptors(
IEnumerable<string> aliases,
string[] domains,
string tenantId)
Expand All @@ -119,14 +123,14 @@ private IDictionary<string, ICollection<IIdentityDescriptor>> CreatePossibleIden
return descriptors;
}

private IDictionary<string, ITeamFoundationIdentity> GetIdentitiesForAliases(
private Dictionary<string, object> GetIdentitiesForAliases(
IDictionary<string, ICollection<IIdentityDescriptor>> aliasDescriptors)
{
var descriptors = aliasDescriptors.SelectMany(ad => ad.Value).ToList();
var descriptorToAliasLookup = aliasDescriptors
.SelectMany(ad => ad.Value.Select(d => new KeyValuePair<string, string>(d.ToString(), ad.Key)))
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase);
var validIdentities = new Dictionary<string, ITeamFoundationIdentity>(StringComparer.OrdinalIgnoreCase);
var validIdentities = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
var lookupResults = _identityManagementService.ReadIdentities(descriptors);
foreach (var identity in lookupResults.Where(id => id != null))
{
Expand Down
50 changes: 50 additions & 0 deletions src/Qwiq.Identity/IdentityFieldValueConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.Qwiq.Identity
{
public class IdentityFieldValueConverter : IdentityValueConverterBase
{
private static readonly IReadOnlyDictionary<string, object> Empty = new Dictionary<string, object>();
[NotNull] private readonly IIdentityManagementService _identityManagementService;

public IdentityFieldValueConverter(
[NotNull] IIdentityManagementService identityManagementService)
{
_identityManagementService = identityManagementService ?? throw new ArgumentNullException(nameof(identityManagementService));
}


public override IReadOnlyDictionary<string, object> Map(IEnumerable<string> values)
{
if (values == null) return Empty;

var identities = _identityManagementService.ReadIdentities(IdentitySearchFactor.DisplayName, values);
var retval = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);
foreach (var identity in identities)
{
var c = identity.Value?.Count();
if (c.GetValueOrDefault(0) == 0)
{
retval.Add(identity.Key, new IdentityFieldValue(identity.Key));
continue;
}

if (c > 1)
{
var m =
$"Multiple identities found matching '{identity.Key}'. Please specify one of the following identities:{string.Join("\r\n- ", identity.Value)}";

throw new MultipleIdentitiesFoundException(m);
}

var v = new IdentityFieldValue(identity.Value.FirstOrDefault());
retval.Add(identity.Key, v);
}

return retval;
}
}
}
42 changes: 42 additions & 0 deletions src/Qwiq.Identity/IdentityValueConverterBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;

namespace Microsoft.Qwiq.Identity
{
public abstract class IdentityValueConverterBase : IIdentityValueConverter<string, object>
{
public virtual object Map(string value)
{
if (string.IsNullOrWhiteSpace(value)) return value;

var r = Map(new[] {value});
if (r != null)
{
var kvp = r.FirstOrDefault();
if (!EqualityComparer<KeyValuePair<string, object>>.Default.Equals(kvp, default(KeyValuePair<string, object>)))
{
if (kvp.Value != null)
{
return kvp.Value;
}
}
}

return value;
}
public abstract IReadOnlyDictionary<string, object> Map(IEnumerable<string> values);

[Obsolete("This method is depreciated and will be removed in a future version.")]
public virtual object Map(object value)
{
if (value is string stringValue) return Map(stringValue);
if (value is IEnumerable<string> stringArray)
{
return Map(stringArray).Select(s=>s.Value).ToArray();
}

return value;
}
}
}
26 changes: 26 additions & 0 deletions src/Qwiq.Identity/MultipleIdentitiesFoundException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Runtime.Serialization;

namespace Microsoft.Qwiq.Identity
{
[Serializable]
public class MultipleIdentitiesFoundException : ApplicationException
{
public MultipleIdentitiesFoundException(string message)
: base(message)
{
}

public MultipleIdentitiesFoundException() : base()
{
}

public MultipleIdentitiesFoundException(string message, Exception innerException) : base(message, innerException)
{
}

protected MultipleIdentitiesFoundException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}
Loading

0 comments on commit 2edb6e1

Please sign in to comment.