Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 130 additions & 0 deletions src/System.Management.Automation/engine/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1578,6 +1578,136 @@ public ValidateCountAttribute(int minLength, int maxLength)
}
}

/// <summary>
/// Validates that each parameter argument has all of the set property names.
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public sealed class ValidatePropertyAttribute : ValidateEnumeratedArgumentsAttribute
{
/// <summary>
/// Gets or sets the custom error message that is displayed to the user
///
/// The item being validated and a text representation of the required object properties
/// is passed as the first and second formatting argument to the ErrorMessage formatting pattern.
/// <example>
/// <code>
/// [ValidateProperty(
/// "A","B","C",
/// ErrorMessage = "The object '{0}' does not possess all the necessary properties: '{1}'.")
/// </code>
/// </example>
/// </summary>
public string ErrorMessage { get; set; }

/// <summary>
/// Defines the required property names.
/// </summary>
public ICollection<string> RequiredPropertyNames
{
get => RequiredProperties.Keys;

private set
{
foreach (string item in value)
{
RequiredProperties.Add(item, typeof(object));
}
}
}

/// <summary>
/// Defines the required property names and their types.
/// </summary>
public IDictionary<string, Type> RequiredProperties { get; } = new Dictionary<string, Type>();

/// <summary>
/// Validates that each parameter argument possesses the required properties.
/// </summary>
/// <param name="element">Object to validate.</param>
/// <exception cref="ValidationMetadataException">
/// if argument is missing one or more required properties
/// </exception>
protected override void ValidateElement(object element)
{
if (element == null)
{
throw new ValidationMetadataException(
errorId: "ArgumentIsEmpty",
innerException: null,
resourceStr: Metadata.ValidateNotNullFailure);
}

var wrappedObject = PSObject.AsPSObject(element);
var missingProperties = new List<string>();

foreach (string property in RequiredProperties.Keys)
{
if (wrappedObject.Properties[property] == null
|| wrappedObject.Properties[property].Value.GetType() != RequiredProperties[property])
{
missingProperties.Add(property);
}
}

if (missingProperties.Count > 0)
{
var errorMessageFormat = string.IsNullOrEmpty(ErrorMessage) ? Metadata.ValidatePropertyFailure : ErrorMessage;
throw new ValidationMetadataException(
"ValidatePropertyFailure", null,
errorMessageFormat,
element.ToString(), PropertySetAsString());
}
}

private string PropertySetAsString()
{
var propertyStrings = new List<string>();
foreach (var key in RequiredProperties.Keys)
{
propertyStrings.Add(string.Format("[{0}]{1}", RequiredProperties[key], key));
}

return string.Join(CultureInfo.CurrentUICulture.TextInfo.ListSeparator, propertyStrings);
}

/// <summary>
/// Initializes a new instance of the ValidatePropertyAttribute class.
/// </summary>
/// <param name="requiredProperties">List of valid values.</param>
/// <exception cref="ArgumentNullException">For null arguments.</exception>
/// <exception cref="ArgumentOutOfRangeException">For invalid arguments.</exception>
public ValidatePropertyAttribute(params string[] requiredProperties)
{
if (requiredProperties == null)
{
throw PSTraceSource.NewArgumentNullException("validValues");
}

if (requiredProperties.Length == 0)
{
throw PSTraceSource.NewArgumentOutOfRangeException("validValues", requiredProperties);
}

RequiredPropertyNames = requiredProperties;
}

/// <summary>
/// Initializes a new instance of the ValidatePropertyAttribute class.
/// </summary>
/// <param name="requiredProperties">A dictionary or hashtable containing the property names and types required.</param>
public ValidatePropertyAttribute(IDictionary requiredProperties)
{
RequiredProperties = new Dictionary<string, Type>();
foreach (var key in requiredProperties.Keys)
{
string propertyName = LanguagePrimitives.ConvertTo<string>(key);
Type propertyType = LanguagePrimitives.ConvertTo<Type>(requiredProperties[key]);

RequiredProperties.Add(propertyName, propertyType);
}
}
}

/// <summary>
/// Optional base class for <see cref="IValidateSetValuesGenerator"/> implementations that want a default implementation to cache valid values.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -790,6 +790,7 @@ internal static class CoreTypes
{ typeof(ValidateNotNullAttribute), new[] { "ValidateNotNull" } },
{ typeof(ValidateNotNullOrEmptyAttribute), new[] { "ValidateNotNullOrEmpty" } },
{ typeof(ValidatePatternAttribute), new[] { "ValidatePattern" } },
{ typeof(ValidatePropertyAttribute), new[] { "ValidateProperty" } },
{ typeof(ValidateRangeAttribute), new[] { "ValidateRange" } },
{ typeof(ValidateScriptAttribute), new[] { "ValidateScript" } },
{ typeof(ValidateSetAttribute), new[] { "ValidateSet" } },
Expand Down
3 changes: 3 additions & 0 deletions src/System.Management.Automation/resources/Metadata.resx
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,9 @@
<data name="ValidateLengthMaxLengthFailure" xml:space="preserve">
<value>The character length of the {1} argument is too long. Shorten the character length of the argument so it is fewer than or equal to "{0}" characters, and then try the command again.</value>
</data>
<data name="ValidatePropertyFailure" xml:space="preserve">
<value>The argument "{0}" does not contain the required property names or values with types specified by the ValidateProperty attribute: {1}. Supply an argument that possesses the correct properties with the appropriate types and then try the command again.</value>
</data>
<data name="ValidateSetFailure" xml:space="preserve">
<value>The argument "{0}" does not belong to the set "{1}" specified by the ValidateSet attribute. Supply an argument that is in the set and then try the command again.</value>
</data>
Expand Down
4 changes: 4 additions & 0 deletions test/powershell/Language/Parser/TypeAccelerator.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,10 @@ Describe "Type accelerators" -Tags "CI" {
Accelerator = 'ValidateSet'
Type = [System.Management.Automation.ValidateSetAttribute]
}
@{
Accelerator = 'ValidateProperty'
Type = [System.Management.Automation.ValidatePropertyAttribute]
}
@{
Accelerator = 'ValidateUserDrive'
Type = [System.Management.Automation.ValidateUserDriveAttribute]
Expand Down
Loading