From 5441af8256dd103eaa52481f25f8fe114eea2d68 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 20 Apr 2021 10:48:46 -0700 Subject: [PATCH 1/8] Add TryPSGetInfo utility function --- src/code/Utils.cs | 289 ++++++++++++++++++++++++++++++++++++++- test/PSGetInfo.Tests.ps1 | 54 ++++++++ test/PSGetModuleInfo.xml | 152 ++++++++++++++++++++ test/PSGetTestUtils.psm1 | 9 +- test/toBeRemoved.txt | 0 5 files changed, 494 insertions(+), 10 deletions(-) create mode 100644 test/PSGetInfo.Tests.ps1 create mode 100644 test/PSGetModuleInfo.xml delete mode 100644 test/toBeRemoved.txt diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 95de2075d..1ad618096 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -2,15 +2,17 @@ // Licensed under the MIT License. using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Management.Automation; using System.Management.Automation.Language; namespace Microsoft.PowerShell.PowerShellGet.UtilClasses { - #region Utils - internal static class Utils { - #region Members + #region Public methods public static string TrimQuotes(string name) { @@ -37,8 +39,289 @@ public static string QuoteName(string name) return "'" + CodeGeneration.EscapeSingleQuotedStringContent(name) + "'"; } + /// + /// Reads a 'PSGetModuleInfo.xml' PowerShell serialized file and returns + /// a PSGetInfo object containing the file contents. + /// + public static bool TryGetPSGetInfo( + string filePath, + out PSGetInfo psGetInfo, + out string errorMsg) + { + psGetInfo = null; + errorMsg = string.Empty; + + if (string.IsNullOrWhiteSpace(filePath)) + { + errorMsg = "GetPSGetInfo: Invalid file path. Filepath cannot be empty or whitespace."; + return false; + } + + try + { + // Read and deserialize information xml file. + var psObjectInfo = (PSObject) PSSerializer.Deserialize( + System.IO.File.ReadAllText( + filePath)); + + psGetInfo = new PSGetInfo() + { + AdditionalMetadata = GetProperty>(nameof(PSGetInfo.AdditionalMetadata), psObjectInfo), + Author = GetProperty(nameof(PSGetInfo.Author), psObjectInfo), + CompanyName = GetProperty(nameof(PSGetInfo.CompanyName), psObjectInfo), + Copyright = GetProperty(nameof(PSGetInfo.Copyright), psObjectInfo), + Dependencies = GetStringArray(GetProperty(nameof(PSGetInfo.Dependencies), psObjectInfo)), + Description = GetProperty(nameof(PSGetInfo.Description), psObjectInfo), + IconUri = GetProperty(nameof(PSGetInfo.IconUri), psObjectInfo), + Includes = new PSGetInclude(GetProperty(nameof(PSGetInfo.Includes), psObjectInfo)), + InstalledDate = GetProperty(nameof(PSGetInfo.InstalledDate), psObjectInfo), + InstalledLocation = GetProperty(nameof(PSGetInfo.InstalledLocation), psObjectInfo), + LicenseUri = GetProperty(nameof(PSGetInfo.LicenseUri), psObjectInfo), + Name = GetProperty(nameof(PSGetInfo.Name), psObjectInfo), + PackageManagementProvider = GetProperty(nameof(PSGetInfo.PackageManagementProvider), psObjectInfo), + PowerShellGetFormatVersion = GetProperty(nameof(PSGetInfo.PowerShellGetFormatVersion), psObjectInfo), + ProjectUri = GetProperty(nameof(PSGetInfo.ProjectUri), psObjectInfo), + PublishedDate = GetProperty(nameof(PSGetInfo.PublishedDate), psObjectInfo), + ReleaseNotes = GetProperty(nameof(PSGetInfo.ReleaseNotes), psObjectInfo), + Repository = GetProperty(nameof(PSGetInfo.Repository), psObjectInfo), + RepositorySourceLocation = GetProperty(nameof(PSGetInfo.RepositorySourceLocation), psObjectInfo), + Tags = GetStringArray(GetProperty(nameof(PSGetInfo.Tags), psObjectInfo)), + Type = GetProperty(nameof(PSGetInfo.Type), psObjectInfo), + UpdatedDate = GetProperty(nameof(PSGetInfo.UpdatedDate), psObjectInfo), + Version = GetProperty(nameof(PSGetInfo.Version), psObjectInfo) + }; + + return true; + } + catch(Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"GetPSGetInfo: Cannot read the PowerShellGet information file with error: {0}", + ex.Message); + + return false; + } + } + + /// + /// Converts an ArrayList of object types to a string array. + /// + public static string[] GetStringArray(ArrayList list) + { + if (list == null) { return null; } + + var strArray = new string[list.Count]; + for (int i=0; i(PSObject psObject) + { + // We only convert Dictionary types. + if (typeof(T) != typeof(Dictionary)) + { + return default(T); + } + + var dict = new Dictionary(); + foreach (var prop in psObject.Properties) + { + dict.Add(prop.Name, prop.Value.ToString()); + } + + return (T)Convert.ChangeType(dict, typeof(T)); + } + + private static T GetProperty( + string Name, + PSObject psObjectInfo) + { + var val = psObjectInfo.Properties[Name]?.Value; + if (val == null) + { + return default(T); + } + + switch (val) + { + case T valType: + return valType; + + case PSObject valPSObject: + switch (valPSObject.BaseObject) + { + case T valBase: + return valBase; + + case PSCustomObject _: + // A base object of PSCustomObject means this is additional metadata + // and type T should be Dictionary. + return ConvertToType(valPSObject); + + default: + return default(T); + } + + default: + return default(T); + } + } + #endregion } + #region PSGetInfo classes + + internal sealed class PSGetInclude + { + #region Properties + + public string[] Cmdlet { get; } + + public string[] Command { get; } + + public string[] DscResource { get; } + + public string[] Function { get; } + + public string[] RoleCapability { get; } + + public string[] Workflow { get; } + + #endregion + + #region Constructor + + /// + /// Constructor + /// + /// Provided hashtable has form: + /// Key: Cmdlet + /// Value: ArrayList of Cmdlet name strings + /// Key: Command + /// Value: ArrayList of Command name strings + /// Key: DscResource + /// Value: ArrayList of DscResource name strings + /// Key: Function + /// Value: ArrayList of Function name strings + /// Key: RoleCapability (deprecated for PSGetV3) + /// Value: ArrayList of RoleCapability name strings + /// Key: Workflow (deprecated for PSGetV3) + /// Value: ArrayList of Workflow name strings + /// + /// Hashtable of PSGet includes + public PSGetInclude(Hashtable includes) + { + if (includes == null) { return; } + + Cmdlet = GetHashTableItem(includes, nameof(Cmdlet)); + Command = GetHashTableItem(includes, nameof(Command)); + DscResource = GetHashTableItem(includes, nameof(DscResource)); + Function = GetHashTableItem(includes, nameof(Function)); + RoleCapability = GetHashTableItem(includes, nameof(RoleCapability)); + Workflow = GetHashTableItem(includes, nameof(Workflow)); + } + + #endregion + + #region Private methods + + private string[] GetHashTableItem( + Hashtable table, + string name) + { + if (table.ContainsKey(name) && + table[name] is PSObject psObjectItem) + { + return Utils.GetStringArray(psObjectItem.BaseObject as ArrayList); + } + + return null; + } + + #endregion + } + + internal sealed class PSGetInfo + { + #region Properties + + public Dictionary AdditionalMetadata { get; set; } + + public string Author { get; set; } + + public string CompanyName { get; set; } + + public string Copyright { get; set; } + + public string[] Dependencies { get; set; } + + public string Description { get; set; } + + public Uri IconUri { get; set; } + + public PSGetInclude Includes { get; set; } + + public DateTime InstalledDate { get; set; } + + public string InstalledLocation { get; set; } + + public Uri LicenseUri { get; set; } + + public string Name { get; set; } + + public string PackageManagementProvider { get; set; } + + public string PowerShellGetFormatVersion { get; set; } + + public Uri ProjectUri { get; set; } + + public DateTime PublishedDate { get; set; } + + public string ReleaseNotes { get; set; } + + public string Repository { get; set; } + + public string RepositorySourceLocation { get; set; } + + public string[] Tags { get; set; } + + public string Type { get; set; } + + public DateTime UpdatedDate { get; set; } + + public Version Version { get; set; } + + #endregion + } + + #endregion + + #region Test Hooks + + public sealed class TestHooks + { + public static PSObject ReadPSGetInfo(string filePath) + { + if (Utils.TryGetPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) + { + return PSObject.AsPSObject(psGetInfo); + } + else + { + throw new PSInvalidOperationException(errorMsg); + } + } + } + #endregion } diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetInfo.Tests.ps1 new file mode 100644 index 000000000..76685ae27 --- /dev/null +++ b/test/PSGetInfo.Tests.ps1 @@ -0,0 +1,54 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +$psGetMod = Get-Module -Name PowerShellGet +if ((! $psGetMod) -or (($psGetMod | Select-Object Version) -lt 3.0.0)) +{ + Write-Verbose -Message "Importing PowerShellGet 3.0.0 for test" -Verbose + Import-Module -Name PowerShellGet -MinimumVersion 3.0.0 -Force +} + +Describe "Read PSGetModuleInfo xml file" -tags CI { + + It "Verifies expected error with null path" { + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($null) } | Should -Throw -ErrorId 'PSInvalidOperationException' + } + + It "Verifies expected error with invalid file path" { + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo('nonePath') } | Should -Throw -ErrorId 'PSInvalidOperationException' + } + + It "Verifies PSGetModuleInfo.xml file is read successfully" { + $fileToRead = Join-Path -Path $PSScriptRoot -ChildPath "PSGetModuleInfo.xml" + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) + # + $psGetInfo.AdditionalMetadata.Keys | Should -HaveCount 22 + $psGetInfo.AdditionalMetadata['copyright'] | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' + $psGetInfo.AdditionalMetadata['tags'] | Should -BeLike 'PSModule PSEdition_Core*' + $psGetInfo.AdditionalMetadata['ItemType'] | Should -BeExactly 'Module' + # + $psGetInfo.Author | Should -BeExactly 'Microsoft Corporation' + $psGetInfo.CompanyName | Should -BeExactly 'Microsoft Corporation' + $psGetInfo.Copyright | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' + $psGetInfo.Dependencies | Should -HaveCount 0 + $psGetInfo.Description | Should -BeLike 'This module provides a convenient way for a user to store*' + $psGetInfo.IconUri | Should -BeNullOrEmpty + $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 + $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' + $psGetInfo.InstalledDate.ToString() | Should -BeExactly '3/25/2021 11:12:41 AM' + $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' + $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' + $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' + $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' + $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty + $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' + $psGetInfo.PublishedDate.ToString() | Should -BeExactly '3/25/2021 6:08:10 PM' + $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty + $psGetInfo.Repository | Should -BeExactly 'PSGallery' + $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' + $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') + $psGetInfo.Type | Should -BeExactly 'Module' + $psGetInfo.UpdatedDate.ToString() | Should -BeExactly '1/1/0001 12:00:00 AM' + $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' + } +} diff --git a/test/PSGetModuleInfo.xml b/test/PSGetModuleInfo.xml new file mode 100644 index 000000000..082a4ebac --- /dev/null +++ b/test/PSGetModuleInfo.xml @@ -0,0 +1,152 @@ + + + + Microsoft.PowerShell.Commands.PSRepositoryItemInfo + System.Management.Automation.PSCustomObject + System.Object + + + Microsoft.PowerShell.SecretManagement + 1.0.0 + Module + This module provides a convenient way for a user to store and retrieve secrets. The secrets are_x000D__x000A_stored in registered extension vaults. An extension vault can store secrets locally or remotely._x000D__x000A_SecretManagement coordinates access to the secrets through the registered vaults._x000D__x000A__x000D__x000A_Go to GitHub for more information about the module and to submit issues:_x000D__x000A_https://github.com/powershell/SecretManagement + Microsoft Corporation + Microsoft Corporation + (c) Microsoft Corporation. All rights reserved. +
2021-03-25T18:08:10-07:00
+ +
2021-03-25T11:12:41.7662015-07:00
+ + + + Microsoft.PowerShell.Commands.DisplayHintType + System.Enum + System.ValueType + System.Object + + DateTime + 2 + + +
+ + https://github.com/PowerShell/SecretManagement/blob/master/LICENSE + https://github.com/powershell/secretmanagement + + + + System.Object[] + System.Array + System.Object + + + PSModule + PSEdition_Core + + + + + System.Collections.Hashtable + System.Object + + + + DscResource + + + + + + + RoleCapability + + + + Function + + + + Cmdlet + + + + Register-SecretVault + Unregister-SecretVault + Get-SecretVault + Set-SecretVaultDefault + Test-SecretVault + Set-Secret + Set-SecretInfo + Get-Secret + Get-SecretInfo + Remove-Secret + + + + + Command + + + + Register-SecretVault + Unregister-SecretVault + Get-SecretVault + Set-SecretVaultDefault + Test-SecretVault + Set-Secret + Set-SecretInfo + Get-Secret + Get-SecretInfo + Remove-Secret + + + + + Workflow + + + + + + + + + + + https://www.powershellgallery.com/api/v2 + PSGallery + NuGet + + + System.Management.Automation.PSCustomObject + System.Object + + + (c) Microsoft Corporation. All rights reserved. + This module provides a convenient way for a user to store and retrieve secrets. The secrets are_x000D__x000A_stored in registered extension vaults. An extension vault can store secrets locally or remotely._x000D__x000A_SecretManagement coordinates access to the secrets through the registered vaults._x000D__x000A__x000D__x000A_Go to GitHub for more information about the module and to submit issues:_x000D__x000A_https://github.com/powershell/SecretManagement + False + True + True + 0 + 15034 + 55046 + 3/25/2021 6:08:10 PM -07:00 + 3/25/2021 6:08:10 PM -07:00 + 3/25/2021 6:08:10 PM -07:00 + PSModule PSEdition_Core PSCmdlet_Register-SecretVault PSCommand_Register-SecretVault PSCmdlet_Unregister-SecretVault PSCommand_Unregister-SecretVault PSCmdlet_Get-SecretVault PSCommand_Get-SecretVault PSCmdlet_Set-SecretVaultDefault PSCommand_Set-SecretVaultDefault PSCmdlet_Test-SecretVault PSCommand_Test-SecretVault PSCmdlet_Set-Secret PSCommand_Set-Secret PSCmdlet_Set-SecretInfo PSCommand_Set-SecretInfo PSCmdlet_Get-Secret PSCommand_Get-Secret PSCmdlet_Get-SecretInfo PSCommand_Get-SecretInfo PSCmdlet_Remove-Secret PSCommand_Remove-Secret PSIncludes_Cmdlet + False + 2021-03-25T18:08:10Z + 1.0.0 + Microsoft Corporation + false + Module + Microsoft.PowerShell.SecretManagement.nuspec|Microsoft.PowerShell.SecretManagement.dll|Microsoft.PowerShell.SecretManagement.format.ps1xml|Microsoft.PowerShell.SecretManagement.psd1|en-US\about_Microsoft.PowerShell.SecretManagement.help.txt|en-US\Microsoft.PowerShell.SecretManagement.dll-Help.xml + a5c858f6-4a8e-41f1-b1ee-0ff8f6ad69d3 + 5.1 + Microsoft Corporation + + + C:\Users\paulhi\OneDrive - Microsoft\Documents\PowerShell\Modules\Microsoft.PowerShell.SecretManagement\1.0.0 +
+
+
diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index ca57a7294..a66d3452f 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -1,10 +1,5 @@ -<##################################################################################### - # File: PSGetTestUtils.psm1 - # - # Copyright (c) Microsoft Corporation, 2020 - #####################################################################################> - -#."$PSScriptRoot\uiproxy.ps1" +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. $psGetMod = Get-Module -Name PowerShellGet if ((! $psGetMod) -or (($psGetMod | Select-Object Version) -lt 3.0.0)) diff --git a/test/toBeRemoved.txt b/test/toBeRemoved.txt deleted file mode 100644 index e69de29bb..000000000 From 2f7cf7d0a27decfe437c091c3b4d0419478cf958 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 20 Apr 2021 12:19:29 -0700 Subject: [PATCH 2/8] Fix Codacy and test issues --- src/code/Utils.cs | 4 ++-- test/PSGetInfo.Tests.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 1ad618096..25f7344a3 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -64,7 +64,7 @@ public static bool TryGetPSGetInfo( System.IO.File.ReadAllText( filePath)); - psGetInfo = new PSGetInfo() + psGetInfo = new PSGetInfo { AdditionalMetadata = GetProperty>(nameof(PSGetInfo.AdditionalMetadata), psObjectInfo), Author = GetProperty(nameof(PSGetInfo.Author), psObjectInfo), @@ -308,7 +308,7 @@ internal sealed class PSGetInfo #region Test Hooks - public sealed class TestHooks + public static class TestHooks { public static PSObject ReadPSGetInfo(string filePath) { diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetInfo.Tests.ps1 index 76685ae27..870dc3089 100644 --- a/test/PSGetInfo.Tests.ps1 +++ b/test/PSGetInfo.Tests.ps1 @@ -35,20 +35,20 @@ Describe "Read PSGetModuleInfo xml file" -tags CI { $psGetInfo.IconUri | Should -BeNullOrEmpty $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' - $psGetInfo.InstalledDate.ToString() | Should -BeExactly '3/25/2021 11:12:41 AM' + $psGetInfo.InstalledDate.ToLongDateString() | Should -BeExactly 'Thursday, March 25, 2021' $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' - $psGetInfo.PublishedDate.ToString() | Should -BeExactly '3/25/2021 6:08:10 PM' + $psGetInfo.PublishedDate.ToLongDateString() | Should -BeExactly 'Thursday, March 25, 2021' $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty $psGetInfo.Repository | Should -BeExactly 'PSGallery' $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') $psGetInfo.Type | Should -BeExactly 'Module' - $psGetInfo.UpdatedDate.ToString() | Should -BeExactly '1/1/0001 12:00:00 AM' + $psGetInfo.UpdatedDate.ToLongDateString() | Should -BeExactly 'Monday, January 1, 0001' $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' } } From 9e03e7e60d1f8b6c9f6684afbe0174de56443cfb Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 20 Apr 2021 12:51:12 -0700 Subject: [PATCH 3/8] Fix DateTime tests --- test/PSGetInfo.Tests.ps1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetInfo.Tests.ps1 index 870dc3089..4503e48bb 100644 --- a/test/PSGetInfo.Tests.ps1 +++ b/test/PSGetInfo.Tests.ps1 @@ -35,20 +35,20 @@ Describe "Read PSGetModuleInfo xml file" -tags CI { $psGetInfo.IconUri | Should -BeNullOrEmpty $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' - $psGetInfo.InstalledDate.ToLongDateString() | Should -BeExactly 'Thursday, March 25, 2021' + $psGetInfo.InstalledDate.Ticks | Should -BeExactly 637522675617662015 $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' - $psGetInfo.PublishedDate.ToLongDateString() | Should -BeExactly 'Thursday, March 25, 2021' + $psGetInfo.PublishedDate.Ticks | Should -BeExactly 637522924900000000 $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty $psGetInfo.Repository | Should -BeExactly 'PSGallery' $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') $psGetInfo.Type | Should -BeExactly 'Module' - $psGetInfo.UpdatedDate.ToLongDateString() | Should -BeExactly 'Monday, January 1, 0001' + $psGetInfo.UpdatedDate.Ticks | Should -BeExactly 0 $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' } } From d6b2172e8109d0580430df583ca68ac73f2eb0b0 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Tue, 20 Apr 2021 13:05:43 -0700 Subject: [PATCH 4/8] Rename method --- src/code/Utils.cs | 4 ++-- test/PSGetInfo.Tests.ps1 | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 25f7344a3..e2afba7f7 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -43,7 +43,7 @@ public static string QuoteName(string name) /// Reads a 'PSGetModuleInfo.xml' PowerShell serialized file and returns /// a PSGetInfo object containing the file contents. /// - public static bool TryGetPSGetInfo( + public static bool TryReadPSGetInfo( string filePath, out PSGetInfo psGetInfo, out string errorMsg) @@ -312,7 +312,7 @@ public static class TestHooks { public static PSObject ReadPSGetInfo(string filePath) { - if (Utils.TryGetPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) + if (Utils.TryReadPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) { return PSObject.AsPSObject(psGetInfo); } diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetInfo.Tests.ps1 index 4503e48bb..d376e329b 100644 --- a/test/PSGetInfo.Tests.ps1 +++ b/test/PSGetInfo.Tests.ps1 @@ -35,20 +35,20 @@ Describe "Read PSGetModuleInfo xml file" -tags CI { $psGetInfo.IconUri | Should -BeNullOrEmpty $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' - $psGetInfo.InstalledDate.Ticks | Should -BeExactly 637522675617662015 + $psGetInfo.InstalledDate.Year | Should -BeExactly 2021 $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' - $psGetInfo.PublishedDate.Ticks | Should -BeExactly 637522924900000000 + $psGetInfo.PublishedDate.Year | Should -BeExactly 2021 $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty $psGetInfo.Repository | Should -BeExactly 'PSGallery' $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') $psGetInfo.Type | Should -BeExactly 'Module' - $psGetInfo.UpdatedDate.Ticks | Should -BeExactly 0 + $psGetInfo.UpdatedDate.Year | Should -BeExactly 1 $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' } } From babc3fcd594858cfca5d00064bc21a89fab84381 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Thu, 22 Apr 2021 10:45:41 -0700 Subject: [PATCH 5/8] Add TryWritePSGetInfo utility method --- src/code/Utils.cs | 364 ++++++++++++++++++++++++++------------- test/PSGetInfo.Tests.ps1 | 60 +++---- test/PSGetTestUtils.psm1 | 57 ++++++ 3 files changed, 329 insertions(+), 152 deletions(-) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index e2afba7f7..478d17a78 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -39,71 +39,6 @@ public static string QuoteName(string name) return "'" + CodeGeneration.EscapeSingleQuotedStringContent(name) + "'"; } - /// - /// Reads a 'PSGetModuleInfo.xml' PowerShell serialized file and returns - /// a PSGetInfo object containing the file contents. - /// - public static bool TryReadPSGetInfo( - string filePath, - out PSGetInfo psGetInfo, - out string errorMsg) - { - psGetInfo = null; - errorMsg = string.Empty; - - if (string.IsNullOrWhiteSpace(filePath)) - { - errorMsg = "GetPSGetInfo: Invalid file path. Filepath cannot be empty or whitespace."; - return false; - } - - try - { - // Read and deserialize information xml file. - var psObjectInfo = (PSObject) PSSerializer.Deserialize( - System.IO.File.ReadAllText( - filePath)); - - psGetInfo = new PSGetInfo - { - AdditionalMetadata = GetProperty>(nameof(PSGetInfo.AdditionalMetadata), psObjectInfo), - Author = GetProperty(nameof(PSGetInfo.Author), psObjectInfo), - CompanyName = GetProperty(nameof(PSGetInfo.CompanyName), psObjectInfo), - Copyright = GetProperty(nameof(PSGetInfo.Copyright), psObjectInfo), - Dependencies = GetStringArray(GetProperty(nameof(PSGetInfo.Dependencies), psObjectInfo)), - Description = GetProperty(nameof(PSGetInfo.Description), psObjectInfo), - IconUri = GetProperty(nameof(PSGetInfo.IconUri), psObjectInfo), - Includes = new PSGetInclude(GetProperty(nameof(PSGetInfo.Includes), psObjectInfo)), - InstalledDate = GetProperty(nameof(PSGetInfo.InstalledDate), psObjectInfo), - InstalledLocation = GetProperty(nameof(PSGetInfo.InstalledLocation), psObjectInfo), - LicenseUri = GetProperty(nameof(PSGetInfo.LicenseUri), psObjectInfo), - Name = GetProperty(nameof(PSGetInfo.Name), psObjectInfo), - PackageManagementProvider = GetProperty(nameof(PSGetInfo.PackageManagementProvider), psObjectInfo), - PowerShellGetFormatVersion = GetProperty(nameof(PSGetInfo.PowerShellGetFormatVersion), psObjectInfo), - ProjectUri = GetProperty(nameof(PSGetInfo.ProjectUri), psObjectInfo), - PublishedDate = GetProperty(nameof(PSGetInfo.PublishedDate), psObjectInfo), - ReleaseNotes = GetProperty(nameof(PSGetInfo.ReleaseNotes), psObjectInfo), - Repository = GetProperty(nameof(PSGetInfo.Repository), psObjectInfo), - RepositorySourceLocation = GetProperty(nameof(PSGetInfo.RepositorySourceLocation), psObjectInfo), - Tags = GetStringArray(GetProperty(nameof(PSGetInfo.Tags), psObjectInfo)), - Type = GetProperty(nameof(PSGetInfo.Type), psObjectInfo), - UpdatedDate = GetProperty(nameof(PSGetInfo.UpdatedDate), psObjectInfo), - Version = GetProperty(nameof(PSGetInfo.Version), psObjectInfo) - }; - - return true; - } - catch(Exception ex) - { - errorMsg = string.Format( - CultureInfo.InvariantCulture, - @"GetPSGetInfo: Cannot read the PowerShellGet information file with error: {0}", - ex.Message); - - return false; - } - } - /// /// Converts an ArrayList of object types to a string array. /// @@ -121,62 +56,6 @@ public static string[] GetStringArray(ArrayList list) } #endregion - - #region Private methods - - private static T ConvertToType(PSObject psObject) - { - // We only convert Dictionary types. - if (typeof(T) != typeof(Dictionary)) - { - return default(T); - } - - var dict = new Dictionary(); - foreach (var prop in psObject.Properties) - { - dict.Add(prop.Name, prop.Value.ToString()); - } - - return (T)Convert.ChangeType(dict, typeof(T)); - } - - private static T GetProperty( - string Name, - PSObject psObjectInfo) - { - var val = psObjectInfo.Properties[Name]?.Value; - if (val == null) - { - return default(T); - } - - switch (val) - { - case T valType: - return valType; - - case PSObject valPSObject: - switch (valPSObject.BaseObject) - { - case T valBase: - return valBase; - - case PSCustomObject _: - // A base object of PSCustomObject means this is additional metadata - // and type T should be Dictionary. - return ConvertToType(valPSObject); - - default: - return default(T); - } - - default: - return default(T); - } - } - - #endregion } #region PSGetInfo classes @@ -233,6 +112,25 @@ public PSGetInclude(Hashtable includes) #endregion + #region Public methods + + public Hashtable ConvertToHashtable() + { + var hashtable = new Hashtable() + { + { nameof(Cmdlet), Cmdlet }, + { nameof(Command), Command }, + { nameof(DscResource), DscResource }, + { nameof(Function), Function }, + { nameof(RoleCapability), RoleCapability }, + { nameof(Workflow), Workflow } + }; + + return hashtable; + } + + #endregion + #region Private methods private string[] GetHashTableItem( @@ -302,6 +200,211 @@ internal sealed class PSGetInfo public Version Version { get; set; } #endregion + + #region Public static methods + + /// + /// Writes the PSGetInfo properties to the specified file path as a + /// PowerShell serialized xml file, maintaining compatibility with + /// PowerShellGet v2 file format. + /// + public bool TryWritePSGetInfo( + string filePath, + out string errorMsg) + { + errorMsg = string.Empty; + + if (string.IsNullOrWhiteSpace(filePath)) + { + errorMsg = "TryWritePSGetInfo: Invalid file path. Filepath cannot be empty or whitespace."; + return false; + } + + try + { + var infoXml = PSSerializer.Serialize( + source: ConvertToCustomObject(), + depth: 5); + + System.IO.File.WriteAllText( + path: filePath, + contents: infoXml); + + return true; + } + catch(Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryWritePSGetInfo: Cannot convert and write the PowerShellGet information to file, with error: {0}", + ex.Message); + + return false; + } + } + + /// + /// Reads a 'PSGetModuleInfo.xml' PowerShell serialized file and returns + /// a PSGetInfo object containing the file contents. + /// + public static bool TryReadPSGetInfo( + string filePath, + out PSGetInfo psGetInfo, + out string errorMsg) + { + psGetInfo = null; + errorMsg = string.Empty; + + if (string.IsNullOrWhiteSpace(filePath)) + { + errorMsg = "TryReadPSGetInfo: Invalid file path. Filepath cannot be empty or whitespace."; + return false; + } + + try + { + // Read and deserialize information xml file. + var psObjectInfo = (PSObject) PSSerializer.Deserialize( + System.IO.File.ReadAllText( + filePath)); + + psGetInfo = new PSGetInfo + { + AdditionalMetadata = GetProperty>(nameof(PSGetInfo.AdditionalMetadata), psObjectInfo), + Author = GetProperty(nameof(PSGetInfo.Author), psObjectInfo), + CompanyName = GetProperty(nameof(PSGetInfo.CompanyName), psObjectInfo), + Copyright = GetProperty(nameof(PSGetInfo.Copyright), psObjectInfo), + Dependencies = Utils.GetStringArray(GetProperty(nameof(PSGetInfo.Dependencies), psObjectInfo)), + Description = GetProperty(nameof(PSGetInfo.Description), psObjectInfo), + IconUri = GetProperty(nameof(PSGetInfo.IconUri), psObjectInfo), + Includes = new PSGetInclude(GetProperty(nameof(PSGetInfo.Includes), psObjectInfo)), + InstalledDate = GetProperty(nameof(PSGetInfo.InstalledDate), psObjectInfo), + InstalledLocation = GetProperty(nameof(PSGetInfo.InstalledLocation), psObjectInfo), + LicenseUri = GetProperty(nameof(PSGetInfo.LicenseUri), psObjectInfo), + Name = GetProperty(nameof(PSGetInfo.Name), psObjectInfo), + PackageManagementProvider = GetProperty(nameof(PSGetInfo.PackageManagementProvider), psObjectInfo), + PowerShellGetFormatVersion = GetProperty(nameof(PSGetInfo.PowerShellGetFormatVersion), psObjectInfo), + ProjectUri = GetProperty(nameof(PSGetInfo.ProjectUri), psObjectInfo), + PublishedDate = GetProperty(nameof(PSGetInfo.PublishedDate), psObjectInfo), + ReleaseNotes = GetProperty(nameof(PSGetInfo.ReleaseNotes), psObjectInfo), + Repository = GetProperty(nameof(PSGetInfo.Repository), psObjectInfo), + RepositorySourceLocation = GetProperty(nameof(PSGetInfo.RepositorySourceLocation), psObjectInfo), + Tags = Utils.GetStringArray(GetProperty(nameof(PSGetInfo.Tags), psObjectInfo)), + Type = GetProperty(nameof(PSGetInfo.Type), psObjectInfo), + UpdatedDate = GetProperty(nameof(PSGetInfo.UpdatedDate), psObjectInfo), + Version = GetProperty(nameof(PSGetInfo.Version), psObjectInfo) + }; + + return true; + } + catch(Exception ex) + { + errorMsg = string.Format( + CultureInfo.InvariantCulture, + @"TryReadPSGetInfo: Cannot read the PowerShellGet information file with error: {0}", + ex.Message); + + return false; + } + } + + #endregion + + #region Private static methods + + private static T ConvertToType(PSObject psObject) + { + // We only convert Dictionary types. + if (typeof(T) != typeof(Dictionary)) + { + return default(T); + } + + var dict = new Dictionary(); + foreach (var prop in psObject.Properties) + { + dict.Add(prop.Name, prop.Value.ToString()); + } + + return (T)Convert.ChangeType(dict, typeof(T)); + } + + private static T GetProperty( + string Name, + PSObject psObjectInfo) + { + var val = psObjectInfo.Properties[Name]?.Value; + if (val == null) + { + return default(T); + } + + switch (val) + { + case T valType: + return valType; + + case PSObject valPSObject: + switch (valPSObject.BaseObject) + { + case T valBase: + return valBase; + + case PSCustomObject _: + // A base object of PSCustomObject means this is additional metadata + // and type T should be Dictionary. + return ConvertToType(valPSObject); + + default: + return default(T); + } + + default: + return default(T); + } + } + + #endregion + + #region Private methods + + private PSObject ConvertToCustomObject() + { + var additionalMetadata = new PSObject(); + foreach (var item in AdditionalMetadata) + { + additionalMetadata.Properties.Add(new PSNoteProperty(item.Key, item.Value)); + } + + var psObject = new PSObject(); + psObject.Properties.Add(new PSNoteProperty(nameof(AdditionalMetadata), additionalMetadata)); + psObject.Properties.Add(new PSNoteProperty(nameof(Author), Author)); + psObject.Properties.Add(new PSNoteProperty(nameof(CompanyName), CompanyName)); + psObject.Properties.Add(new PSNoteProperty(nameof(Copyright), Copyright)); + psObject.Properties.Add(new PSNoteProperty(nameof(Dependencies), Dependencies)); + psObject.Properties.Add(new PSNoteProperty(nameof(Description), Description)); + psObject.Properties.Add(new PSNoteProperty(nameof(IconUri), IconUri)); + psObject.Properties.Add(new PSNoteProperty(nameof(Includes), Includes.ConvertToHashtable())); + psObject.Properties.Add(new PSNoteProperty(nameof(InstalledDate), InstalledDate)); + psObject.Properties.Add(new PSNoteProperty(nameof(InstalledLocation), InstalledLocation)); + psObject.Properties.Add(new PSNoteProperty(nameof(LicenseUri), LicenseUri)); + psObject.Properties.Add(new PSNoteProperty(nameof(Name), Name)); + psObject.Properties.Add(new PSNoteProperty(nameof(PackageManagementProvider), PackageManagementProvider)); + psObject.Properties.Add(new PSNoteProperty(nameof(PowerShellGetFormatVersion), PowerShellGetFormatVersion)); + psObject.Properties.Add(new PSNoteProperty(nameof(ProjectUri), ProjectUri)); + psObject.Properties.Add(new PSNoteProperty(nameof(PublishedDate), PublishedDate)); + psObject.Properties.Add(new PSNoteProperty(nameof(ReleaseNotes), ReleaseNotes)); + psObject.Properties.Add(new PSNoteProperty(nameof(Repository), Repository)); + psObject.Properties.Add(new PSNoteProperty(nameof(RepositorySourceLocation), RepositorySourceLocation)); + psObject.Properties.Add(new PSNoteProperty(nameof(Tags), Tags)); + psObject.Properties.Add(new PSNoteProperty(nameof(Type), Type)); + psObject.Properties.Add(new PSNoteProperty(nameof(UpdatedDate), UpdatedDate)); + psObject.Properties.Add(new PSNoteProperty(nameof(Version), Version)); + + return psObject; + } + + #endregion } #endregion @@ -312,7 +415,7 @@ public static class TestHooks { public static PSObject ReadPSGetInfo(string filePath) { - if (Utils.TryReadPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) + if (PSGetInfo.TryReadPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) { return PSObject.AsPSObject(psGetInfo); } @@ -321,6 +424,23 @@ public static PSObject ReadPSGetInfo(string filePath) throw new PSInvalidOperationException(errorMsg); } } + + public static void WritePSGetInfo( + string filePath, + PSObject psObjectGetInfo) + { + if (psObjectGetInfo.BaseObject is PSGetInfo psGetInfo) + { + if (! psGetInfo.TryWritePSGetInfo(filePath, out string errorMsg)) + { + throw new PSInvalidOperationException(errorMsg); + } + } + else + { + throw new PSArgumentException("psObjectGetInfo argument is not a PSGetInfo type."); + } + } } #endregion diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetInfo.Tests.ps1 index d376e329b..5090b3d90 100644 --- a/test/PSGetInfo.Tests.ps1 +++ b/test/PSGetInfo.Tests.ps1 @@ -1,6 +1,8 @@ # Copyright (c) Microsoft Corporation. # Licensed under the MIT License. +Import-Module "$psscriptroot\PSGetTestUtils.psm1" -Force + $psGetMod = Get-Module -Name PowerShellGet if ((! $psGetMod) -or (($psGetMod | Select-Object Version) -lt 3.0.0)) { @@ -8,7 +10,11 @@ if ((! $psGetMod) -or (($psGetMod | Select-Object Version) -lt 3.0.0)) Import-Module -Name PowerShellGet -MinimumVersion 3.0.0 -Force } -Describe "Read PSGetModuleInfo xml file" -tags CI { +Describe "Read PSGetModuleInfo xml file" -tags 'CI' { + + BeforeAll { + $fileToRead = Join-Path -Path $PSScriptRoot -ChildPath "PSGetModuleInfo.xml" + } It "Verifies expected error with null path" { { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($null) } | Should -Throw -ErrorId 'PSInvalidOperationException' @@ -19,36 +25,30 @@ Describe "Read PSGetModuleInfo xml file" -tags CI { } It "Verifies PSGetModuleInfo.xml file is read successfully" { + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) + CheckForExpectedPSGetInfo $psGetInfo + } +} + +Describe "Write PSGetModuleInfo xml file" -tags 'CI' { + + BeforeAll { $fileToRead = Join-Path -Path $PSScriptRoot -ChildPath "PSGetModuleInfo.xml" + $fileToWrite = Join-Path -Path $TestDrive -ChildPath "PSGetModuleInfo_Write.xml" + } + + It "Verifies expected error with null path" { + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetInfo($null, $psGetInfo) } | Should -Throw -ErrorId 'PSInvalidOperationException' + } + + It "Verifies file write is successful" { $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) - # - $psGetInfo.AdditionalMetadata.Keys | Should -HaveCount 22 - $psGetInfo.AdditionalMetadata['copyright'] | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' - $psGetInfo.AdditionalMetadata['tags'] | Should -BeLike 'PSModule PSEdition_Core*' - $psGetInfo.AdditionalMetadata['ItemType'] | Should -BeExactly 'Module' - # - $psGetInfo.Author | Should -BeExactly 'Microsoft Corporation' - $psGetInfo.CompanyName | Should -BeExactly 'Microsoft Corporation' - $psGetInfo.Copyright | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' - $psGetInfo.Dependencies | Should -HaveCount 0 - $psGetInfo.Description | Should -BeLike 'This module provides a convenient way for a user to store*' - $psGetInfo.IconUri | Should -BeNullOrEmpty - $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 - $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' - $psGetInfo.InstalledDate.Year | Should -BeExactly 2021 - $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' - $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' - $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' - $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' - $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty - $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' - $psGetInfo.PublishedDate.Year | Should -BeExactly 2021 - $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty - $psGetInfo.Repository | Should -BeExactly 'PSGallery' - $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' - $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') - $psGetInfo.Type | Should -BeExactly 'Module' - $psGetInfo.UpdatedDate.Year | Should -BeExactly 1 - $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetInfo($fileToWrite, $psGetInfo) } | Should -Not -Throw + } + + It "Verifes written file can be read successfully" { + $newGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToWrite) + CheckForExpectedPSGetInfo $newGetInfo } } diff --git a/test/PSGetTestUtils.psm1 b/test/PSGetTestUtils.psm1 index a66d3452f..bfeaea5a2 100644 --- a/test/PSGetTestUtils.psm1 +++ b/test/PSGetTestUtils.psm1 @@ -445,3 +445,60 @@ $($ReleaseNotes -join "`r`n") return $PSScriptInfoString } } + +<# +Checks that provided PSGetInfo object contents match the expected data +from the test information file: PSGetModuleInfo.xml +#> +function CheckForExpectedPSGetInfo +{ + param ($psGetInfo) + + $psGetInfo.AdditionalMetadata.Keys | Should -HaveCount 22 + $psGetInfo.AdditionalMetadata['copyright'] | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' + $psGetInfo.AdditionalMetadata['description'] | Should -BeLike 'This module provides a convenient way for a user to store and retrieve secrets*' + $psGetInfo.AdditionalMetadata['requireLicenseAcceptance'] | Should -BeExactly 'False' + $psGetInfo.AdditionalMetadata['isLatestVersion'] | Should -BeExactly 'True' + $psGetInfo.AdditionalMetadata['isAbsoluteLatestVersion'] | Should -BeExactly 'True' + $psGetInfo.AdditionalMetadata['versionDownloadCount'] | Should -BeExactly '0' + $psGetInfo.AdditionalMetadata['downloadCount'] | Should -BeExactly '15034' + $psGetInfo.AdditionalMetadata['packageSize'] | Should -BeExactly '55046' + $psGetInfo.AdditionalMetadata['published'] | Should -BeExactly '3/25/2021 6:08:10 PM -07:00' + $psGetInfo.AdditionalMetadata['created'] | Should -BeExactly '3/25/2021 6:08:10 PM -07:00' + $psGetInfo.AdditionalMetadata['lastUpdated'] | Should -BeExactly '3/25/2021 6:08:10 PM -07:00' + $psGetInfo.AdditionalMetadata['tags'] | Should -BeLike 'PSModule PSEdition_Core PSCmdlet_Register-SecretVault*' + $psGetInfo.AdditionalMetadata['developmentDependency'] | Should -BeExactly 'False' + $psGetInfo.AdditionalMetadata['updated'] | Should -BeExactly '2021-03-25T18:08:10Z' + $psGetInfo.AdditionalMetadata['NormalizedVersion'] | Should -BeExactly '1.0.0' + $psGetInfo.AdditionalMetadata['Authors'] | Should -BeExactly 'Microsoft Corporation' + $psGetInfo.AdditionalMetadata['IsPrerelease'] | Should -BeExactly 'false' + $psGetInfo.AdditionalMetadata['ItemType'] | Should -BeExactly 'Module' + $psGetInfo.AdditionalMetadata['FileList'] | Should -BeLike 'Microsoft.PowerShell.SecretManagement.nuspec|Microsoft.PowerShell.SecretManagement.dll*' + $psGetInfo.AdditionalMetadata['GUID'] | Should -BeExactly 'a5c858f6-4a8e-41f1-b1ee-0ff8f6ad69d3' + $psGetInfo.AdditionalMetadata['PowerShellVersion'] | Should -BeExactly '5.1' + $psGetInfo.AdditionalMetadata['CompanyName'] | Should -BeExactly 'Microsoft Corporation' + # + $psGetInfo.Author | Should -BeExactly 'Microsoft Corporation' + $psGetInfo.CompanyName | Should -BeExactly 'Microsoft Corporation' + $psGetInfo.Copyright | Should -BeExactly '(c) Microsoft Corporation. All rights reserved.' + $psGetInfo.Dependencies | Should -HaveCount 0 + $psGetInfo.Description | Should -BeLike 'This module provides a convenient way for a user to store*' + $psGetInfo.IconUri | Should -BeNullOrEmpty + $psGetInfo.Includes.Cmdlet | Should -HaveCount 10 + $psGetInfo.Includes.Cmdlet[0] | Should -BeExactly 'Register-SecretVault' + $psGetInfo.InstalledDate.Year | Should -BeExactly 2021 + $psGetInfo.InstalledLocation | Should -BeLike 'C:\Users\*' + $psGetInfo.LicenseUri | Should -BeExactly 'https://github.com/PowerShell/SecretManagement/blob/master/LICENSE' + $psGetInfo.Name | Should -BeExactly 'Microsoft.PowerShell.SecretManagement' + $psGetInfo.PackageManagementProvider | Should -BeExactly 'NuGet' + $psGetInfo.PowerShellGetFormatVersion | Should -BeNullOrEmpty + $psGetInfo.ProjectUri | Should -BeExactly 'https://github.com/powershell/secretmanagement' + $psGetInfo.PublishedDate.Year | Should -BeExactly 2021 + $psGetInfo.ReleasedNotes | Should -BeNullOrEmpty + $psGetInfo.Repository | Should -BeExactly 'PSGallery' + $psGetInfo.RepositorySourceLocation | Should -BeExactly 'https://www.powershellgallery.com/api/v2' + $psGetInfo.Tags | Should -BeExactly @('PSModule', 'PSEdition_Core') + $psGetInfo.Type | Should -BeExactly 'Module' + $psGetInfo.UpdatedDate.Year | Should -BeExactly 1 + $psGetInfo.Version.ToString() | Should -BeExactly '1.0.0' +} From 3c687eae2c5585be694bd6473066215d36d257ec Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Thu, 22 Apr 2021 10:58:09 -0700 Subject: [PATCH 6/8] Fix Codacy issue --- src/code/Utils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index 478d17a78..fd236b6cd 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -116,7 +116,7 @@ public PSGetInclude(Hashtable includes) public Hashtable ConvertToHashtable() { - var hashtable = new Hashtable() + var hashtable = new Hashtable { { nameof(Cmdlet), Cmdlet }, { nameof(Command), Command }, From cbdf40c9415fc9cdca0f8518e656b5ee0c6ccf91 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Fri, 23 Apr 2021 13:07:06 -0700 Subject: [PATCH 7/8] Small syntax change --- src/code/Utils.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index fd236b6cd..d883af1c9 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -419,10 +419,8 @@ public static PSObject ReadPSGetInfo(string filePath) { return PSObject.AsPSObject(psGetInfo); } - else - { - throw new PSInvalidOperationException(errorMsg); - } + + throw new PSInvalidOperationException(errorMsg); } public static void WritePSGetInfo( @@ -435,11 +433,11 @@ public static void WritePSGetInfo( { throw new PSInvalidOperationException(errorMsg); } + + return; } - else - { - throw new PSArgumentException("psObjectGetInfo argument is not a PSGetInfo type."); - } + + throw new PSArgumentException("psObjectGetInfo argument is not a PSGetInfo type."); } } From 83d4fc2b0820f99c51a883e866296fb3f1fed145 Mon Sep 17 00:00:00 2001 From: Paul Higinbotham Date: Mon, 26 Apr 2021 08:48:51 -0700 Subject: [PATCH 8/8] Address CR comments --- src/code/Utils.cs | 82 +++++++++---------- ....Tests.ps1 => PSGetResourceInfo.Tests.ps1} | 16 ++-- 2 files changed, 49 insertions(+), 49 deletions(-) rename test/{PSGetInfo.Tests.ps1 => PSGetResourceInfo.Tests.ps1} (76%) diff --git a/src/code/Utils.cs b/src/code/Utils.cs index d883af1c9..440a18d86 100644 --- a/src/code/Utils.cs +++ b/src/code/Utils.cs @@ -58,9 +58,9 @@ public static string[] GetStringArray(ArrayList list) #endregion } - #region PSGetInfo classes + #region PSGetResourceInfo classes - internal sealed class PSGetInclude + internal sealed class PSGetIncludes { #region Properties @@ -98,7 +98,7 @@ internal sealed class PSGetInclude /// Value: ArrayList of Workflow name strings /// /// Hashtable of PSGet includes - public PSGetInclude(Hashtable includes) + public PSGetIncludes(Hashtable includes) { if (includes == null) { return; } @@ -149,7 +149,7 @@ private string[] GetHashTableItem( #endregion } - internal sealed class PSGetInfo + internal sealed class PSGetResourceInfo { #region Properties @@ -167,7 +167,7 @@ internal sealed class PSGetInfo public Uri IconUri { get; set; } - public PSGetInclude Includes { get; set; } + public PSGetIncludes Includes { get; set; } public DateTime InstalledDate { get; set; } @@ -204,11 +204,11 @@ internal sealed class PSGetInfo #region Public static methods /// - /// Writes the PSGetInfo properties to the specified file path as a + /// Writes the PSGetResourceInfo properties to the specified file path as a /// PowerShell serialized xml file, maintaining compatibility with /// PowerShellGet v2 file format. /// - public bool TryWritePSGetInfo( + public bool TryWrite( string filePath, out string errorMsg) { @@ -244,12 +244,12 @@ public bool TryWritePSGetInfo( } /// - /// Reads a 'PSGetModuleInfo.xml' PowerShell serialized file and returns - /// a PSGetInfo object containing the file contents. + /// Reads a PSGet resource xml (PowerShell serialized) file and returns + /// a PSGetResourceInfo object containing the file contents. /// - public static bool TryReadPSGetInfo( + public static bool TryRead( string filePath, - out PSGetInfo psGetInfo, + out PSGetResourceInfo psGetInfo, out string errorMsg) { psGetInfo = null; @@ -268,31 +268,31 @@ public static bool TryReadPSGetInfo( System.IO.File.ReadAllText( filePath)); - psGetInfo = new PSGetInfo + psGetInfo = new PSGetResourceInfo { - AdditionalMetadata = GetProperty>(nameof(PSGetInfo.AdditionalMetadata), psObjectInfo), - Author = GetProperty(nameof(PSGetInfo.Author), psObjectInfo), - CompanyName = GetProperty(nameof(PSGetInfo.CompanyName), psObjectInfo), - Copyright = GetProperty(nameof(PSGetInfo.Copyright), psObjectInfo), - Dependencies = Utils.GetStringArray(GetProperty(nameof(PSGetInfo.Dependencies), psObjectInfo)), - Description = GetProperty(nameof(PSGetInfo.Description), psObjectInfo), - IconUri = GetProperty(nameof(PSGetInfo.IconUri), psObjectInfo), - Includes = new PSGetInclude(GetProperty(nameof(PSGetInfo.Includes), psObjectInfo)), - InstalledDate = GetProperty(nameof(PSGetInfo.InstalledDate), psObjectInfo), - InstalledLocation = GetProperty(nameof(PSGetInfo.InstalledLocation), psObjectInfo), - LicenseUri = GetProperty(nameof(PSGetInfo.LicenseUri), psObjectInfo), - Name = GetProperty(nameof(PSGetInfo.Name), psObjectInfo), - PackageManagementProvider = GetProperty(nameof(PSGetInfo.PackageManagementProvider), psObjectInfo), - PowerShellGetFormatVersion = GetProperty(nameof(PSGetInfo.PowerShellGetFormatVersion), psObjectInfo), - ProjectUri = GetProperty(nameof(PSGetInfo.ProjectUri), psObjectInfo), - PublishedDate = GetProperty(nameof(PSGetInfo.PublishedDate), psObjectInfo), - ReleaseNotes = GetProperty(nameof(PSGetInfo.ReleaseNotes), psObjectInfo), - Repository = GetProperty(nameof(PSGetInfo.Repository), psObjectInfo), - RepositorySourceLocation = GetProperty(nameof(PSGetInfo.RepositorySourceLocation), psObjectInfo), - Tags = Utils.GetStringArray(GetProperty(nameof(PSGetInfo.Tags), psObjectInfo)), - Type = GetProperty(nameof(PSGetInfo.Type), psObjectInfo), - UpdatedDate = GetProperty(nameof(PSGetInfo.UpdatedDate), psObjectInfo), - Version = GetProperty(nameof(PSGetInfo.Version), psObjectInfo) + AdditionalMetadata = GetProperty>(nameof(PSGetResourceInfo.AdditionalMetadata), psObjectInfo), + Author = GetProperty(nameof(PSGetResourceInfo.Author), psObjectInfo), + CompanyName = GetProperty(nameof(PSGetResourceInfo.CompanyName), psObjectInfo), + Copyright = GetProperty(nameof(PSGetResourceInfo.Copyright), psObjectInfo), + Dependencies = Utils.GetStringArray(GetProperty(nameof(PSGetResourceInfo.Dependencies), psObjectInfo)), + Description = GetProperty(nameof(PSGetResourceInfo.Description), psObjectInfo), + IconUri = GetProperty(nameof(PSGetResourceInfo.IconUri), psObjectInfo), + Includes = new PSGetIncludes(GetProperty(nameof(PSGetResourceInfo.Includes), psObjectInfo)), + InstalledDate = GetProperty(nameof(PSGetResourceInfo.InstalledDate), psObjectInfo), + InstalledLocation = GetProperty(nameof(PSGetResourceInfo.InstalledLocation), psObjectInfo), + LicenseUri = GetProperty(nameof(PSGetResourceInfo.LicenseUri), psObjectInfo), + Name = GetProperty(nameof(PSGetResourceInfo.Name), psObjectInfo), + PackageManagementProvider = GetProperty(nameof(PSGetResourceInfo.PackageManagementProvider), psObjectInfo), + PowerShellGetFormatVersion = GetProperty(nameof(PSGetResourceInfo.PowerShellGetFormatVersion), psObjectInfo), + ProjectUri = GetProperty(nameof(PSGetResourceInfo.ProjectUri), psObjectInfo), + PublishedDate = GetProperty(nameof(PSGetResourceInfo.PublishedDate), psObjectInfo), + ReleaseNotes = GetProperty(nameof(PSGetResourceInfo.ReleaseNotes), psObjectInfo), + Repository = GetProperty(nameof(PSGetResourceInfo.Repository), psObjectInfo), + RepositorySourceLocation = GetProperty(nameof(PSGetResourceInfo.RepositorySourceLocation), psObjectInfo), + Tags = Utils.GetStringArray(GetProperty(nameof(PSGetResourceInfo.Tags), psObjectInfo)), + Type = GetProperty(nameof(PSGetResourceInfo.Type), psObjectInfo), + UpdatedDate = GetProperty(nameof(PSGetResourceInfo.UpdatedDate), psObjectInfo), + Version = GetProperty(nameof(PSGetResourceInfo.Version), psObjectInfo) }; return true; @@ -413,9 +413,9 @@ private PSObject ConvertToCustomObject() public static class TestHooks { - public static PSObject ReadPSGetInfo(string filePath) + public static PSObject ReadPSGetResourceInfo(string filePath) { - if (PSGetInfo.TryReadPSGetInfo(filePath, out PSGetInfo psGetInfo, out string errorMsg)) + if (PSGetResourceInfo.TryRead(filePath, out PSGetResourceInfo psGetInfo, out string errorMsg)) { return PSObject.AsPSObject(psGetInfo); } @@ -423,13 +423,13 @@ public static PSObject ReadPSGetInfo(string filePath) throw new PSInvalidOperationException(errorMsg); } - public static void WritePSGetInfo( + public static void WritePSGetResourceInfo( string filePath, PSObject psObjectGetInfo) { - if (psObjectGetInfo.BaseObject is PSGetInfo psGetInfo) + if (psObjectGetInfo.BaseObject is PSGetResourceInfo psGetInfo) { - if (! psGetInfo.TryWritePSGetInfo(filePath, out string errorMsg)) + if (! psGetInfo.TryWrite(filePath, out string errorMsg)) { throw new PSInvalidOperationException(errorMsg); } @@ -437,7 +437,7 @@ public static void WritePSGetInfo( return; } - throw new PSArgumentException("psObjectGetInfo argument is not a PSGetInfo type."); + throw new PSArgumentException("psObjectGetInfo argument is not a PSGetResourceInfo type."); } } diff --git a/test/PSGetInfo.Tests.ps1 b/test/PSGetResourceInfo.Tests.ps1 similarity index 76% rename from test/PSGetInfo.Tests.ps1 rename to test/PSGetResourceInfo.Tests.ps1 index 5090b3d90..023c23c18 100644 --- a/test/PSGetInfo.Tests.ps1 +++ b/test/PSGetResourceInfo.Tests.ps1 @@ -17,15 +17,15 @@ Describe "Read PSGetModuleInfo xml file" -tags 'CI' { } It "Verifies expected error with null path" { - { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($null) } | Should -Throw -ErrorId 'PSInvalidOperationException' + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo($null) } | Should -Throw -ErrorId 'PSInvalidOperationException' } It "Verifies expected error with invalid file path" { - { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo('nonePath') } | Should -Throw -ErrorId 'PSInvalidOperationException' + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo('nonePath') } | Should -Throw -ErrorId 'PSInvalidOperationException' } It "Verifies PSGetModuleInfo.xml file is read successfully" { - $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo($fileToRead) CheckForExpectedPSGetInfo $psGetInfo } } @@ -38,17 +38,17 @@ Describe "Write PSGetModuleInfo xml file" -tags 'CI' { } It "Verifies expected error with null path" { - $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) - { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetInfo($null, $psGetInfo) } | Should -Throw -ErrorId 'PSInvalidOperationException' + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo($fileToRead) + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetResourceInfo($null, $psGetInfo) } | Should -Throw -ErrorId 'PSInvalidOperationException' } It "Verifies file write is successful" { - $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToRead) - { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetInfo($fileToWrite, $psGetInfo) } | Should -Not -Throw + $psGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo($fileToRead) + { [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::WritePSGetResourceInfo($fileToWrite, $psGetInfo) } | Should -Not -Throw } It "Verifes written file can be read successfully" { - $newGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetInfo($fileToWrite) + $newGetInfo = [Microsoft.PowerShell.PowerShellGet.UtilClasses.TestHooks]::ReadPSGetResourceInfo($fileToWrite) CheckForExpectedPSGetInfo $newGetInfo } }