diff --git a/Tests/TransformTagAttribute.tests.ps1 b/Tests/TransformTagAttribute.tests.ps1 new file mode 100644 index 000000000..5ce505e25 --- /dev/null +++ b/Tests/TransformTagAttribute.tests.ps1 @@ -0,0 +1,68 @@ +Describe 'TransformTagAttribute' { + BeforeAll { + function Test-TagBinding { + [CmdletBinding()] + param ( + [VaporShell.Core.TransformTag()] + [object[]] $Tags + ) + + $Tags + } + } + + It 'Should return one or more tags when given ' -TestCases @( + @{ + TestName = 'multiple hashtables' + InputData = @{ one = 1 }, @{ two = 2 } + ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' } + } + @{ + TestName = 'multiple PSObjects' + InputData = [PSCustomObject]@{ one = 1 }, [PSCustomObject]@{ two = 2 } + ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' } + } + @{ + TestName = 'a single hashtable with multiple keys' + InputData = [PSCustomobject]@{ one = 1; two = 2 } + ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' }, [PSCustomObject]@{ Key = 'two'; Value = '2' } + } + @{ + TestName = 'an existing VSTag object' + InputData = Add-VSTag -Key one -Value '1' + ExpectedResult = [PSCustomObject]@{ Key = 'one'; Value = '1' } + } + @{ + TestName = 'a hashtable with Key and Value keys' + InputData = @{Key = 'Name'; Value = 'Harold'} + ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + } + @{ + TestName = 'a hashtable with lowercase key and value keys' + InputData = @{ key = 'Name'; value = 'Harold'} + ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + } + @{ + TestName = 'a hashtable with mixed case key and value keys' + InputData = @{ kEy = 'Name'; ValUE = 'Harold'} + ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + } + @{ + TestName = 'a PSCustomObject key and value properties' + InputData = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + } + @{ + TestName = 'a PSCustomObject lowercase key and value properties' + InputData = [PSCustomObject]@{ key = 'Name'; value = 'Harold' } + ExpectedResult = [PSCustomObject]@{ Key = 'Name'; Value = 'Harold' } + } + ) { + param ( + $InputData, + $ExpectedResult + ) + + Assert-Equivalent -Actual (Test-TagBinding -Tags $InputData) -Expected $ExpectedResult + } +} diff --git a/VaporShell.Core/VaporShell.Core.cs b/VaporShell.Core/VaporShell.Core.cs index 59d9a15f1..4413b0fc3 100644 --- a/VaporShell.Core/VaporShell.Core.cs +++ b/VaporShell.Core/VaporShell.Core.cs @@ -9,33 +9,43 @@ public class TransformTagAttribute : ArgumentTransformationAttribute { private EngineIntrinsics engineIntrinsics; - private IEnumerable ConvertToTag(object key, object value) + private PSObject ConvertToTag(object key, object value) { PSObject tag = new PSObject(); - tag.Members.Add(new PSNoteProperty("Key", key)); - tag.Members.Add(new PSNoteProperty("Value", value)); - tag.TypeNames.Insert(0,"Vaporshell.Resource.Tag"); + tag.Members.Add(new PSNoteProperty("Key", key.ToString())); + tag.Members.Add(new PSNoteProperty("Value", value.ToString())); + tag.TypeNames.Insert(0, "Vaporshell.Resource.Tag"); - yield return tag; + return tag; } - private IEnumerable TransformHashtable(IDictionary inputData) + private bool TryGetKey(IDictionary dictionary, string keyName, ref string KeyValue) { - if (inputData.Contains("Key") && inputData.Contains("Value")) + foreach (object key in dictionary.Keys) { - foreach (PSObject tag in this.ConvertToTag(inputData["Key"],inputData["Value"])) + if (key.ToString().ToLower() == keyName.ToLower()) { - yield return tag; + KeyValue = dictionary[key].ToString(); + return true; } } + + return false; + } + + private IEnumerable TransformHashtable(IDictionary inputData) + { + string keyName = ""; + string value = ""; + if (this.TryGetKey(inputData, "key", ref keyName) && this.TryGetKey(inputData, "value", ref value)) + { + yield return this.ConvertToTag(keyName, value); + } else { foreach (string key in inputData.Keys) { - foreach (PSObject tag in this.ConvertToTag(key,inputData[key])) - { - yield return tag; - } + yield return this.ConvertToTag(key, inputData[key]); } } } @@ -45,80 +55,74 @@ private IEnumerable TransformPSObject(PSObject inputData) var props = new List(); foreach (var property in inputData.Properties) { - props.Add(property.Name); + props.Add(property.Name.ToLower()); } - if (props.Contains("Key") && props.Contains("Value")) - { - object key = null; - object value = null; - foreach (var property in inputData.Properties) - { - if (property.Name == "Key") - { - key = property.Value; - } - else if (property.Name == "Value") - { - value = property.Value; - } - } - foreach (PSObject tag in this.ConvertToTag(key,value)) - { - yield return tag; - } + if (props.Contains("key") && props.Contains("value")) + { + yield return this.ConvertToTag(inputData.Properties["Key"].Value, inputData.Properties["Value"].Value); } else { foreach (var property in inputData.Properties) { - foreach (PSObject tag in this.ConvertToTag(property.Name,property.Value)) - { - yield return tag; - } + yield return this.ConvertToTag(property.Name, property.Value); } } } - private object TransformSingle(object inputData) + private IEnumerable TransformSingle(object inputData) { if (inputData is IDictionary) { - return this.TransformHashtable(inputData as IDictionary); + foreach (PSObject tag in this.TransformHashtable(inputData as IDictionary)) + { + yield return tag; + } } else if (inputData is PSObject) { PSObject psObject = inputData as PSObject; if (psObject.TypeNames.Contains("Vaporshell.Resource.Tag")) { - return inputData; + yield return psObject; } else if (psObject.TypeNames.Contains("System.Management.Automation.PSCustomObject")) { - return this.TransformPSObject(psObject); + foreach (PSObject tag in this.TransformPSObject(psObject)) + { + yield return tag; + } } } - return null; } - private IEnumerable TransformData(object inputData) + private IEnumerable TransformData(object inputData) { if (inputData is Array) { foreach (object item in inputData as Array) { - yield return this.TransformSingle(item); + // This is returning an unenumerated array + foreach (PSObject tag in this.TransformSingle(item)) + { + yield return tag; + } } } else { - yield return this.TransformSingle(inputData); + foreach (PSObject tag in this.TransformSingle(inputData)) + { + yield return tag; + } } } public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) { this.engineIntrinsics = engineIntrinsics; + return this.TransformData(inputData); } } diff --git a/VaporShell.Core/VaporShell.Core.csproj b/VaporShell.Core/VaporShell.Core.csproj index 2d9368eee..dbc4fbfbd 100644 --- a/VaporShell.Core/VaporShell.Core.csproj +++ b/VaporShell.Core/VaporShell.Core.csproj @@ -3,8 +3,9 @@ netstandard2.0 - - + + + diff --git a/VaporShell.Core/VaporShell.Core.sln b/VaporShell.Core/VaporShell.Core.sln new file mode 100644 index 000000000..0878d3480 --- /dev/null +++ b/VaporShell.Core/VaporShell.Core.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29806.167 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "VaporShell.Core", "VaporShell.Core.csproj", "{8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FF3CC38-B1B4-4F93-8F90-7FDF82AFD193}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {115A389F-865B-4A5F-9761-0E606AB74BF5} + EndGlobalSection +EndGlobal